Архитектура Popper.js

Popper.js — это библиотека для управления позиционированием всплывающих элементов относительно других элементов на странице. Основная цель библиотеки — гарантировать корректное размещение попперов (tooltip, dropdown, меню и других overlay-компонентов) в любых условиях: при изменении размеров окна, прокрутке документа или динамическом изменении контента.

Основные компоненты

Архитектура Popper.js строится вокруг трёх ключевых компонентов: Popper, Reference и Modifiers.

  • Popper — элемент, который нужно позиционировать. Это может быть tooltip, контекстное меню или любой overlay-компонент. Popper отвечает за визуальное отображение и позиционирование относительно Reference.
  • Reference — элемент, относительно которого происходит позиционирование Popper. Reference задаёт точку привязки и область, вокруг которой Popper будет адаптироваться.
  • Modifiers — функции, изменяющие поведение и положение Popper. Modifiers выполняются по цепочке и могут управлять смещением, предотвращением выхода за границы окна, адаптивным изменением позиции и другими аспектами.

Модель позиционирования

Popper.js использует контекстную модель позиционирования, основанную на вычислении прямоугольных областей (bounding boxes) элементов Reference и Popper. Основные этапы вычислений:

  1. Сбор информации о Reference и Popper: Popper.js получает размеры и координаты элементов через getBoundingClientRect() и учитывает свойства CSS, такие как transform и margin.

  2. Вычисление начальной позиции: На основе предпочтительной стороны (placement) и размеров элементов рассчитывается первичная позиция Popper.

  3. Применение модификаторов: Каждый модификатор получает объект с текущей позицией и размерами и возвращает изменённое состояние. Популярные модификаторы:

    • offset — задаёт смещение Popper относительно Reference.
    • flip — автоматически меняет сторону позиционирования, если Popper не помещается в область видимости.
    • preventOverflow — предотвращает выход Popper за границы контейнера или окна.
    • arrow — позиционирует стрелку Popper в точке привязки к Reference.
  4. Обновление DOM: После вычислений Popper.js применяет вычисленные стили к элементу Popper и, при необходимости, к стрелке или другим дочерним элементам.

Система модификаторов

Модификаторы представляют собой цепочку функций, выполняемых в строгом порядке. Каждый модификатор получает объект state, содержащий следующие ключевые свойства:

  • state.elements — элементы Popper, Reference и стрелки.
  • state.styles — текущие стили, применяемые к Popper и стрелке.
  • state.placement — текущее положение Popper (top, bottom, left, right, с вариантами start/end).
  • state.rects — размеры и координаты Popper и Reference.
  • state.modifiersData — промежуточные данные, используемые другими модификаторами.

Модификаторы могут быть основными и дополнительными. Основные модификаторы (например, flip или preventOverflow) отвечают за корректность позиционирования, дополнительные — за стилизацию или кастомные вычисления.

Жизненный цикл Popper

Popper.js управляет состоянием Popper через жизненный цикл, включающий:

  1. Инициализация: создаётся объект Popper с указанием Reference, Popper и списка модификаторов.
  2. Расчёт позиции: выполняется первичное вычисление позиции Popper.
  3. Ререндер: при изменении размеров окна или Reference происходит перерасчёт позиции с повторным вызовом модификаторов.
  4. Обновление стилей: Popper.js применяет новые стили к DOM.
  5. Дестрой: удаление Popper из DOM и отмена подписок на события окна или scroll-контейнеры.

Обработка событий и реактивность

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

  • прокрутке родительских элементов,
  • изменению размеров Reference,
  • изменениям DOM, влияющим на размер Popper.

С помощью встроенной функции update() можно вручную инициировать перерасчёт позиции, что полезно при динамическом контенте или анимациях.

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

Popper.js минимизирует количество перерисовок за счёт:

  • кеширования размеров и координат элементов,
  • выполнения модификаторов только при необходимости,
  • использования requestAnimationFrame для синхронизации обновлений с перерисовкой браузера.

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

Расширяемость

Система модификаторов делает Popper.js гибким и расширяемым. Создание собственного модификатора требует определения:

  • имени модификатора (name),
  • порядка выполнения (phase),
  • функции fn, которая получает state и возвращает модифицированные данные.

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

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