Предотвращение выхода за границы

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

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


Модификатор preventOverflow

Модификатор preventOverflow отвечает за удержание всплывающего элемента внутри заданных границ.

Основные функции:

  • предотвращает выход popper-элемента за пределы контейнера
  • корректирует позицию по осям X и Y
  • учитывает прокрутку, размеры viewport и родительских контейнеров

Пример настройки:

createPopper(referenceElement, popperElement, {
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        boundary: 'clippingParents',
      },
    },
  ],
});

Параметры модификатора preventOverflow

boundary

Определяет границы, внутри которых должен оставаться popper.

Возможные значения:

  • 'clippingParents' — все родительские элементы с overflow
  • 'viewport' — границы окна браузера
  • 'document' — весь документ
  • конкретный DOM-элемент

Пример:

boundary: document.body

rootBoundary

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

Варианты:

  • 'viewport' (по умолчанию)
  • 'document'
rootBoundary: 'document'

padding

Добавляет отступ от границы, чтобы popper не “прилипал” к краю.

padding: 8

Можно задавать разные значения по сторонам:

padding: {
  top: 10,
  right: 5,
  bottom: 10,
  left: 5,
}

altAxis

По умолчанию Popper корректирует позицию только по основной оси (в зависимости от placement). Параметр altAxis включает коррекцию по дополнительной оси.

altAxis: true

Это особенно важно для предотвращения наложений в сложных интерфейсах.


tether

Определяет, должен ли popper оставаться “привязанным” к reference-элементу.

tether: true

Если отключить:

tether: false

popper может полностью оторваться от reference, чтобы остаться в пределах границ.


tetherOffset

Позволяет задать дополнительное смещение при tether-поведении.

tetherOffset: 10

Или функция:

tetherOffset: ({ popper, reference }) => {
  return popper.width / 2;
}

Как работает алгоритм предотвращения выхода

  1. Вычисляется положение popper относительно reference
  2. Определяются границы (boundary и rootBoundary)
  3. Проверяется, выходит ли popper за пределы
  4. При необходимости координаты корректируются
  5. Применяется tether-логика для сохранения связи с reference

Взаимодействие с другими модификаторами

flip

Модификатор flip меняет сторону размещения, если текущая не помещается.

Пример:

modifiers: [
  {
    name: 'flip',
    options: {
      fallbackPlacements: ['top', 'right'],
    },
  },
  {
    name: 'preventOverflow',
  },
]

Связка flip + preventOverflow обеспечивает:

  • смену позиции при нехватке места
  • точную подгонку внутри границ

offset

Используется для задания смещения, которое затем учитывается preventOverflow.

{
  name: 'offset',
  options: {
    offset: [0, 10],
  },
}

Типичные проблемы и их решения

1. Popper выходит за пределы контейнера

Причины:

  • неправильно задан boundary
  • контейнер имеет overflow: hidden

Решение:

boundary: 'viewport'

или указание конкретного контейнера.


2. Popper “залипает” у края

Причина:

  • отсутствует padding

Решение:

padding: 10

3. Popper смещается слишком далеко от reference

Причина:

  • отключен tether

Решение:

tether: true

4. Некорректная работа при скролле

Причина:

  • неправильные границы или rootBoundary

Решение:

rootBoundary: 'viewport'

Практический пример

createPopper(button, tooltip, {
  placement: 'bottom',
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 12],
      },
    },
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top', 'right'],
      },
    },
    {
      name: 'preventOverflow',
      options: {
        boundary: 'viewport',
        padding: 8,
        altAxis: true,
        tether: true,
      },
    },
  ],
});

Влияние CSS на поведение preventOverflow

Некоторые CSS-свойства напрямую влияют на работу Popper:

overflow

.container {
  overflow: hidden;
}

Ограничивает область видимости и влияет на clippingParents.


position

Родительские элементы с position: relative | absolute | fixed участвуют в вычислениях.


transform

.container {
  transform: translateZ(0);
}

Создаёт новый контекст наложения и может изменить поведение позиционирования.


Расширенные сценарии

Ограничение внутри модального окна

boundary: modalElement

Позволяет удерживать popper внутри модального контейнера.


Работа внутри iframe

Необходимо учитывать document внутри iframe:

rootBoundary: 'document'

Кастомная логика ограничения

Можно комбинировать модификаторы или писать собственные:

const customModifier = {
  name: 'customPrevent',
  enabled: true,
  phase: 'main',
  fn({ state }) {
    // кастомная логика
  },
};

Ключевые принципы

  • предотвращение выхода — это не просто ограничение, а динамическая адаптация позиции
  • корректная настройка boundary критична для стабильного UI
  • комбинация flip, offset и preventOverflow даёт наилучший результат
  • влияние CSS-контекста необходимо учитывать при отладке