Floating UI — это современная библиотека для позиционирования всплывающих элементов в веб-приложениях. Помимо точного позиционирования, важным аспектом является управление фокусом и порядком табуляции для обеспечения доступности и корректного взаимодействия с пользователем.
Когда всплывающий элемент появляется на странице, необходимо определить, куда будет установлен фокус. Floating UI не управляет фокусом автоматически, но предоставляет инструменты для интеграции с библиотеками вроде React, Vue или нативными DOM-методами.
Первичный фокус Обычно фокус устанавливается на первый интерактивный элемент внутри floating-элемента (кнопка, ссылка, input). Это позволяет пользователю сразу взаимодействовать с элементом через клавиатуру.
const floatingRef = document.querySelector('#tooltip');
const firstFocusable = floatingRef.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
firstFocusable?.focus();Фокус после закрытия После закрытия всплывающего элемента рекомендуется возвращать фокус на элемент-триггер, который вызвал открытие. Это предотвращает потерю контекста для пользователя.
const trigger = document.querySelector('#open-tooltip');
trigger.focus();Фокус-контейнер Для сложных floating-элементов (например, меню или модальные окна) удобно использовать focus trap — ограничение фокуса внутри контейнера. Floating UI интегрируется с библиотеками вроде focus-trap или можно реализовать самостоятельно:
floatingRef.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
const focusableElements = Array.from(floatingRef.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
));
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
}
}
});Tab order определяет последовательность, в которой элементы получают фокус при навигации клавишей Tab. Для floating-элементов важно корректно интегрировать их в общий порядок табуляции страницы.
Встраивание в документ Floating UI позиционирует
элементы в DOM рядом с корнем (document.body) для
предотвращения переполнения контейнеров. Чтобы сохранить логический
порядок табуляции, нужно явно управлять tabindex:
<div id="menu" tabindex="-1">
<button>Пункт 1</button>
<button>Пункт 2</button>
</div>
Здесь tabindex="-1" предотвращает случайное попадание
фокуса на контейнер, сохраняя фокус на интерактивных элементах.
Динамическая вставка Всплывающие элементы часто создаются динамически. В таких случаях важно убедиться, что первый интерактивный элемент получает фокус автоматически, а остальные элементы правильно участвуют в последовательности табуляции.
Управление клавишами Для сложных компонентов, таких как дропдауны или контекстные меню, полезно обрабатывать клавиши стрелок, Enter и Escape, чтобы пользователь мог навигировать без мыши. Floating UI предоставляет middleware для определения размеров, позиции и видимости, что помогает корректно позиционировать элементы при перемещении фокуса.
В React можно комбинировать Floating UI с хуками для управления фокусом:
import { useFloating } from '@floating-ui/react-dom';
function Dropdown() {
const { x, y, reference, floating, strategy } = useFloating();
const [isOpen, setIsOpen] = React.useState(false);
React.useEffect(() => {
if (isOpen) {
const firstFocusable = floating.current?.querySelector('button, [tabindex]:not([tabindex="-1"])');
firstFocusable?.focus();
}
}, [isOpen]);
return (
<>
<button ref={reference} onCl ick={() => setIsOpen(prev => !prev)}>Меню</button>
{isOpen && (
<div ref={floating} style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}>
<button>Пункт 1</button>
<button>Пункт 2</button>
</div>
)}
</>
);
}
ARIA-атрибуты Всплывающие элементы должны иметь
role и aria-* атрибуты для корректной работы с
экранными считывателями. Например:
<div role="menu" aria-labelledby="menu-button">
<button role="menuitem">Пункт 1</button>
<button role="menuitem">Пункт 2</button>
</div>Esc для закрытия Обработка клавиши Escape позволяет пользователю быстро закрыть floating-элемент и вернуть фокус на триггер.
Tab trap Обеспечивает фокус только внутри всплывающего элемента, предотвращая переход на элементы страницы, которые временно недоступны.
Эти подходы позволяют создавать полностью интерактивные, доступные и предсказуемые floating-элементы в любых веб-приложениях.