Библиотека Tabbable предназначена для определения
элементов на странице, доступных для навигации с помощью клавиши
Tab. Она позволяет автоматически определять, какие элементы
являются фокусируемыми, и исключать элементы, которые не должны
участвовать в последовательности табуляции. Tabbable работает с любыми
HTML-элементами, поддерживает динамически изменяемый DOM и корректно
обрабатывает скрытые или недоступные элементы.
Фокусируемыми считаются элементы, у которых:
tabindex ≥ 0.<input>,
<select>, <textarea>,
<button>, <a href>.display: none или
visibility: hidden.disabled.Пример использования базовой функции:
import { tabbable } from 'tabbable';
const container = document.getElementById('form-container');
const focusableElements = tabbable(container);
console.log(focusableElements);
// Массив всех элементов, на которые можно перейти табом
Часто возникает необходимость учитывать элементы, находящиеся вне видимой области страницы. Tabbable позволяет фильтровать такие элементы, опираясь на их видимость и размеры:
getBoundingClientRect) полностью находится за границами
окна просмотра.Пример фильтрации элементов вне viewport:
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top < window.innerHeight &&
rect.bottom > 0 &&
rect.left < window.innerWidth &&
rect.right > 0
);
}
const allFocusable = tabbable(document.body);
const visibleFocusable = allFocusable.filter(isInViewport);
console.log(visibleFocusable);
Tabbable поддерживает параметры для тонкой настройки:
includeContainer — включает сам
контейнер в список элементов, если он фокусируемый.displayCheck — функция, определяющая,
учитывать ли элемент в последовательности. Можно переопределить
стандартную проверку видимости.getShadowRoot — позволяет учитывать
элементы внутри shadow DOM.Пример использования опций:
const focusableElements = tabbable(container, {
includeContainer: true,
displayCheck: (node) => {
// Игнорировать элементы за пределами viewport
return isInViewport(node);
}
});
При изменении DOM (добавление/удаление элементов) последовательность
табуляции может меняться. Tabbable обеспечивает актуальный список
фокусируемых элементов при каждом вызове функции. Рекомендуется вызывать
tabbable() при каждом событии, влияющем на структуру
страницы:
const observer = new MutationObserver(() => {
const focusable = tabbable(document.body);
console.log(focusable);
});
observer.observe(document.body, { childList: true, subtree: true });
Для компонентов с динамическим открытием (модальные окна, выпадающие списки) важно:
Пример фокусировки модального окна:
const modal = document.getElementById('modal');
const openModalButton = document.getElementById('openModal');
openModalButton.addEventListener('click', () => {
modal.style.display = 'block';
const focusables = tabbable(modal);
if (focusables.length) focusables[0].focus();
});
position: absolute или fixed,
которые находятся за пределами viewport, по умолчанию учитываются в
табуляции. Использование кастомной функции displayCheck
позволяет исключить их.overflow: hidden или
clip-path, можно также исключить, проверяя видимую площадь
через getBoundingClientRect().При больших DOM рекомендуется:
MutationObserver для обновления только при
изменении видимого контента.Эти подходы позволяют сохранять корректную навигацию с клавиатуры даже при сложной разметке и динамических интерфейсах.
iframe и shadow DOM,
если указана функция getShadowRoot.function deepTabbable(root) {
const nodes = tabbable(root);
root.querySelectorAll('*').forEach(node => {
if (node.shadowRoot) nodes.push(...deepTabbable(node.shadowRoot));
});
return nodes;
}
getBoundingClientRect.Эти методы обеспечивают корректную и предсказуемую навигацию клавиатурой, соответствующую современным стандартам доступности.