Библиотека Tabbable предназначена для определения элементов на странице, доступных для навигации с помощью клавиши Tab. Она обеспечивает точное определение фокусируемых элементов, учитывая различные состояния DOM, стили и атрибуты. Основной задачей является упрощение работы с доступностью (accessibility) в веб-приложениях.
Для подключения библиотеки используется стандартный импорт:
import tabbable from 'tabbable';
Функция tabbable(container) возвращает массив всех
элементов внутри контейнера, которые могут получить фокус с помощью Tab.
Если контейнер не указан, анализируется весь документ:
const focusableElements = tabbable(document.body);
console.log(focusableElements);
Ключевое свойство — учет CSS-свойств, таких как
display: none или visibility: hidden, а также
атрибутов disabled и tabindex=-1. Элементы с
этими свойствами исключаются из возвращаемого массива.
tabbable и focusableБиблиотека предоставляет два метода:
tabbable(container) — возвращает элементы, доступные
для навигации по Tab.focusable(container) — возвращает элементы, которые
теоретически могут получить фокус, но не обязательно через Tab.Пример использования:
import { tabbable, focusable } from 'tabbable';
const allFocusable = focusable(document.body);
const tabOrder = tabbable(document.body);
focusable полезен при программной установке фокуса, а
tabbable — для управления последовательностью Tab.
Tabbable корректно работает с вложенными и скрытыми элементами, учитывая следующие нюансы:
iframe.fieldset с атрибутом disabled блокирует
все вложенные элементы.tabindexЭлементы с явным tabindex обрабатываются отдельно:
tabindex="0" — элемент включается в стандартный порядок
Tab.tabindex="-1" — элемент исключается из порядка Tab, но
остается фокусируемым программно.tabindex="1" и выше) создают
приоритет в порядке навигации.Пример сортировки элементов по tabindex:
const ordered = tabbable(document.body).sort((a, b) => {
const aIndex = a.tabIndex || 0;
const bIndex = b.tabIndex || 0;
return aIndex - bIndex;
});
Tabbable возвращает массив элементов DOM, но для сложной логики часто используется дополнительная фильтрация и обработка:
const buttons = tabbable(document.body).filter(el => el.tagName === 'BUTTON');
buttons.forEach(btn => btn.addEventListener('focus', () => console.log('Button focused')));
Это позволяет:
Для динамически изменяющихся страниц важно повторно вызывать
tabbable после добавления или удаления элементов:
function updateFocusableElements() {
return tabbable(document.querySelector('#dynamic-container'));
}
Это гарантирует актуальность массива элементов, доступных для Tab.
В тестах на доступность важно проверять:
tabindex.Пример теста с использованием Jest:
import tabbable from 'tabbable';
test('Проверка элементов, доступных по Tab', () => {
document.body.innerHTML = `
<button>Кнопка 1</button>
<div style="display:none">
<button>Скрытая кнопка</button>
</div>
<input type="text" tabindex="2">
<button tabindex="1">Кнопка с tabindex</button>
`;
const elements = tabbable(document.body);
const tags = elements.map(el => el.tagName);
expect(tags).toEqual(['BUTTON', 'BUTTON', 'INPUT']);
});
Для модальных окон важно блокировать доступ к элементам вне окна:
const modal = document.getElementById('modal');
const modalElements = tabbable(modal);
Тестирование фокусировки гарантирует, что Tab не покидает модальное окно до его закрытия.
Tabbable позволяет использовать кастомные функции для фильтрации элементов:
const focusableLinks = tabbable(document.body, {
getShadowRoot: el => el.shadowRoot
}).filter(el => el.tagName === 'A');
Это особенно полезно при работе с Web Components и Shadow DOM, где стандартные методы могут не обнаружить элементы внутри теневых корней.
Для больших DOM-структур рекомендуется:
focusable для программной установки
фокуса без полного пересчета порядка Tab.let cachedTabbable = tabbable(container);
function refreshTabbable() {
cachedTabbable = tabbable(container);
}
Tabbable является основой для:
Её интеграция с Unit тестами позволяет уверенно контролировать поведение фокуса, предотвращая ошибки навигации и повышая общую доступность веб-приложений.