ARIA спецификация

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

Элементы делятся на три категории:

  • Tabbable – элементы, доступные для перемещения фокуса с помощью Tab.
  • Focusable – элементы, которые могут принимать фокус программно, но не обязательно включены в порядок табуляции.
  • Disabled/Hidden – элементы, которые игнорируются библиотекой при вычислении порядка.

Установка и подключение

Для работы с Tabbable используется стандартный npm-пакет:

npm install tabbable

Подключение в проект:

import { tabbable, focusable } from 'tabbable';

Функции возвращают массив DOM-элементов, упорядоченных в соответствии с их логическим порядком на странице.

Основные функции

tabbable(container, options)

Возвращает массив элементов, доступных для навигации с клавиши Tab. Параметры:

  • container – DOM-элемент или документ, внутри которого осуществляется поиск.

  • options – объект с настройками:

    • includeContainer – включить сам контейнер, если он tabbable (по умолчанию false).
    • getShadowRoot – функция, возвращающая shadow root для обхода теневых DOM-деревьев.

Пример:

const tabbableElements = tabbable(document.body);
tabbableElements.forEach(el => console.log(el.tagName));

focusable(container, options)

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

const focusableElements = focusable(document.body, { includeContainer: true });

Интеграция с ARIA

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

  • aria-hidden="true" – элементы игнорируются.
  • role="presentation" или role="none" – элементы не добавляются в порядок табуляции.
  • tabindex="-1" – элемент focusable, но не tabbable.
  • tabindex="0" – элемент tabbable и включен в стандартный порядок.

Это позволяет создавать интерфейсы, полностью соответствующие спецификации ARIA, включая корректное управление доступностью для экранных читалок.

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

  • Интерактивные элементы (button, a[href], input, select, textarea) по умолчанию считаются tabbable, если они не disabled.
  • Элементы с contenteditable – автоматически focusable.
  • Теневой DOM – при использовании getShadowRoot библиотека рекурсивно обходит shadow DOM, учитывая фокусируемые элементы внутри.

Обработка динамических изменений DOM

Tabbable не поддерживает автоматическое отслеживание изменений DOM. Для динамически добавляемых элементов необходимо повторно вызывать функции tabbable или focusable после изменения структуры страницы. Это обеспечивает актуальный порядок табуляции и предотвращает проблемы с доступностью.

Настройка при сложных интерфейсах

Для сложных компонентов, таких как модальные окна, всплывающие панели или кастомные виджеты:

  • Используется комбинация tabbable и управления программным фокусом через element.focus().
  • Применяются фильтры по селекторам или ролям, чтобы исключить скрытые или неактивные элементы.
  • Shadow DOM и iframe обрабатываются отдельными вызовами функций с указанием соответствующих контейнеров.

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

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

let first = focusableElements[0];
let last = focusableElements[focusableElements.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();
    }
  }
});

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

Производительность и рекомендации

  • Для больших DOM-структур рекомендуется ограничивать контейнеры поиска, чтобы избежать перебора всех элементов страницы.
  • Использование includeContainer и рекурсивного обхода shadow DOM должно быть оправдано конкретной задачей, так как каждая рекурсия увеличивает время выполнения.
  • Таблицы и списки должны быть проверены на корректное применение tabindex для интерактивных ячеек.

Совместимость и ограничения

  • Поддержка современных браузеров: Chrome, Firefox, Safari, Edge.
  • Старые версии IE (до 11) могут требовать полифиллов для корректной работы с tabindex и shadow DOM.
  • Работа с iframe требует отдельного доступа к документу внутри iframe и вызова tabbable отдельно.

Практические советы

  • Всегда проверять порядок табуляции после динамического рендеринга компонентов.
  • Скрытые элементы (display: none, visibility: hidden) игнорируются автоматически.
  • Использование Tabbable вместе с ARIA ролями и состояниями обеспечивает максимальную доступность и совместимость со вспомогательными технологиями.