Floating UI — это библиотека для позиционирования всплывающих элементов относительно их якорей с точной настройкой и поддержкой динамических изменений DOM. Интеграция с шаблонами позволяет создавать гибкие компоненты пользовательского интерфейса, такие как тултипы, поповеры, меню и модальные окна, с минимальными усилиями и высокой степенью контроля.
Reference и Floating элементы В Floating UI
ключевыми являются два элемента: reference (якорь) и
floating (всплывающий элемент). Reference —
это элемент, относительно которого позиционируется всплывающий
компонент, а floating — сам компонент, который необходимо
разместить.
Middleware Middleware позволяют управлять поведением
позиционирования. Они могут автоматически смещать элемент
(offset), избегать выхода за границы экрана
(flip, shift), добавлять стрелки
(arrow) и многое другое. Middleware применяется в цепочке и
возвращает объект с координатами, которые затем используются для
рендеринга.
import { computePosition, offset, flip, shift, arrow } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
const arrowElement = floating.querySelector('.arrow');
computePosition(reference, floating, {
placement: 'top',
middleware: [
offset(8),
flip(),
shift({ padding: 5 }),
arrow({ element: arrowElement })
]
}).then(({ x, y, middlewareData }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`
});
Object.assign(arrowElement.style, {
left: `${middlewareData.arrow?.x ?? 0}px`,
top: `${middlewareData.arrow?.y ?? 0}px`
});
});
Floating UI легко интегрируется с системами шаблонов, такими как Handlebars, EJS или Vue/React-шаблонами. Основной принцип — создание DOM-элементов из шаблона, после чего Floating UI позиционирует их относительно якоря.
Пример с шаблоном Handlebars:
import Handlebars from 'handlebars';
import { computePosition, offset, flip } from '@floating-ui/dom';
const template = Handlebars.compile(`
<div class="tooltip">
{{text}}
<div class="arrow"></div>
</div>
`);
const tooltipHTML = template({ text: 'Подсказка' });
document.body.insertAdjacentHTML('beforeend', tooltipHTML);
const reference = document.querySelector('#button');
const floating = document.querySelector('.tooltip');
const arrowElement = floating.querySelector('.arrow');
computePosition(reference, floating, {
placement: 'bottom',
middleware: [offset(10), flip()]
}).then(({ x, y }) => {
floating.style.left = `${x}px`;
floating.style.top = `${y}px`;
});
Здесь Floating UI обеспечивает точное позиционирование, а шаблон отвечает за визуальную структуру.
Для интерактивных интерфейсов важно обновлять позицию всплывающего
элемента при изменении DOM или размеров окна. Floating UI предоставляет
функцию autoUpdate, которая автоматически пересчитывает
позицию.
import { autoUpdate } from '@floating-ui/dom';
const cleanup = autoUpdate(reference, floating, () => {
computePosition(reference, floating, { placement: 'top' }).then(({ x, y }) => {
floating.style.left = `${x}px`;
floating.style.top = `${y}px`;
});
});
// Для остановки наблюдения
// cleanup();
Использование autoUpdate особенно полезно при работе с
шаблонами, где контент может динамически изменяться (например, при
подгрузке данных или открытии модального окна с переменным
содержимым).
Arrow middleware позволяет позиционировать стрелку относительно всплывающего элемента. В шаблоне можно заранее определить элемент стрелки и подключить его к Floating UI.
<div class="tooltip">
<span>Текст подсказки</span>
<div class="arrow"></div>
</div>
Middleware возвращает координаты для стрелки через объект
middlewareData.arrow, которые применяются к стилям стрелки.
Это обеспечивает точное визуальное выравнивание даже при изменении
положения всплывающего элемента.
absolute или
fixed; влияет на поведение при прокрутке и позиционировании
относительно viewport.Оптимизация заключается в минимизации количества пересчетов и
правильном использовании middleware. Например, для статичных тултипов
достаточно одного вызова computePosition, тогда как для
динамических меню с анимацией лучше использовать
autoUpdate.
React В React Floating UI используется через хуки,
например useFloating. Шаблон реализуется в JSX:
import { useFloating } from '@floating-ui/react';
function Tooltip({ children, label }) {
const { x, y, reference, floating } = useFloating({ placement: 'top' });
return (
<>
<button ref={reference}>{children}</button>
<div ref={floating} style={{ position: 'absolute', left: x ?? 0, top: y ?? 0 }}>
{label}
<div className="arrow"></div>
</div>
</>
);
}
Vue В Vue Floating UI интегрируется через
ref и реактивные вычисления позиции:
<template>
<button ref="reference">Кнопка</button>
<div ref="floating" :style="{ left: `${x}px`, top: `${y}px` }">
Подсказка
<div class="arrow"></div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { computePosition, offset, flip } from '@floating-ui/dom';
const reference = ref(null);
const floating = ref(null);
const x = ref(0);
const y = ref(0);
onMounted(() => {
computePosition(reference.value, floating.value, { placement: 'bottom', middleware: [offset(8), flip()] })
.then(({ x: px, y: py }) => {
x.value = px;
y.value = py;
});
});
</script>
computePosition
или autoUpdate.middleware для обработки крайних случаев
выхода за границы экрана.autoUpdate, чтобы
позиции всплывающих элементов корректировались автоматически.Интеграция Floating UI с шаблонами позволяет создавать интерфейсы, где позиционирование и внешний вид разделены, а динамическое поведение компонентов управляется централизованно через библиотеку. Это обеспечивает масштабируемость и удобство сопровождения кода.