activate()

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

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

activate() переводит объект ловушки в активное состояние и инициирует все связанные с этим процессы:

  • вычисление доступных фокусируемых элементов;
  • установка обработчиков клавиатуры и событий фокуса;
  • перенос фокуса на начальный элемент;
  • блокировка перемещения фокуса за пределы контейнера.

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


Сигнатура метода

trap.activate(options?)

Метод может вызываться без параметров или с объектом конфигурации, позволяющим временно изменить настройки активации.

Возвращаемое значение отсутствует.


Общая последовательность активации

Внутренний процесс работы activate() состоит из нескольких этапов.

1. Проверка состояния ловушки

Если ловушка уже активна, повторная активация не выполняется. Это предотвращает дублирование обработчиков событий и конфликт логики.

2. Сохранение текущего фокуса

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

3. Определение фокусируемых элементов

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

В список входят:

  • элементы <button>
  • <a> с атрибутом href
  • <input>, <select>, <textarea>
  • элементы с tabindex
  • другие интерактивные элементы

Элементы со свойствами disabled, hidden или tabindex="-1" исключаются из списка.

4. Назначение обработчиков

После активации устанавливаются слушатели событий:

  • keydown
  • focusin
  • mousedown / touchstart (в зависимости от конфигурации)

Эти обработчики обеспечивают контроль перемещения фокуса.

5. Перенос фокуса

Фокус перемещается на элемент, определённый настройками:

  • initialFocus
  • первый tabbable-элемент
  • fallbackFocus

Если ни один элемент не найден, используется резервный элемент.


Базовый пример использования

import { createFocusTrap } from 'focus-trap';

const modal = document.querySelector('#modal');

const trap = createFocusTrap(modal);

trap.activate();

После вызова activate() клавиша Tab начинает перемещать фокус только между элементами внутри #modal. При попытке выйти за границы контейнера фокус возвращается обратно.


Перехват клавиши Tab

Ключевая функция ловушки — циклическая навигация по фокусируемым элементам.

Предположим, контейнер содержит три элемента:

[Input] → [Button] → [Link]

При нажатии Tab на последнем элементе:

[Link] → Tab → [Input]

При использовании Shift + Tab на первом элементе:

[Input] → Shift+Tab → [Link]

Такой цикл создаёт замкнутую область навигации.


Обработчик события focusin

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

Это происходит, например:

  • при программном вызове element.focus()
  • при клике мышью вне контейнера
  • при взаимодействии со сторонними скриптами

Алгоритм проверки:

  1. Определяется текущий элемент фокуса.
  2. Проверяется его принадлежность контейнеру.
  3. Если элемент находится вне ловушки, фокус возвращается на допустимый элемент.

Опция onActivate

Метод activate() может вызывать пользовательскую функцию при активации ловушки.

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

Функция выполняется сразу после перехода ловушки в активное состояние.


Опция onPostActivate

Иногда требуется выполнить действия после установки фокуса. Для этого используется onPostActivate.

const trap = createFocusTrap(modal, {
  onPostActivate() {
    console.log('Focus already set');
  }
});

Разница между событиями:

Событие Момент вызова
onActivate сразу после активации
onPostActivate после установки фокуса

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

Метод activate() автоматически выбирает элемент для начального фокуса.

Приоритет выбора следующий:

  1. значение initialFocus
  2. первый tabbable-элемент
  3. fallbackFocus

Пример настройки

const trap = createFocusTrap(modal, {
  initialFocus: '#first-input'
});

После активации фокус сразу перемещается на элемент с идентификатором first-input.


Использование fallbackFocus

Иногда контейнер может не содержать фокусируемых элементов. В этом случае требуется резервный элемент.

const trap = createFocusTrap(modal, {
  fallbackFocus: '#modal'
});

Если список tabbable-элементов пуст, фокус будет установлен на контейнер.

Для этого контейнер должен иметь:

<div id="modal" tabindex="-1">

Временные параметры активации

Метод activate() может принимать собственные параметры, которые переопределяют конфигурацию ловушки только для текущего запуска.

trap.activate({
  onActivate() {
    console.log('Activated with custom options');
  }
});

Такие параметры не изменяют исходную конфигурацию объекта.


Повторная активация

Если ловушка уже активна, вызов activate() не выполняет повторную инициализацию.

Это предотвращает:

  • повторную установку обработчиков
  • конфликт событий
  • потерю текущего состояния фокуса

Проверка активности выполняется внутри внутреннего состояния ловушки.


Активация нескольких ловушек

Focus-trap поддерживает стек активных ловушек. При активации новой ловушки предыдущая временно приостанавливается.

Пример:

trap1.activate();
trap2.activate();

В этом случае:

  • trap1 приостанавливается
  • trap2 становится активной

После деактивации trap2 первая ловушка автоматически восстанавливается.

Такой механизм особенно полезен при вложенных модальных окнах.


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

Во время активации обрабатываются клики за пределами контейнера.

Поведение определяется параметром clickOutsideDeactivates.

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

При клике вне контейнера ловушка автоматически деактивируется.


Опция escapeDeactivates

Часто требуется закрытие модального окна клавишей Escape.

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

При нажатии Escape:

  1. ловушка деактивируется
  2. фокус возвращается на исходный элемент

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

Focus-trap запоминает элемент, который был активен до вызова activate().

После деактивации выполняется восстановление фокуса:

Element A → activate() → Trap → deactivate() → Element A

Поведение можно изменить с помощью параметра returnFocusOnDeactivate.


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

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

В этом случае используется опция delayInitialFocus.

const trap = createFocusTrap(modal, {
  delayInitialFocus: true
});

Это позволяет дождаться обновления DOM перед установкой фокуса.


Пример модального окна

import { createFocusTrap } from 'focus-trap';

const modal = document.querySelector('#modal');
const openButton = document.querySelector('#open');

const trap = createFocusTrap(modal, {
  initialFocus: '#name',
  escapeDeactivates: true,
  clickOutsideDeactivates: true
});

openButton.addEventListener('click', () => {
  modal.classList.add('active');
  trap.activate();
});

HTML-структура:

<div id="modal" class="modal">
  <input id="name">
  <button>Submit</button>
  <button>Cancel</button>
</div>

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

  • фокус автоматически устанавливается на поле ввода
  • клавиша Tab перемещает фокус только внутри окна
  • Escape закрывает ловушку

Особенности работы с Shadow DOM

Focus-trap корректно работает с элементами внутри Shadow DOM, если они доступны для табуляции. Библиотека анализирует распределённые узлы и поддерживает вложенные структуры компонентов.

Однако при сложных веб-компонентах может потребоваться явная настройка fallbackFocus.


Производительность

Активация ловушки требует:

  • анализа DOM-дерева
  • построения списка tabbable-элементов
  • установки обработчиков

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


Типичные ошибки при использовании

1. Отсутствие фокусируемых элементов

Без fallbackFocus активация может привести к ошибке.

2. Невозможность фокусировки контейнера

Контейнер должен иметь tabindex="-1".

3. Конфликт с другими скриптами

Некоторые UI-библиотеки могут программно менять фокус.

4. Активация до появления DOM

Если контейнер ещё не добавлен в документ, ловушка не сможет определить фокусируемые элементы.


Роль метода activate() в жизненном цикле Focus-trap

Жизненный цикл ловушки состоит из трёх основных этапов:

  1. создание (createFocusTrap)
  2. активация (activate)
  3. деактивация (deactivate)

Метод activate() запускает весь механизм ограничения фокуса и переводит интерфейс в режим контролируемой навигации. Именно на этом этапе происходит подключение всех обработчиков и формирование замкнутой области фокуса внутри заданного контейнера.