Comparing version 3.0.0 to 3.1.0
# Changelog | ||
## 3.1.0 | ||
- Add `focusTrap.isFocusable` and `focusTrap.isTabbable` functions. | ||
## 3.0.0 | ||
@@ -4,0 +8,0 @@ |
65
index.js
@@ -12,6 +12,7 @@ var candidateSelectors = [ | ||
]; | ||
var candidateSelector = candidateSelectors.join(','); | ||
var matches = Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; | ||
module.exports = function tabbable(el, options) { | ||
function tabbable(el, options) { | ||
options = options || {}; | ||
@@ -24,9 +25,6 @@ | ||
var untouchabilityChecker = new UntouchabilityChecker(elementDocument); | ||
var candidates = el.querySelectorAll(candidateSelectors.join(',')); | ||
var candidates = el.querySelectorAll(candidateSelector); | ||
if (options.includeContainer) { | ||
var containerIsCandidate = candidateSelectors.some(function(selector) { | ||
return matches.call(el, selector); | ||
}); | ||
if (containerIsCandidate) { | ||
if (matches.call(el, candidateSelector)) { | ||
candidates = Array.prototype.slice.apply(candidates); | ||
@@ -40,14 +38,6 @@ candidates.unshift(el); | ||
candidate = candidates[i]; | ||
candidateTabindex = getTabindex(candidate); | ||
if ( | ||
candidateTabindex < 0 | ||
|| candidate.disabled | ||
|| isHiddenInput(candidate) | ||
|| isNonTabbableRadio(candidate) | ||
|| untouchabilityChecker.isUntouchable(candidate) | ||
) { | ||
continue; | ||
} | ||
if (!isNodeMatchingSelectorTabbable(candidate, untouchabilityChecker)) continue; | ||
candidateTabindex = getTabindex(candidate); | ||
if (candidateTabindex === 0) { | ||
@@ -72,2 +62,41 @@ regularTabbables.push(candidate); | ||
tabbable.isTabbable = isTabbable; | ||
tabbable.isFocusable = isFocusable; | ||
function isNodeMatchingSelectorTabbable(node, untouchabilityChecker) { | ||
if ( | ||
!isNodeMatchingSelectorFocusable(node, untouchabilityChecker) | ||
|| isNonTabbableRadio(node) | ||
|| getTabindex(node) < 0 | ||
) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
function isTabbable(node, untouchabilityChecker) { | ||
if (!node) throw new Error('No node provided'); | ||
if (matches.call(node, candidateSelector) === false) return false; | ||
return isNodeMatchingSelectorTabbable(node, untouchabilityChecker); | ||
} | ||
function isNodeMatchingSelectorFocusable(node, untouchabilityChecker) { | ||
untouchabilityChecker = untouchabilityChecker || new UntouchabilityChecker(node.ownerDocument || node); | ||
if ( | ||
node.disabled | ||
|| isHiddenInput(node) | ||
|| untouchabilityChecker.isUntouchable(node) | ||
) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
var focusableCandidateSelector = candidateSelectors.concat('iframe').join(','); | ||
function isFocusable(node, untouchabilityChecker) { | ||
if (!node) throw new Error('No node provided'); | ||
if (matches.call(node, focusableCandidateSelector) === false) return false; | ||
return isNodeMatchingSelectorFocusable(node, untouchabilityChecker); | ||
} | ||
function getTabindex(node) { | ||
@@ -94,3 +123,3 @@ var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10); | ||
function isContentEditable(node) { | ||
return node.contentEditable === "true"; | ||
return node.contentEditable === 'true'; | ||
} | ||
@@ -173,1 +202,3 @@ | ||
} | ||
module.exports = tabbable; |
{ | ||
"name": "tabbable", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Returns an array of all tabbable DOM nodes within a containing node.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -5,8 +5,6 @@ # tabbable | ||
Returns an array of all\* tabbable DOM nodes within a containing node. | ||
Returns an array of all\* tabbable DOM nodes within a containing node. (\* "all" has some necessary caveats, which you'll learn about by reading below.) | ||
(\* "all" has some necessary caveats, which you'll learn about by reading below.) | ||
The following are considered tabbable: | ||
The array of tabbable nodes should include the following: | ||
- `<button>`s | ||
@@ -21,3 +19,3 @@ - `<input>`s | ||
Any of the above will *not* be added to the array, though, if any of the following are also true about it: | ||
Any of the above will *not* be considered tabbable, though, if any of the following are also true about it: | ||
@@ -29,7 +27,7 @@ - negative `tabindex` | ||
**If you think a node should be included in your array of tabbables *but it's not*, all you need to do is add `tabindex="0"` to deliberately include it.** (Or if it is in your array but you don't want it, you can add `tabindex="-1" to deliberately exclude it.) This will also result in more consistent cross-browser behavior. For information about why your special node might *not* be included, see ["More details"](#more-details), below. | ||
**If you think a node should be included in your array of tabbables *but it's not*, all you need to do is add `tabindex="0"` to deliberately include it.** (Or if it is in your array but you don't want it, you can add `tabindex="-1"` to deliberately exclude it.) This will also result in more consistent cross-browser behavior. For information about why your special node might *not* be included, see ["More details"](#more-details), below. | ||
## Goals | ||
- Accurate | ||
- Accurate (or, as accurate as possible & reasonable) | ||
- No dependencies | ||
@@ -57,2 +55,4 @@ - Small | ||
### tabbable | ||
``` | ||
@@ -68,9 +68,9 @@ tabbable(rootNode, [options]) | ||
### rootNode | ||
#### rootNode | ||
Type: `Node`. **Required.** | ||
### options | ||
#### options | ||
#### includeContainer | ||
##### includeContainer | ||
@@ -81,6 +81,24 @@ Type: `boolean`. Default: `false`. | ||
### tabbable.isTabbable | ||
``` | ||
tabbable.isTabbable(node) | ||
``` | ||
Returns a boolean indicating whether the provided node is considered tabbable. | ||
### tabbable.isFocusable | ||
``` | ||
tabbable.isFocusable(node) | ||
``` | ||
Returns a boolean indicating whether the provided node is considered *focusable*. | ||
All tabbable elements are focusable, but not all focusable elements are tabbable. For example, elements with `tabindex="-1"` are focusable but not tabbable. | ||
## More details | ||
- **Tabbable tries to identify elements that are reliably tabbable across (not dead) browsers.** Browsers are stupidly inconsistent in their behavior, though — especially for edge-case elements like `<object>` and `<iframe>` — so this means *some* elements that you *can* tab to in *some* browsers will be left out of the results. (To learn more about that stupid inconsistency, see this [amazing table](https://allyjs.io/data-tables/focusable.html)). To provide better consistency across browsers and ensure the elements you *want* in your tabbables list show up there, **try adding `tabindex="0"` to edge-case elements that Tabbable ignores**. | ||
- (Exemplifying the above ^^:) **The tabbability of `<iframe>`s, `<embed>`s, `<object>`s, and `<svg>`s is [inconsistent across browsers](https://allyjs.io/data-tables/focusable.html), so if you need an accurate read on one of these elements you should give it a `tabindex`. (You'll also need to pay attention to the `focusable` attribute on SVGs in IE & Edge.) | ||
- (Exemplifying the above ^^:) **The tabbability of `<iframe>`s, `<embed>`s, `<object>`s, and `<svg>`s is [inconsistent across browsers](https://allyjs.io/data-tables/focusable.html)**, so if you need an accurate read on one of these elements you should try giving it a `tabindex`. (You'll also need to pay attention to the `focusable` attribute on SVGs in IE & Edge.) But you also might *not* be able to get an accurate read — so you should avoid relying on it. | ||
- **Radio groups have some edge cases, which you can avoid by always having a `checked` one in each group** (and that is what you should usually do anyway). If there is no `checked` radio in the radio group, *all* of the radios will be considered tabbable. (Some browsers do this, otherwise don't — there's not consistency.) | ||
@@ -90,3 +108,4 @@ - If you're thinking, "Why not just use the right `querySelectorAll`?", you *may* be on to something ... but, as with most "just" statements, you're probably not. For example, a simple `querySelectorAll` approach will not figure out whether an element is *hidden*, and therefore not actually tabbable. (That said, if you do think Tabbable can be simplified or otherwise improved, I'd love to hear your idea.) | ||
- Although Tabbable tries to deal with positive tabindexes, **you should not use positive tabindexes**. Accessibility experts seem to be in (rare) unanimous and clear consent about this: rely on the order of elements in the document. | ||
- Safari on Mac OS X does not Tab to `<a>` elements by default: you have to change a setting to get the standard behavior. Tabbable does not know whether you've changed that setting or not, so it will include `<a>` elements in its list. | ||
***Feedback and contributions more than welcome!*** |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
190
105
15599
6