1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | var FOCUS_SENTINEL_CLASS = 'mdc-dom-focus-sentinel';
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | var FocusTrap = (function () {
|
32 | function FocusTrap(root, options) {
|
33 | if (options === void 0) { options = {}; }
|
34 | this.root = root;
|
35 | this.options = options;
|
36 |
|
37 | this.elFocusedBeforeTrapFocus = null;
|
38 | }
|
39 | |
40 |
|
41 |
|
42 |
|
43 | FocusTrap.prototype.trapFocus = function () {
|
44 | var focusableEls = this.getFocusableElements(this.root);
|
45 | if (focusableEls.length === 0) {
|
46 | throw new Error('FocusTrap: Element must have at least one focusable child.');
|
47 | }
|
48 | this.elFocusedBeforeTrapFocus =
|
49 | document.activeElement instanceof HTMLElement ? document.activeElement :
|
50 | null;
|
51 | this.wrapTabFocus(this.root);
|
52 | if (!this.options.skipInitialFocus) {
|
53 | this.focusInitialElement(focusableEls, this.options.initialFocusEl);
|
54 | }
|
55 | };
|
56 | |
57 |
|
58 |
|
59 |
|
60 | FocusTrap.prototype.releaseFocus = function () {
|
61 | [].slice.call(this.root.querySelectorAll("." + FOCUS_SENTINEL_CLASS))
|
62 | .forEach(function (sentinelEl) {
|
63 | sentinelEl.parentElement.removeChild(sentinelEl);
|
64 | });
|
65 | if (!this.options.skipRestoreFocus && this.elFocusedBeforeTrapFocus) {
|
66 | this.elFocusedBeforeTrapFocus.focus();
|
67 | }
|
68 | };
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | FocusTrap.prototype.wrapTabFocus = function (el) {
|
77 | var _this = this;
|
78 | var sentinelStart = this.createSentinel();
|
79 | var sentinelEnd = this.createSentinel();
|
80 | sentinelStart.addEventListener('focus', function () {
|
81 | var focusableEls = _this.getFocusableElements(el);
|
82 | if (focusableEls.length > 0) {
|
83 | focusableEls[focusableEls.length - 1].focus();
|
84 | }
|
85 | });
|
86 | sentinelEnd.addEventListener('focus', function () {
|
87 | var focusableEls = _this.getFocusableElements(el);
|
88 | if (focusableEls.length > 0) {
|
89 | focusableEls[0].focus();
|
90 | }
|
91 | });
|
92 | el.insertBefore(sentinelStart, el.children[0]);
|
93 | el.appendChild(sentinelEnd);
|
94 | };
|
95 | |
96 |
|
97 |
|
98 |
|
99 | FocusTrap.prototype.focusInitialElement = function (focusableEls, initialFocusEl) {
|
100 | var focusIndex = 0;
|
101 | if (initialFocusEl) {
|
102 | focusIndex = Math.max(focusableEls.indexOf(initialFocusEl), 0);
|
103 | }
|
104 | focusableEls[focusIndex].focus();
|
105 | };
|
106 | FocusTrap.prototype.getFocusableElements = function (root) {
|
107 | var focusableEls = [].slice.call(root.querySelectorAll('[autofocus], [tabindex], a, input, textarea, select, button'));
|
108 | return focusableEls.filter(function (el) {
|
109 | var isDisabledOrHidden = el.getAttribute('aria-disabled') === 'true' ||
|
110 | el.getAttribute('disabled') != null ||
|
111 | el.getAttribute('hidden') != null ||
|
112 | el.getAttribute('aria-hidden') === 'true';
|
113 | var isTabbableAndVisible = el.tabIndex >= 0 &&
|
114 | el.getBoundingClientRect().width > 0 &&
|
115 | !el.classList.contains(FOCUS_SENTINEL_CLASS) && !isDisabledOrHidden;
|
116 | var isProgrammaticallyHidden = false;
|
117 | if (isTabbableAndVisible) {
|
118 | var style = getComputedStyle(el);
|
119 | isProgrammaticallyHidden =
|
120 | style.display === 'none' || style.visibility === 'hidden';
|
121 | }
|
122 | return isTabbableAndVisible && !isProgrammaticallyHidden;
|
123 | });
|
124 | };
|
125 | FocusTrap.prototype.createSentinel = function () {
|
126 | var sentinel = document.createElement('div');
|
127 | sentinel.setAttribute('tabindex', '0');
|
128 |
|
129 | sentinel.setAttribute('aria-hidden', 'true');
|
130 | sentinel.classList.add(FOCUS_SENTINEL_CLASS);
|
131 | return sentinel;
|
132 | };
|
133 | return FocusTrap;
|
134 | }());
|
135 | export { FocusTrap };
|
136 |
|
\ | No newline at end of file |