В веб-разработке управление фокусом играет ключевую роль для
доступности и удобства навигации с клавиатуры. Элемент считается
фокусируемым, если на него можно переместить фокус с
помощью клавиши Tab или через программный вызов
element.focus(). Браузеры предоставляют стандартные
механизмы фокусировки, но их поведение может быть непредсказуемым при
работе с динамическим DOM, модальными окнами, выпадающими списками или
кастомными компонентами.
Фокусировка напрямую связана с порядком табуляции
(tabindex) и интерактивностью элементов
(<button>, <a href>,
<input>). Однако при сложных интерфейсах ручное
управление становится трудоемким: требуется отслеживать DOM-изменения,
скрывать элементы из таб-циклов и предотвращать потерю фокуса.
display: none или visibility: hidden остаются
в DOM, и их нужно исключать из цикла фокуса.tabindex и фокусируемость
элементов.Tabbable — это легковесная JavaScript-библиотека для
определения всех фокусируемых элементов внутри
контейнера и управления их табуляционным порядком. Основная
задача библиотеки — предоставить корректный список элементов, на которые
можно перемещать фокус, исключая скрытые или неинтерактивные узлы.
tabbable(container) — возвращает массив элементов
внутри container, которые могут быть достигнуты при
последовательной навигации клавишей Tab.isTabbable(element) — проверяет, является ли конкретный
элемент фокусируемым в текущем контексте.focusable(container) — возвращает элементы, которые
могут получить фокус, даже если они не включены в порядок табуляции
(например, с отрицательным tabindex).querySelectorAllСтандартный метод
querySelectorAll('[tabindex], input, button, a, select, textarea')
лишь формально выбирает интерактивные элементы. Библиотека
добавляет:
disabled или
aria-hidden="true".display: none,
visibility: hidden).offsetParent и
getClientRects().tabindex.import tabbable from 'tabbable';
const modal = document.querySelector('.modal');
const focusableElements = tabbable(modal);
// Перемещение фокуса на первый интерактивный элемент
if (focusableElements.length > 0) {
focusableElements[0].focus();
}
В этом примере библиотека возвращает только реально доступные
элементы, что особенно важно при работе с модальными окнами или
всплывающими компонентами. Без использования Tabbable
разработчику пришлось бы вручную проверять стили, атрибуты и состояние
элементов.
В интерактивных компонентах часто необходимо замкнуть фокус внутри определенного контейнера. Например, в модальном окне:
modal.addEventListener('keydown', (event) => {
if (event.key === 'Tab') {
const focusable = tabbable(modal);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
});
Этот паттерн обеспечивает циклическую навигацию и предотвращает уход фокуса за пределы модального окна.
tabindex="0" — элемент включен в естественный порядок
табуляции.tabindex="-1" — элемент доступен для программного
фокуса, но не включается в цикл Tab.disabled — полностью исключает элемент из
табуляции.aria-hidden="true" — учитывается библиотекой и
исключает элемент из фокусируемых.Tabbable минимизирует количество обходов DOM за
счет:
getComputedStyle и
getClientRects() только при необходимости.Библиотека легко интегрируется с React, Vue или Svelte для управления фокусом в компонентной архитектуре. Пример для React:
import { useEffect, useRef } from 'react';
import tabbable from 'tabbable';
function Modal({ isOpen }) {
const modalRef = useRef(null);
useEffect(() => {
if (isOpen && modalRef.current) {
const elements = tabbable(modalRef.current);
if (elements.length) elements[0].focus();
}
}, [isOpen]);
return (
<div ref={modalRef} className="modal">
<button>Закрыть</button>
<input type="text" />
</div>
);
}
Такой подход гарантирует, что фокус всегда корректно перемещается на первый интерактивный элемент при открытии модального окна.
Использование Tabbable:
Эти свойства делают библиотеку незаменимым инструментом при разработке сложных, интерактивных веб-компонентов.