Select с позиционированием опций

Popper.js — это мощная библиотека для управления позиционированием всплывающих элементов относительно их «опорных» элементов. В контексте sel ect-компонентов она используется для корректного отображения выпадающих списков опций независимо от размеров окна, прокрутки страницы или положения родительского элемента.

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

import { createPopper } fr om '@popperjs/core';

const button = document.querySelector('#select-button');
const dropdown = document.querySelector('#select-dropdown');

createPopper(button, dropdown, {
  placement: 'bottom-start',
});
  • placement определяет начальное позиционирование (top, bottom, left, right) с опциональными уточнениями (start, end).
  • Popper автоматически учитывает ограничения viewport и может переносить элемент, чтобы он не выходил за границы экрана.

Модификаторы Popper.js для select

Popper.js использует модификаторы, которые изменяют поведение поппера. Для select-списков наиболее важные:

  1. offset Позволяет задавать смещение относительно reference, чтобы выпадающий список не прилипал к кнопке:
createPopper(button, dropdown, {
  placement: 'bottom-start',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8], // [по горизонтали, по вертикали]
      },
    },
  ],
});
  1. flip Автоматически меняет позицию поппера, если нет места в текущем направлении. Особенно важно при адаптивной верстке:
modifiers: [
  {
    name: 'flip',
    options: {
      fallbackPlacements: ['top-start', 'right-start'],
    },
  },
]
  1. preventOverflow Обеспечивает, чтобы dropdown не выходил за границы контейнера или viewport:
modifiers: [
  {
    name: 'preventOverflow',
    options: {
      padding: 8, // отступ от края экрана
    },
  },
]
  1. computeStyles Контролирует рендеринг стилей, например использование transform или top/left для позиционирования:
modifiers: [
  {
    name: 'computeStyles',
    options: {
      adaptive: true, // меняет position при прокрутке
      gpuAcceleration: true, // ускоряет анимацию
    },
  },
]

Динамическое управление списком опций

В select-компонентах содержимое списка часто изменяется динамически. Popper.js предоставляет методы обновления позиции поппера:

const popperInstance = createPopper(button, dropdown, { placement: 'bottom-start' });

// при изменении содержимого списка
popperInstance.update();

Если список становится выше или ниже, чем ожидалось, update() перерассчитывает все параметры и корректно отображает dropdown.


Использование виртуальных элементов

Popper.js поддерживает виртуальные reference-элементы, что полезно для кастомных селектов, где trigger не является реальным DOM-элементом:

const virtualReference = {
  getBoundingClientRect: () => ({
    width: 100,
    height: 40,
    top: 200,
    right: 300,
    bottom: 240,
    left: 200,
  }),
  contextElement: document.body,
};

createPopper(virtualReference, dropdown, { placement: 'bottom' });

Такой подход позволяет позиционировать dropdown даже относительно координат, полученных из пользовательских вычислений.


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

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

  • flip для выбора альтернативного положения.
  • preventOverflow с padding для безопасного расстояния от краев экрана.
  • eventListeners для обновления позиции при прокрутке или ресайзе:
modifiers: [
  {
    name: 'eventListeners',
    options: {
      scroll: true,
      resize: true,
    },
  },
]

Анимация появления и скрытия dropdown

Popper.js сам по себе не управляет анимацией, но позволяет безопасно комбинировать позиционирование с CSS-анимациями:

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

#select-dropdown.show {
  opacity: 1;
  transform: scaleY(1);
}
button.addEventListener('click', () => {
  dropdown.classList.toggle('show');
  popperInstance.update();
});

Важно вызывать update() после изменения класса, чтобы Popper пересчитал размеры и корректно отобразил dropdown.


Обработка сложных сценариев

  1. Dropdown с прокручиваемым контентом Использовать preventOverflow с boundariesElement: 'viewport' или кастомным контейнером.

  2. Многоуровневые меню Для вложенных списков можно создавать отдельные Popper-инстансы, привязанные к родительской опции.

  3. Динамическое изменение reference При смене кнопки или позиции элемента достаточно вызвать popperInstance.setOptions({}) и update() для пересчета.


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