Профилирование Popper

Popper.js — это библиотека для управления позиционированием всплывающих элементов в веб-приложениях. Она используется для создания тултипов, дропдаунов, поповер-меню и других плавающих элементов, которые должны точно следовать за своим «reference element» (элементом-ориентиром) независимо от прокрутки страницы, изменения размеров окна или ограничений контейнера.

Основная концепция Popper.js строится вокруг reference element и popper element. Reference element — это элемент DOM, относительно которого будет позиционироваться Popper. Popper element — это сам плавающий элемент, который отображается поверх остального контента.

Попер.js использует алгоритм, основанный на объекте состояния (state), который содержит:

  • elements — текущие DOM-элементы (reference и popper);
  • rects — размеры и координаты элементов;
  • modifiersData — данные, созданные модификаторами;
  • placement — текущая выбранная позиция (top, bottom, left, right и их вариации с -start и -end).

Инициализация Popper

Popper создаётся с помощью функции createPopper(reference, popper, options):

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

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

const popperInstance = createPopper(reference, popper, {
  placement: 'top',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8],
      },
    },
  ],
});

Ключевые моменты при инициализации:

  • placement задаёт изначальное предпочтительное положение.
  • Модификаторы (modifiers) позволяют гибко настраивать поведение Popper: смещение, предотвращение выхода за границы окна (preventOverflow), автоматический выбор позиции (flip) и другие.

Модификаторы Popper.js

Модификаторы — это функции, которые обрабатывают состояние поппера на каждом цикле обновления. Каждый модификатор имеет структуру:

{
  name: 'modifierName',
  enabled: true,
  phase: 'main',
  fn: ({ state, options, name }) => { /* логика */ },
  effect: ({ state, options, name }) => () => { /* очистка */ },
  options: { /* настройки */ },
}

Основные модификаторы:

  • offset — задаёт смещение поппера относительно reference element.
  • preventOverflow — предотвращает выход поппера за пределы видимой области.
  • flip — автоматически меняет позицию поппера, если выбранная позиция недоступна.
  • arrow — управляет стрелкой тултипа или поповера, синхронизируя её с reference element.
  • computeStyles — отвечает за установку CSS-свойств (top, left, transform) для точного позиционирования.

Работа с фазами модификаторов

Popper.js обновляет позиционирование по циклу, разделённому на фазы:

  1. beforeRead — предварительные вычисления перед чтением размеров.
  2. read — получение размеров и положения элементов.
  3. afterRead — промежуточная обработка данных.
  4. beforeMain — подготовка к основной фазе изменения стилей.
  5. main — основная логика модификатора, например, изменение координат или применения CSS.
  6. afterMain — финальная доработка после основной логики.
  7. beforeWrite — подготовка к записи изменений в DOM.
  8. write — применение стилей и классов к элементам.
  9. afterWrite — финальная очистка или обновление.

Разделение на фазы позволяет создавать сложные модификаторы без риска конфликтов и гарантирует предсказуемость поведения.

Контроль позиции и профилирование

Для точного контроля Popper.js предоставляет доступ к объекту state. Он позволяет:

  • Чтение текущих размеров reference и popper: state.rects.reference, state.rects.popper.
  • Определение текущей позиции: state.placement.
  • Доступ к данным модификаторов: state.modifiersData.

Пример динамического смещения в зависимости от размеров окна:

const dynamicOffset = {
  name: 'dynamicOffset',
  enabled: true,
  phase: 'main',
  fn({ state }) {
    const windowWidth = window.innerWidth;
    const offset = windowWidth > 800 ? 20 : 10;
    state.styles.popper.transform = `translate3d(0, ${offset}px, 0)`;
  },
};

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

Popper.js поддерживает интеллектуальное обновление через методы:

  • update() — ручной пересчёт позиции.
  • forceUpdate() — принудительное обновление без оптимизации.
  • destroy() — удаление Popper и освобождение ресурсов.

Для больших приложений рекомендуется:

  • Использовать passive события при обработке прокрутки.
  • Минимизировать количество модификаторов с тяжелой логикой.
  • Вызывать update() только при необходимости.

Взаимодействие с CSS и адаптивность

Popper.js управляет только позиционированием, а внешний вид остаётся за CSS. Для адаптивного дизайна:

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

Стрелки и визуальная привязка

Модификатор arrow автоматически вычисляет положение стрелки и корректирует смещение:

{
  name: 'arrow',
  options: {
    element: '.tooltip-arrow',
    padding: 5,
  },
}
  • element — селектор стрелки.
  • padding — минимальное расстояние от границы поппера до стрелки.

Итеративное профилирование

Для точного профилирования Popper.js:

  1. Логировать state.rects и state.placement при обновлениях.
  2. Использовать onFirstUpdate для начальной настройки.
  3. Комбинировать offset, flip и кастомные модификаторы для сложных UI-компонентов.

Такой подход позволяет создавать полностью управляемые плавающие элементы с предсказуемым поведением на любых экранах и условиях прокрутки.