Popper.js изначально разрабатывался как библиотека для динамического позиционирования всплывающих элементов, таких как тултипы, всплывающие окна и выпадающие меню. Однако управление фокусом является критическим аспектом при работе с интерактивными элементами, особенно для доступности (accessibility) и правильного взаимодействия с пользователем. Важно понимать, как Popper.js взаимодействует с фокусируемыми элементами и какие методы предоставляет для контроля поведения фокуса.
Popper.js создаёт popper-элемент относительно reference-элемента. Фокусировка на reference-элементе или popper-элементе влияет на его видимость и поведение:
Popper.js не управляет фокусом напрямую, но важно правильно интегрировать его с механизмами фокусировки:
import { createPopper } from '@popperjs/core';
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');
const popperInstance = createPopper(button, tooltip, {
placement: 'bottom',
modifiers: [
{ name: 'offset', options: { offset: [0, 8] } },
],
});
В этом примере popper создаётся и позиционируется относительно кнопки. Для корректного управления фокусом необходимо учитывать два момента:
Чтобы обеспечить доступность и удобство для клавиатурных пользователей, необходимо передавать фокус на popper-элемент при его отображении:
button.addEventListener('click', () => {
tooltip.setAttribute('data-show', '');
tooltip.focus(); // Передача фокуса на popper
});
Если popper содержит несколько интерактивных элементов (например, меню с несколькими ссылками), рекомендуется устанавливать фокус на первый фокусируемый элемент:
const firstFocusable = tooltip.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (firstFocusable) firstFocusable.focus();
После закрытия popper важно вернуть фокус на reference-элемент, чтобы не потерять контекст взаимодействия:
function closePopper() {
tooltip.removeAttribute('data-show');
button.focus(); // Возврат фокуса на reference-элемент
}
Это особенно важно для всплывающих окон, меню и диалогов, чтобы соблюсти ARIA-рекомендации и корректную навигацию с клавиатуры.
Popper.js поддерживает систему модификаторов, которые можно использовать для интеграции с управлением фокусом. Наиболее полезные:
Пример использования модификатора preventOverflow с focus management:
const popperInstance = createPopper(button, tooltip, {
placement: 'bottom-start',
modifiers: [
{
name: 'preventOverflow',
options: { padding: 8 },
},
{
name: 'focusTrap',
enabled: true,
fn({ state }) {
const focusable = state.elements.popper.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
focusable.forEach(el => el.addEventListener('keydown', trapFocus));
}
}
],
});
В этом примере модификатор focusTrap реализует базовую
логику удержания фокуса внутри popper.
Для сложных интерфейсов (модальные окна, тултипы с формами) рекомендуется создавать focus trap, чтобы фокус не уходил за пределы popper, пока он активен:
function trapFocus(event) {
const focusableElements = tooltip.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
const firstEl = focusableElements[0];
const lastEl = focusableElements[focusableElements.length - 1];
if (event.key === 'Tab') {
if (event.shiftKey && document.activeElement === firstEl) {
event.preventDefault();
lastEl.focus();
} else if (!event.shiftKey && document.activeElement === lastEl) {
event.preventDefault();
firstEl.focus();
}
}
}
Focus trap совместно с Popper.js позволяет создавать полностью управляемые интерактивные элементы, которые корректно реагируют на клавиатурную навигацию.
При управлении фокусом необходимо синхронизировать ARIA-атрибуты с состоянием popper:
aria-haspopup="true" на reference-элементе.aria-expanded="true/false" для индикации видимости
popper.aria-controls с указанием id popper-элемента.role="tooltip" или role="menu"
на popper-элементе в зависимости от его назначения.Пример:
button.setAttribute('aria-haspopup', 'true');
button.setAttribute('aria-expanded', tooltip.hasAttribute('data-show'));
button.setAttribute('aria-controls', 'tooltip-id');
tooltip.setAttribute('role', 'tooltip');
Такое управление позволяет пользователям с ассистивными технологиями корректно ориентироваться между reference и popper-элементами.
Popper.js обеспечивает гибкость позиционирования, но управление фокусом требует явной логики. Правильная интеграция обеспечивает интерактивность, удобство и доступность пользовательских интерфейсов.