Отложенная инициализация

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

Фокусировка в DOM определяется элементами с атрибутом tabindex и интерактивными тегами (button, input, select, a[href]). Focus-trap оборачивает контейнер, контролируя все события клавиатуры, чтобы фокус оставался внутри этого контейнера до явного выхода.

Создание простого трапа

Для базовой инициализации достаточно импортировать библиотеку и вызвать createFocusTrap:

import { createFocusTrap } from 'focus-trap';

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

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

// Активация трапа
focusTrap.activate();

// Деактивация трапа
focusTrap.deactivate();

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

  • activate() фиксирует фокус внутри контейнера.
  • deactivate() возвращает фокус на элемент, активировавший трап, если опция returnFocusOnDeactivate включена (по умолчанию true).
  • Опции onActivate и onDeactivate позволяют интегрировать визуальные эффекты или другие действия при активации/деактивации трапа.

Отложенная инициализация

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

let focusTrap;

function openModal() {
  const modal = document.createElement('div');
  modal.id = 'modal';
  modal.innerHTML = `
    <button id="close">Закрыть</button>
    <input placeholder="Имя" />
    <button>Отправить</button>
  `;
  document.body.appendChild(modal);

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

  focusTrap.activate();
}

Особенности отложенной инициализации:

  • Трап создаётся после того, как элемент добавлен в DOM, что гарантирует корректное определение всех фокусируемых элементов.
  • Можно динамически назначать обработчики закрытия и другие опции.
  • Позволяет создавать несколько независимых трапов для разных модальных окон без конфликтов.

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

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

  • escapeDeactivates — деактивация при нажатии Esc (true по умолчанию).
  • clickOutsideDeactivates — деактивация при клике вне контейнера.
  • initialFocus — элемент, который получит фокус при активации трапа. Можно указать селектор или функцию, возвращающую элемент.
  • fallbackFocus — элемент, на который фокус будет перемещён, если initialFocus не найден.
focusTrap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  initialFocus: '#modal input',
  fallbackFocus: '#modal button'
});

Интеграция с динамическим интерфейсом

В одностраничных приложениях (SPA) часто используется создание компонентов на лету. Отложенная инициализация Focus-trap обеспечивает:

  1. Изоляцию фокуса при динамическом рендере модальных окон.
  2. Предсказуемую навигацию для клавиатурных пользователей.
  3. Гибкость в назначении поведения на события активации и деактивации.

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

document.getElementById('openModalBtn').addEventListener('click', () => {
  openModal();
});

document.body.addEventListener('click', (event) => {
  if(event.target.matches('#close')) {
    focusTrap.deactivate();
  }
});

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

  • Создавать трапы только по необходимости, избегая постоянной инициализации для скрытых элементов.
  • Использовать returnFocusOnDeactivate для автоматического возврата фокуса к элементу, открывшему модал.
  • Проверять наличие элементов перед назначением initialFocus и fallbackFocus, чтобы избежать ошибок.

Взаимодействие с другими библиотеками

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


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