Alert dialog

Focus-trap — это библиотека для управления фокусом внутри определённых областей веб-страницы. Основная задача — удерживать фокус внутри модального окна, диалогового окна или другой интерактивной зоны, предотвращая переход на элементы за пределами этого контейнера с помощью клавиши Tab или Shift+Tab. Это критически важно для доступности (accessibility) и соответствия стандартам WCAG, так как позволяет пользователям клавиатуры и вспомогательных технологий корректно взаимодействовать с интерфейсом.

Фокус в DOM управляется свойством document.activeElement. Focus-trap автоматически перехватывает события клавиатуры и изменяет порядок фокусировки, чтобы фокус оставался внутри целевой области.

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

Библиотека распространяется через npm и CDN.

npm install focus-trap

Для использования через ES-модули:

import { createFocusTrap } from 'focus-trap';

Для классического подключения через CDN:

<script src="https://unpkg.com/focus-trap@6/dist/focus-trap.min.js"></script>

Создание и инициализация ловушки фокуса

Focus-trap создается с помощью функции createFocusTrap, в которую передается контейнер DOM и набор опций.

const modal = document.getElementById('alert-dialog');
const focusTrap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  allowOutsideClick: true,
  fallbackFocus: modal
});

Ключевые опции:

  • escapeDeactivates — если true, нажатие клавиши Escape деактивирует фокус-трап.
  • clickOutsideDeactivates — позволяет закрывать модальное окно при клике вне него.
  • allowOutsideClick — разрешает обработку клика вне контейнера, не деактивируя фокус-трап.
  • fallbackFocus — элемент, на который фокус вернется, если в контейнере нет фокусируемых элементов.

Управление жизненным циклом

Focus-trap имеет методы управления:

  • activate() — активирует ловушку фокуса.
  • deactivate() — деактивирует ловушку и возвращает фокус пользователю (если задано returnFocusOnDeactivate).
focusTrap.activate();

document.getElementById('close-btn').addEventListener('click', () => {
  focusTrap.deactivate();
});

Опция returnFocusOnDeactivate позволяет контролировать, возвращается ли фокус на элемент, который его инициировал.

const focusTrap = createFocusTrap(modal, {
  returnFocusOnDeactivate: true
});

Применение в Alert Dialog

Alert dialog — это модальное окно, требующее немедленного внимания пользователя. Фокус должен быть строго внутри диалога до момента его закрытия.

Пример структуры HTML:

<div id="alert-dialog" role="alertdialog" aria-modal="true" aria-labelledby="dialog-title" aria-describedby="dialog-desc">
  <h2 id="dialog-title">Внимание</h2>
  <p id="dialog-desc">Произошла ошибка при сохранении данных.</p>
  <button id="confirm-btn">Ок</button>
</div>

JavaScript для интеграции с Focus-trap:

const dialog = document.getElementById('alert-dialog');
const confirmBtn = document.getElementById('confirm-btn');

const focusTrap = createFocusTrap(dialog, {
  escapeDeactivates: true,
  clickOutsideDeactivates: false,
  fallbackFocus: confirmBtn,
  onActivate: () => dialog.style.display = 'block',
  onDeactivate: () => dialog.style.display = 'none'
});

document.getElementById('show-dialog').addEventListener('click', () => {
  focusTrap.activate();
});

confirmBtn.addEventListener('click', () => {
  focusTrap.deactivate();
});

Доступность и ARIA

Для alert dialog критично использовать правильные ARIA-атрибуты:

  • role=“alertdialog” — обозначает диалог, который требует внимания.
  • aria-modal=“true” — сообщает ассистивным технологиям, что фокус ограничен.
  • aria-labelledby и aria-describedby — связывают диалог с заголовком и описанием для экранных читалок.

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

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

  • Тайм-ауты и асинхронные модальные окна: Focus-trap поддерживает динамическое добавление элементов, поэтому ловушка фокуса может активироваться после загрузки контента.
  • Несколько ловушек на странице: каждая ловушка управляется независимо. При этом рекомендуется деактивировать предыдущую ловушку перед активацией новой.
  • Комбинация с фреймворками: библиотека совместима с React, Vue и Angular через прямое обращение к DOM или обертки компонентов.

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

  • Всегда задавать fallbackFocus, чтобы избежать ситуации без фокусируемых элементов.
  • Для alert dialog рекомендуется отключать clickOutsideDeactivates, чтобы пользователь был вынужден взаимодействовать с самим диалогом.
  • Активировать ловушку сразу после появления диалога, а деактивировать при его закрытии.
  • Проверять совместимость с экранными читалками и клавиатурной навигацией, чтобы соответствовать стандартам доступности.