Система координат и области видимости

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

Координаты и reference

Reference — это элемент, относительно которого позиционируется popper. Popper.js вычисляет его bounding client rect, включающий top, left, right, bottom, width и height. Эти значения формируют точку отсчета для popper.

Popper.js применяет к этим координатам следующие преобразования:

  • Скролл контейнеров — координаты reference корректируются с учетом прокрутки всех родительских элементов с прокруткой.
  • CSS трансформации — любые transform свойства (например, translate, scale) на родительских контейнерах влияют на вычисленную позицию.
  • Границы контейнера — если popper привязан к контейнеру, ограничения по ширине и высоте учитываются автоматически.

Popper и offset

Popper.js позволяет задавать смещение через опцию modifiers с модификатором offset. Смещение работает в локальных координатах popper:

const popperInstance = Popper.createPopper(referenceElement, popperElement, {
  modifiers: [{
    name: 'offset',
    options: {
      offset: [10, 20], // [skew по оси x, skew по оси y]
    },
  }],
});

Первое значение массива — сдвиг по основной оси (main axis), второе — по перекрестной оси (cross axis). Основная ось зависит от выбранного placement (top, bottom, left, right).

Области видимости (clipping)

Popper.js учитывает ограничивающие области видимости, называемые clipping parents. Это элементы, которые могут обрезать popper по своим границам. Ключевые моменты:

  • Вычисление clipping rectangle происходит на основе всех ancestor элементов, у которых overflow установлен как hidden, scroll или auto.
  • Контейнер viewport всегда считается дополнительной областью ограничения.
  • Если popper выходит за пределы clipping rectangle, Popper.js может автоматически применять модификатор preventOverflow, чтобы корректировать позицию.

PreventOverflow

Модификатор preventOverflow позволяет popper оставаться внутри видимой области. Его конфигурация включает:

  • mainAxis — ограничение по основной оси.
  • altAxis — ограничение по перекрестной оси.
  • boundary — элемент или массив элементов, задающих границы.
  • tether — логика «привязки» popper к reference. Если true, popper не выходит за границы даже при смещении.

Пример:

const popperInstance = Popper.createPopper(referenceElement, popperElement, {
  modifiers: [{
    name: 'preventOverflow',
    options: {
      boundary: 'viewport',
      mainAxis: true,
      altAxis: true,
      tether: true,
    },
  }],
});

Влияние стратегии позиционирования

Popper.js поддерживает две стратегии позиционирования:

  • absolute — popper позиционируется относительно ближайшего относительно позиционированного контейнера.
  • fixed — popper фиксирован относительно окна браузера, игнорируя прокрутку родительских контейнеров.

Выбор стратегии влияет на вычисление координат reference, а также на поведение clipping. Например, при fixed popper не обрезается контейнерами с прокруткой.

Основные алгоритмы расчета координат

  1. Получение rect reference — Popper.js считывает координаты reference через getBoundingClientRect.
  2. Вычисление rect popper — размеры popper учитываются с padding, margin и border.
  3. Применение placement — определяется начальная позиция popper относительно reference (top, bottom, left, right).
  4. Применение модификаторов — offset, preventOverflow, flip.
  5. Финальное позиционирование — popper получает transform: translate3d(x, y, 0) или абсолютные координаты top/left в зависимости от стратегии.

Flip и динамическая адаптация

Модификатор flip позволяет менять сторону, если popper выходит за границы clipping rectangle. Flip работает в системе координат Popper.js, проверяя:

  • Достаточно ли места по основной оси.
  • Достаточно ли места по перекрестной оси (приоритетная проверка altAxis).
  • Возможность корректного смещения с offset.

Практическое взаимодействие с scroll и resize

Popper.js реагирует на события:

  • scroll — пересчитывает позиции, чтобы popper оставался видимым.
  • resize — корректирует размеры popper и reference.
  • mutation — изменения DOM (например, появление/скрытие контента) автоматически обновляют координаты.

Механизм update() позволяет вручную инициировать пересчет координат с учетом всех модификаторов:

popperInstance.update();

Итоговые особенности системы координат

  • Координаты popper относительны reference, с учетом scroll и transform.
  • Все смещения и ограничения применяются через модификаторы, которые могут комбинироваться.
  • Система поддерживает динамическое реагирование на viewport и DOM изменения.
  • Clipping rectangle обеспечивает корректное отображение popper внутри видимой области, предотвращая выход за границы контейнеров.

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