Кастомные обработчики событий

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

Ключевой принцип работы — установка ловушки фокуса (focus trap) на определённый элемент и перенаправление событий Tab и Shift+Tab для циклической навигации внутри него.


Инициализация Focus-trap

Для создания ловушки фокуса используется функция createFocusTrap, принимающая два параметра:

import { createFocusTrap } from 'focus-trap';

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

const trap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  initialFocus: '#inputName',
  fallbackFocus: modal,
  onActivate: () => console.log('Trap activated'),
  onDeactivate: () => console.log('Trap deactivated')
});

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

  • escapeDeactivates — отключает ловушку при нажатии клавиши Escape.
  • clickOutsideDeactivates — позволяет деактивировать ловушку при клике вне контейнера.
  • initialFocus — элемент, который получает фокус при активации.
  • fallbackFocus — элемент для фокуса, если указанный initialFocus не найден.
  • onActivate и onDeactivate — колбэки, вызываемые при включении и выключении ловушки.

Кастомные обработчики событий

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

onActivate и onDeactivate

Эти колбэки используются для запуска дополнительных действий при активации и деактивации ловушки:

const trap = createFocusTrap(modal, {
  onActivate: () => {
    modal.classList.add('active');
    console.log('Фокус заблокирован внутри модального окна');
  },
  onDeactivate: () => {
    modal.classList.remove('active');
    console.log('Фокус возвращён пользователю');
  }
});

Часто их используют для управления стилями, анимациями и состояниями интерфейса.


onPostActivate и onPostDeactivate

Эти события вызываются после того, как фокус фактически был перемещён или возвращён:

const trap = createFocusTrap(modal, {
  onPostActivate: () => {
    console.log('Фокус установлен на первый интерактивный элемент');
  },
  onPostDeactivate: () => {
    console.log('Фокус вернулся на исходный элемент');
  }
});

Используются для синхронизации с внешними библиотеками или дополнительной логики после завершения основных действий.


onFocusOutside

Позволяет реагировать на попытку выхода фокуса за пределы контейнера, даже если ловушка активна:

const trap = createFocusTrap(modal, {
  onFocusOutside: (event) => {
    console.log('Попытка ухода фокуса:', event.target);
    event.preventDefault(); // блокирует уход фокуса
  }
});

Эта опция полезна для логики валидации или ограничения взаимодействия с интерфейсом.


onActivate и onDeactivate с промисами

Можно интегрировать асинхронные действия при активации или деактивации ловушки:

const trap = createFocusTrap(modal, {
  onActivate: async () => {
    await fetch('/api/log-activation');
    console.log('Ловушка активирована и лог отправлен на сервер');
  },
  onDeactivate: async () => {
    await fetch('/api/log-deactivation');
    console.log('Ловушка деактивирована и лог отправлен на сервер');
  }
});

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


События клавиатуры и пользовательские хэндлеры

Focus-trap автоматически перехватывает клавиши Tab и Shift+Tab для циклической навигации. Для расширенной кастомизации можно использовать onKeyDown:

const trap = createFocusTrap(modal, {
  onKeyDown: (event) => {
    if (event.key === 'Enter') {
      console.log('Нажата клавиша Enter внутри ловушки');
    }
  }
});

Это позволяет обрабатывать любые клавиатурные действия внутри контейнера без выхода за его пределы.


Интеграция с динамическим DOM

Если содержимое контейнера меняется динамически, ловушка корректно отслеживает новые интерактивные элементы при каждом цикле:

const trap = createFocusTrap(modal, {
  allowOutsideClick: true,
  tabbableOptions: { displayCheck: 'none' } // игнорирует скрытые элементы
});

Опция tabbableOptions позволяет задавать фильтры для интерактивных элементов, например игнорировать скрытые или неактивные кнопки.


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

Для сложных интерфейсов с несколькими модальными окнами можно создавать несколько ловушек и активировать их по очереди:

const trap1 = createFocusTrap(modal1);
const trap2 = createFocusTrap(modal2);

trap1.activate(); // первый модальный
trap2.activate(); // второй модальный, автоматически блокирует первый

Focus-trap гарантирует корректное переключение фокуса и предотвращает конфликт активных ловушек.


Вывод

Использование кастомных обработчиков в Focus-trap позволяет не только удерживать фокус внутри элемента, но и интегрировать дополнительные бизнес-логики, синхронизировать визуальные состояния и обрабатывать события клавиатуры. Гибкая система колбэков (onActivate, onDeactivate, onPostActivate, onFocusOutside) делает библиотеку мощным инструментом для создания доступных и управляемых интерфейсов.