Библиотека Tabbable предназначена для определения
элементов страницы, на которые можно перейти с помощью клавиши
Tab. Основная цель — управление фокусом и доступностью
динамически создаваемого контента. Элементы считаются «фокусируемыми»,
если они могут принимать фокус по стандартам HTML и не имеют атрибута
tabindex="-1" или disabled.
Ключевой особенностью Tabbable является динамическое определение фокусируемых элементов, что особенно важно для SPA (Single Page Application) или страниц с динамическим рендерингом.
Установка через npm:
npm install tabbable
Импорт в проект:
import { tabbable } from 'tabbable';
Функция tabbable(root, options) принимает два аргумента:
корневой элемент DOM, в пределах которого будет выполняться поиск, и
необязательный объект настроек.
Простейший пример:
const container = document.getElementById('dynamic-container');
const focusableElements = tabbable(container);
console.log(focusableElements);
Особенности работы:
display: none или
visibility: hidden.tabindex="-1" исключаются.disabled также не включаются.Опционально можно использовать параметр
includeContainer:
const elementsIncludingContainer = tabbable(container, { includeContainer: true });
Если контейнер сам является фокусируемым, он будет добавлен в результирующий массив.
При работе с динамическим контентом важно учитывать, что элементы могут появляться после первоначального рендеринга. Tabbable выполняет живой поиск только на момент вызова функции. Для динамического обновления списка:
function updateTabbableElements() {
const container = document.getElementById('dynamic-container');
const focusable = tabbable(container);
console.log(focusable);
}
// Пример добавления нового элемента
const newButton = document.createElement('button');
newButton.textContent = 'Новый элемент';
document.getElementById('dynamic-container').appendChild(newButton);
// Обновление списка фокусируемых элементов
updateTabbableElements();
Важно: Tabbable не отслеживает DOM автоматически. Любое добавление или удаление элементов требует повторного вызова функции.
Можно управлять тем, какие элементы включать в список:
const focusable = tabbable(container, {
includeContainer: false,
displayCheck: 'full' // проверка видимости: 'full' или 'non-zero-area'
});
displayCheck: 'full' — учитываются только полностью
видимые элементы.displayCheck: 'non-zero-area' — учитываются элементы с
ненулевой областью (иногда полезно для анимаций).Эта настройка особенно важна для динамического контента, который может быть скрыт через CSS до появления на странице.
tabindextabindex играет ключевую роль в управлении порядком
табуляции:
tabindex="0" — элемент участвует в естественном порядке
табуляции.tabindex="-1" — элемент исключён из табуляции, но может
быть сфокусирован программно (element.focus()).tabindex="1" и выше — приоритетный порядок
фокусировки.При динамическом добавлении элементов стоит явно задавать
tabindex, если требуется определённый порядок.
const dynamicInput = document.createElement('input');
dynamicInput.setAttribute('tabindex', '0');
container.appendChild(dynamicInput);
После получения списка фокусируемых элементов можно управлять фокусом:
const focusable = tabbable(container);
if (focusable.length > 0) {
focusable[0].focus(); // первый элемент получает фокус
}
Для циклической навигации по динамическим элементам:
let currentIndex = 0;
function focusNext() {
const elements = tabbable(container);
currentIndex = (currentIndex + 1) % elements.length;
elements[currentIndex].focus();
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
focusNext();
}
});
Tabbable хорошо интегрируется с:
Особенно важно вызывать tabbable() после рендеринга
динамических компонентов, чтобы получить актуальный порядок табуляции. В
React или Vue это обычно делается в useEffect или
mounted хукe после изменения DOM.
При больших списках элементов важно учитывать:
displayCheck: 'non-zero-area' может
ускорить обработку для сложных UI.Примеры реальных сценариев:
Использование Tabbable обеспечивает доступность (a11y) и предотвращает потерю фокуса при изменении DOM.