focus-trap 2.4.2 → 2.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +9 -1
- package/dist/focus-trap.js +363 -0
- package/dist/focus-trap.min.js +1 -0
- package/index.d.ts +2 -2
- package/index.js +4 -1
- package/package.json +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.6
|
|
4
|
+
|
|
5
|
+
- Add slight delay before moving focus to the first element in the trap.
|
|
6
|
+
This should prevent an occasional bug caused when the first element in the trap will close the trap if it picks up on the event that triggered the trap's opening.
|
|
7
|
+
|
|
8
|
+
## 2.4.5
|
|
9
|
+
|
|
10
|
+
- Fix `"main"` field in `package.json`.
|
|
11
|
+
|
|
12
|
+
## 2.4.4
|
|
13
|
+
|
|
14
|
+
- Publish UMD build so people can download it from `unpkg.com`.
|
|
15
|
+
|
|
16
|
+
## 2.4.3
|
|
17
|
+
|
|
18
|
+
- Fixed: TypeScript signature for `activate` function.
|
|
19
|
+
|
|
3
20
|
## 2.4.2
|
|
4
21
|
|
|
5
22
|
- Added: TypeScript declaration file.
|
package/README.md
CHANGED
|
@@ -34,6 +34,8 @@ For more advanced usage (e.g. focus traps within focus traps), you can also paus
|
|
|
34
34
|
npm install focus-trap
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
You can also use a UMD version published to `unpkg.com` as `dist/focus-trap.js` and `dist/focus-trap.min.js`.
|
|
38
|
+
|
|
37
39
|
## Browser Support
|
|
38
40
|
|
|
39
41
|
IE9+
|
|
@@ -62,7 +64,7 @@ Returns a new focus trap on `element`.
|
|
|
62
64
|
- **clickOutsideDeactivates** {boolean}: Default: `false`. If `true`, a click outside the focus trap will deactivate the focus trap and allow the click event to do its thing.
|
|
63
65
|
- **returnFocusOnDeactivate** {boolean}: Default: `true`. If `false`, when the trap is deactivated, focus will *not* return to the element that had focus before activation.
|
|
64
66
|
|
|
65
|
-
### focusTrap.activate()
|
|
67
|
+
### focusTrap.activate([activateOptions])
|
|
66
68
|
|
|
67
69
|
Activates the focus trap, adding various event listeners to the document.
|
|
68
70
|
|
|
@@ -76,6 +78,12 @@ If none of the above exist, an error will be thrown. You cannot have a focus tra
|
|
|
76
78
|
|
|
77
79
|
Returns the `focusTrap`.
|
|
78
80
|
|
|
81
|
+
`activateOptions`:
|
|
82
|
+
|
|
83
|
+
These options are used to override the focus trap's default behavior for this particular activation.
|
|
84
|
+
|
|
85
|
+
- **onActivate** {function | null | false}: Default: whatever you chose for `createOptions.onActivate`. `null` or `false` are the equivalent of a `noop`.
|
|
86
|
+
|
|
79
87
|
### focusTrap.deactivate([deactivateOptions])
|
|
80
88
|
|
|
81
89
|
Deactivates the focus trap.
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.focusTrap = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
2
|
+
var tabbable = require('tabbable');
|
|
3
|
+
|
|
4
|
+
var listeningFocusTrap = null;
|
|
5
|
+
|
|
6
|
+
function focusTrap(element, userOptions) {
|
|
7
|
+
var tabbableNodes = [];
|
|
8
|
+
var firstTabbableNode = null;
|
|
9
|
+
var lastTabbableNode = null;
|
|
10
|
+
var nodeFocusedBeforeActivation = null;
|
|
11
|
+
var active = false;
|
|
12
|
+
var paused = false;
|
|
13
|
+
var tabEvent = null;
|
|
14
|
+
|
|
15
|
+
var container = (typeof element === 'string')
|
|
16
|
+
? document.querySelector(element)
|
|
17
|
+
: element;
|
|
18
|
+
|
|
19
|
+
var config = userOptions || {};
|
|
20
|
+
config.returnFocusOnDeactivate = (userOptions && userOptions.returnFocusOnDeactivate !== undefined)
|
|
21
|
+
? userOptions.returnFocusOnDeactivate
|
|
22
|
+
: true;
|
|
23
|
+
config.escapeDeactivates = (userOptions && userOptions.escapeDeactivates !== undefined)
|
|
24
|
+
? userOptions.escapeDeactivates
|
|
25
|
+
: true;
|
|
26
|
+
|
|
27
|
+
var trap = {
|
|
28
|
+
activate: activate,
|
|
29
|
+
deactivate: deactivate,
|
|
30
|
+
pause: pause,
|
|
31
|
+
unpause: unpause,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return trap;
|
|
35
|
+
|
|
36
|
+
function activate(activateOptions) {
|
|
37
|
+
if (active) return;
|
|
38
|
+
|
|
39
|
+
var defaultedActivateOptions = {
|
|
40
|
+
onActivate: (activateOptions && activateOptions.onActivate !== undefined)
|
|
41
|
+
? activateOptions.onActivate
|
|
42
|
+
: config.onActivate,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
active = true;
|
|
46
|
+
paused = false;
|
|
47
|
+
nodeFocusedBeforeActivation = document.activeElement;
|
|
48
|
+
|
|
49
|
+
if (defaultedActivateOptions.onActivate) {
|
|
50
|
+
defaultedActivateOptions.onActivate();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addListeners();
|
|
54
|
+
return trap;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function deactivate(deactivateOptions) {
|
|
58
|
+
if (!active) return;
|
|
59
|
+
|
|
60
|
+
var defaultedDeactivateOptions = {
|
|
61
|
+
returnFocus: (deactivateOptions && deactivateOptions.returnFocus !== undefined)
|
|
62
|
+
? deactivateOptions.returnFocus
|
|
63
|
+
: config.returnFocusOnDeactivate,
|
|
64
|
+
onDeactivate: (deactivateOptions && deactivateOptions.onDeactivate !== undefined)
|
|
65
|
+
? deactivateOptions.onDeactivate
|
|
66
|
+
: config.onDeactivate,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
removeListeners();
|
|
70
|
+
|
|
71
|
+
if (defaultedDeactivateOptions.onDeactivate) {
|
|
72
|
+
defaultedDeactivateOptions.onDeactivate();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (defaultedDeactivateOptions.returnFocus) {
|
|
76
|
+
setTimeout(function () {
|
|
77
|
+
tryFocus(nodeFocusedBeforeActivation);
|
|
78
|
+
}, 0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
active = false;
|
|
82
|
+
paused = false;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function pause() {
|
|
87
|
+
if (paused || !active) return;
|
|
88
|
+
paused = true;
|
|
89
|
+
removeListeners();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function unpause() {
|
|
93
|
+
if (!paused || !active) return;
|
|
94
|
+
paused = false;
|
|
95
|
+
addListeners();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function addListeners() {
|
|
99
|
+
if (!active) return;
|
|
100
|
+
|
|
101
|
+
// There can be only one listening focus trap at a time
|
|
102
|
+
if (listeningFocusTrap) {
|
|
103
|
+
listeningFocusTrap.pause();
|
|
104
|
+
}
|
|
105
|
+
listeningFocusTrap = trap;
|
|
106
|
+
|
|
107
|
+
updateTabbableNodes();
|
|
108
|
+
// Ensure that the focused element doesn't capture the event that caused the focus trap activation
|
|
109
|
+
setTimeout(function () {
|
|
110
|
+
tryFocus(firstFocusNode());
|
|
111
|
+
}, 0);
|
|
112
|
+
document.addEventListener('focus', checkFocus, true);
|
|
113
|
+
document.addEventListener('click', checkClick, true);
|
|
114
|
+
document.addEventListener('mousedown', checkPointerDown, true);
|
|
115
|
+
document.addEventListener('touchstart', checkPointerDown, true);
|
|
116
|
+
document.addEventListener('keydown', checkKey, true);
|
|
117
|
+
|
|
118
|
+
return trap;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function removeListeners() {
|
|
122
|
+
if (!active || listeningFocusTrap !== trap) return;
|
|
123
|
+
|
|
124
|
+
document.removeEventListener('focus', checkFocus, true);
|
|
125
|
+
document.removeEventListener('click', checkClick, true);
|
|
126
|
+
document.removeEventListener('mousedown', checkPointerDown, true);
|
|
127
|
+
document.removeEventListener('touchstart', checkPointerDown, true);
|
|
128
|
+
document.removeEventListener('keydown', checkKey, true);
|
|
129
|
+
|
|
130
|
+
listeningFocusTrap = null;
|
|
131
|
+
|
|
132
|
+
return trap;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getNodeForOption(optionName) {
|
|
136
|
+
var optionValue = config[optionName];
|
|
137
|
+
var node = optionValue;
|
|
138
|
+
if (!optionValue) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
if (typeof optionValue === 'string') {
|
|
142
|
+
node = document.querySelector(optionValue);
|
|
143
|
+
if (!node) {
|
|
144
|
+
throw new Error('`' + optionName + '` refers to no known node');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (typeof optionValue === 'function') {
|
|
148
|
+
node = optionValue();
|
|
149
|
+
if (!node) {
|
|
150
|
+
throw new Error('`' + optionName + '` did not return a node');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return node;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function firstFocusNode() {
|
|
157
|
+
var node;
|
|
158
|
+
if (getNodeForOption('initialFocus') !== null) {
|
|
159
|
+
node = getNodeForOption('initialFocus');
|
|
160
|
+
} else if (container.contains(document.activeElement)) {
|
|
161
|
+
node = document.activeElement;
|
|
162
|
+
} else {
|
|
163
|
+
node = tabbableNodes[0] || getNodeForOption('fallbackFocus');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!node) {
|
|
167
|
+
throw new Error('You can\'t have a focus-trap without at least one focusable element');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return node;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// This needs to be done on mousedown and touchstart instead of click
|
|
174
|
+
// so that it precedes the focus event
|
|
175
|
+
function checkPointerDown(e) {
|
|
176
|
+
if (config.clickOutsideDeactivates && !container.contains(e.target)) {
|
|
177
|
+
deactivate({ returnFocus: false });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function checkClick(e) {
|
|
182
|
+
if (config.clickOutsideDeactivates) return;
|
|
183
|
+
if (container.contains(e.target)) return;
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
e.stopImmediatePropagation();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function checkFocus(e) {
|
|
189
|
+
if (container.contains(e.target)) return;
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
e.stopImmediatePropagation();
|
|
192
|
+
// Checking for a blur method here resolves a Firefox issue (#15)
|
|
193
|
+
if (typeof e.target.blur === 'function') e.target.blur();
|
|
194
|
+
|
|
195
|
+
if (tabEvent) {
|
|
196
|
+
readjustFocus(tabEvent);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function checkKey(e) {
|
|
201
|
+
if (e.key === 'Tab' || e.keyCode === 9) {
|
|
202
|
+
handleTab(e);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (config.escapeDeactivates !== false && isEscapeEvent(e)) {
|
|
206
|
+
deactivate();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function handleTab(e) {
|
|
211
|
+
updateTabbableNodes();
|
|
212
|
+
|
|
213
|
+
if (e.target.hasAttribute('tabindex') && Number(e.target.getAttribute('tabindex')) < 0) {
|
|
214
|
+
return tabEvent = e;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
var currentFocusIndex = tabbableNodes.indexOf(e.target);
|
|
219
|
+
|
|
220
|
+
if (e.shiftKey) {
|
|
221
|
+
if (e.target === firstTabbableNode || tabbableNodes.indexOf(e.target) === -1) {
|
|
222
|
+
return tryFocus(lastTabbableNode);
|
|
223
|
+
}
|
|
224
|
+
return tryFocus(tabbableNodes[currentFocusIndex - 1]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (e.target === lastTabbableNode) return tryFocus(firstTabbableNode);
|
|
228
|
+
|
|
229
|
+
tryFocus(tabbableNodes[currentFocusIndex + 1]);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function updateTabbableNodes() {
|
|
233
|
+
tabbableNodes = tabbable(container);
|
|
234
|
+
firstTabbableNode = tabbableNodes[0];
|
|
235
|
+
lastTabbableNode = tabbableNodes[tabbableNodes.length - 1];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function readjustFocus(e) {
|
|
239
|
+
if (e.shiftKey) return tryFocus(lastTabbableNode);
|
|
240
|
+
|
|
241
|
+
tryFocus(firstTabbableNode);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function isEscapeEvent(e) {
|
|
246
|
+
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function tryFocus(node) {
|
|
250
|
+
if (!node || !node.focus) return;
|
|
251
|
+
if (node === document.activeElement) return;
|
|
252
|
+
|
|
253
|
+
node.focus();
|
|
254
|
+
if (node.tagName.toLowerCase() === 'input') {
|
|
255
|
+
node.select();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = focusTrap;
|
|
260
|
+
|
|
261
|
+
},{"tabbable":2}],2:[function(require,module,exports){
|
|
262
|
+
module.exports = function(el) {
|
|
263
|
+
var basicTabbables = [];
|
|
264
|
+
var orderedTabbables = [];
|
|
265
|
+
|
|
266
|
+
// A node is "available" if
|
|
267
|
+
// - it's computed style
|
|
268
|
+
var isUnavailable = createIsUnavailable();
|
|
269
|
+
|
|
270
|
+
var candidateSelectors = [
|
|
271
|
+
'input',
|
|
272
|
+
'select',
|
|
273
|
+
'a[href]',
|
|
274
|
+
'textarea',
|
|
275
|
+
'button',
|
|
276
|
+
'[tabindex]',
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
var candidates = el.querySelectorAll(candidateSelectors);
|
|
280
|
+
|
|
281
|
+
var candidate, candidateIndex;
|
|
282
|
+
for (var i = 0, l = candidates.length; i < l; i++) {
|
|
283
|
+
candidate = candidates[i];
|
|
284
|
+
candidateIndex = candidate.tabIndex;
|
|
285
|
+
|
|
286
|
+
if (
|
|
287
|
+
candidateIndex < 0
|
|
288
|
+
|| (candidate.tagName === 'INPUT' && candidate.type === 'hidden')
|
|
289
|
+
|| candidate.disabled
|
|
290
|
+
|| isUnavailable(candidate)
|
|
291
|
+
) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (candidateIndex === 0) {
|
|
296
|
+
basicTabbables.push(candidate);
|
|
297
|
+
} else {
|
|
298
|
+
orderedTabbables.push({
|
|
299
|
+
tabIndex: candidateIndex,
|
|
300
|
+
node: candidate,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var tabbableNodes = orderedTabbables
|
|
306
|
+
.sort(function(a, b) {
|
|
307
|
+
return a.tabIndex - b.tabIndex;
|
|
308
|
+
})
|
|
309
|
+
.map(function(a) {
|
|
310
|
+
return a.node
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
Array.prototype.push.apply(tabbableNodes, basicTabbables);
|
|
314
|
+
|
|
315
|
+
return tabbableNodes;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function createIsUnavailable() {
|
|
319
|
+
// Node cache must be refreshed on every check, in case
|
|
320
|
+
// the content of the element has changed
|
|
321
|
+
var isOffCache = [];
|
|
322
|
+
|
|
323
|
+
// "off" means `display: none;`, as opposed to "hidden",
|
|
324
|
+
// which means `visibility: hidden;`. getComputedStyle
|
|
325
|
+
// accurately reflects visiblity in context but not
|
|
326
|
+
// "off" state, so we need to recursively check parents.
|
|
327
|
+
|
|
328
|
+
function isOff(node, nodeComputedStyle) {
|
|
329
|
+
if (node === document.documentElement) return false;
|
|
330
|
+
|
|
331
|
+
// Find the cached node (Array.prototype.find not available in IE9)
|
|
332
|
+
for (var i = 0, length = isOffCache.length; i < length; i++) {
|
|
333
|
+
if (isOffCache[i][0] === node) return isOffCache[i][1];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
nodeComputedStyle = nodeComputedStyle || window.getComputedStyle(node);
|
|
337
|
+
|
|
338
|
+
var result = false;
|
|
339
|
+
|
|
340
|
+
if (nodeComputedStyle.display === 'none') {
|
|
341
|
+
result = true;
|
|
342
|
+
} else if (node.parentNode) {
|
|
343
|
+
result = isOff(node.parentNode);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
isOffCache.push([node, result]);
|
|
347
|
+
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return function isUnavailable(node) {
|
|
352
|
+
if (node === document.documentElement) return false;
|
|
353
|
+
|
|
354
|
+
var computedStyle = window.getComputedStyle(node);
|
|
355
|
+
|
|
356
|
+
if (isOff(node, computedStyle)) return true;
|
|
357
|
+
|
|
358
|
+
return computedStyle.visibility === 'hidden';
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
},{}]},{},[1])(1)
|
|
363
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.focusTrap=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){var tabbable=require("tabbable");var listeningFocusTrap=null;function focusTrap(element,userOptions){var tabbableNodes=[];var firstTabbableNode=null;var lastTabbableNode=null;var nodeFocusedBeforeActivation=null;var active=false;var paused=false;var tabEvent=null;var container=typeof element==="string"?document.querySelector(element):element;var config=userOptions||{};config.returnFocusOnDeactivate=userOptions&&userOptions.returnFocusOnDeactivate!==undefined?userOptions.returnFocusOnDeactivate:true;config.escapeDeactivates=userOptions&&userOptions.escapeDeactivates!==undefined?userOptions.escapeDeactivates:true;var trap={activate:activate,deactivate:deactivate,pause:pause,unpause:unpause};return trap;function activate(activateOptions){if(active)return;var defaultedActivateOptions={onActivate:activateOptions&&activateOptions.onActivate!==undefined?activateOptions.onActivate:config.onActivate};active=true;paused=false;nodeFocusedBeforeActivation=document.activeElement;if(defaultedActivateOptions.onActivate){defaultedActivateOptions.onActivate()}addListeners();return trap}function deactivate(deactivateOptions){if(!active)return;var defaultedDeactivateOptions={returnFocus:deactivateOptions&&deactivateOptions.returnFocus!==undefined?deactivateOptions.returnFocus:config.returnFocusOnDeactivate,onDeactivate:deactivateOptions&&deactivateOptions.onDeactivate!==undefined?deactivateOptions.onDeactivate:config.onDeactivate};removeListeners();if(defaultedDeactivateOptions.onDeactivate){defaultedDeactivateOptions.onDeactivate()}if(defaultedDeactivateOptions.returnFocus){setTimeout(function(){tryFocus(nodeFocusedBeforeActivation)},0)}active=false;paused=false;return this}function pause(){if(paused||!active)return;paused=true;removeListeners()}function unpause(){if(!paused||!active)return;paused=false;addListeners()}function addListeners(){if(!active)return;if(listeningFocusTrap){listeningFocusTrap.pause()}listeningFocusTrap=trap;updateTabbableNodes();setTimeout(function(){tryFocus(firstFocusNode())},0);document.addEventListener("focus",checkFocus,true);document.addEventListener("click",checkClick,true);document.addEventListener("mousedown",checkPointerDown,true);document.addEventListener("touchstart",checkPointerDown,true);document.addEventListener("keydown",checkKey,true);return trap}function removeListeners(){if(!active||listeningFocusTrap!==trap)return;document.removeEventListener("focus",checkFocus,true);document.removeEventListener("click",checkClick,true);document.removeEventListener("mousedown",checkPointerDown,true);document.removeEventListener("touchstart",checkPointerDown,true);document.removeEventListener("keydown",checkKey,true);listeningFocusTrap=null;return trap}function getNodeForOption(optionName){var optionValue=config[optionName];var node=optionValue;if(!optionValue){return null}if(typeof optionValue==="string"){node=document.querySelector(optionValue);if(!node){throw new Error("`"+optionName+"` refers to no known node")}}if(typeof optionValue==="function"){node=optionValue();if(!node){throw new Error("`"+optionName+"` did not return a node")}}return node}function firstFocusNode(){var node;if(getNodeForOption("initialFocus")!==null){node=getNodeForOption("initialFocus")}else if(container.contains(document.activeElement)){node=document.activeElement}else{node=tabbableNodes[0]||getNodeForOption("fallbackFocus")}if(!node){throw new Error("You can't have a focus-trap without at least one focusable element")}return node}function checkPointerDown(e){if(config.clickOutsideDeactivates&&!container.contains(e.target)){deactivate({returnFocus:false})}}function checkClick(e){if(config.clickOutsideDeactivates)return;if(container.contains(e.target))return;e.preventDefault();e.stopImmediatePropagation()}function checkFocus(e){if(container.contains(e.target))return;e.preventDefault();e.stopImmediatePropagation();if(typeof e.target.blur==="function")e.target.blur();if(tabEvent){readjustFocus(tabEvent)}}function checkKey(e){if(e.key==="Tab"||e.keyCode===9){handleTab(e)}if(config.escapeDeactivates!==false&&isEscapeEvent(e)){deactivate()}}function handleTab(e){updateTabbableNodes();if(e.target.hasAttribute("tabindex")&&Number(e.target.getAttribute("tabindex"))<0){return tabEvent=e}e.preventDefault();var currentFocusIndex=tabbableNodes.indexOf(e.target);if(e.shiftKey){if(e.target===firstTabbableNode||tabbableNodes.indexOf(e.target)===-1){return tryFocus(lastTabbableNode)}return tryFocus(tabbableNodes[currentFocusIndex-1])}if(e.target===lastTabbableNode)return tryFocus(firstTabbableNode);tryFocus(tabbableNodes[currentFocusIndex+1])}function updateTabbableNodes(){tabbableNodes=tabbable(container);firstTabbableNode=tabbableNodes[0];lastTabbableNode=tabbableNodes[tabbableNodes.length-1]}function readjustFocus(e){if(e.shiftKey)return tryFocus(lastTabbableNode);tryFocus(firstTabbableNode)}}function isEscapeEvent(e){return e.key==="Escape"||e.key==="Esc"||e.keyCode===27}function tryFocus(node){if(!node||!node.focus)return;if(node===document.activeElement)return;node.focus();if(node.tagName.toLowerCase()==="input"){node.select()}}module.exports=focusTrap},{tabbable:2}],2:[function(require,module,exports){module.exports=function(el){var basicTabbables=[];var orderedTabbables=[];var isUnavailable=createIsUnavailable();var candidateSelectors=["input","select","a[href]","textarea","button","[tabindex]"];var candidates=el.querySelectorAll(candidateSelectors);var candidate,candidateIndex;for(var i=0,l=candidates.length;i<l;i++){candidate=candidates[i];candidateIndex=candidate.tabIndex;if(candidateIndex<0||candidate.tagName==="INPUT"&&candidate.type==="hidden"||candidate.disabled||isUnavailable(candidate)){continue}if(candidateIndex===0){basicTabbables.push(candidate)}else{orderedTabbables.push({tabIndex:candidateIndex,node:candidate})}}var tabbableNodes=orderedTabbables.sort(function(a,b){return a.tabIndex-b.tabIndex}).map(function(a){return a.node});Array.prototype.push.apply(tabbableNodes,basicTabbables);return tabbableNodes};function createIsUnavailable(){var isOffCache=[];function isOff(node,nodeComputedStyle){if(node===document.documentElement)return false;for(var i=0,length=isOffCache.length;i<length;i++){if(isOffCache[i][0]===node)return isOffCache[i][1]}nodeComputedStyle=nodeComputedStyle||window.getComputedStyle(node);var result=false;if(nodeComputedStyle.display==="none"){result=true}else if(node.parentNode){result=isOff(node.parentNode)}isOffCache.push([node,result]);return result}return function isUnavailable(node){if(node===document.documentElement)return false;var computedStyle=window.getComputedStyle(node);if(isOff(node,computedStyle))return true;return computedStyle.visibility==="hidden"}}},{}]},{},[1])(1)});
|
package/index.d.ts
CHANGED
|
@@ -63,8 +63,8 @@ declare module "focus-trap" {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export interface FocusTrap {
|
|
66
|
-
activate(activateOptions
|
|
67
|
-
deactivate(deactivateOptions
|
|
66
|
+
activate(activateOptions?: ActivateOptions): void;
|
|
67
|
+
deactivate(deactivateOptions?: DeactivateOptions): void;
|
|
68
68
|
pause(): void;
|
|
69
69
|
unpause(): void;
|
|
70
70
|
}
|
package/index.js
CHANGED
|
@@ -104,7 +104,10 @@ function focusTrap(element, userOptions) {
|
|
|
104
104
|
listeningFocusTrap = trap;
|
|
105
105
|
|
|
106
106
|
updateTabbableNodes();
|
|
107
|
-
|
|
107
|
+
// Ensure that the focused element doesn't capture the event that caused the focus trap activation
|
|
108
|
+
setTimeout(function () {
|
|
109
|
+
tryFocus(firstFocusNode());
|
|
110
|
+
}, 0);
|
|
108
111
|
document.addEventListener('focus', checkFocus, true);
|
|
109
112
|
document.addEventListener('click', checkClick, true);
|
|
110
113
|
document.addEventListener('mousedown', checkPointerDown, true);
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.6",
|
|
4
4
|
"description": "Trap focus within a DOM node.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "eslint .",
|
|
9
9
|
"demo-bundle": "browserify demo/js/index.js -o demo/demo-bundle.js",
|
|
10
|
+
"clean": "del-cli dist && make-dir dist",
|
|
11
|
+
"build-dev": "npm run clean && browserify index.js -s focusTrap > dist/focus-trap.js",
|
|
12
|
+
"minify": "uglifyjs dist/focus-trap.js > dist/focus-trap.min.js",
|
|
13
|
+
"build": "npm run build-dev && npm run minify",
|
|
10
14
|
"start": "budo demo/js/index.js:demo-bundle.js --dir demo --live",
|
|
11
|
-
"test": "npm run lint"
|
|
15
|
+
"test": "npm run lint",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
12
17
|
},
|
|
13
18
|
"repository": {
|
|
14
19
|
"type": "git",
|
|
@@ -34,9 +39,13 @@
|
|
|
34
39
|
"devDependencies": {
|
|
35
40
|
"browserify": "^13.3.0",
|
|
36
41
|
"budo": "^9.4.1",
|
|
37
|
-
"
|
|
42
|
+
"del-cli": "^1.1.0",
|
|
43
|
+
"eslint": "^3.13.1",
|
|
44
|
+
"make-dir-cli": "^1.0.0",
|
|
45
|
+
"uglify-js": "^3.3.22"
|
|
38
46
|
},
|
|
39
47
|
"files": [
|
|
48
|
+
"dist",
|
|
40
49
|
"index.js",
|
|
41
50
|
"index.d.ts"
|
|
42
51
|
]
|