Возврат фокуса к триггеру

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

Фокус в веб-приложении перемещается по элементам с помощью клавиши Tab и Shift+Tab. Focus-trap создаёт «ловушку», перехватывая события навигации и возвращая фокус к первому или последнему элементу при попытке выйти за пределы контейнера.

Инициализация ловушки фокуса

Для работы с Focus-trap требуется импорт библиотеки и создание инстанса с помощью метода createFocusTrap. Стандартная структура выглядит так:

import { createFocusTrap } from 'focus-trap';

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

const trap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  fallbackFocus: modal,
  returnFocusOnDeactivate: true
});

Параметры конфигурации:

  • escapeDeactivates — закрытие ловушки при нажатии Escape.
  • clickOutsideDeactivates — деактивация при клике вне контейнера.
  • fallbackFocus — элемент, на который вернётся фокус, если внутри нет доступных для фокуса элементов.
  • returnFocusOnDeactivate — возврат фокуса к элементу, который инициировал активацию ловушки.

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

Ловушка фокуса активируется вызовом метода trap.activate(). В этот момент Focus-trap запоминает текущий активный элемент, чтобы вернуть на него фокус после деактивации. Деактивация происходит с помощью trap.deactivate(). При этом:

  • Фокус автоматически возвращается на исходный элемент, если включён параметр returnFocusOnDeactivate.
  • Если параметр отключён, можно вручную указать элемент для фокуса.
const openButton = document.getElementById('openModal');
openButton.addEventListener('click', () => trap.activate());

const closeButton = document.getElementById('closeModal');
closeButton.addEventListener('click', () => trap.deactivate());

Возврат фокуса к триггеру

Ключевой аспект — корректное возвращение фокуса. Focus-trap обеспечивает консистентность пользовательского опыта:

  1. При активации ловушки фокуса текущий элемент сохраняется.
  2. При деактивации фокус возвращается на сохранённый элемент.
  3. Можно настроить альтернативный элемент для возврата через опцию setReturnFocus.
const trap = createFocusTrap(modal, {
  returnFocusOnDeactivate: false
});

// Сохранение триггера вручную
let triggerElement;
openButton.addEventListener('click', () => {
  triggerElement = document.activeElement;
  trap.activate();
});

// Возврат на триггер после закрытия
closeButton.addEventListener('click', () => {
  trap.deactivate();
  if (triggerElement) triggerElement.focus();
});

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

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

trap.updateContainer(modal);

Если внутри нет доступных интерактивных элементов, фокус перемещается на fallbackFocus.

Работа с вложенными ловушками

При наличии нескольких модальных окон или всплывающих элементов важно учитывать приоритет ловушек. Focus-trap поддерживает вложенные ловушки:

  • Активируется только последняя созданная.
  • Деактивация возвращает фокус к предыдущей активной ловушке.
  • Можно использовать опцию checkCanReturnFocus для асинхронной проверки возможности возврата фокуса.
const innerTrap = createFocusTrap(innerModal, {
  returnFocusOnDeactivate: true
});
innerTrap.activate();

Настройка доступности

Для обеспечения доступности важно:

  • Указывать атрибуты aria-hidden на элементах вне активной ловушки.
  • Использовать tabindex="0" или tabindex="-1" для элементов, на которые требуется вернуть фокус.
  • Проверять, что fallbackFocus является интерактивным элементом.

Отладка и события

Focus-trap предоставляет события:

  • onActivate — срабатывает при активации.
  • onDeactivate — при деактивации.
  • onPostActivate — после завершения активации.
  • onPostDeactivate — после завершения деактивации.

Это позволяет создавать пользовательские анимации и отслеживать жизненный цикл модального окна.

const trap = createFocusTrap(modal, {
  onActivate: () => console.log('Trap activated'),
  onDeactivate: () => console.log('Trap deactivated')
});

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

Рекомендации по использованию

  • Использовать returnFocusOnDeactivate: true для стандартных модальных окон.
  • Для сложных интерфейсов сохранять триггер вручную.
  • Обновлять контейнер при динамических изменениях DOM.
  • Настраивать fallbackFocus на безопасный интерактивный элемент.
  • Следить за вложенными ловушками, чтобы избежать конфликтов фокуса.

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