原生拖放API需按dragstart→drag→dragend→dragenter→dragover→dragleave→drop顺序处理事件,且dragstart中必须调用setData()、dragenter/dragover中必须preventDefault()才能启用拖放功能。

原生拖放 API(Drag and Drop API)是浏览器内置的一套事件机制,无需第三方库就能实现元素拖拽与放置,但需注意其行为逻辑和兼容细节,不是简单绑定几个事件就能跑通。
核心事件链与触发时机
拖放过程由 7 个关键事件组成,按顺序触发:
-
dragstart:用户开始拖拽(鼠标按下并移动一定距离),在此设置
dataTransfer数据(如setData('text/plain', 'id-123')) - drag:拖拽中持续触发(一般不用)
- dragend:拖拽结束(无论是否释放到有效区域)
-
dragenter:被拖元素首次进入目标区域时触发(需
e.preventDefault()才能继续) -
dragover:在目标区域内持续触发(必须
e.preventDefault(),否则drop不会触发) - dragleave:离开目标区域时触发
-
drop:在目标区域松开鼠标时触发(也需
e.preventDefault(),否则默认行为是打开文件或导航)
让元素可拖拽的两个前提
仅给元素加 draggable="true" 属性还不够,必须满足:
- 元素本身支持拖拽(
<img>、<a>默认可拖;<div>等需显式设draggable="true") - 在
dragstart中至少调用一次e.dataTransfer.setData(),否则部分浏览器(如 Safari)会禁用拖拽
目标区域必须“接受拖入”
即使监听了 drop,若没处理 dragenter 和 dragover,拖入时会出现禁止图标(?)。正确写法:
立即学习“Java免费学习笔记(深入)”;
const dropZone = document.getElementById('drop-area');
dropZone.addEventListener('dragenter', e => e.preventDefault());
dropZone.addEventListener('dragover', e => e.preventDefault()); // 必须!
dropZone.addEventListener('drop', e => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
// 执行插入、移动等逻辑
});
常见坑与实用建议
原生 API 行为较“保守”,实际开发中容易踩坑:
-
不支持拖拽排序(如列表项互换位置):API 只提供“从 A 拖到 B”,不自动交换 DOM 节点,需手动操作(如用
insertBefore或appendChild) -
无法获取拖拽中的鼠标相对坐标:
dataTransfer不暴露坐标,需结合mousemove+setDragImage()自定义拖影 -
移动端默认不支持:iOS Safari 和 Android Chrome 均未启用该 API,需降级为
touchstart/move/end方案 - 可调用
e.dataTransfer.setDragImage(elem, x, y)替换默认拖影(如显示半透明副本),x/y是相对于元素左上角的偏移










