Ручное управление обновлениями

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


Создание Popper с отключённым автообновлением

Для отключения автоматических обновлений используется опция modifiers с модификатором eventListeners, где scroll и resize устанавливаются в false:

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

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

const popperInstance = createPopper(reference, popper, {
  modifiers: [
    {
      name: 'eventListeners',
      options: {
        scroll: false,
        resize: false,
      },
    },
  ],
});

В этом примере Popper не будет автоматически реагировать на изменения размеров или прокрутки страницы. Всё управление позицией теперь лежит на разработчике.


Ручной вызов обновления

Popper.js предоставляет метод update() для принудительного пересчёта позиции:

popperInstance.update();

Метод возвращает промис, который разрешается после завершения всех вычислений и применений стилей. Это позволяет синхронизировать позицию с другими асинхронными действиями, например, с анимацией открытия всплывающего элемента:

popperInstance.update().then(() => {
  console.log('Позиция обновлена');
});

Использование метода forceUpdate()

Если необходимо немедленно пересчитать позицию без асинхронной задержки, применяется метод forceUpdate():

popperInstance.forceUpdate();

Отличие от update() заключается в том, что forceUpdate() выполняет синхронное обновление, игнорируя оптимизации библиотеки и любые очереди изменений.


Комбинирование с динамическими модификаторами

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

popperInstance.setOptions({
  modifiers: [
    {
      name: 'offset',
      options: { offset: [0, 20] },
    },
    {
      name: 'preventOverflow',
      options: { padding: 8 },
    },
  ],
});
popperInstance.update();

Таким образом, можно изменять смещение, границы и другие параметры без пересоздания Popper-экземпляра.


Оптимизация производительности при частых изменениях

В случаях, когда элементы часто перемещаются или изменяются размеры контейнеров, рекомендуется:

  1. Отключить автоматические слушатели событий (scroll, resize).
  2. Использовать update() только по необходимости, например, при завершении анимации или при конкретном событии DOM.
  3. Объединять несколько изменений в один вызов обновления, чтобы избежать лишних пересчётов.

Пример объединения событий:

let updateScheduled = false;

function scheduleUpdate() {
  if (!updateScheduled) {
    updateScheduled = true;
    requestAnimationFrame(() => {
      popperInstance.update();
      updateScheduled = false;
    });
  }
}

document.addEventListener('scroll', scheduleUpdate);
document.addEventListener('resize', scheduleUpdate);

Такой подход снижает нагрузку при высокочастотных событиях.


Отслеживание и синхронизация с внешними библиотеками

Ручное управление обновлениями идеально сочетается с библиотеками анимаций или кастомными скролл-плагинами. После изменения позиции таргета или размеров всплывающего элемента необходимо явно вызвать update():

gsap.to(reference, { x: 100, duration: 0.5, onUpdate: () => popperInstance.update() });

Это гарантирует, что Popper синхронизирован с внешними трансформациями.


Полезные сценарии применения

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

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