Floating UI — это библиотека для позиционирования всплывающих элементов в веб-приложениях. Основная цель её использования — точное управление положением таких элементов (tooltip, popover, dropdown и др.) относительно целевого элемента. Эффективная работа с библиотекой требует понимания, как минимизировать вычислительные затраты и оптимизировать производительность.
Floating UI опирается на модель «middleware», которая выполняет цепочку операций для вычисления позиции элемента. Каждое промежуточное звено (middleware) может изменять положение, учитывать ограничения экрана, смещения, перекрытия и пр. Основные middleware:
offset — смещает элемент относительно reference.flip — проверяет доступное пространство и при
необходимости меняет сторону.shift — корректирует элемент, чтобы он оставался в
видимой области.size — динамически подбирает размеры
floating-элемента.Каждое middleware выполняет вычисления с координатами, размерами и ограничениями viewport. Если их применять без оптимизации, количество операций растёт пропорционально числу элементов и middleware, что может вызвать тормоза на страницах с множеством интерактивных блоков.
Использование autoUpdate только при
необходимости Функция
autoUpdate(reference, floating, callback) отслеживает
изменения размеров и прокрутки, вызывая перерасчёт позиции. В проектах с
большим количеством элементов постоянное слежение создаёт нагрузку.
Решение:
import { autoUpdate } from '@floating-ui/dom';
const cleanup = autoUpdate(reference, floating, updatePosition, {
ancestorResize: false, // отключение слежения за родителями
animationFrame: true // вызов обновления в requestAnimationFrame
});
Это уменьшает количество лишних пересчётов.
Кеширование размеров и координат Если
floating-элемент не меняет свои размеры часто, имеет смысл хранить их в
памяти и использовать повторно, избегая вызова
getBoundingClientRect на каждом событии.
Отсроченные вычисления через
requestAnimationFrame Перерасчёт позиций при
скролле или resize лучше делать через
requestAnimationFrame, чтобы сгруппировать несколько
событий в один вызов:
let ticking = false;
function onScrollOrResize() {
if (!ticking) {
requestAnimationFrame(() => {
updatePosition();
ticking = false;
});
ticking = true;
}
}
window.addEventListener('scroll', onScrollOrResize);
window.addEventListener('resize', onScrollOrResize);Использование всех middleware одновременно повышает точность, но увеличивает вычислительную нагрузку. Рекомендуется:
offset применять всегда, если нужен сдвиг.flip использовать только при динамических всплывающих
элементах, которые могут пересекать границы viewport.shift актуален, когда floating-элемент может выходить
за экран.size стоит включать только при изменении размеров
контента или ограничении высоты/ширины.Пример оптимальной цепочки middleware:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
computePosition(reference, floating, {
middleware: [
offset(10),
flip(),
shift({ padding: 5 })
]
});
Здесь исключено использование size, если размеры
фиксированы.
В сложных интерфейсах можно включать middleware только при выполнении определённых условий:
const middleware = [offset(8)];
if (shouldFlip) {
middleware.push(flip());
}
if (shouldShift) {
middleware.push(shift({ padding: 4 }));
}
computePosition(reference, floating, { middleware });
Это снижает лишние проверки, особенно при множестве элементов на странице.
Вместо отдельного вызова computePosition на каждый
floating-элемент лучше группировать их обновления:
const floatingElements = [...document.querySelectorAll('.tooltip')];
function batchUpdate() {
floatingElements.forEach(({ reference, floating }) => {
computePosition(reference, floating, { middleware });
});
}
Комбинирование с requestAnimationFrame или
IntersectionObserver дополнительно уменьшает нагрузку.
Методы getBoundingClientRect и
offsetWidth/Height вызывают layout
thrashing, если выполняются многократно. Для оптимизации:
autoUpdate и middleware выборочно.requestAnimationFrame для группировки
перерасчётов.Эти подходы позволяют сохранить высокую производительность даже при работе с большим количеством интерактивных floating-элементов и сложными интерфейсами.