getElementRects

Функция getElementRects в библиотеке Floating UI отвечает за вычисление геометрии двух ключевых элементов:

  • reference — элемент, относительно которого позиционируется всплывающий блок
  • floating — сам позиционируемый элемент (tooltip, dropdown, popover и т.д.)

Результатом работы функции является набор прямоугольников (DOMRect-подобных структур), описывающих размеры и координаты этих элементов в единой системе координат.


Контракт функции

getElementRects является частью платформенно-зависимого адаптера (platform), который передаётся в computePosition. Сигнатура функции:

type GetElementRects = (args: {
  reference: Element | VirtualElement;
  floating: HTMLElement;
  strategy: 'absolute' | 'fixed';
}) => Promise<{
  reference: Rect;
  floating: Rect;
}>;

Где:

  • Rect — объект с полями:

    {
      x: number;
      y: number;
      width: number;
      height: number;
    }

Что именно вычисляется

1. Прямоугольник reference-элемента

Для обычного DOM-элемента используется:

element.getBoundingClientRect()

Но Floating UI нормализует результат:

  • преобразует значения в объект Rect
  • учитывает стратегию позиционирования (fixed или absolute)
  • корректирует координаты при наличии скролла

2. Прямоугольник floating-элемента

Особенность:

  • floating-элемент может быть ещё не отрисован
  • его размеры могут зависеть от CSS (например, display: none или position)

Поэтому:

  • иногда требуется временно измерить элемент
  • или использовать заранее известные размеры

Пример реализации

Базовая реализация (упрощённая):

const platform = {
  async getElementRects({reference, floating, strategy}) {
    const referenceRect = reference.getBoundingClientRect();
    const floatingRect = floating.getBoundingClientRect();

    return {
      reference: {
        x: referenceRect.x,
        y: referenceRect.y,
        width: referenceRect.width,
        height: referenceRect.height,
      },
      floating: {
        x: floatingRect.x,
        y: floatingRect.y,
        width: floatingRect.width,
        height: floatingRect.height,
      },
    };
  }
};

Влияние стратегии позиционирования

Параметр strategy влияет на систему координат:

absolute

  • координаты считаются относительно ближайшего позиционированного предка
  • учитывается скролл контейнеров

fixed

  • координаты относительно viewport
  • игнорируется скролл родительских контейнеров
  • используется position: fixed

getElementRects должен учитывать это различие, иначе позиционирование будет некорректным.


Работа с VirtualElement

Floating UI поддерживает виртуальные элементы — объекты, не являющиеся DOM-узлами, но имеющие метод:

getBoundingClientRect()

Пример:

const virtualElement = {
  getBoundingClientRect() {
    return {
      x: 100,
      y: 200,
      width: 0,
      height: 0,
      top: 200,
      left: 100,
      right: 100,
      bottom: 200,
    };
  }
};

getElementRects должен работать с такими объектами так же, как с обычными элементами.


Нормализация координат

Floating UI использует единый формат:

  • x, y вместо left, top
  • всегда положительные значения ширины и высоты
  • отсутствие привязки к DOM API

Это позволяет:

  • абстрагироваться от браузерных различий
  • использовать библиотеку вне DOM (например, в canvas или WebGL)

Проблемы измерения floating-элемента

1. Элемент скрыт (display: none)

getBoundingClientRect() возвращает:

width = 0
height = 0

Решение:

  • временно показать элемент
  • или использовать visibility: hidden вместо display: none

2. Элемент ещё не вставлен в DOM

Решение:

  • вставить в DOM перед вычислением
  • либо использовать предсказуемые размеры

Асинхронность

getElementRects возвращает Promise, потому что:

  • измерения могут зависеть от layout
  • возможны кастомные платформы (например, React Native)
  • иногда требуется ожидание рендера

Расширенные сценарии

Кастомная платформа

Floating UI позволяет полностью заменить механизм измерения:

const customPlatform = {
  async getElementRects(args) {
    // кастомная логика
  }
};

Используется, когда:

  • работа идёт вне браузера
  • требуется особая система координат
  • используются нестандартные контейнеры

Учёт трансформаций (transform)

getBoundingClientRect() уже учитывает CSS transform, но:

  • координаты становятся относительными к viewport
  • при вложенных трансформациях возможны сложности

Floating UI не устраняет это полностью, поэтому важно:

  • избегать сложных трансформаций у родительских элементов
  • или учитывать их вручную

Связь с computePosition

getElementRects — один из ключевых этапов в computePosition:

  1. Получение rect’ов через getElementRects
  2. Вычисление базовой позиции
  3. Применение middleware (offset, flip, shift и т.д.)
  4. Возврат финальных координат

Ошибка на этапе getElementRects приводит к:

  • неправильному позиционированию
  • скачкам интерфейса
  • некорректной работе middleware

Оптимизация

Частые вызовы могут вызывать layout thrashing. Рекомендуется:

  • кешировать результаты, если элементы не изменяются
  • избегать лишних вызовов computePosition
  • использовать ResizeObserver и IntersectionObserver

Практический пример

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

const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');

computePosition(button, tooltip).then(({x, y}) => {
  Object.assign(tooltip.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
});

Внутри:

  • вызывается getElementRects
  • вычисляются размеры button и tooltip
  • на основе этого определяется позиция

Ключевые особенности

  • единый формат геометрии (Rect)
  • поддержка виртуальных элементов
  • абстракция над DOM API
  • зависимость от стратегии позиционирования
  • возможность полной кастомизации

Частые ошибки

  • использование display: none для floating-элемента
  • игнорирование стратегии fixed
  • попытка измерить элемент до его рендера
  • несогласованность координат (разные системы отсчёта)

Роль в архитектуре Floating UI

getElementRects выполняет фундаментальную задачу:

  • переводит реальные DOM-элементы в абстрактные геометрические данные
  • служит входной точкой для всех вычислений позиционирования
  • отделяет логику измерения от логики позиционирования

Это делает библиотеку гибкой и независимой от конкретной среды выполнения.