focus-trap.js
1.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Limit focus to focusable elements inside `element`
* @param {HTMLElement} element - DOM element to focus trap inside
* @return {Function} cleanup function
*/
function focusTrap(element) {
const focusableElements = getFocusableElements(element)
const firstFocusableEl = focusableElements[0]
const lastFocusableEl = focusableElements[focusableElements.length - 1]
// Wait for the case the element was not yet rendered
setTimeout(() => firstFocusableEl.focus(), 50)
/**
* Get all focusable elements inside `element`
* @param {HTMLElement} element - DOM element to focus trap inside
* @return {HTMLElement[]} List of focusable elements
*/
function getFocusableElements(element = document) {
return [
...element.querySelectorAll(
'a, button, details, input, select, textarea, [tabindex]:not([tabindex="-1"])'
),
].filter((e) => !e.hasAttribute('disabled'))
}
function handleKeyDown(e) {
const TAB = 9
const isTab = e.key.toLowerCase() === 'tab' || e.keyCode === TAB
if (!isTab) return
if (e.shiftKey) {
if (document.activeElement === firstFocusableEl) {
lastFocusableEl.focus()
e.preventDefault()
}
} else {
if (document.activeElement === lastFocusableEl) {
firstFocusableEl.focus()
e.preventDefault()
}
}
}
element.addEventListener('keydown', handleKeyDown)
return function cleanup() {
element.removeEventListener('keydown', handleKeyDown)
}
}