Библиотека Tabbable предназначена для идентификации элементов в DOM, на которые пользователь может перемещаться с помощью клавиши Tab. Она используется в сценариях, где важно управление фокусом, например, при реализации модальных окон, кастомных компонентов формы и интерактивных виджетов.
Ключевой принцип работы: Tabbable не изменяет DOM и не управляет фокусом напрямую. Она анализирует элементы, учитывает их свойства и возвращает список элементов, которые могут быть доступны пользователю через клавиатуру.
Основная функция библиотеки —
tabbable(container, options):
import { tabbable } from 'tabbable';
const elements = tabbable(document.body);
container — корневой элемент, внутри которого производится поиск.
options — объект с настройками фильтрации:
includeContainer (boolean) — включать ли сам контейнер
в результат, если он tabbable.getShadowRoot (function) — функция для обхода Shadow
DOM.Функция возвращает массив элементов, упорядоченных так, как они будут перебираться при нажатии Tab.
Tabbable использует строгие правила для определения доступности элемента:
Видимость: элемент должен быть видимым
(display ≠ none, visibility ≠
hidden).
Фокусируемость: элемент поддерживает фокус:
<input>, <select>,
<textarea>, <button>,
<a href>tabindex ≥ 0Состояние: элементы не должны быть отключены
(disabled) или иметь родительский блок с
aria-hidden="true".
Порядок в массиве, возвращаемом Tabbable, соответствует естественному порядку табуляции в браузере:
tabindex.Важно учитывать, что браузеры обрабатывают tabindex
по-разному, но Tabbable обеспечивает единообразие, вычисляя порядок
программно.
// Пример сортировки по tabIndex вручную
const sorted = tabbable(document.body).sort((a, b) => {
return (a.tabIndex || 0) - (b.tabIndex || 0);
});
Для точного управления можно использовать функции фильтрации:
const focusableInputs = tabbable(document.body).filter(el => el.tagName === 'INPUT');
Опции фильтрации через options:
includeDisabled — включать элементы с
disabled.includeOnlyFocusable — исключить элементы, которые хоть
и tabbable, но не могут получать фокус программно.Фильтрация особенно полезна в сложных интерфейсах с множеством элементов управления, когда нужно ограничить область навигации клавишей Tab.
Tabbable поддерживает Shadow DOM, что важно для современных веб-компонентов.
const elements = tabbable(document.body, {
getShadowRoot: el => el.shadowRoot
});
getShadowRoot возвращает
shadowRoot для каждого элемента, если он существует.Модальное окно:
const modal = document.querySelector('.modal');
const focusable = tabbable(modal);
// Устанавливаем фокус на первый элемент
focusable[0].focus();
// Перемещение фокуса по циклу внутри модального окна
modal.addEventListener('keydown', e => {
if (e.key === 'Tab') {
const index = focusable.indexOf(document.activeElement);
const nextIndex = e.shiftKey ? index - 1 : index + 1;
e.preventDefault();
focusable[(nextIndex + focusable.length) % focusable.length].focus();
}
});
Фильтрация по типу элемента:
const onlyButt ons = tabbable(document.body).filter(el => el.tagName === 'BUTTON');
disabled.tabindex > 0 только при необходимости,
чтобы избежать нарушения естественного порядка табуляции.getShadowRoot,
чтобы корректно учитывать вложенные tabbable-элементы.Tabbable обеспечивает точное и предсказуемое управление порядком фокусировки, упрощая работу с клавиатурной навигацией и повышая доступность веб-приложений.