Как определить, что мышь находится над контейнером модального окна при перетаскивании?
Что проверяют
Различаете ли вы нативный HTML5 drag-and-drop и собственное перетаскивание на Pointer Events: события и подводные камни у них разные. Следом обычно спрашивают, почему подсветка зоны мигает, когда курсор проходит над дочерними элементами модалки.
Как отвечать
Сначала уточните, какое перетаскивание имеется в виду.
Нативный DnD (draggable="true", перетаскивание файлов). Во время перетаскивания mousemove не приходит, работают события цели:
dragenterиdragover: курсор над контейнером. Вdragoverнужно вызватьpreventDefault(), иначеdropне сработает;dragleave: курсор ушёл, но событие приходит и при переходе на дочерний элемент. Решения: счётчик, который растёт вdragenterи уменьшается вdragleave(курсор над контейнером, пока он больше нуля), илиpointer-events: noneу детей на время перетаскивания;drop: финальная проверка, данные берутся изevent.dataTransfer.
Своё перетаскивание на pointerdown/pointermove/pointerup. Здесь курсор почти всегда над самим перетаскиваемым элементом, поэтому mouseenter на модалке не сработает, а после setPointerCapture все события и так уходят в захвативший элемент. Нужна проверка попадания:
- по координатам:
getBoundingClientRect()модалки и сравнение сclientX/clientY. Прямоугольник лучше посчитать при старте и обновлять при скролле и ресайзе; - через DOM:
document.elementFromPoint(x, y)иmodal.contains(el). Перетаскиваемому призраку ставятpointer-events: none, иначе вернётся он сам.
Проверку в pointermove стоит проводить не чаще кадра через requestAnimationFrame. В React готовые библиотеки вроде dnd-kit решают это через алгоритмы коллизий, например pointerWithin.
Где спотыкаются
Вешают mousemove на документ при нативном DnD и удивляются, что координаты не приходят.
Используют contains() как проверку координат. Он отвечает, вложен ли один узел в другой, а не где находится курсор.
Не ставят touch-action: none перетаскиваемому элементу. На телефоне браузер начинает прокручивать страницу, присылает pointercancel, и перетаскивание обрывается.