Библиотека Tabbable предназначена для определения элементов на веб-странице, которые могут получать фокус с помощью клавиши Tab. Это критически важно при работе с интерактивными интерфейсами, где необходимо управлять навигацией между элементами формы, кнопками, ссылками или кастомными компонентами.
Элемент считается табулируемым, если он отвечает следующим условиям:
display не
none, visibility не hidden,
размер больше нуля).disabled.<input>, <button>,
<a> с href, либо кастомный элемент с
tabindex ≥ 0.Библиотека предоставляет два основных метода:
tabbable(element) — возвращает массив всех элементов
внутри указанного контейнера, которые можно табулировать.isTabbable(element) — проверяет, можно ли конкретный
элемент выделить с помощью клавиши Tab.Эти методы учитывают как естественные элементы
формы, так и элементы с явно заданным
tabindex.
Перетаскиваемые элементы (draggable) требуют особого
внимания, так как пользователь может изменять их положение, что влияет
на логическую последовательность табуляции. В
стандартной HTML-структуре табуляция происходит по порядку DOM, что
иногда ломает UX при динамическом перемещении элементов.
opacity: 0,
display: none) — Tabbable исключает их из навигации.ghost element). Без
корректного управления tabindex такие элементы могут либо
быть недоступны для фокуса, либо блокировать табуляцию.tabindex для перетаскиваемых элементовTabbable учитывает tabindex и позволяет
управлять последовательностью фокуса независимо от DOM. Основные
подходы:
tabindex
(tabindex="1", "2", …) задаёт явный порядок
табуляции. Используется для приоритетной фокусировки.tabindex="0" делает элемент доступным
для табуляции в стандартном порядке DOM.tabindex="-1" убирает элемент из
последовательности табуляции, но позволяет программно фокусироваться
через .focus().Для динамически перемещаемых элементов рекомендуется:
tabindex="-1"
у перетаскиваемого элемента, чтобы он не мешал последовательности
табуляции.tabindex для
всех элементов контейнера в соответствии с новым визуальным
порядком.import { tabbable, isTabbable } from 'tabbable';
const container = document.querySelector('.draggable-list');
const items = Array.from(container.querySelectorAll('.draggable'));
items.forEach(item => {
item.addEventListener('dragstart', () => {
item.setAttribute('tabindex', '-1');
});
item.addEventListener('dragend', () => {
updateTabOrder(container);
});
});
function updateTabOrder(container) {
const tabbableItems = tabbable(container);
tabbableItems.forEach((el, index) => {
el.setAttribute('tabindex', '0');
});
}
В этом примере:
tabbable(container) возвращает актуальный список всех
элементов, доступных для табуляции.dragstart убирает элемент из последовательности, чтобы
избежать прерывания навигации.dragend пересчитывает порядок табуляции для всех
видимых элементов.Если перетаскиваемый элемент находится внутри нескольких вложенных
контейнеров, важно учитывать скрытие родительских
элементов. Tabbable исключает элементы, которые находятся
внутри display: none или visibility: hidden.
Это предотвращает потерю фокуса при манипуляциях с DOM, но требует
пересчёта после каждого изменения структуры.
Рекомендации:
tabbable(container) после завершения
всех DOM-операций.Функция isTabbable(element) позволяет определить, будет
ли элемент доступен для клавиши Tab после всех
манипуляций с DOM и стилями. Это особенно полезно для перетаскиваемых
компонентов, так как:
Пример использования:
if (isTabbable(draggedElement)) {
draggedElement.focus();
}
Такой подход гарантирует, что фокус попадёт только на элемент, который реально доступен пользователю.
tabindex в соответствии с новым визуальным
порядком.Использование Tabbable в сочетании с drag-and-drop позволяет сохранять интуитивную и доступную навигацию, даже при динамических изменениях интерфейса.