Плагин sticky

Плагин sticky в библиотеке Tippy.js предназначен для динамического обновления позиции tooltip’а в случаях, когда его опорный элемент (reference) или сам tooltip изменяют своё положение на странице после инициализации. Это особенно важно при работе с анимациями, прокруткой, изменениями layout’а или при использовании нестатических контейнеров.

По умолчанию Tippy вычисляет позицию один раз при показе подсказки. Если после этого координаты элемента изменяются, tooltip остаётся на старом месте. Плагин sticky устраняет это ограничение, обеспечивая «прилипание» подсказки к целевому элементу.


Подключение плагина

Плагин необходимо явно импортировать и зарегистрировать:

import tippy, { sticky } from 'tippy.js';

tippy('.btn', {
  content: 'Подсказка',
  sticky: true,
  plugins: [sticky],
});

Ключевые моменты:

  • sticky добавляется в массив plugins
  • опция sticky активирует поведение

Принцип работы

Плагин использует механизм непрерывного обновления позиции через requestAnimationFrame. Это позволяет:

  • отслеживать изменения координат reference-элемента
  • учитывать CSS-анимации и трансформации (transform, translate, scale)
  • корректно реагировать на скролл внутри контейнеров

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


Значения опции sticky

Опция sticky принимает несколько значений:

true

sticky: true
  • включает полное отслеживание
  • позиция обновляется при любых изменениях

"reference"

sticky: 'reference'
  • отслеживается только перемещение reference-элемента
  • изменения самого tooltip’а игнорируются

Используется, когда:

  • tooltip имеет фиксированный размер
  • изменения происходят только у целевого элемента

"popper"

sticky: 'popper'
  • отслеживается только tooltip (popper)
  • полезно при динамическом изменении содержимого tooltip’а

Пример с анимацией

tippy('.animated', {
  content: 'Двигаюсь вместе с элементом',
  sticky: true,
  plugins: [sticky],
});
.animated {
  animation: move 2s infinite alternate;
}

@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(200px);
  }
}

Без sticky tooltip остаётся на исходной позиции. С плагином он следует за элементом.


Взаимодействие с Popper.js

Tippy.js использует Popper.js для позиционирования. Плагин sticky фактически принудительно вызывает обновление Popper через:

instance.popperInstance.update();

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


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

Постоянные обновления могут повлиять на производительность, особенно:

  • при большом количестве tooltip’ов
  • на слабых устройствах
  • при сложных DOM-структурах

Рекомендации:

  • использовать sticky только там, где это действительно необходимо
  • ограничивать количество активных tooltip’ов
  • выбирать более узкий режим (reference или popper)

Комбинация с другими опциями

followCursor

tippy('.cursor', {
  followCursor: true,
  sticky: true,
  plugins: [sticky],
});
  • followCursor управляет положением относительно курсора
  • sticky дополнительно отслеживает изменения DOM

appendTo

tippy('.item', {
  appendTo: document.body,
  sticky: true,
  plugins: [sticky],
});
  • полезно при сложных layout’ах
  • предотвращает проблемы с overflow и clipping

animation

Плагин хорошо работает с CSS-анимациями:

tippy('.box', {
  animation: 'scale',
  sticky: true,
  plugins: [sticky],
});

Работа с прокруткой контейнеров

Вложенные скролл-контейнеры часто вызывают проблемы с позиционированием. sticky решает их автоматически:

tippy('.inside-scroll', {
  sticky: true,
  plugins: [sticky],
});

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


Ограничения

  • не отключает необходимость правильной настройки z-index
  • не исправляет проблемы с overflow: hidden
  • не влияет на вычисление placement (только обновляет позицию)

Отладка

При некорректной работе:

  1. Проверить, подключён ли плагин в plugins
  2. Убедиться, что sticky установлен в true или нужное значение
  3. Проверить наличие CSS-трансформаций (transform) у родительских элементов
  4. Убедиться, что tooltip не ограничен контейнером с overflow: hidden

Практические сценарии использования

Двигающиеся элементы интерфейса

  • draggable-компоненты
  • анимированные карточки
  • элементы с hover-эффектами

Динамический контент

  • изменение размеров tooltip’а
  • загрузка данных через AJAX
  • реактивные интерфейсы (React, Vue)

Сложные layout’ы

  • вложенные scroll-контейнеры
  • flex/grid с изменяющимися размерами
  • sticky/absolute позиционирование

Внутренний механизм жизненного цикла

При активации tooltip:

  1. Создаётся экземпляр Popper
  2. Запускается цикл обновления (requestAnimationFrame)
  3. На каждом кадре вызывается update()
  4. При скрытии tooltip цикл останавливается

Это гарантирует, что обновления происходят только при необходимости.


Альтернатива без sticky

Вместо использования плагина можно вручную вызывать:

instance.popperInstance.update();

Однако:

  • требуется контроль событий
  • усложняется код
  • возрастает риск ошибок

Плагин sticky инкапсулирует эту логику и делает её декларативной.


Сравнение режимов

Режим Отслеживание reference Отслеживание tooltip
true да да
"reference" да нет
"popper" нет да

Особенности в разных браузерах

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

Рекомендации по использованию

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

Пример комплексного использования

import tippy, { sticky } from 'tippy.js';

tippy('.card', {
  content: 'Информация',
  placement: 'top',
  animation: 'shift-away',
  sticky: 'reference',
  plugins: [sticky],
});

Такой вариант:

  • отслеживает только движение элемента
  • снижает нагрузку
  • сохраняет корректное позиционирование при анимациях