Критические уведомления используются для информирования о событиях, требующих немедленного внимания пользователя. К таким событиям относятся ошибки операций, предупреждения о потере данных, запросы подтверждения опасных действий, а также сообщения о сбоях системы. В интерфейсе они обычно реализуются в виде модальных окон, которые временно блокируют взаимодействие с остальной частью страницы.
Главная задача такого интерфейса — гарантированно направить внимание пользователя на сообщение, исключив случайное взаимодействие с элементами, находящимися за пределами уведомления.
Проблема возникает из-за поведения клавиатурной навигации. Если пользователь перемещается по интерфейсу с помощью клавиши Tab, фокус может покинуть модальное окно и перейти к элементам страницы, которые находятся под ним. Визуально пользователь остаётся в модальном окне, однако логически фокус находится вне него. Это создаёт серьёзные проблемы доступности и приводит к некорректному взаимодействию.
Библиотека Focus-trap решает эту проблему путём создания так называемой ловушки фокуса.
В обычном HTML-документе порядок перехода фокуса определяется следующими правилами:
Для модального окна такое поведение недопустимо. Пользователь не должен иметь возможности перейти к элементам вне активного диалога, пока окно не будет закрыто.
Без дополнительного управления фокус может:
Такие ошибки особенно критичны для пользователей:
Focus trap — механизм, который ограничивает перемещение фокуса внутри заданного контейнера.
Основные свойства ловушки фокуса:
Таким образом обеспечивается полностью замкнутый цикл навигации.
Focus-trap — небольшая JavaScript-библиотека, реализующая управление фокусом внутри заданного DOM-контейнера.
Основные возможности:
Библиотека используется в интерфейсных компонентах:
Установка выполняется через пакетный менеджер npm.
npm install focus-trap
Импорт в модуле:
import { createFocusTrap } from "focus-trap";
Ловушка создаётся для определённого DOM-элемента — контейнера, внутри которого должен удерживаться фокус.
HTML-разметка критического уведомления:
<div id="alert-modal" class="modal">
<h2>Ошибка операции</h2>
<p>Файл не удалось сохранить.</p>
<button id="retry">Повторить</button>
<button id="cancel">Отмена</button>
</div>
Создание ловушки:
const modal = document.querySelector("#alert-modal");
const trap = createFocusTrap(modal);
Активация:
trap.activate();
Деактивация:
trap.deactivate();
После активации пользователь может перемещаться по элементам внутри модального окна, однако переход за его пределы становится невозможным.
Focus-trap автоматически определяет элементы, которые могут принимать фокус. К ним относятся:
tabindexВнутренне библиотека использует алгоритм поиска элементов с учётом:
tabindexdisabledvisibilitydisplayЭлементы, скрытые или недоступные для взаимодействия, исключаются из последовательности.
В критических уведомлениях важно, чтобы фокус сразу попадал на значимый элемент.
Чаще всего это:
Focus-trap позволяет задать начальный элемент через параметр
initialFocus.
const trap = createFocusTrap(modal, {
initialFocus: "#retry"
});
При активации ловушки фокус автоматически перемещается на кнопку Повторить.
После закрытия критического уведомления пользователь должен вернуться к элементу, который вызвал окно.
Focus-trap отслеживает этот элемент автоматически.
Пример сценария:
Такое поведение соответствует требованиям доступности интерфейсов (WCAG).
Критические уведомления часто закрываются клавишей Escape.
Focus-trap поддерживает эту возможность через параметр
escapeDeactivates.
const trap = createFocusTrap(modal, {
escapeDeactivates: true
});
При нажатии Escape ловушка автоматически деактивируется.
В критических уведомлениях иногда запрещается взаимодействие с областью страницы за пределами окна.
Focus-trap предоставляет параметр:
clickOutsideDeactivates
Пример:
const trap = createFocusTrap(modal, {
clickOutsideDeactivates: false
});
Теперь клики вне модального окна игнорируются.
Это важно для предупреждений, связанных с:
Иногда модальное окно появляется, когда фокус находится вне DOM-структуры интерфейса или на элементе, который не должен участвовать в навигации.
В таких случаях используется параметр fallbackFocus.
const trap = createFocusTrap(modal, {
fallbackFocus: modal
});
Если внутри контейнера не найден ни один фокусируемый элемент, фокус будет установлен на сам контейнер.
HTML:
<button id="delete">Удалить файл</button>
<div id="alert" class="modal" hidden>
<h2>Удалить файл?</h2>
<p>Это действие невозможно отменить.</p>
<button id="confirm">Удалить</button>
<button id="cancel">Отмена</button>
</div>
Jav * aScript:
import { createFocusTrap } from "focus-trap";
const modal = document.querySelector("#alert");
const openBtn = document.querySelector("#delete");
const cancelBtn = document.querySelector("#cancel");
const trap = createFocusTrap(modal, {
initialFocus: "#cancel"
});
openBtn.addEventListener("click", () => {
modal.hidden = false;
trap.activate();
});
cancelBtn.addEventListener("click", () => {
trap.deactivate();
modal.hidden = true;
});
Поведение интерфейса:
Иногда поверх модального окна может появляться дополнительный диалог. Например:
Focus-trap корректно обрабатывает вложенные ловушки.
Сценарий:
После закрытия второго окна управление возвращается к первой ловушке.
Focus-trap предоставляет события, связанные с жизненным циклом ловушки.
Основные хуки:
onActivateonDeactivateonPostActivateonPostDeactivateПример:
const trap = createFocusTrap(modal, {
onActivate() {
console.log("Ловушка активирована");
},
onDeactivate() {
console.log("Ловушка отключена");
}
});
Такие обработчики позволяют:
Focus-trap часто используется совместно с фреймворками:
В экосистеме React существует обёртка focus-trap-react, которая автоматически управляет жизненным циклом компонента.
Основная логика при этом остаётся той же: при монтировании компонента активируется ловушка, при размонтировании — деактивируется.
Правильная реализация критических уведомлений должна соответствовать требованиям:
Основные правила:
Focus-trap реализует эти требования на уровне управления DOM-фокусом.
Отсутствие фокусируемых элементов
Если внутри модального окна нет ни одного интерактивного элемента, ловушка не сможет корректно работать.
Решение — добавить tabindex="-1" контейнеру или
использовать fallbackFocus.
Скрытые элементы
Иногда элементы остаются в DOM, но скрываются через CSS. Библиотека может определить их как доступные для фокуса.
Рекомендуется:
display: noneНесинхронное открытие модального окна
Если ловушка активируется до появления элементов в DOM, фокус может не установиться.
Правильная последовательность:
Focus-trap не выполняет тяжёлых операций и практически не влияет на производительность интерфейса. Основные действия происходят только при:
Поэтому библиотека подходит даже для сложных интерфейсов с большим количеством компонентов.
Использование специализированной библиотеки вместо самостоятельной реализации обеспечивает:
В крупных интерфейсных системах управление фокусом становится одной из ключевых задач доступности, а Focus-trap предоставляет готовый и надёжный механизм её решения.