{"id":12114,"library":"tabbable","title":"tabbable: Identify Tabbable DOM Nodes","description":"tabbable is a JavaScript utility library designed to accurately identify and return an array of all keyboard-tabbable DOM nodes within a specified container element. It systematically determines tabbability based on standard HTML semantics (e.g., `<button>`, `<input>`, `<a>` with `href`), explicit `tabindex` attributes, and various visibility and accessibility rules. The library is actively maintained, currently at version `6.4.0`, and receives regular minor and patch updates to enhance browser compatibility, support new web standards like the `inert` attribute, and address issues in virtual DOM environments like JSDOM. Its key differentiators include a zero-dependency footprint, small bundle size, high accuracy in diverse scenarios, and optimized performance. It supports a broad range of modern desktop browsers (Chrome, Edge, Firefox, Safari, Opera), but crucially, it dropped support for Internet Explorer browsers starting with v6.0.0. The library also provides granular control over how visibility checks are performed via the `displayCheck` option, accommodating various application needs.","status":"active","version":"6.4.0","language":"javascript","source_language":"en","source_url":"https://github.com/focus-trap/tabbable","tags":["javascript","typescript"],"install":[{"cmd":"npm install tabbable","lang":"bash","label":"npm"},{"cmd":"yarn add tabbable","lang":"bash","label":"yarn"},{"cmd":"pnpm add tabbable","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"ESM is the recommended import style as shown in documentation, though CommonJS is also supported.","wrong":"const { tabbable } = require('tabbable');","symbol":"tabbable","correct":"import { tabbable } from 'tabbable';"},{"note":"Used to get all focusable elements, which includes tabbable elements and those with `tabindex=\"-1\"`.","wrong":"const focusable = require('tabbable').focusable;","symbol":"focusable","correct":"import { focusable } from 'tabbable';"},{"note":"Introduced in v6.2.0, provides the computed tab index for an element, aligning with tabbable's internal logic.","wrong":"import { getTabIndex } from 'tabbable/dist/getTabIndex';","symbol":"getTabIndex","correct":"import { getTabIndex } from 'tabbable';"}],"quickstart":{"code":"import { tabbable, focusable, getTabIndex } from 'tabbable';\n\n// Helper to create a DOM structure for testing in a browser or JSDOM environment\nfunction createTestDOM(htmlString) {\n  const container = document.createElement('div');\n  container.innerHTML = htmlString;\n  // Append to body to ensure elements are considered 'attached' for visibility checks\n  document.body.appendChild(container);\n  return container;\n}\n\nconst testHtml = `\n  <div id=\"root-container\">\n    <button id=\"btn1\">Click Me</button>\n    <input type=\"text\" placeholder=\"Enter text\" />\n    <a href=\"#\" id=\"link1\">A link</a>\n    <span tabindex=\"0\" id=\"span1\">Custom tabbable</span>\n    <div tabindex=\"-1\" id=\"div1\">Focusable but not tabbable</div>\n    <button disabled id=\"btn2\">Disabled Button</button>\n    <a href=\"#\" style=\"display: none;\" id=\"link2\">Hidden Link</a>\n    <p>Some text</p>\n    <textarea id=\"textarea1\"></textarea>\n  </div>\n`;\n\nconst containerElement = createTestDOM(testHtml);\n\nconsole.log('--- Tabbable elements ---');\nconst tabbableElements = tabbable(containerElement);\ntabbableElements.forEach(el => {\n  console.log(`Tabbable: ${el.outerHTML}, TabIndex: ${getTabIndex(el)}`);\n});\n\nconsole.log('\\n--- Focusable elements (including non-tabbable) ---');\nconst focusableElements = focusable(containerElement);\nfocusableElements.forEach(el => {\n  console.log(`Focusable: ${el.outerHTML}, TabIndex: ${getTabIndex(el)}`);\n});\n\n// Clean up the added DOM element\ndocument.body.removeChild(containerElement);","lang":"typescript","description":"Demonstrates how to find both tabbable and focusable elements within a given DOM node using the `tabbable` and `focusable` functions, and how to retrieve their `tabIndex`."},"warnings":[{"fix":"Migrate your application to modern browsers. If IE11 support is critical, you must use `tabbable` v5.x or earlier.","message":"Support for Internet Explorer (all versions) has been officially dropped. The library no longer guarantees functionality or provides fixes for IE environments.","severity":"breaking","affected_versions":">=6.0.0"},{"fix":"For full compatibility and consistent behavior, consider polyfilling the `inert` attribute or implementing custom focus management for browsers that do not natively support it. Always test in target environments.","message":"The `inert` HTML attribute, which prevents focus and interaction, is not consistently supported across all major browsers (notably Firefox and Safari as of February 2023). While `tabbable` includes checks for `inert`, its effectiveness depends on browser-level support.","severity":"gotcha","affected_versions":">=6.1.0"},{"fix":"Upgrade JSDOM to version 26 or newer to ensure accurate `inert` attribute handling. If upgrading is not feasible, be aware that manual checks for inertness or adjustments to `displayCheck` might be necessary.","message":"When running in JSDOM environments, versions prior to v26 may exhibit issues with CSS selectors related to the `inert` attribute, leading to incorrect identification of tabbable nodes. `tabbable` v6.4.0 re-enabled a CSS selector fast path for `inert`.","severity":"gotcha","affected_versions":">=6.1.1 <6.4.0"},{"fix":"Ensure the container element is attached to the document (e.g., `document.body.appendChild(container)`) when using `displayCheck='full'` or `displayCheck='non-zero-area'`. Alternatively, set `displayCheck='none'` explicitly if working with detached DOM nodes.","message":"The default `displayCheck='full'` option may inaccurately determine all nodes are hidden if the container element is not attached to the document. In such cases, `tabbable` may revert to `displayCheck='none'` behavior.","severity":"gotcha","affected_versions":">=5.3.2"},{"fix":"If supporting legacy browsers, install and include a `CSS.escape` polyfill (e.g., `npm install css.escape`).","message":"Very old browser environments might require a polyfill for the `CSS.escape` API, especially if you have radio buttons with special characters in their `name` attributes. Without it, `tabbable` might not work correctly with such elements.","severity":"gotcha","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure your JSDOM setup is current (v26+). Verify that elements within the trapped container are not inadvertently hidden or marked `inert` in a way not supported by your browser/JSDOM version. Consider adding `tabindex=\"0\"` to explicitly make an element tabbable if it should be.","cause":"This error often originates from `focus-trap` (which uses `tabbable`) when the underlying `tabbable` library cannot find any tabbable elements in a JSDOM environment or due to incorrect `inert` attribute handling.","error":"focus-trap must have at least one tabbable node in it"},{"fix":"Ensure that any DOM nodes passed to `tabbable` (or its internal functions) are attached to the document. This issue was largely fixed in `v6.1.0` to handle detached nodes more gracefully, but may still manifest in specific edge cases or older versions.","cause":"This crash occurs when a DOM node being evaluated by `tabbable` is detached from the document, causing `isHidden()` to fail during a call to `getRootNode()`.","error":"TypeError: Cannot read properties of undefined (reading 'getRootNode')"}],"ecosystem":"npm"}