Создание боковой панели

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

Библиотека обеспечивает:

  • Автоматическое перенаправление фокуса на первый доступный элемент внутри контейнера.
  • Циклическую навигацию по элементам внутри trap.
  • Возможность восстановления фокуса на исходный элемент после деактивации trap.

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

Focus-trap можно использовать через npm или CDN:

npm install focus-trap

Или подключить через CDN:

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

Импорт через ES-модули:

import { createFocusTrap } from 'focus-trap';

Создание trap для боковой панели

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

<div id="sidebar" class="sidebar">
  <button id="closeBtn">Закрыть</button>
  <a href="#home">Главная</a>
  <a href="#about">О проекте</a>
  <a href="#contact">Контакты</a>
  <input type="text" placeholder="Поиск">
</div>

<button id="openBtn">Открыть панель</button>

Инициализация Focus-trap

const sidebar = document.getElementById('sidebar');

const focusTrap = createFocusTrap(sidebar, {
  // Перемещаем фокус на первый элемент при активации
  initialFocus: '#closeBtn',

  // Разрешаем выход по нажатию Escape
  escapeDeactivates: true,

  // Восстанавливаем фокус на кнопке открытия после закрытия
  onDeactivate: () => document.getElementById('openBtn').focus(),

  // Поддержка циклической навигации
  tabbableOptions: { displayCheck: 'full' }
});

document.getElementById('openBtn').addEventListener('click', () => {
  sidebar.style.display = 'block';
  focusTrap.activate();
});

document.getElementById('closeBtn').addEventListener('click', () => {
  sidebar.style.display = 'none';
  focusTrap.deactivate();
});

Настройки и опции

initialFocus — элемент, который получает фокус при активации trap. Можно передавать селектор или DOM-элемент.

escapeDeactivates — булево значение, определяющее возможность деактивации trap при нажатии Escape.

clickOutsideDeactivates — позволяет закрывать trap при клике вне контейнера:

clickOutsideDeactivates: true

allowOutsideClick — если true, клики вне контейнера проходят, но trap сохраняется активным.

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

tabbableOptions — настройки для элементов, которые могут быть доступны через Tab.

Управление динамическим содержимым

Если боковая панель подгружает элементы динамически, рекомендуется пересоздавать trap или обновлять список tabbable-элементов:

focusTrap.updateTabbables();

Это гарантирует корректную навигацию по новым элементам.

Интеграция с анимацией

Для плавного появления панели с анимацией нужно активировать trap после завершения анимации, иначе фокус может установиться на скрытый элемент:

sidebar.classList.add('show'); // CSS-анимация
sidebar.addEventListener('transitionend', () => {
  focusTrap.activate();
}, { once: true });

Совместимость с ARIA

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

<div id="sidebar" role="dialog" aria-modal="true">

Focus-trap автоматически поддерживает ARIA-совместимые модальные контейнеры, но явное указание aria-modal повышает совместимость с экранными читалками.

Советы по практике

  • Не использовать display: none до активации trap — элементы с display: none не могут получить фокус.
  • Всегда восстанавливать фокус на элемент открытия — предотвращает потерю контекста для пользователей клавиатуры.
  • Использовать initialFocus для логичного UX — обычно первый интерактивный элемент в панели или кнопка закрытия.
  • Комбинировать с управлением Escape и кликами вне панели — обеспечивает естественное поведение закрытия.

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