Библиотека Tabbable предназначена для определения элементов веб-страницы, которые могут получать фокус при навигации с помощью клавиши Tab. Она особенно полезна при создании доступных интерфейсов, модальных окон, диалогов и сложных форм. Помимо определения tabbable-элементов, критически важно корректно обрабатывать события клавиатуры, такие как Escape и Enter, поскольку они напрямую влияют на пользовательский опыт и взаимодействие с интерфейсом.
Escape (Esc) Клавиша Escape традиционно используется для закрытия модальных окон, отмены текущих действий или выхода из интерактивных компонентов. В контексте tabbable-элементов это позволяет:
Enter Клавиша Enter применяется для активации элементов управления:
Для корректной обработки Escape и Enter необходимо сначала определить, какие элементы страницы являются tabbable. В библиотеке Tabbable используется функция:
import { tabbable } from 'tabbable';
const container = document.querySelector('#modal');
const focusableElements = tabbable(container);
<button>,
<input>, <textarea>,
<a href=""> и любые элементы с
tabindex="0" или положительным значением
tabindex.Фокусировка может быть ограничена контейнером, что особенно важно при работе с модальными окнами.
Escape часто используется для выхода из модального окна или диалогового компонента. Реализация может выглядеть следующим образом:
const modal = document.querySelector('#modal');
const openButton = document.querySelector('#openModal');
openButton.addEventListener('click', () => {
modal.style.display = 'block';
const elements = tabbable(modal);
if (elements.length) elements[0].focus();
});
modal.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
modal.style.display = 'none';
openButton.focus();
}
});
Особенности реализации:
Enter должен корректно активировать элемент, на котором находится фокус:
modal.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
const activeElement = document.activeElement;
if (activeElement.tagName === 'BUTTON') {
activeElement.click();
} else if (activeElement.tagName === 'INPUT' && activeElement.type === 'text') {
console.log('Текст подтверждён:', activeElement.value);
}
}
});
Особенности реализации:
document.activeElement позволяет определить
текущий tabbable-элемент.click().Локализация фокуса Все tabbable-элементы должны оставаться внутри контейнера, чтобы навигация Tab не выходила за пределы модального окна.
Обработчики на контейнере Для предотвращения
множественных слушателей клавиш рекомендуется устанавливать обработчики
keydown на контейнер, а не на каждый элемент.
Управление состоянием Escape должен возвращать фокус на элемент, инициировавший открытие окна. Enter должен учитывать текущее состояние элемента (например, disabled кнопки игнорируется).
Совместимость с Screen Reader Escape и Enter должны поддерживать стандартные сценарии использования, чтобы элементы оставались доступными для технологий чтения с экрана.
В некоторых интерфейсах Escape и Enter могут сочетаться с клавишами Shift или Ctrl для расширенной функциональности:
modal.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && event.shiftKey) {
console.log('Shift + Enter активирован');
}
if (event.key === 'Escape' && event.ctrlKey) {
console.log('Ctrl + Escape активирован');
}
});
Чтобы избежать потери фокуса при закрытии окна, часто используют паттерн:
let lastFocusedElement;
openButton.addEventListener('click', () => {
lastFocusedElement = document.activeElement;
modal.style.display = 'block';
tabbable(modal)[0].focus();
});
modal.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
modal.style.display = 'none';
lastFocusedElement.focus();
}
});
Это гарантирует, что пользователь вернётся к логическому месту взаимодействия, сохраняя контекст и непрерывность навигации.
Tabbable работает с динамически добавляемыми элементами. Если элементы появляются после открытия окна, важно обновлять список tabbable-элементов:
function updateFocusable() {
return tabbable(modal);
}
modal.addEventListener('keydown', (event) => {
const elements = updateFocusable();
if (event.key === 'Tab') {
// логика обхода фокуса
}
});
Таким образом, Escape и Enter остаются корректно привязанными к актуальным tabbable-элементам даже при изменении DOM.
Эта методология позволяет создавать интерактивные, доступные интерфейсы, где Escape и Enter полностью управляют пользовательским взаимодействием, а библиотека Tabbable обеспечивает строгий контроль над фокусом и навигацией.