Библиотека Tabbable предназначена для выявления и
управления элементами веб-страницы, доступными для фокусировки с помощью
клавиши Tab. Основное преимущество использования Tabbable заключается в
точном определении интерактивных элементов, включая ссылки, кнопки, поля
ввода и кастомные компоненты, которые могут быть сделаны фокусируемыми
через атрибут tabindex.
Tabbable не полагается исключительно на стандартные селекторы CSS,
такие как :focusable, а учитывает следующие факторы:
display: none,
visibility: hidden, opacity: 0)disabled)tabindex и его значение<a> с href,
<button>, <input> и т.д.)Для интеграции Tabbable используется функция tabbable()
из пакета:
import { tabbable } from 'tabbable';
const container = document.querySelector('#form-container');
const focusableElements = tabbable(container);
console.log(focusableElements);
Пояснения к коду:
tabbable(container) возвращает массив элементов,
которые доступны для навигации через Tab в указанном контейнере.tabindex="-1" не включаются, так как они
исключены из стандартного порядка табуляции.Tabbable особенно полезна при динамическом управлении интерфейсом, когда доступность элементов зависит от состояния приложения. Пример: включение и отключение определённых полей формы на основе выбранного значения радиокнопок.
const radioButtons = document.querySelectorAll('input[name="mode"]');
const formFields = document.querySelector('#dynamic-fields');
radioButtons.forEach(radio => {
radio.addEventListener('change', () => {
const activeElements = tabbable(formFields);
activeElements.forEach(el => el.disabled = true);
if (radio.value === 'advanced') {
const advancedInputs = formFields.querySelectorAll('.advanced');
advancedInputs.forEach(el => el.disabled = false);
}
});
});
Ключевые моменты:
disabled позволяет
исключать или включать элементы в последовательность табуляции без
изменения DOM-структуры.В дополнение к стандартной табуляции, можно использовать фильтры, чтобы исключать или включать элементы на основе кастомных критериев:
const focusableInputs = tabbable(formFields).filter(el => !el.classList.contains('readonly'));
Пояснения:
filter позволяет применять бизнес-логику к массиву
элементов, возвращаемых Tabbable.Tabbable хорошо сочетается с современными фреймворками (React, Vue, Svelte), где структура DOM может изменяться динамически. Основные принципы интеграции:
querySelector для получения DOM-элемента.tabindex или
disabled.Пример с динамическим компонентом React:
import { useEffect, useRef, useState } from 'react';
import { tabbable } from 'tabbable';
function DynamicForm({ mode }) {
const containerRef = useRef(null);
useEffect(() => {
const elements = tabbable(containerRef.current);
elements.forEach(el => {
if (mode === 'basic') {
el.disabled = el.classList.contains('advanced');
} else {
el.disabled = false;
}
});
}, [mode]);
return (
<div ref={containerRef}>
<input className="basic" />
<input className="advanced" />
</div>
);
}
Для интерфейсов с модальными окнами, аккордеонами или вкладками Tabbable позволяет реализовать:
Пример локального цикла в модальном окне:
const modal = document.querySelector('#modal');
const focusableInModal = tabbable(modal);
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
const first = focusableInModal[0];
const last = focusableInModal[focusableInModal.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
});
Особенности реализации:
tabbable(modal) после изменений DOM.MutationObserver:
позволяет автоматически пересчитывать элементы при изменении DOM.disabled и tabindex, а не удалять элементы,
чтобы сохранить целостность интерфейса.Tabbable обеспечивает надежный контроль над навигацией с клавиатуры и идеально подходит для интеграции с условной логикой динамических интерфейсов, где доступность элементов зависит от состояния приложения.