Трассировка фокуса

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

Библиотека анализирует DOM и возвращает список элементов, которые фокусируются естественным образом или с помощью атрибута tabindex. Она учитывает такие свойства, как:

  • tabindex
  • видимость элемента (display: none, visibility: hidden)
  • состояние элемента (disabled, aria-hidden)

Получение tabbable-элементов

Базовая функция библиотеки — tabbable(container). Она принимает контейнер DOM и возвращает массив всех элементов, доступных для табуляции.

Пример использования:

import tabbable from 'tabbable';

const container = document.querySelector('#form-section');
const focusableElements = tabbable(container);

console.log(focusableElements);

Возвращаемый массив отсортирован в порядке визуального потока документа, что гарантирует естественный порядок табуляции для пользователя.

Учет атрибута tabindex

tabindex определяет порядок фокусировки:

  • tabindex="0" — элемент включен в обычный порядок табуляции.
  • tabindex="-1" — элемент может получить фокус программно, но не через Tab.
  • tabindex="n" (n > 0) — элемент получает приоритетную фокусировку, с меньшими числами вначале.

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

Игнорирование невидимых элементов

Элементы, которые не видны пользователю, автоматически исключаются:

  • CSS display: none
  • CSS visibility: hidden
  • Элементы, скрытые через opacity: 0 или clip-path, не исключаются автоматически, так как технически они доступны для фокуса, если это разрешено браузером.

Также библиотека учитывает aria-hidden="true", предотвращая попадание скрытых от ассистивных технологий элементов в массив tabbable.

Обработка встроенных интерактивных элементов

Список встроенных элементов, которые tabbable считает фокусируемыми по умолчанию:

  • <a> с href
  • <button>
  • <input> (все типы, кроме hidden)
  • <select>
  • <textarea>
  • <iframe>
  • <audio> и <video> с атрибутом controls

Для всех остальных элементов требуется явное указание tabindex, чтобы их включить в последовательность табуляции.

Использование с кастомными компонентами

Для сложных UI-компонентов, таких как модальные окна, кастомные селекты или карусели, tabbable позволяет:

  1. Определять текущий фокус и его место в дереве DOM.
  2. Создавать циклическую навигацию: при достижении последнего элемента фокус возвращается к первому.
  3. Временно блокировать фокус на фоне модального окна, исключая элементы вне модального контейнера.

Пример ограничения фокуса внутри модального окна:

const modal = document.querySelector('#modal');
const focusable = tabbable(modal);

let first = focusable[0];
let last = focusable[focusable.length - 1];

modal.addEventListener('keydown', (e) => {
  if (e.key === 'Tab') {
    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first.focus();
    }
  }
});

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

Tabbable использует рекурсивный обход DOM, что делает алгоритм точным, но потенциально затратным на больших страницах. Для оптимизации рекомендуется:

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

Расширенные возможности

Библиотека поддерживает дополнительные опции:

  • getShadowRoot — для работы с теневыми DOM.
  • includeContainer — если требуется, чтобы сам контейнер считался tabbable.
  • tabbableOptions — позволяет добавлять кастомные селекторы или фильтры для исключения/включения элементов.
const focusableWithCustomFilter = tabbable(container, {
  getShadowRoot: true,
  includeContainer: false,
  tabbableOptions: {
    displayCheck: 'full' // или 'none', для строгой фильтрации видимости
  }
});

Сценарии применения

  • Фокусировка внутри модальных окон и всплывающих меню
  • Списки и таблицы с динамическими элементами
  • Навигация для пользователей клавиатуры
  • Проверка доступности интерфейсов

Tabbable становится критическим инструментом для разработки доступных интерфейсов и контроля последовательности фокусировки, особенно в сложных динамических веб-приложениях.