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

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

Focus-trap работает на основе нескольких ключевых принципов:

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

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

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

npm install focus-trap

После установки можно импортировать её в проект:

import { createFocusTrap } from 'focus-trap';

Для проектов, использующих CommonJS:

const { createFocusTrap } = require('focus-trap');

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

Фокус-трап создается для конкретного контейнера. В качестве примера используется модальное окно с идентификатором modal:

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

const focusTrap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  initialFocus: '#first-input',
  fallbackFocus: modal,
});

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

  • escapeDeactivates — если true, нажатие клавиши Escape деактивирует трап.
  • clickOutsideDeactivates — разрешает закрытие трапа при клике вне контейнера.
  • initialFocus — элемент, который получит фокус при активации трапа. Может быть селектором CSS или DOM-элементом.
  • fallbackFocus — элемент, который получит фокус, если initialFocus не найден.

Активация и деактивация

Фокус-трап необходимо активировать после создания:

focusTrap.activate();

Для временного отключения используется метод:

focusTrap.deactivate();

При деактивации фокус возвращается на элемент, который был активен до активации трапа, если не задано иное.

Настройка поведения табуляции

Focus-trap автоматически обрабатывает нажатия Tab и Shift+Tab. Цикличная навигация обеспечивается за счёт вычисления всех фокусируемых элементов внутри контейнера:

  • <a> с href
  • <button>
  • <input>, <select>, <textarea>
  • [tabindex], кроме tabindex="-1"

Если фокус находится на последнем элементе и пользователь нажимает Tab, фокус перемещается на первый элемент. При обратной навигации (Shift+Tab) аналогично с последним элементом.

Обработка динамически добавляемых элементов

Focus-trap поддерживает элементы, которые появляются в DOM после активации трапа. Для этого используется метод updateTabbables():

focusTrap.updateTabbables();

Этот метод пересчитывает список доступных элементов для фокуса, что необходимо при динамическом рендеринге форм или списков внутри контейнера.

Управление событиями

Библиотека позволяет задавать пользовательские коллбэки для ключевых событий:

const focusTrap = createFocusTrap(modal, {
  onActivate: () => console.log('Трап активирован'),
  onDeactivate: () => console.log('Трап деактивирован'),
});
  • onActivate вызывается при вызове activate().
  • onDeactivate вызывается при вызове deactivate().

Дополнительно можно реагировать на клики вне контейнера и нажатия клавиши Escape через опции clickOutsideDeactivates и escapeDeactivates.

Интеграция с модальными окнами

Для модального окна с кнопкой открытия и закрытия пример выглядит так:

const openButton = document.getElementById('open-modal');
const closeButton = document.getElementById('close-modal');

openButton.addEventListener('click', () => {
  focusTrap.activate();
  modal.style.display = 'block';
});

closeButton.addEventListener('click', () => {
  focusTrap.deactivate();
  modal.style.display = 'none';
});

Такой подход обеспечивает:

  • Автоматическое управление фокусом внутри модального окна.
  • Возврат фокуса на кнопку открытия после закрытия.
  • Контроль над клавиатурной навигацией без дополнительных слушателей keydown.

Советы по доступности

  • Всегда задавать initialFocus на первый логический элемент формы или интерактивного блока.
  • Использовать aria-hidden для скрытия элементов за пределами активного трапа.
  • Проверять поведение при динамическом добавлении и удалении элементов в контейнере.

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

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

Для программного контроля доступна опция returnFocusOnDeactivate, которая позволяет выбрать, куда вернётся фокус после закрытия трапа:

returnFocusOnDeactivate: document.getElementById('custom-focus')

Работа с асинхронным контентом

Если модальное окно или панель подгружается динамически, необходимо убедиться, что активация трапа происходит после вставки элементов в DOM. В противном случае initialFocus может не сработать, и фокус будет установлен на fallbackFocus.

fetch('/form.html')
  .then(res => res.text())
  .then(html => {
    modal.innerHTML = html;
    focusTrap.activate();
  });

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