Floating UI — это библиотека, предназначенная для точного позиционирования всплывающих элементов относительно других элементов на странице. В контексте Web Components она становится особенно полезной, поскольку обеспечивает гибкость при работе с инкапсулированной разметкой и Shadow DOM.
При использовании Custom Elements и Shadow DOM позиционирование всплывающих элементов сталкивается с проблемой изоляции контекста. Floating UI решает это через предоставление функций, которые принимают корень документа или конкретный контейнер, позволяя вычислять позиции даже внутри Shadow DOM.
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
class TooltipElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
`;
}
connectedCallback() {
const trigger = this.shadowRoot.querySelector('#trigger');
const tooltip = this.shadowRoot.querySelector('#tooltip');
trigger.addEventListener('mouseenter', async () => {
tooltip.style.display = 'block';
const { x, y } = await computePosition(trigger, tooltip, {
middleware: [offset(10), flip(), shift({ padding: 5 })],
});
Object.assign(tooltip.style, {
left: `${x}px`,
top: `${y}px`,
});
});
trigger.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
});
}
}
customElements.define('tooltip-element', TooltipElement);
Middleware в Floating UI позволяет изменять поведение позиционирования:
Эти функции можно комбинировать, чтобы обеспечить адаптивное и безопасное позиционирование всплывающих компонентов внутри Shadow DOM.
Floating UI умеет работать с элементами внутри Shadow DOM, если передавать правильные референсы:
const trigger = this.shadowRoot.querySelector('#trigger');
const floating = this.shadowRoot.querySelector('#tooltip');
computePosition(trigger, floating, { middleware: [offset(8), flip()] });
Важно, что Floating UI не требует глобальных селекторов, что идеально подходит для инкапсулированных компонентов, предотвращая конфликты стилей и идентификаторов.
При изменении размеров окна или содержимого компонента, позиция всплывающего элемента может стать некорректной. Для этого Floating UI предоставляет autoUpdate:
import { autoUpdate } from '@floating-ui/dom';
autoUpdate(trigger, tooltip, () => {
computePosition(trigger, tooltip, { middleware: [offset(10), flip()] })
.then(({ x, y }) => {
Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px` });
});
});
autoUpdate автоматически пересчитывает координаты при:
Для многоуровневых модальных окон, кастомных dropdown и комбобоксов Floating UI позволяет:
placement (top, bottom-start,
right-end и др.).computePosition(trigger, floating, {
placement: 'bottom-start',
middleware: [
offset(12),
flip({ fallbackPlacements: ['top', 'right'] }),
shift({ padding: 5 })
],
});
autoUpdate или слушатели событий
resize и scroll.