Концепция модификаторов

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


Что такое модификаторы

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

Структура модификатора обычно выглядит так:

const modifier = {
  name: 'название',
  enabled: true,
  phase: 'phase',
  fn: ({ state, name, options }) => { /* логика модификатора */ },
  options: { /* дополнительные параметры */ },
};

Ключевые свойства:

  • name — уникальное имя модификатора.
  • enabled — активирован ли модификатор.
  • phase — стадия жизненного цикла, на которой выполняется модификатор (read, beforeMain, main, afterMain, write).
  • fn — функция, реализующая логику модификатора.
  • options — объект с настройками, специфичными для данного модификатора.

Фазы модификаторов

Жизненный цикл Popper.js разделён на фазы, что позволяет модификаторам работать последовательно и независимо. Основные фазы:

  1. read — чтение размеров и позиций элементов.
  2. beforeMain — подготовка данных перед основной логикой.
  3. main — основной этап вычисления положения поппера.
  4. afterMain — корректировка позиции после основной логики.
  5. write — применение вычисленных стилей и атрибутов к DOM.

Правильное определение фазы критично для производительности: тяжелые вычисления лучше помещать на ранние стадии, а манипуляции с DOM — на фазу write.


Встроенные модификаторы

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

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

Пример использования встроенных модификаторов:

const popperInstance = Popper.createPopper(referenceElement, popperElement, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8], // смещение по оси X и Y
      },
    },
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top', 'right', 'left'], // альтернативные позиции
      },
    },
  ],
});

Создание кастомных модификаторов

Кастомные модификаторы позволяют реализовать уникальные сценарии. Основные шаги:

  1. Определить имя и включение:
const myModifier = {
  name: 'highlight',
  enabled: true,
  phase: 'main',
};
  1. Написать функцию fn:
fn({ state, options }) {
  const popper = state.elements.popper;
  if (state.placement.startsWith('top')) {
    popper.style.backgroundColor = options.color || 'yellow';
  }
}
  1. Добавить модификатор при создании Popper:
Popper.createPopper(referenceElement, popperElement, {
  modifiers: [myModifier],
});

Последовательность выполнения модификаторов

  • Модификаторы выполняются по порядку, указанному в массиве modifiers.

  • Модификаторы одной фазы выполняются последовательно, каждая функция получает актуальное состояние state, которое может быть изменено предыдущими модификаторами.

  • state содержит:

    • elements — DOM-элементы (popper, reference, arrow).
    • rects — размеры элементов.
    • modifiersData — данные модификаторов для передачи информации между ними.
    • placement — текущая позиция поппера.
    • styles — объект CSS-стилей, применяемых к popper.

Это обеспечивает гибкую и модульную архитектуру, где модификаторы могут как дополнять, так и полностью переопределять поведение друг друга.


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

  • Модификаторы могут передавать данные друг другу через state.modifiersData.
  • Один модификатор может использовать результаты другого, например arrow использует данные popperOffsets, рассчитанные модификатором computeStyles.
  • При конфликте модификаторов важна их последовательность: поздние модификаторы могут переопределять значения, установленные ранними.

Практические рекомендации

  • Использовать встроенные модификаторы для типовых задач, чтобы минимизировать сложность.
  • Для кастомных модификаторов использовать отдельные фазы: вычисления на main, обновление DOM на write.
  • Разбивать сложную логику на несколько модификаторов для упрощения поддержки и тестирования.
  • Всегда учитывать производительность, избегая тяжёлых операций в фазе main.

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