Библиотека Tabbable в JavaScript предназначена для
определения фокусуируемых элементов на веб-странице, то есть элементов,
на которые пользователь может переместить фокус с помощью клавиши
Tab. Она работает как с элементами по умолчанию
фокусируемыми (input, button,
a[href]), так и с элементами, у которых явно задан атрибут
tabindex.
Методы библиотеки позволяют:
tabbable(container, options) Возвращает массив элементов внутри контейнера, которые можно фокусировать. Параметры:
container — DOM-элемент или документ.
options (необязательный объект):
includeContainer — включать ли сам контейнер, если он
фокусируемый.getShadowRoot — функция для обхода shadow DOM.Пример использования:
import { tabbable } from 'tabbable';
const container = document.querySelector('#form');
const focusableElements = tabbable(container);
console.log(focusableElements);
// Выведет массив элементов, доступных для Tab
true или false.import { isTabbable } from 'tabbable';
const button = document.querySelector('#submit');
if (isTabbable(button)) {
button.focus();
}
Tabbable корректно обрабатывает shadow DOM, что
важно при использовании веб-компонентов. Для этого можно использовать
опцию getShadowRoot:
const focusableInShadow = tabbable(container, {
getShadowRoot: (node) => node.shadowRoot
});
Скрытые элементы (display: none или
visibility: hidden) и элементы с атрибутом
disabled автоматически исключаются из списка. Это позволяет
строить динамические формы и диалоги без необходимости вручную
фильтровать недоступные элементы.
При создании модальных окон важно замкнуть фокус внутри модального
контейнера. Использование tabbable позволяет определить
первый и последний фокусируемые элементы и реализовать
циклический Tab:
const modal = document.querySelector('.modal');
const focusableEls = tabbable(modal);
const firstEl = focusableEls[0];
const lastEl = focusableEls[focusableEls.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstEl) {
e.preventDefault();
lastEl.focus();
} else if (!e.shiftKey && document.activeElement === lastEl) {
e.preventDefault();
firstEl.focus();
}
}
});
В формах, где элементы создаются или удаляются динамически,
tabbable позволяет обновлять порядок навигации без ручного
контроля tabindex:
const formContainer = document.querySelector('#dynamicForm');
function updateFocusable() {
const focusableEls = tabbable(formContainer);
console.log('Доступные для Tab элементы:', focusableEls);
}
Для кастомных селекторов, выпадающих списков или табов важно корректно определить доступные элементы. Tabbable помогает:
aria-hidden и другими
атрибутами доступности.Хотя Tabbable учитывает tabindex, для сложных
интерфейсов иногда нужно комбинировать его с пользовательской
логикой:
const focusableEls = tabbable(container).sort((a, b) => {
return (a.tabIndex || 0) - (b.tabIndex || 0);
});
Это позволяет гибко управлять навигацией, например, помещая кнопки действий в конец списка, но оставляя их доступными для клавиатуры.
В React, Vue или Svelte библиотека легко интегрируется через хуки или lifecycle-методы:
useEffect(() => {
const focusableEls = tabbable(containerRef.current);
focusableEls[0].focus();
}, [containerRef]);
Такой подход упрощает работу с динамическими компонентами и улучшает доступность приложений.
aria-hidden="true" не
включаются в список.tabindex="-1" доступны
только через метод focusable, но не через
tabbable.Это позволяет точно контролировать, какие элементы доступны для клавиатурной навигации, а какие — только программно.
Использование библиотеки Tabbable обеспечивает корректное управление фокусом, улучшает доступность интерфейсов и упрощает работу с динамическими компонентами в современных веб-приложениях.