Ограничение области flip

Модификатор flip в Popper.js отвечает за автоматическое изменение размещения всплывающего элемента (popper), если в текущем положении он выходит за границы допустимой области. По умолчанию библиотека стремится сохранить выбранное положение (top, bottom, left, right), однако при нехватке пространства выполняет «переворот» (flip) — например, с bottom на top.

В реальных интерфейсах неконтролируемый flip может приводить к нежелательным эффектам:

  • popper выходит за пределы контейнера с прокруткой
  • позиционирование нарушает визуальную иерархию
  • элемент «прыгает» между сторонами при незначительном скролле
  • возникают конфликты с фиксированными или ограниченными областями

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


Основные параметры ограничения области

Модификатор flip принимает объект настроек, среди которых ключевыми являются:

createPopper(reference, popper, {
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: 'clippingParents',
        rootBoundary: 'viewport',
        altBoundary: false,
        padding: 8,
      },
    },
  ],
});

boundary

Определяет область, в пределах которой проверяется переполнение.

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

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

Пример ограничения конкретным контейнером:

const container = document.querySelector('.container');

createPopper(reference, popper, {
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: container,
      },
    },
  ],
});

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


rootBoundary

Задает корневую область отсечения:

  • 'viewport' — границы окна браузера
  • 'document' — весь документ

Разница проявляется при прокрутке:

  • viewport учитывает только видимую часть
  • document позволяет учитывать скрытые области
options: {
  rootBoundary: 'document'
}

altBoundary

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

  • false — используется popper
  • true — используется reference-элемент

Это влияет на сценарии, где reference находится внутри ограниченного контейнера, а popper — вне его.

options: {
  altBoundary: true
}

padding

Добавляет внутренний отступ от границы области. Это предотвращает «прилипание» popper к краям.

options: {
  padding: 16
}

Можно задавать объект:

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

Алгоритм работы flip с учетом границ

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

Если ни одно положение не подходит — используется исходное (в зависимости от конфигурации других модификаторов, например preventOverflow)


Альтернативные размещения (fallbackPlacements)

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

createPopper(reference, popper, {
  placement: 'bottom',
  modifiers: [
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top', 'right'],
      },
    },
  ],
});

Порядок имеет значение — Popper проверяет их последовательно.


Ограничение flip внутри прокручиваемого контейнера

Частый сценарий — popper внутри блока с overflow: auto.

<div class="scroll-container">
  <button id="ref">Кнопка</button>
  <div id="pop">Tooltip</div>
</div>
const container = document.querySelector('.scroll-container');

createPopper(ref, pop, {
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: container,
        rootBoundary: 'document',
      },
    },
  ],
});

Результат:

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

Взаимодействие с preventOverflow

flip и preventOverflow работают совместно:

  • flip меняет сторону размещения
  • preventOverflow корректирует позицию внутри текущей стороны

Пример:

modifiers: [
  {
    name: 'flip',
    options: {
      boundary: 'viewport',
    },
  },
  {
    name: 'preventOverflow',
    options: {
      boundary: 'viewport',
    },
  },
]

Если flip не может найти подходящее положение, preventOverflow сдвигает popper внутри доступной области.


Практические сценарии ограничения области

1. Tooltip внутри модального окна

const modal = document.querySelector('.modal');

createPopper(reference, tooltip, {
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: modal,
      },
    },
  ],
});

Исключается выход tooltip за границы модального окна.


createPopper(button, dropdown, {
  placement: 'bottom-start',
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: 'clippingParents',
        padding: 8,
      },
    },
  ],
});

Позволяет dropdown корректно «переворачиваться» внутри панели.


3. Запрет flip за пределы viewport

createPopper(ref, pop, {
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: 'viewport',
        rootBoundary: 'viewport',
      },
    },
  ],
});

Полезно для всплывающих подсказок в полноэкранных интерфейсах.


Ошибки и подводные камни

1. Игнорирование overflow у родителей Если контейнер имеет overflow: hidden, popper может визуально обрезаться, даже если расчеты корректны.

2. Несоответствие boundary и layout Использование viewport при вложенной верстке может давать неожиданные flip-поведения.

3. Частые перевороты при скролле Возникают, если границы заданы слишком жестко или отсутствует padding.

4. Неправильный altBoundary Может приводить к некорректным вычислениям, если reference и popper находятся в разных контекстах.


Рекомендации по настройке

  • Для большинства случаев: boundary: 'clippingParents'

  • Для сложных UI (модалки, панели): использовать конкретный DOM-элемент

  • Для стабильности:

    • задавать padding
    • ограничивать fallbackPlacements
  • Для предсказуемости:

    • синхронизировать настройки flip и preventOverflow

Минимальная конфигурация с контролем области

createPopper(reference, popper, {
  placement: 'bottom',
  modifiers: [
    {
      name: 'flip',
      options: {
        boundary: 'clippingParents',
        fallbackPlacements: ['top'],
        padding: 8,
      },
    },
    {
      name: 'preventOverflow',
      options: {
        boundary: 'clippingParents',
      },
    },
  ],
});

Обеспечивает:

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