Web Components

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

Принцип работы: Focus-trap создаёт виртуальный «замкнутый цикл» фокуса внутри элемента. Когда пользователь перемещается по интерактивным элементам с помощью клавиши Tab или Shift + Tab, фокус не покидает контейнер, а при достижении последнего или первого элемента возвращается к началу или концу списка соответственно.


Установка и подключение

Библиотека распространяется через NPM и поддерживает стандартные модули ES6.

npm install focus-trap

Подключение через ES-модуль:

import { createFocusTrap } from 'focus-trap';

Также доступна UMD-версия для использования через тег <script>:

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

Создание фокус-трапа

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

const modal = document.getElementById('modal');
const focusTrap = createFocusTrap(modal, {
  onActivate: () => modal.classList.add('active'),
  onDeactivate: () => modal.classList.remove('active'),
  clickOutsideDeactivates: true,
  escapeDeactivates: true,
  initialFocus: '#modal input:first-of-type'
});

Ключевые параметры:

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

Методы управления фокусом

Focus-trap предоставляет основные методы для управления состоянием:

  • activate() – активирует фокус-трап.
  • deactivate() – деактивирует фокус-трап, возвращая фокус на исходный элемент.
  • pause() – временно приостанавливает работу фокус-трапа.
  • unpause() – возобновляет работу после паузы.

Пример использования с модальным окном:

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

document.getElementById('closeModal').addEventListener('click', () => {
  focusTrap.deactivate();
});

Варианты конфигурации

Focus-trap позволяет тонко настраивать поведение:

  • returnFocusOnDeactivate (по умолчанию true) – возвращает фокус на элемент, который был активен до активации трапа.
  • fallbackFocus – элемент, который получает фокус, если initialFocus не найден.
  • allowOutsideClick – разрешает клики вне контейнера без деактивации.
  • tabbableOptions – объект для настройки поиска элементов, доступных для фокусировки (например, displayCheck: 'none' игнорирует скрытые элементы).

Пример расширенной конфигурации:

const focusTrap = createFocusTrap(modal, {
  initialFocus: '#modal input',
  fallbackFocus: modal,
  clickOutsideDeactivates: false,
  allowOutsideClick: true,
  tabbableOptions: { displayCheck: 'full' }
});

Интеграция с Web Components

При использовании веб-компонентов важно учитывать Shadow DOM, так как стандартный поиск интерактивных элементов внутри Shadow DOM не всегда работает автоматически. Focus-trap поддерживает передачу tabbable elements вручную:

import { tabbable } from 'focus-trap';

class MyModal extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <div id="container">
        <input type="text" />
        <button id="close">Close</button>
      </div>
    `;

    const focusTrap = createFocusTrap(shadow.getElementById('container'), {
      initialFocus: shadow.querySelector('input'),
      onDeactivate: () => console.log('Trap deactivated')
    });

    shadow.getElementById('close').addEventListener('click', () => focusTrap.deactivate());
    focusTrap.activate();
  }
}

customElements.define('my-modal', MyModal);

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

  • Необходимо явно передавать элементы для фокусировки (initialFocus).
  • Методы tabbable() можно использовать для получения всех интерактивных элементов внутри Shadow DOM.
  • Shadow DOM изолирует стили, поэтому визуальные эффекты активации фокус-трапа лучше реализовать через кастомные свойства или CSS внутри компонента.

Продвинутая работа с динамическим контентом

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

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

const listContainer = document.getElementById('list');
const focusTrap = createFocusTrap(listContainer);

focusTrap.activate();

document.getElementById('addItem').addEventListener('click', () => {
  const newItem = document.createElement('button');
  newItem.textContent = 'New Item';
  listContainer.appendChild(newItem);
});

Focus-трап автоматически учитывает добавленные кнопки при навигации через Tab.


Особенности доступности

Focus-trap улучшает accessibility:

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

Для соблюдения ARIA-стандартов рекомендуется использовать aria-modal="true" на контейнерах, где активен фокус-трап, а также управлять видимостью элементов за пределами контейнера с помощью aria-hidden.


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

  • Использовать один фокус-трап на каждый интерактивный компонент.
  • Всегда задавать initialFocus или fallbackFocus, чтобы предотвратить потерю фокуса.
  • Для веб-компонентов учитывать Shadow DOM и передавать нужные элементы явно.
  • Динамически изменяемые элементы автоматически поддерживаются, но при необходимости можно вручную вызывать tabbable() для кастомного управления.
  • Проверять совместимость с ассистивными технологиями и браузерами, особенно при сложных контейнерах.

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