Метод updateContainerElements() используется для
динамического обновления контейнеров, внутри которых действует ловушка
фокуса. В библиотеке focus-trap контейнеры определяют
область DOM, в пределах которой пользователь может перемещать фокус с
помощью клавиатуры. Когда структура интерфейса изменяется — например,
при добавлении новых элементов, изменении модального окна или
переключении активной панели — первоначально заданный список контейнеров
может устареть.
updateContainerElements() позволяет изменить этот список
без уничтожения текущей ловушки и создания новой.
Метод особенно важен в динамических интерфейсах: SPA-приложениях, сложных модальных системах, интерфейсах с вкладками, аккордеонами и условным рендерингом.
Внутренний механизм focus-trap строится вокруг понятия контейнеров. Контейнер — это DOM-элемент, внутри которого разрешено перемещение фокуса.
При инициализации ловушки контейнеры передаются в функцию создания:
import { createFocusTrap } from 'focus-trap';
const trap = createFocusTrap(document.querySelector('#modal'));
В этом примере контейнером является элемент #modal. Все
фокусируемые элементы внутри него образуют замкнутую область
навигации.
Поддерживается несколько контейнеров:
const trap = createFocusTrap([
document.querySelector('#panel-left'),
document.querySelector('#panel-right')
]);
Фокус может перемещаться между элементами внутри обоих контейнеров, но не выходит за их пределы.
Если DOM-структура изменяется (например, один контейнер удаляется, а
другой добавляется), первоначальный список контейнеров перестает
соответствовать реальному интерфейсу. В такой ситуации применяется
updateContainerElements().
trap.updateContainerElements(containerElements)
Параметр
| Параметр | Тип | Описание |
|---|---|---|
containerElements |
HTMLElement |
HTMLElement[] |
Метод принимает:
Переданные элементы полностью заменяют предыдущий список контейнеров.
Внутри focus-trap выполняются несколько этапов:
Метод не:
Обновляется исключительно область, в которой удерживается фокус.
Интерфейс модального окна может изменять внутренний контент, полностью заменяя корневой контейнер.
const trap = createFocusTrap('#modal');
trap.activate();
const newContainer = document.querySelector('#modal-step-2');
trap.updateContainerElements(newContainer);
После вызова метода:
Иногда интерфейс состоит из нескольких логически связанных областей. Например:
const modal = document.querySelector('#modal');
const toolbar = document.querySelector('#toolbar');
const trap = createFocusTrap([modal, toolbar]);
trap.activate();
Если интерфейс перестраивается:
const newPanel = document.querySelector('#side-panel');
trap.updateContainerElements([
modal,
newPanel
]);
После обновления фокус будет перемещаться внутри:
modalside-panelПанель toolbar исключается из ловушки.
В современных интерфейсах элементы часто появляются и исчезают без перезагрузки страницы.
Типичные сценарии:
Рассмотрим пошаговую форму.
<div id="modal">
<div id="step1">
<button>Далее</button>
</div>
<div id="step2" hidden>
<input type="text">
<button>Готово</button>
</div>
</div>
const step1 = document.querySelector('#step1');
const step2 = document.querySelector('#step2');
const trap = createFocusTrap(step1);
trap.activate();
document.querySelector('#step1 button').addEventListener('click', () => {
step1.hidden = true;
step2.hidden = false;
trap.updateContainerElements(step2);
});
При переходе на второй шаг:
В фреймворках (React, Vue, Svelte) DOM может изменяться после обновления состояния.
Пример в React-подобной логике:
useEffect(() => {
trap.updateContainerElements(ref.current);
}, [currentStep]);
Когда компонент перерисовывается и контейнер меняется, ловушка получает обновлённый DOM-элемент.
Если ловушка уже активна, updateContainerElements()
выполняет дополнительную проверку текущего фокуса.
Возможны два сценария:
1. Фокус находится внутри нового контейнера
Никаких изменений не требуется. Навигация продолжается нормально.
2. Фокус находится вне новых контейнеров
Focus-trap выполняет перемещение фокуса:
fallbackFocus, если он указан.Пример конфигурации:
const trap = createFocusTrap('#modal', {
fallbackFocus: '#modal'
});
Контейнер может содержать элементы, которые появляются асинхронно.
const trap = createFocusTrap('#menu');
trap.activate();
После загрузки новых элементов:
loadMenuItems().then(() => {
const menu = document.querySelector('#menu');
trap.updateContainerElements(menu);
});
Это заставляет библиотеку пересканировать фокусируемые элементы.
Focus-trap использует библиотеку tabbable для определения элементов, доступных через клавишу Tab.
Каждый вызов updateContainerElements() инициирует:
tabbable элементовИменно поэтому метод применяется после значительных изменений DOM.
Иногда разработчики создают новый trap вместо обновления контейнера.
Неэффективный подход:
trap.deactivate();
trap = createFocusTrap(newContainer);
trap.activate();
Правильный способ:
trap.updateContainerElements(newContainer);
Преимущества:
Если переданный контейнер:
tabindexfocus-trap может не иметь точки для установки фокуса.
Решение — использование fallbackFocus.
const trap = createFocusTrap('#modal', {
fallbackFocus: '#modal'
});
Даже если контейнер пуст, фокус будет установлен на сам контейнер.
Перед обновлением контейнеров важно убедиться, что элементы существуют.
Неправильный пример:
trap.updateContainerElements(document.querySelector('#missing'));
Если элемент отсутствует, возможны ошибки.
Безопасный вариант:
const container = document.querySelector('#panel');
if (container) {
trap.updateContainerElements(container);
}
Некоторые интерфейсы (например, в React) используют порталы, где части модального окна рендерятся в разных частях DOM.
const trap = createFocusTrap([
modalContent,
portalFooter
]);
При изменении портала:
trap.updateContainerElements([
modalContent,
newPortalFooter
]);
Фокус остается ограниченным этими областями независимо от их положения в DOM.
trap.updateContainerElements(newElement);
если newElement еще не добавлен в DOM.
Следует выполнять обновление после рендера.
Метод ожидает массив элементов, а не NodeList.
Неправильно:
trap.updateContainerElements(document.querySelectorAll('.panel'));
Правильно:
trap.updateContainerElements(
Array.from(document.querySelectorAll('.panel'))
);
Метод принимает DOM-элементы, а не селекторы.
Неправильно:
trap.updateContainerElements('#modal');
Правильно:
trap.updateContainerElements(document.querySelector('#modal'));
updateContainerElements() выполняет пересканирование DOM
внутри контейнеров.
На больших интерфейсах частые вызовы могут привести к избыточной нагрузке.
Рекомендуемые практики:
В сложных модальных интерфейсах контейнер может изменяться при:
function switchTab(tabElement) {
document.querySelectorAll('.tab').forEach(el => {
el.hidden = true;
});
tabElement.hidden = false;
trap.updateContainerElements(tabElement);
}
Фокус всегда ограничен активной вкладкой.
Главная задача focus-trap — предотвращать выход фокуса за пределы активного интерфейса, например модального окна.
updateContainerElements() обеспечивает корректную работу
этой логики в динамических интерфейсах, где DOM постоянно
изменяется.
Благодаря этому:
Метод является ключевым инструментом управления ловушкой фокуса в приложениях со сложной и изменяемой структурой DOM.