Vue component to trap the focus within a DOM element
npm install focus-trap focus-trap-vue@legacy
npm install focus-trap focus-trap-vue
This library exports one single named export FocusTrap
and requires
focus-trap
as a peer
dependency. So you can locally import the component or declare it globally:
import { FocusTrap } from 'focus-trap-vue'
Vue.component('FocusTrap', FocusTrap)
import { FocusTrap } from 'focus-trap-vue'
createApp(App)
.component('FocusTrap', FocusTrap)
.mount('#app')
Note this documentation is for Vue 3 and some props/events might not exist in the Vue 2 version
FocusTrap
can be controlled in three different ways:
- by using the
active
Boolean prop - by using
v-model:active
(uses theactive
prop, Vue 3 only) - by calling the
activate
/deactivate
method on the component
The recommended approach is using v-model:active
and it should contain one single child:
<focus-trap v-model:active="isActive">
<modal-dialog tabindex="-1">
<p>Do you accept the cookies?</p>
<button @click="acceptCookies">Yes</button>
<button @click="isActive = false">No</button>
</modal-dialog>
</focus-trap>
When isActive
becomes true
, it activates the focus trap. By default it sets
the focus to its child, so make sure the element is a focusable element. If it's
not you wil need to give it the tabindex="-1"
attribute. You can also
customize the initial element focused. This element should be an element that
the user can interact with. For example, an input. It's a good practice to
always focus an interactable element instead of the modal container:
<focus-trap v-model:active="isActive" :initial-focus="() => $refs.nameInput">
<modal-dialog>
<p>What name do you want to use?</p>
<form @submit.prevent="setName">
<label>
New Name
<input ref="nameInput" />
</label>
<button>Change name</button>
</form>
</modal-dialog>
</focus-trap>
FocusTrap
also accepts other props:
escapeDeactivates
:boolean
returnFocusOnDeactivate
:boolean
allowOutsideClick
:boolean | ((e: MouseEvent | TouchEvent) => boolean)
clickOutsideDeactivates
:boolean | ((e: MouseEvent | TouchEvent) => boolean)
initialFocus
:string | (() => Element)
Selector or function returning an ElementfallbackFocus
:string | (() => Element)
Selector or function returning an ElementdelayInitialFocus
:boolean
tabbableOptions
:FocusTrapTabbableOptions
Options passed totabbableOptions
Please, refer to focus-trap documentation to know what they do.
FocusTrap
emits 2 events. They are in-sync with the prop active
activate
: Whenever the trap activatesdeactivate
: Whenever the trap deactivates (note it can also be deactivated by pressing Esc or clicking outside)
FocusTrap
can be used without v-model:active
. In that case, you will use the
methods and probably need to initialize the trap as deactivated, otherwise,
the focus will start as active:
<button @click="() => $refs.focusTrap.activate()">Show the modal</button>
<focus-trap :active="false" ref="focusTrap">
<modal-dialog>
<p>Hello there!</p>
<button @click="() => $refs.focusTrap.deactivate()">Okay...</button>
</modal-dialog>
</focus-trap>
Note the use of arrow functions, this is necessary because we are accessing
$refs
which are unset on first render.