Настройка fallback позиций

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

Ключевая задача fallback-механизма — сохранить видимость и доступность popper-элемента независимо от изменений размеров окна, прокрутки или ограничений родительских контейнеров.


Базовый механизм работы

При задании основного размещения через свойство placement (например, "top"), Popper.js пытается разместить элемент строго в указанной позиции. Если вычисления показывают, что элемент выходит за границы viewport или clipping-контейнера, активируется модификатор flip.

Модификатор flip:

  • проверяет доступное пространство вокруг reference-элемента
  • выбирает альтернативную позицию
  • применяет её в реальном времени

Простейший пример:

createPopper(referenceElement, popperElement, {
  placement: 'top',
  modifiers: [
    {
      name: 'flip',
    },
  ],
});

В этом случае при нехватке места сверху popper может автоматически переместиться вниз.


Управление fallback-позициями

По умолчанию Popper.js использует логическую последовательность fallback-позиций, основанную на противоположных направлениях:

  • topbottom
  • leftright
  • bottomtop
  • rightleft

Однако поведение можно полностью контролировать через параметр fallbackPlacements.

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

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

В этом случае порядок будет строго следующим:

  1. top (основной)
  2. right
  3. bottom
  4. left

Алгоритм выбора позиции

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

  • наличие свободного пространства
  • вероятность выхода за границы
  • размер popper-элемента
  • ограничения контейнера (boundary)

Алгоритм:

  1. Применяется основная позиция
  2. Проверяется переполнение (overflow)
  3. Если есть overflow — перебираются fallback-позиции
  4. Выбирается первая подходящая

Если ни одна позиция не подходит полностью, выбирается наименее проблемная.


Влияние границ (boundary)

Fallback-позиции напрямую зависят от области, в пределах которой разрешено размещение. Эта область задаётся через параметр boundary.

Пример:

{
  name: 'flip',
  options: {
    boundary: 'viewport',
  },
}

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

  • viewport — границы окна браузера
  • clippingParents — ближайшие ограничивающие контейнеры
  • конкретный DOM-элемент

Если boundary слишком ограничен, fallback-позиции будут переключаться чаще.


Связь с модификатором preventOverflow

flip часто используется вместе с preventOverflow. Первый отвечает за смену позиции, второй — за корректировку внутри текущей позиции.

Пример комбинирования:

createPopper(referenceElement, popperElement, {
  placement: 'top',
  modifiers: [
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['bottom', 'right'],
      },
    },
    {
      name: 'preventOverflow',
      options: {
        padding: 8,
      },
    },
  ],
});

Поведение:

  • сначала пробуется top
  • при overflow → bottom, затем right
  • если даже там есть проблемы — preventOverflow подстраивает положение

Приоритет fallback-позиций

Порядок в массиве fallbackPlacements критически важен. Popper.js не пытается «оптимизировать» список — он строго следует заданной последовательности.

Неудачный пример:

fallbackPlacements: ['left', 'right', 'bottom']

Если пространство справа идеально, но слева мало места — всё равно сначала проверяется left.

Оптимальный подход:

  • располагать наиболее вероятные позиции первыми
  • учитывать направление интерфейса (LTR/RTL)
  • учитывать поведение пользователя (например, выпадающие меню чаще открываются вниз)

Автоматическая генерация fallback

Если fallbackPlacements не задан, Popper генерирует список автоматически, основываясь на основной позиции.

Пример:

placement: 'top-start'

Автоматические fallback:

  • bottom-start
  • top-end
  • bottom-end

Это особенно важно для вариаций с -start и -end.


Работа с вариациями (start / end)

Fallback-позиции могут включать вариации выравнивания:

fallbackPlacements: [
  'top-start',
  'top-end',
  'bottom-start',
  'bottom-end'
]

Это позволяет более точно контролировать положение относительно reference-элемента.

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

  • start выравнивает по началу (лево или верх)
  • end — по концу (право или низ)
  • fallback учитывает как направление, так и выравнивание

Ограничение fallback-поведения

Иногда требуется полностью отключить fallback или сильно его ограничить.

Пример отключения:

{
  name: 'flip',
  enabled: false,
}

Пример ограничения:

fallbackPlacements: []

В этом случае popper остаётся в основной позиции даже при переполнении.


Стратегии выбора fallback

Существует несколько подходов к формированию fallback-цепочек:

1. Противоположная позиция

['bottom']

Минимальный вариант — только зеркальная позиция.

2. Перпендикулярные направления

['right', 'left']

Используется при ограничении по вертикали.

3. Полный цикл

['top', 'right', 'bottom', 'left']

Максимальная гибкость, но больше вычислений.

4. Приоритет UX

['bottom', 'bottom-start', 'bottom-end']

Ориентация на привычное поведение интерфейса.


Производительность и fallback

Чем больше fallback-позиций:

  • тем больше вычислений
  • тем выше нагрузка при частых обновлениях (scroll, resize)

Оптимизация:

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

Частые ошибки

Отсутствие fallbackPlacements при сложной верстке

В сложных layout (например, внутри scroll-контейнеров) стандартного поведения недостаточно.

Неправильный порядок

Логически неверный порядок приводит к «скачкам» popper.

Игнорирование boundary

Без явного указания границ поведение может быть непредсказуемым.

Слишком длинный список

Избыточные fallback-позиции ухудшают производительность без заметной пользы.


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

createPopper(button, tooltip, {
  placement: 'top',
  modifiers: [
    {
      name: 'flip',
      options: {
        fallbackPlacements: [
          'bottom',
          'right',
          'left'
        ],
        padding: 8,
      },
    },
    {
      name: 'preventOverflow',
      options: {
        boundary: document.body,
      },
    },
  ],
});

Поведение:

  • приоритет: сверху
  • если нет места — снизу
  • затем справа и слева
  • учитываются границы body
  • добавлен внутренний отступ 8px

Глубокая настройка через altBoundary

Опция altBoundary позволяет учитывать альтернативный контейнер при вычислениях:

{
  name: 'flip',
  options: {
    altBoundary: true,
  },
}

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


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

rootBoundary задаёт глобальные ограничения:

{
  name: 'flip',
  options: {
    rootBoundary: 'document',
  },
}

Варианты:

  • viewport
  • document

Это влияет на то, как рассчитываются fallback-позиции в масштабируемых или прокручиваемых документах.


Поведение при динамических изменениях

Fallback-позиции пересчитываются при:

  • изменении размеров окна
  • прокрутке
  • изменении размеров элементов
  • изменении DOM

Popper использует систему обновлений (update lifecycle), чтобы автоматически адаптировать позицию.


Кастомные сценарии

Иногда требуется динамически менять fallback:

const isMobile = window.innerWidth < 768;

createPopper(ref, pop, {
  modifiers: [
    {
      name: 'flip',
      options: {
        fallbackPlacements: isMobile
          ? ['bottom']
          : ['right', 'left'],
      },
    },
  ],
});

Это позволяет адаптировать поведение под разные устройства.


Итоговая структура настройки

Ключевые параметры модификатора flip:

{
  name: 'flip',
  options: {
    fallbackPlacements: [...],
    boundary: ...,
    rootBoundary: ...,
    padding: ...,
    altBoundary: ...,
  }
}

Грамотная настройка этих параметров обеспечивает:

  • устойчивое позиционирование
  • предсказуемое поведение
  • отсутствие визуальных артефактов
  • корректную работу в сложных интерфейсах