getDimensions

Функция getDimensions в экосистеме Floating UI отвечает за получение геометрических характеристик элемента: его ширины и высоты. Это базовый слой вычислений, на котором строятся более сложные механизмы позиционирования, такие как определение координат, выравнивание и работа middleware.

В отличие от прямого использования DOM API (offsetWidth, clientWidth, getBoundingClientRect), getDimensions предоставляет унифицированный и предсказуемый способ извлечения размеров независимо от типа платформы (DOM, React Native, кастомные адаптеры).


Контекст использования

getDimensions применяется внутри платформенного адаптера (platform), который передаётся в computePosition. Floating UI устроен так, что вся работа с окружением (DOM или любая другая среда) абстрагирована через интерфейс платформы.

Пример контекста:

computePosition(reference, floating, {
  platform: {
    getDimensions(element) {
      // реализация
    }
  }
});

В браузерной версии библиотека уже содержит реализацию по умолчанию, однако понимание её устройства важно для:

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

Сигнатура функции

type Dimensions = {
  width: number;
  height: number;
};

function getDimensions(element: Element | VirtualElement): Dimensions;

Функция принимает:

  • element — DOM-элемент или виртуальный элемент

Возвращает:

  • объект с числовыми значениями width и height

Базовая реализация для DOM

Стандартная реализация в браузере выглядит предельно просто:

function getDimensions(element) {
  return {
    width: element.offsetWidth,
    height: element.offsetHeight
  };
}

Однако в реальности используются более точные методы, учитывающие особенности рендеринга.


Различия методов измерения

offsetWidth / offsetHeight

  • Включают padding и border
  • Не включают margin
  • Округляются до целых значений
  • Быстрые

clientWidth / clientHeight

  • Включают padding
  • Исключают border
  • Исключают скроллбар

getBoundingClientRect()

  • Возвращает дробные значения
  • Учитывает трансформации (transform)
  • Самый точный способ

Почему Floating UI не всегда использует getBoundingClientRect

Несмотря на точность, getBoundingClientRect имеет особенности:

  • зависит от текущего layout
  • может вызывать reflow
  • возвращает размеры с учётом масштабирования и трансформаций

В задачах позиционирования иногда требуется именно “layout size”, а не визуально трансформированный размер.


Реализация с использованием getBoundingClientRect

Более точная версия:

function getDimensions(element) {
  const { width, height } = element.getBoundingClientRect();
  return { width, height };
}

Такой подход используется, если:

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

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

Inline-элементы (span, a) могут давать неожиданные результаты:

  • offsetWidth может быть равен 0
  • размеры зависят от содержимого и переноса строк

Floating UI предполагает, что:

  • элементы либо блочные
  • либо уже имеют вычисляемые размеры

Работа с виртуальными элементами

Floating UI поддерживает виртуальные элементы — объекты, имитирующие DOM API.

Пример:

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

В этом случае getDimensions реализуется так:

function getDimensions(element) {
  const rect = element.getBoundingClientRect();
  return {
    width: rect.width,
    height: rect.height
  };
}

Связь с другими методами платформы

getDimensions используется совместно с:

  • getElementRects — получение прямоугольников reference и floating
  • getClippingRect — определение области отсечения
  • getOffsetParent — вычисление контекста позиционирования

Размеры элемента напрямую влияют на:

  • вычисление смещений (offset)
  • работу shift и flip
  • корректное позиционирование стрелки (arrow)

Влияние box-sizing

Свойство CSS box-sizing влияет на интерпретацию размеров:

  • content-box — ширина не включает padding и border
  • border-box — ширина включает всё

getDimensions должен учитывать, что:

  • offsetWidth всегда возвращает border-box
  • getBoundingClientRect тоже соответствует border-box

Особенности при transform

CSS-свойства:

transform: scale(1.5);
transform: rotate(45deg);

влияют на результат getBoundingClientRect, но не влияют на offsetWidth.

Это означает:

  • визуальный размер ≠ layout размер
  • возможны ошибки позиционирования при неправильном выборе метода

Производительность

Частые вызовы getDimensions могут вызывать:

  • layout thrashing
  • лишние reflow

Оптимизации:

  • кэширование размеров
  • батчинг измерений
  • использование ResizeObserver

Пример кэширования:

let cached;

function getDimensions(element) {
  if (!cached) {
    cached = {
      width: element.offsetWidth,
      height: element.offsetHeight
    };
  }
  return cached;
}

Поведение при скрытых элементах

Если элемент:

display: none;

то:

  • offsetWidth = 0
  • getBoundingClientRect() возвращает нули

Floating UI не может корректно позиционировать такие элементы.

Решение:

  • использовать visibility: hidden
  • или временно отображать элемент для измерения

Проблемы с SVG

SVG-элементы:

  • не поддерживают offsetWidth
  • требуют getBBox() или getBoundingClientRect()

Пример:

function getDimensions(element) {
  if (element instanceof SVGElement) {
    const rect = element.getBoundingClientRect();
    return { width: rect.width, height: rect.height };
  }

  return {
    width: element.offsetWidth,
    height: element.offsetHeight
  };
}

Поддержка масштабирования (zoom)

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

zoom: 2;

или системного масштабирования:

  • getBoundingClientRect учитывает масштаб
  • offsetWidth — нет

Это важно при:

  • high-DPI дисплеях
  • кастомных zoom-интерфейсах

Точность и округление

  • offsetWidth округляет значения
  • getBoundingClientRect возвращает float

Floating UI предпочитает точность, особенно при:

  • сложных вычислениях middleware
  • работе со стрелками
  • позиционировании на границах viewport

Рекомендованный подход

Комбинированная реализация:

function getDimensions(element) {
  const rect = element.getBoundingClientRect();

  return {
    width: rect.width || element.offsetWidth,
    height: rect.height || element.offsetHeight
  };
}

Преимущества:

  • точность
  • fallback
  • универсальность

Расширение для кастомных платформ

В нестандартных окружениях (Canvas, WebGL, Native):

function getDimensions(element) {
  return {
    width: element.width,
    height: element.height
  };
}

Главное требование:

  • возвращать реальные размеры элемента в текущей системе координат

Ошибки и крайние случаи

  1. Элемент не в DOM

    • размеры могут быть некорректны
  2. Анимации

    • размеры могут меняться между кадрами
  3. Flex и Grid

    • размеры могут зависеть от контейнера
  4. Scrollbars

    • могут влиять на итоговую ширину

Практическая роль в позиционировании

getDimensions напрямую влияет на:

  • центрирование (placement: center)
  • вычисление смещений
  • поведение flip
  • корректность shift

Ошибки в размерах приводят к:

  • “прыгающим” элементам
  • неправильному выравниванию
  • выходу за границы viewport

Минимальный контракт функции

Для корректной работы Floating UI достаточно:

function getDimensions(element) {
  return {
    width: Number,
    height: Number
  };
}

Требования:

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

Связь с computePosition

Внутри computePosition:

  1. Получаются размеры reference
  2. Получаются размеры floating
  3. Вычисляются координаты
  4. Применяются middleware

getDimensions участвует на первом этапе, задавая основу всех последующих вычислений.