fix: fix a11y for audio/video controls in dialog (#2031)
This commit is contained in:
parent
3a91ad75b8
commit
ad9609738b
58
src/routes/_components/MediaControlsFix.html
Normal file
58
src/routes/_components/MediaControlsFix.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!--
|
||||
Nasty hack for video/audio with controls in dialogs. Audio/video controls in a dialog
|
||||
can't work probably with tab focusing per aria-practices guidelines, so we add
|
||||
an extra focusable element after the video/audio element that just loops focus back around
|
||||
to the beginning of the dialog.
|
||||
Things that are impossible to fix, however:
|
||||
- Shift-Tab cycling to the last element inside the video/audio shadow
|
||||
- Pressing Esc while inside the video/audio shadow closes the dialog
|
||||
See: https://github.com/w3c/aria-practices/issues/1772
|
||||
-->
|
||||
<div class="media-controls-fix"
|
||||
on:focus="onFocus()"
|
||||
on:documentKeydown="onDocumentKeydown(event)"
|
||||
aria-hidden="true"
|
||||
tabindex="0"
|
||||
></div>
|
||||
<style>
|
||||
.media-controls-fix {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { FOCUSABLE_ELEMENTS_QUERY } from '../_thirdparty/a11y-dialog/a11y-dialog'
|
||||
import { documentKeydown } from '../_utils/events'
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
tabKeyHadShift: undefined
|
||||
}),
|
||||
events: {
|
||||
documentKeydown
|
||||
},
|
||||
methods: {
|
||||
onDocumentKeydown (event) {
|
||||
if (event.key === 'Tab') {
|
||||
this.set({ tabKeyHadShift: !!event.shiftKey })
|
||||
}
|
||||
},
|
||||
onFocus () {
|
||||
const { tabKeyHadShift } = this.get()
|
||||
const modal = document.querySelector('.modal-dialog-contents')
|
||||
if (tabKeyHadShift) { // user typed Shift-Tab
|
||||
// focus the last element in the modal dialog which is not this element.
|
||||
// This _should_ be the last element inside of the video/audio controls, but it's not possible
|
||||
// to target these shadow elements, so just target the whole video/audio controls.
|
||||
const elements = modal.querySelectorAll(FOCUSABLE_ELEMENTS_QUERY)
|
||||
elements[elements.length - 2].focus()
|
||||
} else { // user typed Tab
|
||||
// focus the first element in the modal dialog, which should be the X (close) button
|
||||
modal.querySelector(FOCUSABLE_ELEMENTS_QUERY).focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -9,6 +9,7 @@
|
|||
height={intrinsicHeight}
|
||||
ref:player
|
||||
/>
|
||||
<MediaControlsFix />
|
||||
{:elseif type === 'audio'}
|
||||
<div class="audio-player-container">
|
||||
<audio
|
||||
|
@ -19,6 +20,7 @@
|
|||
ref:player
|
||||
/>
|
||||
</div>
|
||||
<MediaControlsFix />
|
||||
{:elseif type === 'gifv'}
|
||||
<video
|
||||
class="media-fit"
|
||||
|
@ -73,6 +75,7 @@
|
|||
</style>
|
||||
<script>
|
||||
import { get } from '../../../_utils/lodash-lite'
|
||||
import MediaControlsFix from '../../MediaControlsFix.html'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
|
@ -91,6 +94,9 @@
|
|||
if (player && !player.paused) {
|
||||
player.pause()
|
||||
}
|
||||
},
|
||||
components: {
|
||||
MediaControlsFix
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// you can at least tab to the video/audio and use other controls, like space bar and left/right)
|
||||
// Original: https://unpkg.com/a11y-dialog@4.0.1/a11y-dialog.js
|
||||
|
||||
const FOCUSABLE_ELEMENTS_QUERY = 'a[href], area[href], input, select, textarea, ' +
|
||||
export const FOCUSABLE_ELEMENTS_QUERY = 'a[href], area[href], input, select, textarea, ' +
|
||||
'button, iframe, object, embed, [contenteditable], [tabindex], ' +
|
||||
'video[controls], audio[controls], summary'
|
||||
const TAB_KEY = 9
|
||||
|
|
|
@ -43,3 +43,12 @@ export function resize (node, callback) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function documentKeydown (node, callback) {
|
||||
document.addEventListener('keydown', callback)
|
||||
return {
|
||||
destroy () {
|
||||
document.removeEventListener('keydown', callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue