Angular

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

Установка через npm:

npm install focus-trap

Подключение в Angular:

import { createFocusTrap, FocusTrap } from 'focus-trap';

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

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

let trap: FocusTrap;

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

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

  • escapeDeactivates — деактивация при нажатии Escape.
  • clickOutsideDeactivates — выход при клике вне контейнера.
  • initialFocus — элемент, который получает фокус при активации.
  • fallbackFocus — элемент, на который фокус вернётся, если указанный initialFocus отсутствует.

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

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

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

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

openModal() {
  trap.activate();
}

closeModal() {
  trap.deactivate();
}

Интеграция с Angular

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

  1. Инициализировать фокус-трап после рендеринга DOM-элементов, например, в ngAfterViewInit.
  2. Хранить ссылку на объект FocusTrap в компоненте, чтобы управлять его активацией и деактивацией.
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { createFocusTrap, FocusTrap } from 'focus-trap';

@Component({
  selector: 'app-modal',
  templateUrl: './modal.component.html',
})
export class ModalComponent implements AfterViewInit {
  @ViewChild('modal') modalRef!: ElementRef;
  private trap!: FocusTrap;

  ngAfterViewInit() {
    this.trap = createFocusTrap(this.modalRef.nativeElement, {
      escapeDeactivates: true,
      clickOutsideDeactivates: true,
    });
  }

  open() {
    this.trap.activate();
  }

  close() {
    this.trap.deactivate();
  }
}

Поддержка динамических элементов

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

trap = createFocusTrap('#modal', {
  allowOutsideClick: true,
  fallbackFocus: '#modal',
});

Для динамического контента часто используют перезапуск фокус-трапа:

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

Особенности работы с формами

Focus-trap корректно обрабатывает элементы форм:

  • input, textarea, select, button — автоматически включаются в порядок табуляции.
  • Для скрытых или disabled-элементов фокус игнорируется.
  • При необходимости можно задать initialFocus на поле формы для удобства пользователя.
trap = createFocusTrap('#form-modal', {
  initialFocus: '#form-modal input[name="username"]',
});

Совместимость с другими библиотеками

Focus-trap можно сочетать с:

  • Angular Material — для модальных диалогов и sidenav.
  • NgRx или любой другой системой управления состоянием — для динамического управления открытием/закрытием.
  • Анимации и transitions — при использовании pause() можно временно позволять пользователю взаимодействовать с элементами вне контейнера.

Рекомендации по производительности

  • Создавать фокус-трап только для интерактивных контейнеров, где необходим контроль фокуса.
  • Деактивировать фокус-трап при закрытии модального окна, чтобы освободить ресурсы.
  • Использовать pause() вместо deactivate() для временных операций, чтобы не терять состояние фокус-трапа.

События и колбэки

Focus-trap поддерживает следующие хуки:

  • onActivate — вызывается при активации.
  • onDeactivate — вызывается при деактивации.
  • onPostActivate — после активации и установки фокуса на initialFocus.
  • onPostDeactivate — после деактивации, позволяет восстановить фокус на исходный элемент.

Пример:

trap = createFocusTrap('#modal', {
  onActivate: () => console.log('Focus trap активирован'),
  onDeactivate: () => console.log('Focus trap деактивирован'),
});

Эти события полезны для логирования, анимаций и интеграции с Angular-сервисами состояния.

Расширенные настройки

  • tabbableOptions — позволяет детально управлять тем, какие элементы включать в порядок табуляции.
  • returnFocusOnDeactivate — возвращает фокус на элемент, с которого он ушёл до активации фокус-трапа.
  • checkCanFocusTrap — асинхронная функция для проверки, готов ли контейнер принять фокус.
trap = createFocusTrap('#modal', {
  returnFocusOnDeactivate: true,
  tabbableOptions: { displayCheck: 'full-visible' },
});

Эти настройки обеспечивают максимальную гибкость при работе с интерактивными элементами сложной структуры.