Динамические виртуальные элементы

В Popper.js ключевым аспектом является позиционирование всплывающих элементов относительно целевых (reference) элементов. Стандартный подход использует реальные DOM-узлы, однако для сложных интерфейсов часто необходимы виртуальные элементы, которые не существуют в DOM, но служат якорями для всплывающих подсказок, тултипов или контекстных меню. Popper.js поддерживает виртуальные элементы через объект с минимальным набором свойств, позволяя создавать динамические интерфейсы без привязки к физическим DOM-элементам.


Создание виртуального элемента

Виртуальный элемент представляет собой объект с обязательным методом getBoundingClientRect. Простейший пример:

const virtualElement = {
  getBoundingClientRect: () => ({
    top: 100,
    left: 150,
    right: 250,
    bottom: 200,
    width: 100,
    height: 100,
  }),
  contextElement: document.body, // опционально
};

Ключевые моменты:

  • Метод getBoundingClientRect должен возвращать объект, аналогичный DOMRect, с координатами относительно окна просмотра.
  • contextElement определяет базовый контейнер для Popper.js. Если не задан, используется document.body.
  • Можно динамически изменять координаты, чтобы Popper реагировал на движение виртуального элемента.

Инициализация Popper для виртуального элемента

Попперы создаются стандартным образом:

import { createPopper } from '@popperjs/core';

const popperInstance = createPopper(virtualElement, tooltipElement, {
  placement: 'top',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 10],
      },
    },
  ],
});

Особенность здесь в том, что Popper не требует DOM-узла для reference, что открывает возможности для привязки подсказок к координатам мыши, кастомным виртуальным областям или элементам, которые будут добавлены в DOM позже.


Динамическое обновление позиции

Для динамических интерфейсов координаты виртуального элемента можно менять на лету. Например, для подсказки, которая следует за мышью:

document.addEventListener('mousemove', (event) => {
  virtualElement.getBoundingClientRect = () => ({
    top: event.clientY,
    left: event.clientX,
    right: event.clientX,
    bottom: event.clientY,
    width: 0,
    height: 0,
  });

  popperInstance.update();
});

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

  • Попперы автоматически корректируют положение при вызове update.
  • Если используется scroll или изменение размеров окна, рекомендуется комбинировать с модификатором eventListeners для автоматического обновления.

Комбинирование с модификаторами Popper.js

Виртуальные элементы поддерживают все модификаторы, которые применяются к реальным DOM-узлам:

  • offset — добавляет смещение относительно виртуального элемента.
  • preventOverflow — ограничивает положение поппера в пределах контейнера.
  • flip — позволяет автоматически менять сторону при нехватке места.

Пример:

const popperInstance = createPopper(virtualElement, tooltipElement, {
  placement: 'right',
  modifiers: [
    { name: 'flip', options: { fallbackPlacements: ['left', 'top'] } },
    { name: 'preventOverflow', options: { padding: 8 } },
  ],
});

В этом случае, даже виртуальный элемент корректно взаимодействует с системой ограничения и автоматической перестановки.


Виртуальные элементы с размерами

Иногда необходимо задавать ширину и высоту виртуального элемента, чтобы Popper мог правильно позиционировать тултип:

const virtualElement = {
  getBoundingClientRect: () => ({
    top: 200,
    left: 300,
    right: 350,
    bottom: 250,
    width: 50,
    height: 50,
  }),
};

Popper использует width и height для точного расчета смещений и размеров всплывающих блоков, особенно когда применяется модификатор preventOverflow.


Преимущества виртуальных элементов

  1. Отсутствие зависимостей от DOM — элементы могут существовать только как координаты.
  2. Динамическая привязка — возможность следить за мышью, кастомными областями или перемещаемыми объектами.
  3. Гибкая интеграция — поддержка всех стандартных модификаторов Popper.js.
  4. Оптимизация производительности — меньше операций с реальным DOM.

Особенности использования

  • При использовании виртуального элемента Popper не создаёт автоматические события для слежения за размерами reference. Для отслеживания необходимо вручную вызывать update при изменении координат.
  • Можно создавать многоуровневые попперы, где reference одного поппера — виртуальный элемент другого.
  • Для анимаций и переходов желательно использовать CSS-трансформации через модификатор computeStyles с опцией gpuAcceleration: true.

Виртуальные элементы открывают новый уровень контроля над позиционированием всплывающих элементов в Popper.js, позволяя строить динамические интерфейсы, свободные от ограничений DOM, с полной поддержкой стандартных модификаторов, смещений и адаптивного поведения.