Для работы с Focus-trap необходимо установить пакет через npm или yarn:
npm install focus-trap
или
yarn add focus-trap
Подключение в проекте на ES6:
import { createFocusTrap } from 'focus-trap';
Для использования в браузере без сборщика можно подключить через CDN:
<script src="https://unpkg.com/focus-trap/dist/focus-trap.umd.js"></script>
После подключения доступна глобальная функция
focusTrap.createFocusTrap.
Focus-trap обеспечивает контроль клавиатурной навигации, ограничивая её внутри заданного контейнера. Это критично для модальных окон, диалогов подтверждения, меню и любых интерактивных областей, где фокус не должен уходить за пределы компонента.
Ключевые моменты:
Для модального диалога подтверждения:
const confirmDialog = document.getElementById('confirm-dialog');
const focusTrap = createFocusTrap(confirmDialog, {
initialFocus: '#confirm-yes',
escapeDeactivates: true,
clickOutsideDeactivates: true
});
// Активация при открытии диалога
function openDialog() {
confirmDialog.style.display = 'block';
focusTrap.activate();
}
// Деактивация при закрытии
function closeDialog() {
focusTrap.deactivate();
confirmDialog.style.display = 'none';
}
Параметры настройки:
initialFocus — элемент, на который будет установлен
фокус при активации.escapeDeactivates — позволяет закрывать ловушку при
нажатии Escape.clickOutsideDeactivates — закрывает ловушку при клике
вне контейнера.allowOutsideClick — если указано, клики по элементам
вне контейнера допускаются без деактивации.returnFocusOnDeactivate — возвращает фокус на элемент,
который был активен до активации ловушки.Focus-trap умеет реагировать на клавиши Tab, Shift+Tab и Escape, предотвращая уход фокуса за пределы контейнера.
Для более гибкого контроля можно использовать события активации и деактивации:
const focusTrap = createFocusTrap(confirmDialog, {
onActivate: () => console.log('Trap activated'),
onDeactivate: () => console.log('Trap deactivated'),
});
Если в приложении используется несколько модальных окон, важно управлять активными ловушками:
const dialogA = createFocusTrap('#dialogA', { escapeDeactivates: true });
const dialogB = createFocusTrap('#dialogB', { escapeDeactivates: true });
function openDialogA() {
dialogB.deactivate(); // гарантируем, что другая ловушка отключена
dialogA.activate();
}
function openDialogB() {
dialogA.deactivate();
dialogB.activate();
}
Поддержка нескольких ловушек требует внимательного управления активацией и деактивацией, чтобы избежать конфликтов фокуса.
Для корректной работы Focus-trap элементы должны
быть видимыми и доступными для фокуса. Чаще всего используется
комбинация display: none и visibility: hidden
при закрытии модального окна.
#confirm-dialog {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 2rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
При открытии окна display меняется на
block, после чего активируется ловушка.
Для модальных диалогов подтверждения важно использовать правильные атрибуты:
<div id="confirm-dialog" role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Подтвердите действие</h2>
<button id="confirm-yes">Да</button>
<button id="confirm-no">Нет</button>
</div>
role="dialog" сообщает вспомогательным технологиям о
характере элемента.aria-modal="true" указывает, что взаимодействие за
пределами диалога временно недоступно.aria-labelledby связывает заголовок с диалогом для
скринридеров.Focus-trap поддерживает:
initialFocus можно
задать как CSS-селектор, функцию или HTMLElement.Пример с асинхронной загрузкой контента:
async function openAsyncDialog() {
await loadDialogContent();
focusTrap.activate();
}
const dialog = document.getElementById('confirm-dialog');
const openBtn = document.getElementById('open-dialog');
const focusTrap = createFocusTrap(dialog, {
initialFocus: '#confirm-yes',
escapeDeactivates: true,
clickOutsideDeactivates: true,
returnFocusOnDeactivate: true
});
openBtn.addEventListener('click', () => {
dialog.style.display = 'block';
focusTrap.activate();
});
dialog.querySelector('#confirm-no').addEventListener('click', () => {
focusTrap.deactivate();
dialog.style.display = 'none';
});
Фокус теперь ограничен внутри модального окна, пользователь может безопасно взаимодействовать с кнопками подтверждения или отмены, а после закрытия фокус возвращается на исходную кнопку открытия.