Popper.js предоставляет возможность использовать виртуальные
элементы (virtual elements) вместо реальных DOM-узлов в
качестве источников для всплывающих подсказок, тултипов или других
попперов. Виртуальный элемент — это объект с интерфейсом, совместимым с
getBoundingClientRect(), что позволяет библиотеке вычислять
позицию поппера без привязки к существующему DOM-элементу.
const virtualElement = {
getBoundingClientRect: () => ({
width: 0,
height: 0,
top: 100,
left: 200,
right: 200,
bottom: 100,
}),
};
Ключевой момент: Popper.js использует только
координаты, возвращаемые getBoundingClientRect, для
вычисления позиции, поэтому свойства элемента, такие как
offsetWidth или offsetHeight, не влияют на
расположение поппера.
Для создания поппера с виртуальным элементом используется стандартная
функция createPopper, где в качестве первого аргумента
передаётся объект, реализующий метод
getBoundingClientRect.
import { createPopper } from '@popperjs/core';
const popperInstance = createPopper(virtualElement, tooltipElement, {
placement: 'top',
});
В этом случае Popper.js будет позиционировать
tooltipElement относительно координат виртуального
элемента, не завися от DOM-структуры страницы.
Особенность: Виртуальные элементы не имеют событий, поэтому любые интерактивные действия должны быть реализованы вручную через слушатели событий на соответствующих элементах интерфейса.
Отсутствие размеров Виртуальные элементы часто
используют width: 0 и height: 0, что
ограничивает возможности для автоматического определения размера
поппера. Если требуется, чтобы поппер учитывал размеры источника, их
необходимо задавать явно в методе
getBoundingClientRect.
Нет родительской иерархии DOM Виртуальный
элемент не находится в DOM, поэтому моды
preventOverflow и flip могут работать
иначе, если источник не взаимодействует с реальными границами
контейнеров.
Ограниченная поддержка событий Поскольку
виртуальный элемент не может получать события, все интерактивные
действия должны быть привязаны к другим элементам. Например, всплывающая
подсказка на выделенном тексте может потребовать слушателей на
mouseup и mousemove, чтобы обновлять
координаты виртуального элемента.
Требуется ручное обновление Для виртуальных
элементов Popper.js не отслеживает изменения положения или
размера автоматически. Любые обновления должны инициироваться
вручную через метод update:
popperInstance.update();
margin, padding,
transform, не влияют на позиционирование
поппера. Всё рассчитывается исключительно на основе
возвращаемого объекта getBoundingClientRect.Поппер для текстового выделения:
function getSelectionRect() {
const selection = window.getSelection();
if (!selection.rangeCount) return null;
const range = selection.getRangeAt(0);
return range.getBoundingClientRect();
}
const virtualElement = {
getBoundingClientRect: () => getSelectionRect() || { top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 },
};
const popperInstance = createPopper(virtualElement, tooltipElement, {
placement: 'top',
});
Здесь координаты виртуального элемента изменяются динамически, но
необходимо вручную вызывать
popperInstance.update() при каждом изменении
выделения, иначе поппер останется на старой позиции.
flip: Поппер будет пытаться изменить
сторону отображения, но из-за отсутствия DOM-контейнера могут быть
некорректные вычисления ограничений.preventOverflow: Работает относительно
boundariesElement. Если виртуальный элемент находится
далеко от реальных элементов DOM, модификатор может сработать
неожиданно.offset: Необходимо задавать явно,
особенно если виртуальный элемент имеет нулевую ширину и высоту, иначе
сдвиг поппера может не соответствовать ожиданиям.createPopper(virtualElement, tooltipElement, {
placement: 'right',
modifiers: [
{ name: 'offset', options: { offset: [0, 10] } },
{ name: 'preventOverflow', options: { boundary: document.body } },
],
});
top,
left, bottom, right,
width, height в
getBoundingClientRect.update()
обязательно, если координаты виртуального элемента изменяются.Виртуальные элементы расширяют возможности Popper.js, позволяя создавать гибкие и динамичные интерфейсы, но требуют внимания к ограничениям: отсутствие DOM, необходимость ручного обновления и точного определения координат. Их использование оправдано там, где позиционирование элементов должно быть независимым от реальной структуры документа.