Библиотека Tabbable в JavaScript предназначена для точного управления фокусом на элементах веб-страницы. Она позволяет определить, какие элементы могут принимать фокус при навигации с помощью клавиши Tab, а какие следует исключить из последовательности фокусировки. Основной объект библиотеки предоставляет функции для получения списка доступных для табулирования элементов и проверки их состояния.
Ключевые моменты работы с Tabbable:
tabindex="-1" можно программно
фокусировать, но они не участвуют в обычной последовательности Tab.<input>, <button>,
<select>, <textarea>,
<a> с атрибутом href, а также любые
элементы с tabindex >= 0.display: none или visibility: hidden, а также
элементы с атрибутом disabled, исключаются из списка
tabbable.tabbable(element, options) Возвращает массив элементов, которые могут принимать фокус внутри указанного родителя.
import tabbable from 'tabbable';
const container = document.getElementById('modal');
const focusableElements = tabbable(container);
Опции позволяют учитывать или игнорировать элементы с определенными атрибутами, например:
includeContainer — включить сам контейнер в
проверку.getShadowRoot — учитывать элементы внутри Shadow
DOM.isTabbable(element) Проверяет, является ли конкретный элемент tabbable.
import { isTabbable } from 'tabbable';
const button = document.querySelector('button');
if (isTabbable(button)) {
button.focus();
}focusFirst / focusLast Удобные вспомогательные функции для установки фокуса на первый или последний tabbable-элемент в контейнере. Особенно полезны при реализации модальных окон и аккордеонов.
import { focusFirst, focusLast } from 'tabbable';
focusFirst(container); // фокус на первый tabbable элемент
focusLast(container); // фокус на последний tabbable элементПри работе с динамическим интерфейсом, например, аккордеонами или модальными окнами, важным становится управление tabbable-элементами в скрытых и видимых состояниях. Элементы, находящиеся внутри скрытых блоков, должны исключаться из последовательности фокусировки, чтобы не нарушать пользовательский опыт.
Подходы к реализации:
Изменение атрибута tabindex: При
сворачивании блока всем вложенным элементам устанавливается
tabindex="-1", чтобы исключить их из табуляции. При
разворачивании значение восстанавливается.
function setTabbableState(container, state) {
const elements = container.querySelectorAll('*');
elements.forEach(el => {
if (state) {
el.removeAttribute('tabindex');
} else {
el.setAttribute('tabindex', -1);
}
});
}Удаление элементов из DOM: Иногда элементы временно удаляются из DOM при сворачивании, что автоматически делает их недоступными для табуляции.
Использование библиотек для управления фокусом: Tabbable позволяет легко динамически проверять состояние элементов при каждом изменении видимости.
Аккордеон: Сворачиваемый блок должен скрывать все tabbable-элементы. При открытии блока следует автоматически установить фокус на первый элемент внутри.
const accordionSection = document.querySelector('#section1');
function toggleAccordion(section) {
const isOpen = section.classList.toggle('open');
if (isOpen) {
focusFirst(section);
} else {
setTabbableState(section, false);
}
}Модальные окна: При открытии модального окна фокус блокируется внутри модального контейнера, предотвращая уход Tab за его пределы. При закрытии окно освобождает фокус.
const modal = document.getElementById('modal');
const triggerButton = document.getElementById('openModal');
triggerButton.addEventListener('click', () => {
modal.style.display = 'block';
focusFirst(modal);
});
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
const tabbables = tabbable(modal);
if (e.shiftKey && document.activeElement === tabbables[0]) {
tabbables[tabbables.length - 1].focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === tabbables[tabbables.length - 1]) {
tabbables[0].focus();
e.preventDefault();
}
}
});focusFirst и
focusLast для управления фокусом при открытии/закрытии
элементов.getShadowRoot
позволяет проверять элементы внутри shadow-корней, что важно для
веб-компонентов.Управление tabbable-элементами обеспечивает корректное и предсказуемое поведение интерфейса, особенно при динамических изменениях состояния элементов. Грамотная организация фокуса повышает доступность и улучшает пользовательский опыт.