Адаптивные тултипы

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 и другие, необходимые для построения адаптивных тултипов.


Основные функции и их роль

computePosition

computePosition отвечает за вычисление координат плавающего элемента относительно целевого элемента:

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

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:

middleware: [
  offset(8),
  flip({ fallbackPlacements: ['bottom', 'right', 'left'] }),
  shift({ padding: 5 }),
  arrow({ element: arrowEl })
]
  • offset задаёт расстояние.
  • flip автоматически подбирает подходящее место.
  • shift не позволяет тултипу вылезать за пределы видимой области.
  • arrow корректирует положение стрелки относительно целевого элемента.

Примеры использования в SPA и реактивных фреймворках

Для реактивных библиотек, таких как React или Vue, Floating UI легко интегрируется через эффекты или watchers, пересчитывая позицию при каждом изменении состояния компонента:

useEffect(() => {
  const cleanup = autoUpdate(reference.current, floating.current, updatePosition);
  return () => cleanup();
}, [reference, floating]);

updatePosition — функция, вызывающая computePosition с нужными middleware.


Ключевые преимущества адаптивных тултипов с Floating UI

  • Автоматическое позиционирование с учётом ограничений экрана.
  • Поддержка смещения, автоматического переворота и смещения внутрь видимой области.
  • Интеграция стрелок и кастомных элементов.
  • Реактивное обновление позиции при изменении DOM и размеров.
  • Универсальность для любых веб-приложений — от простых страниц до сложных SPA.

Эта система обеспечивает гибкость, минимизирует ошибки позиционирования и делает интерфейс более предсказуемым и удобным.