Доступность для скринридеров

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

Tabbable возвращает массив элементов, которые считаются доступными для табуляции. Ключевые критерии:

  • Элемент должен быть видимым (не скрытым через display: none или visibility: hidden).
  • Элемент не должен быть отключённым (disabled).
  • Элемент должен быть интерактивным по своей природе или иметь атрибут tabindex.

Использование функции tabbable

Основной метод библиотеки — функция tabbable(root, options), где:

  • root — DOM-элемент, внутри которого производится поиск tabbable-элементов. Если передать document.body, поиск осуществляется по всей странице.

  • options — объект с дополнительными настройками:

    • includeContainer — включает переданный элемент в результирующий массив, если он сам по себе tabbable.
    • getShadowRoot — функция, возвращающая shadow DOM для обхода вложенных элементов.
    • displayCheck — задаёт способ проверки видимости: 'full' (полная проверка CSS), 'non-zero-area' (проверка размеров).

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

import { tabbable } from 'tabbable';

const elements = tabbable(document.body, { includeContainer: true });
console.log(elements); // Список всех интерактивных элементов

Обработка атрибутов tabindex

Элемент с tabindex получает особое значение:

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

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

Особенности работы с различными типами элементов

Формы и кнопки

Элементы <button>, <input>, <select>, <textarea> автоматически считаются tabbable, если они не отключены и видимы. Для <input type="hidden"> исключение — такие элементы не учитываются.

Ссылки

Элементы <a> с атрибутом href включаются в последовательность табуляции. Если href отсутствует, элемент становится фокусируемым только при явном tabindex.

Контейнеры и роли ARIA

Элементы с ролями ARIA, такими как role="button" или role="link", могут быть сделаны tabbable с помощью tabindex="0". Tabbable корректно определяет такие элементы, что обеспечивает совместимость со скринридерами.

Взаимодействие с динамическим контентом

Для интерфейсов с модальными окнами, аккордеонами или вкладками важно обновлять список tabbable-элементов при изменении DOM. Пример для модального окна:

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

focusableElements[0].focus(); // Устанавливаем фокус на первый элемент при открытии

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

Интеграция с фокусными трэпами

Для обеспечения доступности часто используется концепция focus trap — ограничение табуляции внутри модального окна или панели. Tabbable предоставляет основу для реализации:

function trapFocus(container) {
  const elements = tabbable(container);
  const first = elements[0];
  const last = elements[elements.length - 1];

  container.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();
      }
    }
  });
}

Такой подход предотвращает потерю фокуса за пределами модального окна и обеспечивает корректное взаимодействие со скринридерами.

Работа с Shadow DOM и веб-компонентами

Shadow DOM добавляет сложность при поиске tabbable-элементов. Tabbable поддерживает обход вложенных shadow root через опцию getShadowRoot:

const elements = tabbable(document.body, {
  getShadowRoot: (node) => node.shadowRoot
});

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

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

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

Расширение функциональности

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

const customElements = tabbable(document.body).filter(el => el.tagName === 'BUTTON' || el.hasAttribute('data-focus'));

Это позволяет ограничивать навигацию клавишами только на определённые интерактивные элементы без изменения структуры DOM.

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