Floating UI — это современная библиотека для управления позиционированием всплывающих элементов (popper, tooltip, dropdown) в веб-приложениях на JavaScript. Она обеспечивает точное управление расположением элемента относительно якоря, учитывает размеры экрана, перекрытия и динамическое изменение контента.
Ключевой принцип работы Floating UI — разделение
вычисления позиции от непосредственного отображения. Это достигается
через core API, которое возвращает объект с
координатами x и y, а также возможные
смещения (offsets), которые позволяют адаптировать
позицию под конкретные условия.
Пример базового вызова:
import { computePosition } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
computePosition(reference, floating).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`
});
});
Вывод: computePosition вычисляет
оптимальные координаты с учётом всех ограничений и может быть вызван
повторно при изменении размера окна или содержимого.
Floating UI использует middleware, которые позволяют управлять поведением позиционирования:
Пример использования middleware:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
computePosition(reference, floating, {
middleware: [offset(10), flip(), shift({ padding: 5 })]
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`
});
});
Для элементов с изменяющимся содержимым или при скролле удобно использовать autoUpdate, которая автоматически пересчитывает позицию:
import { autoUpdate } from '@floating-ui/dom';
autoUpdate(reference, floating, () => {
computePosition(reference, floating, { middleware: [offset(10), flip()] })
.then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
});
Особенности: autoUpdate отслеживает
изменения размеров, скролла и видимости элемента, что исключает ручное
управление событиями resize/scroll.
Floating UI оптимизирована для высокой производительности, но при работе с большим числом элементов важно понимать узкие места:
computePosition на каждом кадре могут
быть дорогостоящими.flip и shift выполняют расчёт
относительно размеров viewport и reference, что при сложной верстке
увеличивает нагрузку.Пример throttling:
import { computePosition, offset } from '@floating-ui/dom';
import throttle from 'lodash.throttle';
const updatePosition = throttle(() => {
computePosition(reference, floating, { middleware: [offset(10)] })
.then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
}, 50);
window.addEventListener('scroll', updatePosition);
window.addEventListener('resize', updatePosition);
Для оценки производительности можно использовать несколько показателей:
performance.now() до и после вызова
computePosition.Пример сбора метрик:
const start = performance.now();
computePosition(reference, floating, { middleware: [offset(10), flip()] })
.then(() => {
const duration = performance.now() - start;
console.log(`Position computed in ${duration.toFixed(2)}ms`);
});
autoUpdate на группу
элементов, если возможно, а не по каждому элементу
отдельно.transform: translate3d
для плавного позиционирования без перерасчета layout.computePosition(reference, floating, {
placement: 'top',
middleware: [
offset(8),
flip(),
shift({ padding: 5 }),
size({ apply({ rects, availableHeight }) {
floating.style.maxHeight = `${availableHeight}px`;
}})
]
});
Особенности: комбинация flip,
shift и size позволяет tooltip адаптироваться
под размеры экрана и длину контента.
autoUpdate(reference, floating, () => {
computePosition(reference, floating, {
middleware: [offset(4), flip(), shift()]
}).then(({ x, y }) => {
floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
});
});
Вывод: использование translate3d
повышает производительность анимаций и уменьшает влияние на layout, что
критично для сложных интерфейсов.
Floating UI предоставляет полный набор инструментов для точного позиционирования элементов, при этом оптимизация и профилирование позволяют масштабировать интерфейсы с большим числом динамических элементов без ущерба производительности.