Svelte

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


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

Focus-trap распространяется как npm-пакет и поддерживает современные сборщики:

npm install focus-trap

Для использования в проекте с ES-модулями:

import { createFocusTrap } from 'focus-trap';

В случае использования через тег <script> в браузере доступен глобальный объект focusTrap.


Создание базового фокустрапа

Фокустрап создаётся с помощью функции createFocusTrap, которой передаётся DOM-элемент и объект опций:

const modal = document.getElementById('modal');
const trap = createFocusTrap(modal, {
  escapeDeactivates: true,
  clickOutsideDeactivates: true,
  allowOutsideClick: true,
});

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

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

Для активации:

trap.activate();

Для деактивации:

trap.deactivate();

Опции управления фокусом

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

  1. initialFocus Определяет элемент, который получает фокус при активации:
const trap = createFocusTrap(modal, {
  initialFocus: '#modal-input'
});

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

  1. fallbackFocus Элемент, на который фокус возвращается, если initialFocus недоступен:
fallbackFocus: modal.querySelector('button')
  1. returnFocusOnDeactivate Автоматическое возвращение фокуса на элемент, активировавший фокустрап:
returnFocusOnDeactivate: true
  1. tabbableOptions Дополнительные настройки библиотеки tabbable, используемой внутри focus-trap для вычисления фокусируемых элементов:
tabbableOptions: { displayCheck: 'none' }

Взаимодействие с Svelte

В Svelte-файлах фокустрап удобно использовать с реактивными привязками и жизненным циклом компонента (onMount, onDestroy):

<script>
  import { onMount, onDestroy } from 'svelte';
  import { createFocusTrap } from 'focus-trap';

  let modal;
  let trap;

  onMount(() => {
    trap = createFocusTrap(modal, {
      escapeDeactivates: true,
      clickOutsideDeactivates: true,
      initialFocus: '#first-input',
      returnFocusOnDeactivate: true
    });
  });

  function openModal() {
    trap.activate();
  }

  function closeModal() {
    trap.deactivate();
  }

  onDestroy(() => {
    if (trap) trap.deactivate();
  });
</script>

<div bind:this={modal} class="modal">
  <input id="first-input" type="text">
  <button on:click={closeModal}>Закрыть</button>
</div>

<button on:click={openModal}>Открыть модальное окно</button>
  • Использование bind:this позволяет получить прямой доступ к DOM-элементу Svelte.
  • onMount гарантирует, что элемент существует в DOM до создания фокустрапа.
  • onDestroy предотвращает утечки памяти при удалении компонента.

Обработка динамического контента

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

trap.updateContainerElements();

Поддержка нескольких фокустрапов

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

const firstTrap = createFocusTrap(firstModal);
const secondTrap = createFocusTrap(secondModal);

firstTrap.activate(); // второй пока не активен
secondTrap.activate(); // первый деактивируется автоматически

Расширенные возможности

  1. Фокусировка вложенных модальных окон — с помощью allowOutsideClick можно разрешить клики за пределами внутреннего модального окна, не деактивируя родительский фокустрап.
  2. Адаптация под SPA — при динамическом рендеринге компонентов в Svelte важно деактивировать старые фокустрапы, чтобы избежать «потерянного» фокуса.
  3. Управление стилями при фокусе — focus-trap не добавляет классов, но можно использовать CSS-селекторы :focus и :focus-visible для визуальной подсветки активного элемента.

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

  • Всегда устанавливать returnFocusOnDeactivate: true для сохранения логики навигации клавиатурой.
  • Для модальных окон, открываемых динамически, инициировать focus-trap только после вставки элемента в DOM.
  • Использовать initialFocus для первого интерактивного элемента, чтобы избежать «потерянного» фокуса при открытии модального окна.
  • Не полагаться на глобальный фокус: каждый отдельный модальный или всплывающий компонент должен иметь собственный фокустрап.

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