Библиотека Tabbable в JavaScript предназначена для
определения элементов на странице, которые могут быть сфокусированы с
помощью клавиши Tab. Она обеспечивает корректное управление
фокусом, особенно в сложных интерфейсах с модальными окнами,
всплывающими панелями и динамическим контентом. В основе работы лежит
проверка атрибутов tabindex, видимости элементов и их
состояния disabled.
Ключевые функции библиотеки:
tabbable(element) — возвращает массив элементов внутри
переданного контейнера, на которые можно перейти с помощью
Tab.isTabbable(element) — проверяет, можно ли
сфокусироваться на конкретном элементе.focusable(element) — проверяет, является ли элемент
фокусируемым вообще (не обязательно с Tab).Эти функции позволяют точно управлять навигацией по интерфейсу, минимизируя неожиданные «прыжки» фокуса.
Использование Tabbable требует понимания его особенностей. Существуют типичные ошибки, которые приводят к проблемам с доступностью и нарушению UX:
tabindexЧастая ошибка — назначение tabindex без понимания его
влияния на порядок фокуса:
<button tabindex="5">Кнопка</button>
<input tabindex="3" type="text">
Проблемы:
tabindex, но пользовательский
опыт станет непредсказуемым.Правильный подход: использовать
tabindex="0" для включения элемента в обычный поток фокуса
и -1 для временного исключения элемента.
Вызов tabbable() на элементах с
display: none или visibility: hidden
возвращает их только при ручной модификации стиля. Пытаться фокусировать
такие элементы приводит к проблемам с UX и доступностью:
const hiddenButton = document.querySelector('#hidden');
hiddenButton.focus(); // Ошибка, элемент не видим
Антипаттерн: игнорирование проверки видимости.
Корректно: использовать tabbable() и
проверять, что массив возвращает видимые элементы.
Если на странице добавляются новые элементы, которые должны
участвовать в навигации, а список tabbable не обновляется,
Tab будет «перескакивать» через новые элементы.
const container = document.querySelector('#modal');
const tabbables = tabbable(container); // старый список
// позже добавили кнопку
container.appendChild(newButton);
// tabbables не обновлен
Последствия: элементы остаются недоступными через
клавиатуру до повторного вызова tabbable().
Решение: всегда пересчитывать tabbable-список после изменений DOM.
tabindex="-1" без явной
причиныЭлементы с tabindex="-1" исключаются из навигации через
Tab. Пытаться установить на них фокус вручную без видимой
цели ухудшает UX:
const button = document.querySelector('#btn');
button.tabIndex = -1;
button.focus(); // Пользователь не ожидает такого поведения
Правильный подход: использовать -1
только для программного управления фокусом, например, при закрытии
модального окна и возвращении фокуса на исходный элемент.
tabbable() на слишком больших
контейнерахЗапрос tabbable(document.body) на странице с большим
количеством интерактивных элементов приводит к потере
производительности и увеличению времени отклика интерфейса.
Антипаттерн: применять на глобальном уровне без необходимости.
Рекомендация: ограничивать поиск конкретными контейнерами и модальными областями, где реально нужна навигация.
react-focus-lock или
focus-trap. Tabbable может конфликтовать с ними, если не
учитывать их внутренние механизмы.tabindex напрямую через
innerHTML, что ломает работу
tabbable() и приводит к непредсказуемому порядку
фокуса.tabbable() для проверки доступных
элементов перед программной фокусировкой.tabindex, отдавая
предпочтение стандартным интерактивным элементам (button,
a[href], input).focus().Эти принципы и антипаттерны помогают сохранять управляемость фокуса, избегать ошибок с клавиатурной навигацией и обеспечивать доступность интерфейса. Tabbable служит мощным инструментом для контроля фокуса, но требует внимательного и осознанного подхода при интеграции в сложные проекты.