Floating UI — это библиотека для позиционирования элементов интерфейса поверх других элементов, обеспечивая корректное размещение всплывающих окон, тултипов, дропдаунов и модальных окон. Она решает проблему динамического позиционирования, учитывая размер экрана, положение целевого элемента и возможные препятствия.
Для использования Floating UI в проекте достаточно установить пакет через npm:
npm install @floating-ui/dom
или подключить через CDN:
<script src="https://cdn.jsdelivr.net/npm/@floating-ui/dom/dist/floating-ui.dom.min.js"></script>
Основной модуль предоставляет функции computePosition,
autoUpdate, offset, flip,
shift и другие, необходимые для построения адаптивных
тултипов.
computePositioncomputePosition отвечает за вычисление координат
плавающего элемента относительно целевого элемента:
import { computePosition } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
computePosition(reference, floating, {
placement: 'top',
middleware: []
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`
});
});
placement — позиция относительно целевого элемента
(top, bottom, left,
right).middleware — массив функций для модификации поведения
(см. ниже).Middleware позволяют изменять стандартное поведение позиционирования:
offset — добавляет смещение между
тултипом и целевым элементом:import { offset } from '@floating-ui/dom';
middleware: [offset(8)] // смещение 8px
flip — автоматически меняет сторону,
если выбранная позиция выходит за пределы видимой области:import { flip } from '@floating-ui/dom';
middleware: [flip()]
shift — смещает элемент внутрь видимой
области, предотвращая обрезку:import { shift } from '@floating-ui/dom';
middleware: [shift({ padding: 5 })]
arrow — позволяет корректно
позиционировать стрелку тултипа:import { arrow } from '@floating-ui/dom';
const arrowEl = document.querySelector('#arrow');
middleware: [arrow({ element: arrowEl })]
Для динамических интерфейсов важно, чтобы тултип корректно
отображался при изменении размеров окна или прокрутке страницы. Для
этого используется autoUpdate:
import { autoUpdate } from '@floating-ui/dom';
autoUpdate(reference, floating, () => {
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(8), flip(), shift()]
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`
});
});
});
autoUpdate автоматически пересчитывает позицию при:
resize)scroll)resize observer)Тултип должен появляться при наведении и исчезать при уходе курсора. Для этого используют стандартные события DOM:
reference.addEventListener('mouseenter', () => {
floating.style.display = 'block';
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(8), flip(), shift()]
}).then(({ x, y }) => {
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
});
});
reference.addEventListener('mouseleave', () => {
floating.style.display = 'none';
});
Такой подход обеспечивает корректное позиционирование даже при адаптивной верстке.
Для визуального выделения важно правильно разместить стрелку:
#tooltip {
position: absolute;
padding: 8px 12px;
background-color: #333;
color: #fff;
border-radius: 4px;
}
#arrow {
position: absolute;
width: 8px;
height: 8px;
background: inherit;
transform: rotate(45deg);
}
JavaScript для расчета позиции стрелки:
import { arrow } from '@floating-ui/dom';
const arrowEl = document.querySelector('#arrow');
computePosition(reference, floating, {
placement: 'top',
middleware: [offset(8), flip(), shift(), arrow({ element: arrowEl })]
}).then(({ x, y, middlewareData }) => {
const { x: arrowX, y: arrowY } = middlewareData.arrow;
Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
Object.assign(arrowEl.style, { left: `${arrowX}px`, top: `${arrowY}px` });
});
Для адаптивного поведения тултипов рекомендуется комбинировать несколько middleware:
middleware: [
offset(8),
flip({ fallbackPlacements: ['bottom', 'right', 'left'] }),
shift({ padding: 5 }),
arrow({ element: arrowEl })
]
offset задаёт расстояние.flip автоматически подбирает подходящее место.shift не позволяет тултипу вылезать за пределы видимой
области.arrow корректирует положение стрелки относительно
целевого элемента.Для реактивных библиотек, таких как React или Vue, Floating UI легко интегрируется через эффекты или watchers, пересчитывая позицию при каждом изменении состояния компонента:
useEffect(() => {
const cleanup = autoUpdate(reference.current, floating.current, updatePosition);
return () => cleanup();
}, [reference, floating]);
updatePosition — функция, вызывающая
computePosition с нужными middleware.
Эта система обеспечивает гибкость, минимизирует ошибки позиционирования и делает интерфейс более предсказуемым и удобным.