Структура модификатора

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

Модификаторы применяются через массив modifiers, который передается при создании экземпляра поппера:

const popperInstance = Popper.createPopper(referenceElement, popperElement, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8]
      }
    }
  ]
});

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

Каждый модификатор — это объект с несколькими обязательными и опциональными полями:

  1. name Уникальное имя модификатора. Обязательно для идентификации. Например: 'offset', 'flip', 'preventOverflow'.

  2. enabled Булево значение, определяющее, активен ли модификатор. По умолчанию true.

  3. phase Фаза жизненного цикла поппера, на которой модификатор выполняется. Возможные значения:

    • beforeRead
    • read
    • afterRead
    • beforeMain
    • main
    • afterMain
    • beforeWrite
    • write
    • afterWrite

    Важно: корректное указание фазы обеспечивает правильный порядок исполнения, что критично для сложных взаимодействий нескольких модификаторов.

  4. requires Массив имён модификаторов, которые должны выполниться до данного. Используется для управления зависимостями. Например, flip требует выполнения preventOverflow.

  5. requiresIfExists Похож на requires, но зависимость необязательна — модификатор выполнится даже если указанные модификаторы отсутствуют.

  6. options Настройки, специфичные для модификатора. Например, у offset это массив [skidding, distance], а у preventOverflow — объект с полями mainAxis, altAxis и boundary.

  7. fn Основная функция модификатора. Принимает объект с текущим состоянием поппера и возвращает изменения:

function fn({ state, options, name }) {
  // state содержит данные о позиционировании, размерах и элементах
  // options — пользовательские настройки модификатора
  // name — имя модификатора
}
  1. effect (опционально) Функция для установки побочных эффектов, например добавления слушателей событий. Возвращает функцию очистки (cleanup), вызываемую при уничтожении поппера или отключении модификатора.
effect({ state, instance, options }) {
  const handleResize = () => instance.update();
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}

Жизненный цикл модификатора

Модификаторы выполняются в строгом порядке, определяемом их фазами и зависимостями:

  1. Чтение данных (read) Модификаторы на этих фазах получают текущие размеры и положение элементов, рассчитывают необходимые параметры.

  2. Основные вычисления (main) Производятся расчёты с учетом offset, boundaries, flip и других параметров.

  3. Запись (write) Модификаторы записывают результат вычислений в DOM, изменяя стиль и положение поппера.

  4. Эффекты (effect) Инициализация событий, подписок и других побочных действий.

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

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

Каждый встроенный модификатор следует той же структуре, что и пользовательский.

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

Создание собственного модификатора позволяет реализовать уникальные сценарии позиционирования:

const customModifier = {
  name: 'logPosition',
  enabled: true,
  phase: 'afterWrite',
  fn({ state }) {
    console.log('Popper position:', state.styles.popper);
  }
};

Добавление его в массив modifiers позволит отслеживать положение поппера после всех вычислений и записи в DOM.

Рекомендации по структурированию модификаторов

  • Чётко указывать фазу — это предотвращает конфликты с другими модификаторами.
  • Использовать requires при зависимостях, чтобы гарантировать правильный порядок.
  • Разделять вычисления и эффекты — функция fn должна только вычислять данные, побочные действия — в effect.
  • Оптимизировать fn — избегать тяжелых синхронных операций в фазе main, чтобы не снижать производительность.

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

При сложных сценариях важно понимать, что:

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

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