inert-polyfill

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

Создание и инициализация focus trap

Для начала работы необходимо создать объект фокус-трапа, передав ему DOM-элемент, который будет ограничивать фокус:

import { createFocusTrap } from 'focus-trap';

const modal = document.querySelector('#modal');
const focusTrap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  allowOutsideClick: true
});

Параметры и их значения:

  • escapeDeactivates — при нажатии клавиши Escape трап деактивируется.
  • clickOutsideDeactivates — клик вне элемента отключает фокус-трап.
  • allowOutsideClick — позволяет клики по элементам вне трапа, не деактивируя его полностью.
  • initialFocus — элемент, который получит фокус при активации трапа.
  • returnFocusOnDeactivate — возвращает фокус на элемент, который его имел до активации.

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

Фокус-трап может быть включен или выключен программно:

focusTrap.activate();   // Включение фокус-трапа
focusTrap.deactivate(); // Отключение фокус-трапа

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

Взаимодействие с inert-polyfill

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

Пример интеграции:

import 'wicg-inert';

const mainContent = document.querySelector('#main-content');
const modal = document.querySelector('#modal');

function openModal() {
  mainContent.inert = true; // Отключаем основной контент
  modal.removeAttribute('hidden'); 
  focusTrap.activate();
}

function closeModal() {
  mainContent.inert = false; // Возвращаем интерактивность основному контенту
  modal.setAttribute('hidden', '');
  focusTrap.deactivate();
}

Ключевые моменты интеграции:

  • Свойство inert полностью блокирует возможность фокусировки и клика на элементы.
  • При использовании focus-trap вместе с inert-polyfill можно быть уверенным, что навигация клавиатурой останется строго внутри модального окна.
  • Важно всегда возвращать inert = false при закрытии окна, чтобы восстановить доступ к основному контенту.

Настройка фокуса на конкретные элементы

Focus-trap поддерживает опцию initialFocus, позволяющую указать, какой элемент получит фокус сразу при активации:

const focusTrap = createFocusTrap(modal, {
  initialFocus: '#modal-close-button'
});

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

initialFocus: () => document.querySelector('.dynamic-input')

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

Обработка событий выхода

Focus-trap позволяет реагировать на события выхода из трапа через onDeactivate:

const focusTrap = createFocusTrap(modal, {
  onDeactivate: () => console.log('Фокус-трап отключен')
});

Это используется для синхронизации с другими логическими компонентами приложения: закрытие модального окна, обновление состояния UI или повторная активация элементов вне трапа.

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

Focus-trap корректно работает с динамическим добавлением и удалением элементов внутри контейнера. Однако при изменении DOM рекомендуется переинициализировать трап или использовать методы updateTabbableNodes() для пересчета фокусируемых элементов.

focusTrap.updateTabbableNodes();

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

Советы по производительности

  • Не следует активировать несколько трапов одновременно. Если необходимо несколько модальных окон, лучше использовать стек или очередь для управления фокусом.
  • Использование inert снижает риск утечки фокуса, но при больших DOM-структурах стоит учитывать, что установка inert на большие контейнеры может вызывать небольшие задержки.
  • Для сложных интерфейсов можно комбинировать allowOutsideClick и clickOutsideDeactivates для гибкого контроля поведения при кликах вне модального окна.

Поддержка браузеров и полифиллы

Focus-trap работает во всех современных браузерах, но для старых версий Safari и Edge рекомендуется использовать inert-polyfill, так как они не поддерживают нативное свойство inert. Полная совместимость обеспечивается подключением скрипта:

<script src="https://unpkg.com/wicg-inert/dist/inert.min.js"></script>

Это гарантирует корректную работу focus-trap даже в браузерах без поддержки inert.

Заключение по применению

Комбинация focus-trap и inert-polyfill обеспечивает полный контроль над фокусом в интерактивных интерфейсах. Она гарантирует:

  • Защиту от случайного взаимодействия с контентом вне активного модального окна.
  • Циклическую и предсказуемую навигацию клавиатурой.
  • Лёгкую интеграцию с динамически изменяющимся контентом.

Такой подход является стандартом при разработке доступных и удобных веб-компонентов.