Типичные ошибки

Неправильное определение контейнера

Одной из частых ошибок является некорректное указание элемента, внутри которого должен работать focus-trap. Библиотека ожидает DOM-элемент, а не селектор в виде строки. Пример неверного использования:

import createFocusTrap from 'focus-trap';

const trap = createFocusTrap('#modal'); // ❌ передаём строку вместо DOM-элемента

Правильный вариант:

const modalElement = document.getElementById('modal');
const trap = createFocusTrap(modalElement); // ✅ передаём DOM-элемент

Передача строки может привести к ошибкам времени выполнения или к тому, что фокус не будет корректно ограничен.


Игнорирование активации и деактивации

Focus-trap создаёт контейнер, но не активирует его автоматически. Часто разработчики создают trap и забывают вызвать метод activate():

const trap = createFocusTrap(modalElement);
// trap.activate(); ❌ забыли вызвать

Это приводит к тому, что фокус всё ещё может покидать модальное окно или диалог. Всегда важно вызывать методы:

trap.activate(); // ✅ активируем фокус-трап
trap.deactivate(); // ✅ при закрытии модального окна

Некорректная работа с динамическим содержимым

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

Ошибка:

trap.activate();
modalElement.innerHTML += '<button id="new-btn">Новая кнопка</button>';

Решение — деактивировать и заново активировать trap после изменения DOM:

trap.deactivate();
trap.activate();

Или использовать опцию allowOutsideClick совместно с динамическим управлением:

const trap = createFocusTrap(modalElement, {
  allowOutsideClick: true
});

Неправильное использование опций

Focus-trap предоставляет множество опций, таких как initialFocus, fallbackFocus, clickOutsideDeactivates, escapeDeactivates. Ошибки в их применении приводят к неожиданному поведению:

  • initialFocus — часто указывают селектор, который отсутствует в DOM на момент активации. В результате фокус может установиться на первый фокусируемый элемент контейнера по умолчанию.
  • fallbackFocus — используется как запасной элемент, если initialFocus недоступен. Неправильное указание или отсутствие fallback может заблокировать фокус.
  • clickOutsideDeactivates — если не установить явно, клик вне контейнера не завершает trap, что иногда противоречит UX.

Пример правильного задания опций:

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

Игнорирование состояния видимости

Focus-trap не проверяет CSS-свойства, такие как display: none или visibility: hidden. Если активировать trap на скрытом элементе, фокус будет непредсказуемо установлен:

modalElement.style.display = 'none';
trap.activate(); // ❌ фокус не установится корректно

Рекомендуется активировать trap только после того, как элемент стал видимым:

modalElement.style.display = 'block';
trap.activate(); // ✅ безопасная активация

Конфликт с другими библиотеками управления фокусом

Использование нескольких библиотек, которые управляют фокусом, может приводить к конфликтам. Например, одновременная работа focus-trap и кастомных обработчиков на keydown для Tab:

modalElement.addEventListener('keydown', (e) => {
  if (e.key === 'Tab') {
    // собственная логика
  }
});
trap.activate();

В результате фокус может “застревать” вне контейнера или прыгать непредсказуемо. Лучший подход — полагаться на встроенные возможности focus-trap и избегать ручной перехватки Tab внутри контейнера.


Необработанный возврат фокуса

Focus-trap поддерживает опцию возврата фокуса на элемент, который был активен перед активацией trap (returnFocusOnDeactivate). Если не включить эту опцию, пользовательский фокус может потеряться после закрытия модального окна:

const trap = createFocusTrap(modalElement, {
  returnFocusOnDeactivate: true
});

Без этой настройки фокус останется либо на body, либо на последнем фокусируемом элементе, что нарушает удобство навигации.


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

Модальные окна или панели часто открываются асинхронно, например, после загрузки данных с сервера. Если активировать trap до того, как элемент реально появится в DOM, focus-trap не сможет корректно установить фокус:

fetch('/modal-content').then(() => {
  trap.activate(); // ❌ элемент может быть ещё не вставлен
});

Правильный подход — запускать activate() после полной вставки содержимого в DOM:

fetch('/modal-content').then((html) => {
  modalElement.innerHTML = html;
  trap.activate(); // ✅ теперь элемент доступен
});

Эти ошибки встречаются наиболее часто при работе с focus-trap и могут нарушать доступность и UX веб-приложений. Их систематическое понимание и правильная настройка позволяют создавать безопасные и предсказуемые модальные интерфейсы.