Sticky позиционирование

Sticky-позиционирование во Floating UI применяется для закрепления плавающего элемента (floating element) относительно якорного (reference element) в пределах заданной области. В отличие от классического CSS position: sticky, библиотека реализует более гибкий механизм, учитывающий столкновения с границами, прокрутку контейнеров и изменение размеров.

Основная задача — обеспечить поведение, при котором floating-элемент “прилипает” к границе, когда достигает её, но остаётся связанным с reference-элементом, пока это возможно.


Базовая идея работы

Sticky-логика строится поверх вычисления координат через computePosition. Floating UI отслеживает:

  • положение reference-элемента
  • границы контейнера (viewport или кастомный контейнер)
  • доступное пространство
  • смещения (offset)

При достижении границы активируется ограничение движения floating-элемента.


Использование middleware shift

Sticky-поведение чаще всего реализуется через middleware shift, который отвечает за сдвиг элемента внутри доступной области.

import { computePosition, shift } from '@floating-ui/dom';

computePosition(referenceEl, floatingEl, {
  middleware: [
    shift({
      padding: 8
    })
  ]
});

Что происходит:

  • элемент позиционируется относительно reference
  • если он выходит за границы — смещается обратно
  • создаётся эффект “прилипания” к краю

Ограничение движения: limiter

Для более точного sticky-поведения используется limiter. Он задаёт правила, насколько далеко элемент может смещаться.

import { shift, limitShift } from '@floating-ui/dom';

shift({
  limiter: limitShift()
});

По умолчанию limitShift не позволяет элементу полностью оторваться от reference.


Частичное закрепление

В некоторых сценариях требуется ограничить движение только по одной оси:

shift({
  mainAxis: true,
  crossAxis: false
});
  • mainAxis — основная ось (например, вертикаль при placement: 'top')
  • crossAxis — поперечная ось

Это позволяет реализовать поведение, близкое к CSS sticky по вертикали или горизонтали.


Sticky внутри контейнера

Floating UI умеет учитывать не только viewport, но и вложенные контейнеры с прокруткой.

computePosition(referenceEl, floatingEl, {
  middleware: [
    shift({
      boundary: containerEl
    })
  ]
});

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

  • границы вычисляются относительно контейнера
  • учитываются overflow и scroll
  • корректно работает при вложенных скроллах

Padding и безопасные зоны

Чтобы избежать прилипания вплотную к краю, используется padding:

shift({
  padding: {
    top: 10,
    bottom: 10,
    left: 5,
    right: 5
  }
});

Это создаёт “зону безопасности”, в пределах которой элемент может свободно двигаться.


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

Sticky-поведение редко используется изолированно. Обычно оно комбинируется:

offset

Добавляет отступ от reference:

offset(8)

flip

Меняет сторону при нехватке места:

flip()

shift

Фиксирует внутри границ:

shift()

Пример полной конфигурации:

computePosition(referenceEl, floatingEl, {
  placement: 'bottom',
  middleware: [
    offset(8),
    flip(),
    shift({
      padding: 8,
      limiter: limitShift()
    })
  ]
});

Sticky при динамическом контенте

Floating UI пересчитывает позицию при изменениях:

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

Для этого используется autoUpdate:

import { autoUpdate } from '@floating-ui/dom';

autoUpdate(referenceEl, floatingEl, () => {
  computePosition(referenceEl, floatingEl, {
    middleware: [shift()]
  }).then(({ x, y }) => {
    Object.assign(floatingEl.style, {
      left: `${x}px`,
      top: `${y}px`
    });
  });
});

Это обеспечивает “живое” sticky-поведение.


Ограничения и нюансы

1. Отсутствие реального CSS sticky Floating UI не изменяет поток документа — позиционирование всегда абсолютное или фиксированное.

2. Производительность Частые пересчёты могут быть затратными при большом количестве элементов.

3. Вложенные контейнеры Важно корректно задавать boundary, иначе поведение может казаться “ломаным”.


Продвинутые сценарии

Ограничение только при выходе за viewport

shift({
  boundary: 'viewport'
});

Полное отключение отрыва от reference

shift({
  limiter: limitShift({
    offset: 0
  })
});

Создание эффекта “плавающего sidebar”

Комбинация:

  • фиксированного reference
  • shift с ограничениями
  • вертикального mainAxis

Внутренний механизм расчёта

Алгоритм sticky-позиционирования:

  1. Вычисляется базовая позиция (computePosition)
  2. Определяются границы (viewport или container)
  3. Проверяется переполнение (overflow)
  4. Применяется сдвиг (shift)
  5. Ограничивается движение (limiter)
  6. Возвращаются финальные координаты

Сравнение с CSS sticky

Характеристика CSS sticky Floating UI sticky
Привязка к DOM Да Нет
Контроль логики Ограниченный Полный
Работа с тултипами Нет Да
Гибкость Низкая Высокая
Поддержка контейнеров Частичная Полная

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

  • тултипы, не выходящие за экран
  • выпадающие меню
  • поповеры внутри scroll-контейнеров
  • контекстные панели
  • закреплённые подсказки

Sticky-позиционирование во Floating UI — это не отдельный режим, а результат комбинации middleware, где ключевую роль играет shift и его ограничители.