Skip to content

Commit ca85261

Browse files
committed
feat(a11y): Content outside inert for assistive technology users
1 parent 8943820 commit ca85261

File tree

1 file changed

+56
-19
lines changed

1 file changed

+56
-19
lines changed

‎src/FocusLoop.vue‎

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
<template>
2-
<divclass="vue-focus-loop">
2+
<div
3+
v-if="isVisible"
4+
ref="VuefocusLoopContainer"
5+
class="vue-focus-loop"
6+
>
37
<div
48
:tabindex="getTabindex"
9+
aria-hidden="true"
510
@focus="handleFocusStart"
611
/>
712
<divref="focusLoop">
813
<slot />
914
</div>
1015
<div
1116
:tabindex="getTabindex"
17+
aria-hidden="true"
1218
@focus="handleFocusEnd"
1319
/>
1420
</div>
@@ -24,6 +30,8 @@ const focusableElementsSelector = [
2430
'[contenteditable]:not([contenteditable="false"])'
2531
].join(',')
2632
33+
let ariaHiddenElements = []
34+
2735
exportdefault{
2836
name:'FocusLoop',
2937
@@ -55,41 +63,70 @@ export default{
5563
},
5664
5765
watch:{
58-
isVisible (val){
59-
this.managePrevFocusElement(val)
60-
this.focusFirst(val)
61-
}
66+
isVisible:'init',
67+
disabled:'init'
6268
},
6369
6470
mounted (){
65-
this.managePrevFocusElement(this.isVisible)
66-
this.focusFirst(this.isVisible)
67-
},
68-
69-
beforeDestroy (){
70-
this.managePrevFocusElement(false)
71+
this.init()
7172
},
7273
7374
methods:{
74-
managePrevFocusElement (visible){
75-
if (!visible &&window.vflPrevFocusedElement){
75+
init (){
76+
this.$nextTick(() =>{
77+
constactive=this.isVisible&&!this.disabled
78+
!this.disabled&&this.focusFirst(active &&this.autoFocus)
79+
this.managePrevFocusElement(active)
80+
this.lockForSwipeScreenReader(active)
81+
if (!active){
82+
ariaHiddenElements = []
83+
}
84+
})
85+
},
86+
87+
managePrevFocusElement (active){
88+
if (!active &&window.vflPrevFocusedElement){
7689
returnwindow.vflPrevFocusedElement.focus()
7790
}
7891
window.vflPrevFocusedElement=document.activeElement
7992
},
8093
94+
getElementsToAriaHidden (focusLoopContainer){
95+
functiongetElements (element){
96+
constchildren=Array.from(element.children)
97+
children.forEach(el=>{
98+
if (el === focusLoopContainer) return
99+
if (!el.contains(focusLoopContainer)){
100+
ariaHiddenElements.push(el)
101+
return
102+
}
103+
getElements(el)
104+
})
105+
}
106+
getElements(document.body)
107+
},
108+
109+
lockForSwipeScreenReader (active=true){
110+
if (active) this.getElementsToAriaHidden(this.$refs.VuefocusLoopContainer)
111+
ariaHiddenElements.forEach(el=>{
112+
if (['SCRIPT', 'STYLE'].includes(el.nodeName) ||el.hasAttribute('aria-live')) return
113+
el.setAttribute('aria-hidden', active.toString())
114+
})
115+
},
116+
117+
focusFirst (isAutoFocus){
118+
if (isAutoFocus){
119+
constelements=this.getFocusableElements()
120+
if (elements.length) setTimeout(() => elements[0].focus(), 200)
121+
}
122+
},
123+
81124
getFocusableElements (){
82125
constfocusableElements=this.$refs.focusLoop.querySelectorAll(focusableElementsSelector)
83126
if (focusableElements &&focusableElements.length) return focusableElements
84127
return []
85128
},
86129
87-
focusFirst (visible){
88-
if (!visible &&!this.autoFocus) return
89-
constelements=this.getFocusableElements()
90-
if (elements.length) setTimeout(() => elements[0].focus(), 200)
91-
},
92-
93130
handleFocusStart (){
94131
constelements=this.getFocusableElements()
95132
if (elements.length){

0 commit comments

Comments
(0)