Тестирование доступности

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

Основной принцип работы заключается в перехвате событий Tab и Shift+Tab и циклическом перемещении фокуса между фокусируемыми элементами внутри контейнера. Библиотека предоставляет API для активации и деактивации «ловушки фокуса», а также настройки поведения при открытии и закрытии компонента.

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

Для подключения используется npm:

npm install focus-trap

Импорт в проект:

import { createFocusTrap } from 'focus-trap';

createFocusTrap возвращает объект с методами activate, deactivate, pause и unpause, что позволяет гибко управлять состоянием фокуса.

Создание базового фокус-трапа

Простейший пример создания фокус-трапа для модального окна:

const modal = document.getElementById('modal');

const focusTrap = createFocusTrap(modal, {
  onActivate: () => modal.classList.add('is-active'),
  onDeactivate: () => modal.classList.remove('is-active'),
});

document.getElementById('openBtn').addEventListener('click', () => focusTrap.activate());
document.getElementById('closeBtn').addEventListener('click', () => focusTrap.deactivate());

Ключевые моменты:

  • onActivate вызывается при активации ловушки, что удобно для добавления стилей.
  • onDeactivate срабатывает при выходе из ловушки, возвращая состояние интерфейса.
  • Метод activate автоматически переводит фокус на первый фокусируемый элемент внутри контейнера.

Настройка поведения

Focus-trap поддерживает несколько опций для тонкой настройки:

  • initialFocus — определяет элемент, который получит фокус при активации. Можно передать селектор, DOM-элемент или функцию:
initialFocus: '#firstInput'
  • fallbackFocus — элемент, на который фокус вернется, если внутри нет фокусируемых элементов.
  • escapeDeactivates — булево значение, разрешающее деактивацию ловушки при нажатии Escape.
  • clickOutsideDeactivates — позволяет закрывать ловушку при клике за пределами контейнера.
  • returnFocusOnDeactivate — возвращает фокус на элемент, с которого была активирована ловушка.

Пример расширенной конфигурации:

const focusTrap = createFocusTrap(modal, {
  initialFocus: () => document.getElementById('firstInput'),
  fallbackFocus: modal,
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  returnFocusOnDeactivate: true,
});

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

В сложных интерфейсах, где элементы рендерятся динамически, важно использовать activate после полной загрузки DOM внутри модального окна:

async function openModal() {
  await renderModalContent(); 
  focusTrap.activate();
}

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

Управление фокусом при нескольких ловушках

Для интерфейсов с несколькими модальными окнами или всплывающими панелями можно применять методы pause и unpause:

const secondaryTrap = createFocusTrap(secondaryModal);

primaryTrap.pause();
secondaryTrap.activate();

// После закрытия вторичной панели
secondaryTrap.deactivate();
primaryTrap.unpause();

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

Работа с формами и интерактивными элементами

Focus-trap корректно обрабатывает:

  • <input>, <button>, <select>, <textarea>
  • Ссылки <a> с href
  • Элементы с tabindex="0" или положительным значением
  • Скринридеры автоматически воспринимают фокус внутри ловушки, улучшая навигацию для пользователей с ограничениями зрения.

Важно избегать установки tabindex="-1" на элементы, которые должны быть доступны через Tab, иначе они будут исключены из цикла фокуса.

Тестирование доступности

Проверка корректной работы фокус-трапа включает:

  1. Навигация с клавиатуры

    • Клавиши Tab и Shift+Tab должны циклически обходить все фокусируемые элементы.
    • При закрытии ловушки фокус возвращается на исходный элемент.
  2. Обработка клавиши Escape

    • При включенной опции escapeDeactivates модальное окно должно закрываться без потери фокуса.
  3. Клики вне контейнера

    • Если активирована опция clickOutsideDeactivates, клик вне модального окна должен деактивировать ловушку.
  4. Совместимость со скринридерами

    • Проверка с использованием VoiceOver, NVDA или JAWS показывает, что фокус остается внутри активной области, и пользователь слышит логическую структуру элементов.
  5. Асинхронный контент

    • Любые динамически добавляемые элементы должны участвовать в цикле фокуса после завершения рендеринга.

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

  • Использовать initialFocus для первого поля формы или кнопки подтверждения.
  • Включать returnFocusOnDeactivate, чтобы пользователь не терял контекст интерфейса.
  • В случае сложных вложенных модалок применять pause/unpause вместо одновременной активации нескольких ловушек.
  • Проверять все формы и диалоги с клавиатурой и скринридером для соответствия WCAG 2.1.

Focus-trap обеспечивает строгое соблюдение правил доступности при работе с интерактивными элементами и модальными окнами, позволяя создавать интерфейсы, безопасные и удобные для пользователей с ограниченными возможностями.