Dropdowns

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


Инициализация Popper.js для Dropdown

Для начала необходимо подключить библиотеку Popper.js (версии 2.x) через npm или CDN. Основная задача Popper — вычислять позицию popper-элемента относительно reference-элемента. В контексте dropdown reference — это кнопка или другой элемент, по которому открывается меню.

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

const button = document.querySelector('#dropdownButton');
const menu = document.querySelector('#dropdownMenu');

const popperInstance = createPopper(button, menu, {
  placement: 'bottom-start', // Позиция меню относительно кнопки
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8], // Смещение по горизонтали и вертикали
      },
    },
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top-start', 'right-start', 'left-start'],
      },
    },
    {
      name: 'preventOverflow',
      options: {
        boundary: 'viewport', // Ограничение выхода за границы окна
      },
    },
  ],
});

Ключевые моменты:

  • placement задаёт базовую позицию dropdown относительно reference.
  • Модификатор offset позволяет сдвигать меню от reference, чтобы избежать наложения.
  • Модификатор flip автоматически меняет положение, если dropdown не помещается на экране.
  • preventOverflow предотвращает выход dropdown за границы viewport или другого контейнера.

Управление видимостью Dropdown

Popper.js отвечает только за позиционирование, поэтому показ и скрытие меню необходимо реализовать отдельно через JS:

button.addEventListener('click', () => {
  menu.classList.toggle('show'); // Класс 'show' отображает меню
  popperInstance.update(); // Обновление позиции при открытии
});

document.addEventListener('click', (event) => {
  if (!button.contains(event.target) && !menu.contains(event.target)) {
    menu.classList.remove('show');
  }
});

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

  • popperInstance.update() гарантирует корректное позиционирование при изменении размеров кнопки или меню.
  • Обработка клика вне dropdown позволяет автоматически закрывать меню, улучшая UX.

Настройка анимации и стилей

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

#dropdownMenu {
  opacity: 0;
  transform: scaleY(0);
  transform-origin: top;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

#dropdownMenu.show {
  opacity: 1;
  transform: scaleY(1);
}

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


Динамические dropdown-элементы

Popper.js поддерживает динамические reference и popper-элементы. Можно создавать dropdown на лету:

function createDropdown(referenceElement, content) {
  const menu = document.createElement('div');
  menu.className = 'dropdown-menu';
  menu.innerHTML = content;
  document.body.appendChild(menu);

  const instance = createPopper(referenceElement, menu, {
    placement: 'bottom-start',
    modifiers: [
      { name: 'offset', options: { offset: [0, 4] } },
    ],
  });

  return { menu, instance };
}

Преимущества:

  • Возможность создавать любое количество dropdown без изменения статической разметки.
  • Легко интегрировать с динамическими интерфейсами, такими как таблицы, списки или SPA.

Использование событий Popper.js

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

const popperInstance = createPopper(button, menu, {
  placement: 'bottom-start',
  modifiers: [
    {
      name: 'eventListeners',
      enabled: true,
    },
    {
      name: 'applyStyles',
      fn({ state }) {
        console.log('Новая позиция:', state.styles.popper);
      },
    },
  ],
});

Это полезно для интеграции с анимациями, адаптивным дизайном или кастомными эффектами.


Адаптация для мобильных устройств

Dropdown на мобильных требует учёта размеров экрана и сенсорного ввода. Popper.js можно настроить через preventOverflow и flip:

modifiers: [
  { name: 'preventOverflow', options: { boundary: 'viewport', padding: 8 } },
  { name: 'flip', options: { fallbackPlacements: ['top', 'left', 'right'] } },
]
  • padding предотвращает прилипание меню к краям экрана.
  • fallbackPlacements задаёт альтернативные позиции для корректного отображения на малых экранах.

Советы по производительности

  • Создавать Popper-инстанс только при открытии dropdown, уничтожать при закрытии (popperInstance.destroy()), чтобы снизить нагрузку.
  • Использовать modifiers: [{ name: 'computeStyles', options: { gpuAcceleration: true } }] для плавной анимации.
  • Минимизировать сложные стили внутри dropdown, чтобы не перегружать браузер при пересчёте позиции.