UNPKG

2.46 kBPlain TextView Raw
1// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.0.9
2
3export const candidateSelectors = [
4 'input',
5 'select',
6 'textarea',
7 'a[href]',
8 'button',
9 '[tabindex]',
10 'audio[controls]',
11 'video[controls]',
12 '[contenteditable]:not([contenteditable="false"])',
13];
14
15function isHidden(node: any) {
16 // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element,
17 // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well.
18 return (
19 node.offsetParent === null || getComputedStyle(node).visibility === 'hidden'
20 );
21}
22
23export function getAllTabbingElements(parentElem: any) {
24 var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(','));
25 var onlyTabbable = [];
26 for (var i = 0; i < tabbableNodes.length; i++) {
27 var node = tabbableNodes[i];
28 if (!node.disabled && getTabindex(node) > -1 && !isHidden(node)) {
29 onlyTabbable.push(node);
30 }
31 }
32 return onlyTabbable;
33}
34
35export function tabTrappingKey(event: any, parentElem: any) {
36 // check if current event keyCode is tab
37 if (!event || event.key !== 'Tab') return;
38
39 if (!parentElem || !parentElem.contains) {
40 if (process && process.env.NODE_ENV === 'development') {
41 console.warn('focus-trap-js: parent element is not defined');
42 }
43 return false;
44 }
45
46 if (!parentElem.contains(event.target)) {
47 return false;
48 }
49
50 var allTabbingElements = getAllTabbingElements(parentElem);
51 var firstFocusableElement = allTabbingElements[0];
52 var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1];
53
54 if (event.shiftKey && event.target === firstFocusableElement) {
55 lastFocusableElement.focus();
56 event.preventDefault();
57 return true;
58 } else if (!event.shiftKey && event.target === lastFocusableElement) {
59 firstFocusableElement.focus();
60 event.preventDefault();
61 return true;
62 }
63 return false;
64}
65
66function getTabindex(node: any) {
67 var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10);
68
69 if (!isNaN(tabindexAttr)) return tabindexAttr;
70 // Browsers do not return tabIndex correctly for contentEditable nodes;
71 // so if they don't have a tabindex attribute specifically set, assume it's 0.
72
73 if (isContentEditable(node)) return 0;
74 return node.tabIndex;
75}
76
77function isContentEditable(node: any) {
78 return node.getAttribute('contentEditable');
79}