Обработка кликов вне элемента

В Floating UI обработка кликов вне элемента является важной частью взаимодействия с всплывающими элементами, такими как тултипы, дропдауны, модальные окна и меню. Библиотека предоставляет гибкие инструменты для отслеживания кликов вне целевого элемента и корректного закрытия или скрытия всплывающих компонентов.


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

Основной инструмент для обработки кликов вне элемента — функция useClickOutside. Она позволяет определить, когда пользователь кликает за пределами заданного DOM-узла, и выполнить соответствующее действие, например, закрыть меню.

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

import { useRef } from 'react';
import { useClickOutside } from '@floating-ui/react';

function Dropdown({ onClose }) {
  const ref = useRef(null);

  useClickOutside(ref, onClose);

  return (
    <div ref={ref} className="dropdown">
      <p>Содержимое меню</p>
    </div>
  );
}

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

  • ref указывает на элемент, за пределами которого будут отслеживаться клики.
  • Второй аргумент — функция, вызываемая при клике вне элемента.
  • useClickOutside корректно работает с вложенными элементами и несколькими всплывающими компонентами.

Настройка исключений

Иногда требуется исключить из обработки клики по определённым элементам, например, по кнопке, открывающей меню. В Floating UI это реализуется через параметр ignore:

useClickOutside(ref, onClose, {
  ignore: [buttonRef.current]
});

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

  • ignore принимает массив DOM-элементов.
  • Клик по элементам в массиве не вызовет функцию закрытия.
  • Позволяет строить сложные взаимодействия, где несколько элементов управляют видимостью одного всплывающего окна.

Интеграция с floating и open состоянием

Для компонентов с Floating UI важно синхронизировать обработку кликов вне элемента с состоянием открытия:

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

function Tooltip() {
  const { refs, open, setOpen } = useFloating({ placement: 'bottom' });

  useClickOutside(refs.floating, () => setOpen(false));

  return (
    <>
      <button ref={refs.reference} onCl ick={() => setOpen(!open)}>
        Нажми меня
      </button>
      {open && <div ref={refs.floating} className="tooltip">Подсказка</div>}
    </>
  );
}

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

  • Состояние open управляет видимостью всплывающего элемента.
  • refs.floating используется для отслеживания кликов вне.
  • При клике вне вызывается setOpen(false), что корректно закрывает всплывающий элемент.

Поддержка порталов и слоёв

Если всплывающее окно рендерится через портал (ReactDOM.createPortal) или находится на другом уровне DOM, useClickOutside корректно определяет клики вне:

  • ref всегда указывает на DOM-узел всплывающего элемента.
  • Портал не влияет на срабатывание клика вне.
  • Позволяет создавать модальные окна и глобальные тултипы без дополнительной логики.

Особенности работы с событиями

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

  • Слушатель кликов добавляется на document.
  • Проверяется, находится ли целевой элемент внутри отслеживаемого элемента (ref.current.contains(event.target)).
  • Если клик вне — вызывается callback.

Можно также контролировать тип события (mousedown, mouseup, click) через опции:

useClickOutside(ref, onClose, { event: 'mousedown' });

Преимущества:

  • Предотвращение нежелательных срабатываний при быстром клике.
  • Гибкость в выборе подходящего события для интерфейса.

Обработка вложенных всплывающих элементов

Когда существует несколько всплывающих компонентов, например, выпадающее меню с подменю:

  • Каждый элемент использует свой useClickOutside.
  • Важно правильно настраивать ignore, чтобы клики по родительскому элементу не закрывали дочернее меню.
  • Floating UI гарантирует корректную работу даже при динамически создаваемых элементах.
useClickOutside(submenuRef, () => setSubmenuOpen(false), { ignore: [parentMenuRef.current] });

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

  • Всегда передавать актуальные ref, чтобы обработка кликов вне оставалась точной.
  • Использовать ignore для элементов, которые управляют открытием/закрытием всплывающих окон.
  • Синхронизировать useClickOutside с состоянием open или аналогичным, чтобы избежать ошибок видимости.
  • Проверять взаимодействие с порталами, если элемент рендерится вне DOM-дерева родителя.

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