Зоны сброса

Библиотека Tabbable предназначена для определения элементов на странице, которые могут получать фокус с помощью клавиши Tab. Она решает задачу идентификации всех интерактивных элементов, включая кнопки, ссылки, поля ввода, а также кастомные компоненты с установленным tabindex. Основной принцип работы заключается в фильтрации DOM-дерева для выделения элементов, доступных для навигации клавиатурой.

Ключевые функции библиотеки:

  • tabbable(container) — возвращает массив всех элементов, которые могут быть табулированы внутри указанного контейнера.
  • isTabbable(element) — проверяет, может ли конкретный элемент получить фокус через Tab.
  • focusable(element) — определяет, можно ли программно сфокусировать элемент (element.focus()), даже если он не является частью последовательности табуляции.

Критерии табулируемости

Элемент считается tabbable, если соблюдаются следующие условия:

  1. Элемент видим (offsetParent не null, display не none, visibility не hidden).

  2. Элемент не отключен (disabled отсутствует или false).

  3. Для элементов с tabindex:

    • tabindex="0" делает элемент частью естественной последовательности табуляции.
    • tabindex > 0 задаёт пользовательский порядок навигации, но его использование не рекомендуется для сложных интерфейсов из-за потенциальных проблем с предсказуемостью.
    • tabindex < 0 исключает элемент из последовательности, но оставляет его доступным для программного фокуса.

Элементы по умолчанию табулируемы:

  • <a> с атрибутом href
  • <button>, <input>, <textarea>, <select>
  • Элементы с явно установленным tabindex ≥ 0

Зоны сброса (Tabbable Zones)

Зоны сброса — это участки DOM, внутри которых можно ограничить фокусировку клавиатурой. Основное применение — модальные окна, всплывающие панели, аккордеоны, где важно, чтобы пользователь не мог случайно переместить фокус вне активной зоны.

Создание зоны сброса

Для реализации зоны сброса достаточно обернуть интерактивные элементы в контейнер и использовать tabbable для отслеживания всех табулируемых элементов:

import { tabbable } from 'tabbable';

const modal = document.querySelector('.modal');
const elements = tabbable(modal);

elements вернёт массив в порядке, в котором элементы должны получать фокус при нажатии Tab.

Перехват и циклическая навигация

Для предотвращения выхода фокуса за пределы зоны используют обработчики событий keydown:

modal.addEventListener('keydown', (e) => {
  if (e.key !== 'Tab') return;

  const elements = tabbable(modal);
  const first = elements[0];
  const last = elements[elements.length - 1];

  if (e.shiftKey && document.activeElement === first) {
    e.preventDefault();
    last.focus();
  } else if (!e.shiftKey && document.activeElement === last) {
    e.preventDefault();
    first.focus();
  }
});
  • Shift+Tab перемещает фокус в обратном порядке.
  • Без Shift фокус движется вперёд.
  • preventDefault() предотвращает стандартное поведение браузера, обеспечивая замкнутую последовательность.

Динамическое обновление зоны

В современных интерфейсах элементы могут динамически добавляться или удаляться. В таких случаях важно обновлять массив tabbable-элементов перед каждой проверкой фокуса:

function getTabbableElements(container) {
  return tabbable(container);
}

Это гарантирует, что новые элементы будут корректно включены в последовательность, а удалённые — исключены.

Взаимодействие с атрибутами ARIA

Для доступности крайне важно учитывать атрибуты ARIA, которые могут скрывать или показывать элементы:

  • aria-hidden="true" исключает элемент из tabbable-последовательности.
  • Элементы внутри скрытых контейнеров (display: none) также автоматически игнорируются.
  • Комбинация tabindex="-1" и программного фокуса позволяет управлять доступностью без изменения визуального порядка элементов.

Оптимизация производительности

При больших интерфейсах повторное вычисление tabbable-элементов может быть затратным. Рекомендуется:

  • Кешировать результаты до изменения DOM.
  • Вызывать tabbable(container) только при добавлении или удалении интерактивных элементов.
  • Ограничивать область поиска конкретным контейнером, а не документом целиком.

Расширенные сценарии

  1. Вложенные модальные окна — каждая модальная зона имеет собственный набор tabbable-элементов и свой обработчик keydown.
  2. Фокус в списках и таблицах — позволяет создать последовательность табуляции, соответствующую визуальному порядку элементов.
  3. Состояния disabled или hidden — библиотека автоматически исключает элементы из последовательности, что предотвращает ошибки навигации.

Практические рекомендации

  • Использовать tabindex="0" для кастомных кнопок и интерактивных блоков.
  • Для модальных окон и всплывающих панелей реализовать замкнутые зоны сброса.
  • Обновлять список tabbable-элементов при динамических изменениях DOM.
  • Сочетать с ARIA-атрибутами для обеспечения полной доступности интерфейса.

Эффективное применение Tabbable обеспечивает предсказуемую и безопасную навигацию с клавиатуры, предотвращает потерю фокуса и повышает доступность веб-приложений.