dragstart中必须调用setData()否则drop不触发;drop目标需preventDefault()阻止默认行为;移动端不支持原生Drag and Drop;dataTransfer数据只能在drag事件链中读写。

dragstart事件里必须设置dataTransfer
不调用 event.dataTransfer.setData(),后续的 drop 事件根本不会触发——浏览器会直接忽略这次拖放。哪怕只是传个空字符串或固定标识符,也得设:
element.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', 'item-id-123');
});
常见错误是只写了 e.dataTransfer.effectAllowed = 'move' 就以为够了,其实这行只影响光标样式和 drop 区域的接受策略,不设数据就等于没“拿起”东西。
注意:setData() 第一个参数是 MIME 类型,'text/plain' 最通用;如果拖的是文件或自定义结构,可用 'application/json' 或自定义类型如 'myapp/item',但接收端必须用对应类型 getData() 才能取到。
drop目标必须阻止默认行为
drop 和 dragover 事件默认会被浏览器拦截,不阻止就会立刻中止拖放流程,drop 根本不会执行:
立即学习“Java免费学习笔记(深入)”;
dropZone.addEventListener('dragover', (e) => {
e.preventDefault(); // 必须!
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault(); // 必须!
const id = e.dataTransfer.getData('text/plain');
console.log(id); // → 'item-id-123'
});
漏掉 dragover 的 preventDefault() 是最常踩的坑——看着拖进去有高亮效果,一松手却啥也没发生,八成就是这儿卡住了。
另外,drop 事件里的 e.target 是实际释放点(可能是子元素),如果逻辑需要操作整个容器,建议用 e.currentTarget 或提前用 closest() 定位。
移动端不支持原生 Drag and Drop API
所有 iOS Safari 和 Android Chrome 都不支持标准的 dragstart/drop 事件。不是兼容性差,是压根没实现。想在手机上做拖放,必须换方案:
- 用
touchstart/touchmove/touchend自己模拟位移和状态 - 引入轻量库如
interact.js或sortablejs(它们内部已处理平台差异) - 对纯 Web App,可降级为点击排序 + 确认弹窗,避免强行套用桌面逻辑
别试图用 try/catch 检测 API 存在就认为安全——iOS 上 'draggable' in document.createElement('div') 返回 true,但事件完全不触发。
dataTransfer只能在drag事件链中读写
dataTransfer 对象的数据是临时绑定在本次拖放过程中的,离开 dragstart→drag→drop 这条链,就无法再访问。比如在 drop 之后异步请求中直接引用 e.dataTransfer,大概率是 undefined 或空值。
正确做法是在 drop 事件里立刻提取并保存:
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain'); // ✅ 立即读
setTimeout(() => {
console.log(data); // ✅ 用闭包变量
}, 0);
});
另一个陷阱:多次快速拖放时,如果前一次还没处理完,新 dragstart 可能覆盖 dataTransfer,所以别依赖全局缓存,每次都在事件里取。










