Кнопки навигации

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

Основной экспорт библиотеки — функция tabbable(node, options), которая возвращает массив элементов, доступных для фокусировки в пределах указанного DOM-узла. Каждый элемент в этом массиве соответствует элементу, на который можно перейти с помощью клавиши Tab, учитывая состояние disabled, visibility и tabindex.

import { tabbable } from 'tabbable';

const focusableElements = tabbable(document.body);
console.log(focusableElements);

Функция возвращает только видимые и активные элементы, автоматически исключая те, которые скрыты с помощью display: none, visibility: hidden, или имеют отрицательный tabindex, за исключением опции includeGuard, о которой речь далее.

Настройка поведения с помощью опций

Функция tabbable поддерживает объект опций:

  • includeContainer — если true, возвращает контейнер вместе с его фокусируемыми потомками, если он сам является фокусируемым элементом.
  • includeOnlyTabbable — по умолчанию true. Если установить false, будут включены элементы с отрицательным tabindex.
  • getShadowRoot — функция, позволяющая обходить Shadow DOM.
  • includeGuard — добавляет элементы-сторожи, используемые в библиотеках для управления циклом фокуса, например, при модальных окнах.

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

const focusableElements = tabbable(document.querySelector('#modal'), {
  includeContainer: true,
  includeOnlyTabbable: true
});

Интеграция с компонентами кнопок навигации

В интерфейсах с динамическими кнопками, например, каруселями или пагинацией, важно правильно управлять порядком табуляции. С библиотекой Tabbable это достигается с помощью фильтрации элементов:

const navigationContainer = document.querySelector('.pagination');
const buttons = tabbable(navigationContainer);

// Перевод фокуса на первую кнопку при открытии страницы
if (buttons.length > 0) {
  buttons[0].focus();
}

// Обработка стрелок для переключения кнопок
navigationContainer.addEventListener('keydown', (e) => {
  const currentIndex = buttons.indexOf(document.activeElement);
  if (e.key === 'ArrowRight' && currentIndex < buttons.length - 1) {
    buttons[currentIndex + 1].focus();
  } else if (e.key === 'ArrowLeft' && currentIndex > 0) {
    buttons[currentIndex - 1].focus();
  }
});

Такой подход гарантирует, что при динамическом изменении контента (например, при добавлении новых страниц) порядок фокуса остаётся корректным и соответствует ожиданиям пользователей клавиатуры.

Работа с вложенными элементами и Shadow DOM

В сложных интерфейсах элементы кнопок могут находиться внутри Shadow DOM. Библиотека Tabbable поддерживает обход таких структур через опцию getShadowRoot:

const focusableInShadow = tabbable(document.querySelector('#component'), {
  getShadowRoot: node => node.shadowRoot
});

Это позволяет создавать полностью фокусируемые интерфейсы даже при использовании Web Components.

Управление циклом фокуса

В модальных диалогах и панелях с кнопками навигации важно не допускать выхода фокуса за пределы компонента. Для этого используются элементы-сторожи:

const modal = document.querySelector('#modal');
const firstFocusable = tabbable(modal)[0];
const lastFocusable = tabbable(modal).slice(-1)[0];

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

Такой паттерн обеспечивает циклическую навигацию, что является стандартом доступности.

Практические рекомендации для кнопок навигации

  • Каждый интерактивный элемент должен быть видимым и доступным через клавишу Tab.
  • Использование отрицательных tabindex допускается только для элементов, которые не должны попадать в порядок табуляции по умолчанию.
  • Для динамически создаваемых кнопок рекомендуется пересчитывать массив фокусируемых элементов после каждого изменения DOM.
  • В компонентах с большим числом кнопок (например, карусели с сотнями слайдов) следует оптимизировать поиск через контейнеры и селекторы, чтобы не просматривать весь документ.

Выводимые возможности

Библиотека Tabbable предоставляет строгий и предсказуемый контроль над фокусируемыми элементами, позволяя:

  • Автоматически фильтровать видимые и активные кнопки.
  • Поддерживать корректный порядок навигации с клавиатуры.
  • Управлять фокусом внутри модальных окон, Shadow DOM и сложных компонентов.
  • Реализовывать циклическую навигацию, предотвращая уход фокуса за пределы контейнера.

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