![require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages](https://cdn.sanity.io/images/cgdhsj6q/production/be8ab80c8efa5907bc341c6fefe9aa20d239d890-1600x1097.png?w=400&fit=max&auto=format)
Security News
require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
@pdanpdan/vue-keyboard-trap
Advanced tools
Vue3 and Vue2 directive for keyboard navigation - roving movement and trapping inside container
Vue directive for keyboard navigation - roving movement and trapping inside container.
Works both for Vue3 and Vue2.
Source code, Issues, Discussions
yarn add @pdanpdan/vue-keyboard-trap
or
npm install @pdanpdan/vue-keyboard-trap
Can be globally registered on the App (plugin mode)
import { createApp } from 'vue';
import { VueKeyboardTrapDirectivePlugin } from '@pdanpdan/vue-keyboard-trap';
import App from './App.vue';
const app = createApp(App);
app.use(VueKeyboardTrapDirectivePlugin, {
// ...options if required
});
app.mount('#app');
or included in specific components (script)
import { defineComponent } from 'vue';
import { VueKeyboardTrapDirectiveFactory } from '@pdanpdan/vue-keyboard-trap';
const KbdTrap = VueKeyboardTrapDirectiveFactory({
// ...options if required
}).directive;
export default defineComponent({
directives: {
KbdTrap,
},
});
or included in specific components (script setup)
import { VueKeyboardTrapDirectiveFactory } from '@pdanpdan/vue-keyboard-trap';
const vKbdTrap = VueKeyboardTrapDirectiveFactory({
// ...options if required
}).directive;
The directive does not require any CSS styles to work, but for cosmetic purposes some example styles are provided in dist/styles/index.sass
.
import '@pdanpdan/vue-keyboard-trap/styles';
or (if the /styles
export is not used by your packer)
import '@pdanpdan/vue-keyboard-trap/dist/styles/index.sass';
Load the javascript from https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap@latest/dist/index.umd.js.
It will expose a global object VueKeyboardTrap
with VueKeyboardTrap.VueKeyboardTrapDirectivePlugin
and VueKeyboardTrap.VueKeyboardTrapDirectiveFactory
.
const { createApp } = Vue;
const { VueKeyboardTrapDirectivePlugin } = VueKeyboardTrap;
const app = createApp({});
app.use(VueKeyboardTrapDirectivePlugin, {
// ...options if required
});
app.mount('#q-app');
or as directive
const { createApp } = Vue;
const { VueKeyboardTrapDirectiveFactory } = VueKeyboardTrap;
const app = createApp({});
const { name, directive } = VueKeyboardTrapDirectiveFactory({
// ...options if required
});
app.directive(name, directive);
app.mount('#q-app');
If you want you can access the SASS cosmetic style from https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap@latest/dist/styles/index.sass.
name
: snake-case name of the directive (without v-
prefix) - default kbd-trap
datasetName
: camelCase name of the data-attribute
to be set on element when trap is enabled - default v${ PascalCase from name}
focusableSelector
: CSS selector for focusable elementsrovingSkipSelector
: CSS selector for elements that should not respond to roving key navigation (input, textarea, ...)gridSkipSelector
: CSS selector that will be applied in .roving.grid mode to exclude elements - must be a series of :not()
selectorsautofocusSelector
: CSS selector for the elements that should be autofocusedtrapTabIndex
: tabIndex value to be used when trap element has a tabIndex of -1 and has no tabindex
attribute (default -9999)focusableSelector
::focus,
a[href]:not([tabindex^="-"]),
area[href]:not([tabindex^="-"]),
input:not([disabled]):not([tabindex^="-"]),
select:not([disabled]):not([tabindex^="-"]),
textarea:not([disabled]):not([tabindex^="-"]),
button:not([disabled]):not([tabindex^="-"]),
iframe:not([tabindex^="-"]),
[tabindex]:not([tabindex^="-"]),
[contenteditable]:not([tabindex^="-"]):not([contenteditable="false"]),
[class*="focusable"]:not([disabled]):not([tabindex^="-"])
By default a
tags without href are not focusable - add a tabindex="0"
attribute on them to make them focusable.
This can be done for all other elements if you want them to be focusable.
rovingSkipSelector
:input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"]),
select:not([disabled]),
select:not([disabled]) *,
textarea:not([disabled]),
[contenteditable]:not([contenteditable="false"]),
[contenteditable]:not([contenteditable="false"]) *
gridSkipSelector
::not([disabled]),
:not([tabindex^="-"])
autofocusSelector
:[autofocus]:not([disabled]):not([autofocus="false"]),
[data-autofocus]:not([disabled]):not([data-autofocus="false"])
Use the value of the directive (boolean) to enable/disable it.
<div v-kbd-trap="directiveEnabled">
The modifiers are reactive so if you use render functions you can dynamically change the behaviour.
.autofocus
- autofocuses the first element that matches autofocusSelector
or (if no such element is found) the first focusable child element when the directive is mounted or enabled (only if it not covered by another element).roving
(or .roving.vertical.horizontal
) - allow roving navigation (Home, End, ArrowKeys).roving.vertical
- allow roving navigation (Home, End, ArrowUp, ArrowDown).roving.horizontal
- allow roving navigation (Home, End, ArrowLeft, ArrowRight).roving.grid
- allow roving navigation (Home, End, ArrowKeys) using dataset attrs on elements [data-${ camelCase from datasetName }-(row|col)]
; [data-${ camelCase from datasetName }-(row|col)~="*"]
is a catchall.roving
used on an element with [role="grid"]
- allow roving navigation (Home, End, ArrowKeys) using role attrs on elements [role="row|gridcell"]
.roving.tabinside
- Tab key navigates to next/prev element inside trap (by default Tab key navigates to next/prev element outside trap in roving mode).escrefocus
- refocus element that was in focus before activating the trap on Esc.escexits
- refocus a parent trap on Esc (has priority over .escrefocus
)TAB
/ SHIFT
+TAB
key
.roving
modifier is used moves to next / previous trap group or focusable element outside the current trap group.roving.tabinside
modifiers are used then move inside the trap groupESC
key
.escexits
modifier is used then refocus the last active focusable element in a parent trap group.escrefocus
modifier is used then refocus the last focusable element that was active before the current trap group got focusHOME
/ END
when .roving
modifier is used
ARROW_KEYS
when .roving
modifier is used (.roving.horizontal.vertical
is the same as .roving
)
.horizontal
modifier is used then only ARROW_LEFT
/ ARROW_RIGHT
keys can be used.vertical
modifier is used then only ARROW_UP
/ ARROW_DOWN
keys can be usedARROW_LEFT
/ ARROW_UP
move to the previous focusable element inside the trap groupARROW_RIGHT
/ ARROW_DOWN
move to the next focusable element inside the trap groupARROW_KEYS
when .roving.grid
modifiers are used or .roving
modifier on a trap element with [role="grid"]
.roving.grid
trap groupsIn order to specify the navigation pattern you must use 2 dataset attributes on the focusable elements inside the .roving
trap group:
data-v-kbd-trap-row
specifies the numeric identifier of the row the element belongs to (numbers need not be consecutive, but their natural order determines the navigation order)data-v-kbd-trap-col
specifies the numeric identifier of the column the element belongs to (numbers need not be consecutive, but their natural order determines the navigation order)Any or both attributes can have a value of *
that means that it is an alement that can be focused from elements having any coresponding (row or col) attribute.
*
for row or col is considered to belong to any row / col.roving
trap groups with [role="grid"]
In order to specify the navigation pattern you must use role attributes [role="row"]
and [role="gridcell"]
.
All focusable element must have [role="gridcell"]
and must be inside [role="row"]
elements inside [role="grid"]
trap element.
The gridcell
s will be considered inline-start aligned in every row.
The directive checks the closest parent DOM Element of the active element that has a [dir="rtl"]
or [dir="ltr
]` attribute.
If the direction is RTL the ARROW_LEFT
and ARROW_RIGHT
keys move in reverse (according to document order of the focusable elements) but consistent to the way the elements are order on screen.
The directive does not require any styles, but it might help the users to have visual hints for navigation.
A default style is provided in dist/styles/index.sass
(can be imported as import from '@pdapdan/vue-keyboard-trap/styles'
, as import from '@pdapdan/vue-keyboard-trap/dist/styles/index.sass'
(if the packer does not use the /styles
export) or included from https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap@latest/dist/styles/index.sass).
There are 3 CSS variables that can be used to customize the aspect of the hints:
--color-v-kbd-trap-enabled
- the text color when directive is enabled--color-v-kbd-trap-disabled
- the text color when directive is disabled--color-v-kbd-trap-background
- the background color of the hint areaIn the default style the hint is positioned on the top-right corner of the trap group.
// defaults
$ColorVKeyboardTrapEnabled: #c33 !default
$ColorVKeyboardTrapDisabled: #999 !default
$ColorVKeyboardTrapBackground: #eeee !default
// place your custom colors on any element and they will be applied on children
// :root
// --color-v-kbd-trap-enabled: #c33
// --color-v-kbd-trap-disabled: #999
// --color-v-kbd-trap-background: #eeee
[data-v-kbd-trap]:after
content: var(--v-kbd-trap, '') var(--v-kbd-trap-esc, '') var(--v-kbd-trap-tab, '') var(--v-kbd-trap-roving, '')
pointer-events: none
position: absolute
top: 2px
right: 2px
font: italic small-caps bold 14px monospace
line-height: 1em
padding: 4px
color: var(--color-v-kbd-trap-disabled, $ColorVKeyboardTrapDisabled)
background-color: var(--color-v-kbd-trap-background, $ColorVKeyboardTrapBackground)
border-radius: 2px
z-index: 1
[data-v-kbd-trap]
--v-kbd-trap: 'Trap'
--v-kbd-trap-esc: ''
--v-kbd-trap-tab: ''
--v-kbd-trap-roving: ''
[data-v-kbd-trap]:focus-within
--v-kbd-trap: 'Trap/'
--v-kbd-trap-esc: 'Esc'
[data-v-kbd-trap-active]
--v-kbd-trap: '' !important
--v-kbd-trap-esc: 'Esc'
--v-kbd-trap-tab: '/Tab'
--v-kbd-trap-roving: ''
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"]
--v-kbd-trap-tab: '/Tab\21C5'
--v-kbd-trap-roving: '/\2962\2963\2965\2964'
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"][data-v-kbd-trap~="tabinside"]
--v-kbd-trap-tab: '/Tab'
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"][data-v-kbd-trap~="vertical"]
--v-kbd-trap-roving: '/\2963\2965'
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"][data-v-kbd-trap~="horizontal"]
--v-kbd-trap-roving: '/\2962\2964'
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"][data-v-kbd-trap~="grid"],
[data-v-kbd-trap-active][data-v-kbd-trap~="roving"][role="grid"]
--v-kbd-trap-roving: '/\229E'
[data-v-kbd-trap-active][data-v-kbd-trap~="escrefocus"]
--v-kbd-trap-esc: 'Esc\2949'
[data-v-kbd-trap-active][data-v-kbd-trap~="escexits"]
--v-kbd-trap-esc: 'Esc\2923'
[data-v-kbd-trap][tabindex="-9999"]
outline: none
[data-v-kbd-trap][data-v-kbd-trap-active]:after
color: var(--color-v-kbd-trap-enabled, $ColorVKeyboardTrapEnabled)
yarn
yarn dev
yarn lint
yarn build
Vue Keyboard Trap (vue-keyboard-trap)
Copyright © 2022-present Dan Popescu.
This application is distributed under , see LICENSE for more information.
FAQs
Vue3 and Vue2 directive for keyboard navigation - roving movement and trapping inside container
The npm package @pdanpdan/vue-keyboard-trap receives a total of 1,200 weekly downloads. As such, @pdanpdan/vue-keyboard-trap popularity was classified as popular.
We found that @pdanpdan/vue-keyboard-trap demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
Security News
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.