Позиционирование в контейнерах с overflow

При работе с библиотекой Tippy.js важно понимать, как тултипы позиционируются относительно своих триггеров и как контейнеры с CSS-свойством overflow: hidden или overflow: auto могут влиять на отображение подсказок. Неправильная настройка приводит к тому, что тултипы обрезаются или располагаются некорректно.


Принцип работы позиционирования Tippy.js

Tippy.js использует Popper.js под капотом для управления позиционированием. Это означает, что тултип создается как отдельный DOM-элемент, который по умолчанию вставляется в конец <body>, а не внутрь родительского контейнера триггера. Такой подход позволяет тултипу оставаться видимым даже при обрезании родительских блоков с overflow, поскольку он не наследует ограничения родителя.

tippy('#myButton', {
  content: 'Подсказка',
});

Здесь #myButton может находиться внутри любого контейнера, но тултип будет позиционироваться относительно окна, а не родителя.


Проблемы при изменении контейнера appendTo

Если указать опцию appendTo и вставить тултип внутрь контейнера с overflow: hidden, появляется обрезание:

tippy('#myButton', {
  content: 'Подсказка',
  appendTo: document.querySelector('#scrollContainer')
});
  • Контейнер #scrollContainer с overflow: hidden обрежет тултип.
  • Визуально тултип может частично или полностью исчезнуть за границами контейнера.
  • Такое поведение оправдано, если необходимо, чтобы тултип был локализован внутри скроллируемого блока, но чаще это приводит к багам.

Вывод: использование appendTo должно учитывать overflow-стили родителя.


Работа с scroll и ограниченными контейнерами

Для контейнеров с overflow: auto или scroll тултипы, вставленные внутрь родителя, не будут автоматически следовать за прокруткой. Встроенные механизмы Popper.js позволяют управлять этим через опцию popperOptions:

tippy('#myButton', {
  content: 'Подсказка',
  popperOptions: {
    modifiers: [
      {
        name: 'preventOverflow',
        options: {
          boundary: 'viewport' // можно заменить на '#scrollContainer'
        }
      }
    ]
  }
});
  • boundary: 'viewport' – тултип остаётся в пределах окна браузера.
  • boundary: '#scrollContainer' – тултип не выйдет за границы скролл-контейнера.

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


Контейнеры с transform и perspective

Если родитель контейнера использует CSS-трансформации (transform: scale(), translate()) или perspective, это создаёт новый контекст наложения. В этом случае тултипы, которые вставляются внутрь <body>, могут неправильно выровняться по координатам:

  • Попытка позиционировать тултип относительно триггера внутри трансформированного блока приведёт к смещению.
  • Решение — использовать appendTo: document.body и полагаться на Popper.js для вычисления позиции относительно окна.

Опции Tippy.js, влияющие на позиционирование

  1. placement – определяет сторону появления тултипа (top, bottom, left, right, с вариантами -start, -end).
  2. flip – позволяет автоматически менять сторону при нехватке места.
  3. offset – задаёт смещение тултипа относительно триггера.
  4. boundary – определяет, где Popper.js будет предотвращать выход тултипа за пределы видимой области.
  5. appendTo – выбор контейнера для вставки тултипа.

Пример:

tippy('#myButton', {
  content: 'Подсказка',
  placement: 'top-start',
  offset: [0, 8],
  flip: true,
  popperOptions: {
    modifiers: [
      {
        name: 'preventOverflow',
        options: {
          boundary: 'viewport'
        }
      }
    ]
  },
  appendTo: document.body
});
  • Смещение на 8px от кнопки.
  • Автоматический флип при нехватке места.
  • Тултип вставлен в <body> и избегает обрезки контейнеров с overflow.

Случаи использования в сложных интерфейсах

  • Скролл-контейнеры с множеством элементов: рекомендуется не вставлять тултип внутрь скролл-контейнера, чтобы избежать обрезки.
  • Модальные окна с overflow: Tippy.js корректно работает, если тултип добавляется в <body> и используется boundary с модальным контейнером.
  • Трансформированные родительские блоки: использовать appendTo: document.body и рассчитывать смещение через Popper.js.

Практические рекомендации

  • Всегда проверять родительский контейнер на overflow перед выбором appendTo.
  • Использовать boundary и preventOverflow, если тултип может пересекать края видимой области.
  • Сохранять Tippy.js внутри body, если контейнер скроллится или имеет трансформации.
  • Комбинировать offset и flip для корректного позиционирования при динамических изменениях DOM.

Такой подход гарантирует, что тултипы будут видимы, корректно позиционированы и не будут обрезаны независимо от структуры контейнеров на странице.