Концепция виртуальных элементов

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


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

Основная структура виртуального элемента в Popper.js выглядит следующим образом:

const virtualElement = {
  getBoundingClientRect: () => ({
    top: 100,
    left: 150,
    bottom: 140,
    right: 190,
    width: 40,
    height: 40
  }),
  contextElement: document.body // опционально, используется для ограничения overflow
};

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

  • getBoundingClientRect() — функция, возвращающая объект с координатами и размерами виртуального элемента. Это имитация стандартного DOM-метода, используемого браузером для расчета позиционирования.
  • contextElement — необязательное свойство, которое позволяет Popper учитывать ограничения overflow при вычислении позиции.

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


Использование виртуальных элементов с Popper.js

Создание popper с виртуальным элементом осуществляется так же, как и с обычным DOM-элементом:

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

const virtualReference = {
  getBoundingClientRect: () => ({
    top: 200,
    left: 300,
    bottom: 240,
    right: 340,
    width: 40,
    height: 40
  })
};

const popperElement = document.querySelector('#popper');

createPopper(virtualReference, popperElement, {
  placement: 'right',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8]
      }
    }
  ]
});

Особенности работы:

  • Popper использует координаты, возвращаемые getBoundingClientRect, для расчета позиции popper.
  • Все стандартные модификаторы (offset, flip, preventOverflow и др.) работают одинаково, как для реальных, так и для виртуальных элементов.
  • Можно динамически изменять координаты виртуального элемента, что позволяет реализовывать анимацию или следование курсору.

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

  1. Следование за курсором мыши:
document.addEventListener('mousemove', (event) => {
  const virtualRef = {
    getBoundingClientRect: () => ({
      top: event.clientY,
      left: event.clientX,
      bottom: event.clientY,
      right: event.clientX,
      width: 0,
      height: 0
    })
  };
  popperInstance.state.elements.reference = virtualRef;
  popperInstance.update();
});
  • В этом примере popper динамически позиционируется рядом с курсором.
  • Ширина и высота виртуального элемента можно установить в ноль, так как popper будет рассчитывать положение на основе точки.
  1. Позиционирование относительно произвольных координат:

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

const point = { x: 500, y: 300 };

const virtualPoint = {
  getBoundingClientRect: () => ({
    top: point.y,
    left: point.x,
    bottom: point.y,
    right: point.x,
    width: 0,
    height: 0
  })
};

createPopper(virtualPoint, popperElement);
  • Полезно для подсказок в графических интерфейсах или canvas-приложениях.

Ограничения и рекомендации

  • Виртуальные элементы не могут использоваться для событий DOM напрямую, так как они физически отсутствуют в дереве документа.
  • Для работы модификаторов, основанных на overflow или boundaries, рекомендуется задавать contextElement — родительский контейнер, в котором Popper должен учитывать ограничения.
  • При обновлении координат необходимо вручную вызывать update() у Popper для пересчета позиции.

Взаимодействие с модификаторами

Виртуальные элементы полностью поддерживают стандартные модификаторы Popper.js:

  • offset — корректирует позицию относительно виртуального элемента.
  • flip — проверяет доступное пространство и меняет placement.
  • preventOverflow — учитывает contextElement для ограничения позиции.
  • arrow — может использоваться, если задать виртуальный элемент и стрелку popper.

Пример использования arrow с виртуальным элементом:

createPopper(virtualReference, popperElement, {
  placement: 'top',
  modifiers: [
    { name: 'arrow', options: { element: '#arrow' } },
    { name: 'offset', options: { offset: [0, 10] } }
  ]
});

Итоговый принцип

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