Позиционирование относительно inline элементов

В библиотеке Floating UI позиционирование элементов относительно inline-элементов требует понимания того, как браузер формирует контекст для inline-контента. Inline-элементы, такие как <span>, <a> или <strong>, занимают только ту область, которая необходима для их контента, и не создают блочного контейнера. Это означает, что для вычисления позиции всплывающих элементов (tooltip, dropdown, popover) важно учитывать точные границы и координаты inline-элемента.

Выбор метода вычисления позиции

Floating UI предоставляет несколько функций для вычисления позиции:

  • computePosition(reference, floating, options) — основной метод, который возвращает координаты x и y для плавающего элемента.
  • autoPlacement — стратегия выбора оптимального направления для размещения, учитывающая доступное пространство.
  • offset — позволяет смещать всплывающий элемент относительно точки reference.

Для inline-элементов особенно важно использовать rect-based reference, так как их размеры динамичны и зависят от текста, стилей и шрифтов.

Пример использования:

import { computePosition, offset, flip, shift } from '@floating-ui/dom';

const reference = document.querySelector('span.reference');
const floating = document.querySelector('div.tooltip');

computePosition(reference, floating, {
  middleware: [
    offset(8),  // отступ 8px
    flip(),     // изменение позиции при недостатке места
    shift({ padding: 5 }) // корректировка по границам окна
  ]
}).then(({ x, y }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`
  });
});

Тонкости работы с inline-элементами

  1. Bounding Client Rect Inline-элементы имеют нестандартные размеры: getBoundingClientRect() возвращает размеры всего текста в элементе, включая возможные переносы строк. Для многострочного inline-элемента координаты верхнего левого угла и нижнего правого угла могут меняться динамически при изменении контента.

  2. Baseline и vertical alignment Floating UI может учитывать вертикальное выравнивание inline-элемента (baseline, top, middle, bottom). Важно использовать middleware shift для корректировки позиции по вертикали, особенно если всплывающее окно должно быть выровнено с текстом, а не с границами блока.

  3. Проблемы с line-height и padding Line-height у inline-элементов может добавлять пространство сверху и снизу текста, что влияет на точку reference. В таких случаях middleware offset помогает корректировать позицию, смещая плавающий элемент на требуемое количество пикселей.

Использование Virtual Elements

Если inline-элемент разбит на несколько текстовых узлов или требуется позиционирование относительно части текста, Floating UI позволяет использовать virtual elements:

const virtualReference = {
  getBoundingClientRect() {
    const range = document.createRange();
    range.setStart(reference.firstChild, 0);
    range.setEnd(reference.firstChild, 4); // первые 4 символа
    return range.getBoundingClientRect();
  }
};

computePosition(virtualReference, floating, {
  middleware: [offset(4), flip(), shift({ padding: 5 })]
});

Такой подход позволяет точно привязать всплывающее окно к подстрочному тексту, а не ко всему inline-элементу целиком.

Middleware для inline-контекста

  • offset — задаёт смещение по горизонтали и вертикали.
  • flip — обеспечивает автоматический выбор направления при ограниченном пространстве.
  • shift — корректирует положение, чтобы элемент не выходил за границы viewport.
  • arrow — используется для позиционирования стрелки относительно inline-элемента, учитывая его динамические размеры.

Пример использования arrow middleware с inline-элементом:

import { arrow } from '@floating-ui/dom';

const arrowElement = document.querySelector('.tooltip-arrow');

computePosition(reference, floating, {
  middleware: [
    offset(10),
    flip(),
    shift({ padding: 5 }),
    arrow({ element: arrowElement })
  ]
}).then(({ x, y, middlewareData }) => {
  Object.assign(floating.style, { left: `${x}px`, top: `${y}px` });
  const { x: arrowX, y: arrowY } = middlewareData.arrow;
  Object.assign(arrowElement.style, {
    left: arrowX != null ? `${arrowX}px` : '',
    top: arrowY != null ? `${arrowY}px` : ''
  });
});

Работа с многострочным inline-текстом

Для многострочных <span> или <a> важно учитывать, что каждая строка может иметь свои координаты. В таких случаях использование range-based virtual elements обеспечивает точное позиционирование по первой или последней строке текста, что критично для подсказок, появляющихся рядом с конкретным словом.

Практические рекомендации

  • Всегда измерять getBoundingClientRect() inline-элемента перед вычислением позиции.
  • Использовать middleware flip и shift для предотвращения выхода всплывающего элемента за границы окна.
  • Для частичных текстовых ссылок применять virtual elements с Range.
  • При сложных стилях (line-height, padding, transform) учитывать смещение через offset.

Понимание этих особенностей позволяет Floating UI корректно и предсказуемо позиционировать элементы в сложных inline-контекстах, делая интерфейсы более точными и удобными для пользователя.