Динамическая конфигурация

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

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


Обновление параметров через setOptions

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

const popperInstance = createPopper(reference, popper, {
  placement: 'bottom',
});

popperInstance.setOptions({
  placement: 'top',
});

Особенности:

  • Обновление происходит асинхронно
  • Пересчитываются все модификаторы
  • Сохраняется текущее состояние DOM

Функциональный вариант

setOptions может принимать функцию, получающую текущие опции:

popperInstance.setOptions((options) => ({
  ...options,
  placement: 'right',
}));

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


Динамическое управление модификаторами

Модификаторы определяют поведение Popper: смещение, предотвращение выхода за границы, стрелки и др. Их можно изменять во время работы.

Добавление модификатора

popperInstance.setOptions((options) => ({
  ...options,
  modifiers: [
    ...options.modifiers,
    {
      name: 'offset',
      options: {
        offset: [0, 20],
      },
    },
  ],
}));

Удаление модификатора

popperInstance.setOptions((options) => ({
  ...options,
  modifiers: options.modifiers.filter(
    (modifier) => modifier.name !== 'offset'
  ),
}));

Обновление существующего модификатора

popperInstance.setOptions((options) => ({
  ...options,
  modifiers: options.modifiers.map((modifier) => {
    if (modifier.name === 'offset') {
      return {
        ...modifier,
        options: {
          offset: [0, 40],
        },
      };
    }
    return modifier;
  }),
}));

Реакция на пользовательские события

Динамическая конфигурация часто используется в обработчиках событий.

Изменение позиции при клике

button.addEventListener('click', () => {
  popperInstance.setOptions({
    placement: 'bottom-start',
  });
});

Переключение поведения при наведении

element.addEventListener('mouseenter', () => {
  popperInstance.setOptions({
    placement: 'top',
  });
});

element.addEventListener('mouseleave', () => {
  popperInstance.setOptions({
    placement: 'bottom',
  });
});

Адаптация к размеру экрана

Popper можно интегрировать с логикой адаптивности.

Использование matchMedia

const mediaQuery = window.matchMedia('(max-width: 768px)');

function updatePlacement(e) {
  popperInstance.setOptions({
    placement: e.matches ? 'bottom' : 'right',
  });
}

mediaQuery.addEventListener('change', updatePlacement);
updatePlacement(mediaQuery);

Динамическое изменение reference и popper элементов

Иногда требуется изменить якорный элемент или сам popper.

Пересоздание экземпляра

popperInstance.destroy();

const newInstance = createPopper(newReference, newPopper, {
  placement: 'top',
});

Обновление через виртуальный элемент

Popper поддерживает виртуальные элементы:

const virtualElement = {
  getBoundingClientRect: () => ({
    width: 0,
    height: 0,
    top: mouseY,
    left: mouseX,
    right: mouseX,
    bottom: mouseY,
  }),
};

createPopper(virtualElement, popper);

Это позволяет динамически привязывать позицию к координатам курсора.


Управление жизненным циклом

Принудительное обновление

popperInstance.update();

Используется при изменениях DOM, которые Popper не может отследить автоматически.

Отложенное обновление

await popperInstance.update();

Полезно при работе с асинхронными изменениями интерфейса.


Условная логика внутри модификаторов

Модификаторы могут содержать динамическую логику.

const dynamicOffset = {
  name: 'offset',
  options: {
    offset: ({ placement }) => {
      if (placement === 'top') {
        return [0, 10];
      }
      return [0, 20];
    },
  },
};

Такой подход позволяет изменять поведение без пересоздания конфигурации.


Работа с состоянием (state)

Каждый модификатор получает доступ к состоянию Popper:

const customModifier = {
  name: 'custom',
  enabled: true,
  phase: 'main',
  fn({ state }) {
    console.log(state.placement);
  },
};

Это позволяет:

  • анализировать текущее размещение
  • учитывать размеры элементов
  • динамически изменять стили

Интеграция с фреймворками

React

useEffect(() => {
  popperInstance.setOptions({
    placement: isOpen ? 'bottom' : 'top',
  });
}, [isOpen]);

Vue

watch(isOpen, (value) => {
  popperInstance.setOptions({
    placement: value ? 'right' : 'left',
  });
});

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

Частое изменение конфигурации может приводить к лишним перерасчётам.

Дебаунс обновлений

let timeout;

function updatePopper() {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    popperInstance.update();
  }, 100);
}

Минимизация изменений

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

Сложные сценарии

Переключение нескольких состояний

function setMode(mode) {
  const configs = {
    tooltip: { placement: 'top' },
    dropdown: { placement: 'bottom-start' },
    context: { placement: 'right-start' },
  };

  popperInstance.setOptions(configs[mode]);
}

Контекстное позиционирование

document.addEventListener('contextmenu', (e) => {
  e.preventDefault();

  const virtualElement = {
    getBoundingClientRect: () => ({
      width: 0,
      height: 0,
      top: e.clientY,
      left: e.clientX,
      right: e.clientX,
      bottom: e.clientY,
    }),
  };

  popperInstance.destroy();
  popperInstance = createPopper(virtualElement, popper, {
    placement: 'right',
  });
});

Расширяемая архитектура

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

  • всплывающие подсказки с адаптивным поведением
  • контекстные меню
  • сложные UI-компоненты (dropdown, select, autocomplete)
  • системы позиционирования с учётом пользовательского ввода

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