Библиотека Floating UI предоставляет мощные инструменты для
динамического позиционирования элементов в интерфейсе. Одним из ключевых
аспектов является реакция на изменения в прокрутке
(scroll) и размере окна
(resize). Эти события напрямую влияют на позиционирование
всплывающих элементов, таких как тултипы, поповеры или дропдауны.
Floating UI обеспечивает оптимизированные механизмы для отслеживания
таких изменений и автоматической корректировки позиции.
Floating UI использует контексты прокрутки (scroll parents) и наблюдатели размеров (resize observers) для определения актуальной позиции элемента относительно цели. Главные концепции:
scroll события.window.resize.При использовании Floating UI рекомендуется применять встроенные механизмы автообновления позиции, чтобы не писать собственный код для слушателей событий.
autoUpdateФункция autoUpdate позволяет автоматически следить за
scroll и resize событий, а также за изменениями DOM:
import { computePosition, autoUpdate } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
const cleanup = autoUpdate(reference, floating, () => {
computePosition(reference, floating, {
placement: 'bottom',
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`,
});
});
});
Ключевые моменты работы autoUpdate:
cleanup, которую необходимо вызвать
при удалении элементов, чтобы убрать подписки и не допустить утечки
памяти.Для оптимизации производительности можно указать конкретные scroll parents, чтобы не реагировать на каждый скролл документа:
import { computePosition, autoUpdate } from '@floating-ui/dom';
const scrollParents = [document.body, document.querySelector('.container')];
const cleanup = autoUpdate(reference, floating, updatePosition, {
ancestorScroll: scrollParents,
});
function updatePosition() {
computePosition(reference, floating).then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
}
Такой подход особенно полезен для сложных интерфейсов с большим количеством вложенных скроллов, где автоматическая подписка на все элементы может быть избыточной.
При постоянной прокрутке и изменении размеров важно минимизировать
лишние вызовы computePosition. Floating UI предлагает
несколько механизмов:
requestAnimationFrame –
обеспечивает синхронизацию с перерисовкой браузера, снижая нагрузку на
главный поток.Пример с requestAnimationFrame:
let frame;
function updatePosition() {
if (frame) cancelAnimationFrame(frame);
frame = requestAnimationFrame(() => {
computePosition(reference, floating).then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
});
}
ResizeObserver напрямуюИногда требуется отслеживать изменения размеров не только
floating элемента, но и reference. Floating UI позволяет это
через ResizeObserver:
const ro = new ResizeObserver(() => updatePosition());
ro.observe(reference);
ro.observe(floating);
// Для очистки подписки
ro.disconnect();
Преимущество – моментальное реагирование на
изменение размеров без необходимости слушать глобальное событие
resize.
Floating UI поддерживает виртуальные reference
элементы, например, для canvas или custom рендеров. В таких
случаях scroll и resize события отслеживаются вручную через
autoUpdate с передачей кастомной логики:
const virtualReference = {
getBoundingClientRect: () => ({
x: 100,
y: 50,
width: 200,
height: 50,
top: 50,
left: 100,
right: 300,
bottom: 100,
}),
};
autoUpdate(virtualReference, floating, updatePosition);
cleanup при удалении
элементов, чтобы предотвратить утечки памяти.requestAnimationFrame для
синхронизации обновлений с рендером.Следование этим подходам обеспечивает стабильное, производительное и предсказуемое позиционирование элементов интерфейса в динамичных приложениях, особенно при частых скроллах и изменениях размеров окна.