Teleport и порталы

Teleport и концепция порталов в Floating UI предназначены для управления рендерингом всплывающих элементов вне их исходного DOM-контекста. Это особенно важно при создании выпадающих меню, тултипов, модальных окон и других интерфейсных компонентов, где нужно избежать конфликтов с overflow, z-index или стилями родительских элементов.


Основные принципы работы

  1. Отделение DOM-структуры от визуального представления Floating UI позволяет рендерить всплывающий элемент в другом месте DOM, сохраняя при этом связь с исходным элементом, относительно которого рассчитываются позиции.

  2. Автоматическое позиционирование Даже при переносе элемента в корень документа (document.body) библиотека корректно рассчитывает смещение, чтобы всплывающий элемент оставался привязанным к целевому объекту.

  3. Совместимость с React и Vanilla JS В React можно использовать портал через createPortal, тогда как в Vanilla JS применяются нативные методы работы с DOM, например, appendChild к document.body.


Создание портала с Teleport

Пример использования в React:

import { useFloating } from '@floating-ui/react';
import { createPortal } from 'react-dom';

function Tooltip({ referenceElement, children }) {
  const { x, y, strategy } = useFloating({
    placement: 'top'
  });

  return createPortal(
    <div
      style={{
        position: strategy,
        top: y ?? 0,
        left: x ?? 0
      }}
    >
      {children}
    </div>,
    document.body
  );
}

Пояснения:

  • useFloating отвечает за вычисление координат элемента.
  • strategy определяет метод позиционирования (absolute или fixed).
  • createPortal перемещает DOM-узел в document.body, сохраняя при этом привязку к исходному элементу.

Использование Teleport в Vanilla JS

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

const button = document.querySelector('#button');
const tooltip = document.createElement('div');
tooltip.textContent = 'Всплывающая подсказка';
document.body.appendChild(tooltip);

computePosition(button, tooltip, {
  middleware: [offset(8), flip(), shift()]
}).then(({ x, y }) => {
  Object.assign(tooltip.style, {
    left: `${x}px`,
    top: `${y}px`,
    position: 'absolute'
  });
});
  • computePosition вычисляет координаты с учётом смещения (offset), возможного переворачивания (flip) и корректировки позиции (shift).
  • Элемент добавляется в document.body, что исключает ограничения родительских контейнеров с overflow: hidden.

Middleware и порталы

Использование Teleport напрямую связано с функциональностью middleware:

  1. Offset – задаёт отступ между целевым элементом и всплывающим.
  2. Flip – переворачивает элемент, если он выходит за пределы видимой области.
  3. Shift – смещает элемент, чтобы полностью вписать в viewport.

Эти middleware корректно работают даже при переносе элемента в порталы, так как Floating UI рассчитывает позицию относительно исходного элемента, а не нового родителя в DOM.


Управление событиями и интерактивностью

При использовании порталов важно учитывать:

  • События мыши и клика – всплывающие элементы, вынесенные в другой контейнер, должны правильно делегировать события к исходному компоненту.
  • Фокусировка и клавиатура – особенно для модальных окон и автокомплитов. Floating UI совместим с focusTrap и другими библиотеками управления фокусом.
  • Анимации – перемещение элемента в портал не мешает применению CSS-анимаций и трансформаций.

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

  • Разносить элемент в портал стоит, когда родительский контейнер имеет overflow: hidden или z-index, ограничивающий видимость.
  • Для больших приложений стоит централизованно управлять порталами через отдельный контейнер <div id="portals-root"></div>.
  • Middleware должны применяться в том порядке, в котором они логически связаны: сначала смещение (offset), затем переворот (flip) и коррекция (shift).

Взаимодействие с другими библиотеками

Teleport идеально сочетается с:

  • React Transition Group – для анимации появления/исчезновения.
  • Framer Motion – для плавного перемещения в порталах.
  • Headless UI и Radix UI – их всплывающие компоненты могут использовать Floating UI для точного позиционирования вне родительского DOM.

Этот подход позволяет создавать сложные интерфейсы с точным позиционированием, независимые от ограничений DOM-контейнера, сохраняя при этом интерактивность, доступность и плавность анимаций.