Popper.js — это библиотека для позиционирования всплывающих элементов, таких как тултипы, выпадающие меню и модальные окна. Одной из ключевых задач при работе с такими элементами является обеспечение доступности, включая поддержку клавиатурной навигации. Popper.js сам по себе отвечает за позиционирование и корректное отображение элементов, но интеграция с клавиатурным управлением требует дополнительной логики на стороне JavaScript.
Навигация с клавиатуры обычно строится вокруг следующих событий:
keydown — основное событие, которое
позволяет перехватывать нажатия клавиш.focus и blur — позволяют
отслеживать активный элемент.tabindex — атрибут HTML, определяющий
порядок обхода элементов клавишей Tab.В Popper.js всплывающий элемент обычно связан с триггером (например, кнопкой), который управляет его открытием и закрытием. Для клавиатурного управления нужно обеспечить:
import { createPopper } from '@popperjs/core';
const button = document.querySelector('#dropdownButton');
const menu = document.querySelector('#dropdownMenu');
const popperInstance = createPopper(button, menu, {
placement: 'bottom-start',
});
function toggleMenu() {
menu.classList.toggle('show');
if (menu.classList.contains('show')) {
menu.querySelector('button, [tabindex]:not([tabindex="-1"])').focus();
}
}
button.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
toggleMenu();
}
});
menu.addEventListener('keydown', (event) => {
const focusableElements = menu.querySelectorAll('button, [tabindex]:not([tabindex="-1"])');
const focusArray = Array.from(focusableElements);
const currentIndex = focusArray.indexOf(document.activeElement);
if (event.key === 'ArrowDown') {
event.preventDefault();
const nextIndex = (currentIndex + 1) % focusArray.length;
focusArray[nextIndex].focus();
}
if (event.key === 'ArrowUp') {
event.preventDefault();
const prevIndex = (currentIndex - 1 + focusArray.length) % focusArray.length;
focusArray[prevIndex].focus();
}
if (event.key === 'Escape') {
menu.classList.remove('show');
button.focus();
}
});
Popper.js управляет позиционированием и может
динамически изменять расположение всплывающего элемента с помощью
модификаторов (modifiers). При реализации клавиатурного
взаимодействия важно учитывать:
Пример добавления модификатора для постоянного обновления позиции при открытии:
const popperInstance = createPopper(button, menu, {
placement: 'bottom-start',
modifiers: [
{
name: 'eventListeners',
enabled: true,
},
],
});
role="menu" и role="menuitem" для
кнопок внутри, чтобы экранные читалки корректно воспринимали
структуру.<div id="dropdownMenu" role="menu">
<button role="menuitem">Опция 1</button>
<button role="menuitem">Опция 2</button>
</div>
Tabindex Использовать tabindex="0"
для элементов, которые должны быть доступны через Tab, и
tabindex="-1" для элементов, которые не должны быть
фокусируемыми по умолчанию.
Фокус при открытии/закрытии При открытии Popper фокус нужно переводить на первый элемент меню. При закрытии возвращать фокус на триггер.
Обработка Esc и Arrow Keys Esc
закрывает меню и возвращает фокус на триггер. Стрелки
ArrowDown и ArrowUp перемещают фокус между
элементами.
ArrowRight и
ArrowLeft для перехода между группами..show или aria-expanded="true/false"
помогает синхронизировать видимость Popper с доступностью.menu.setAttribute('aria-hidden', !menu.classList.contains('show'));
button.setAttribute('aria-expanded', menu.classList.contains('show'));
Для сложных интерфейсов Popper.js часто интегрируют с библиотеками управления состоянием (React, Vue, Svelte). В этих случаях клавиатурная навигация реализуется через:
isOpen;useEffect (React) или
watch (Vue);popperInstance.update() при изменении содержимого
Popper для корректного позиционирования перед назначением фокуса.Клавиатурная навигация в Popper.js требует внимания к фокусу, ARIA-атрибутам, и динамическому позиционированию, чтобы интерфейсы оставались доступными и предсказуемыми для пользователей, использующих клавиатуру.