关键事件共6个:dragstart(必须设dataTransfer)、drag(可忽略)、dragend(拖拽结束)、dragenter(必须preventDefault)、dragover(必须preventDefault)、drop(执行业务逻辑)。
JavaScript 实现拖放功能不难,但默认行为干扰多、浏览器兼容细节多,直接用原生 drag / drop 事件链最容易出错——比如拖着没反应、松手不触发 drop、或被浏览器默认下载/导航劫持。
拖放是多阶段事件流,缺一不可。关键事件共 6 个,按实际触发顺序排列:
dragstart:拖拽开始,**必须在这里设置 dataTransfer,否则后续 drop 拿不到数据**drag:持续触发(可忽略)dragend:拖拽结束(无论是否成功释放)dragen
ter:首次进入目标区域,**必须调用 event.preventDefault(),否则 dragover 和 drop 不会触发**dragover:持续触发(类似 drag),**也必须 preventDefault(),否则无法释放到目标上**drop:松手释放,此时才能读取 dataTransfer 并执行业务逻辑漏掉任意一个 preventDefault()(尤其 dragenter 和 dragover),drop 就永远不会来。
drop 事件拿不到 dataTransfer.files 或自定义数据根本原因是 dataTransfer 的写入只在 dragstart 阶段有效,且不同数据类型有不同写法:
立即学习“Java免费学习笔记(深入)”;
setData('text/plain', 'hello')
setData('text/html', 'hi')
选中的):不能直接塞进 setData,需用 setDragImage() + 自定义属性模拟,或改用 FileList 通过 dataTransfer.items 传递(仅限本地文件拖入页面)setData('text/plain', element.id),简单可靠注意:dataTransfer.getData() 只能读取同类型、且在 dragstart 中明确 setData 过的内容;未设置的类型返回空字符串。
默认只有 同时,在 别忘了目标容器也要监听 最常被忽略的是:即使所有事件都绑了,只要漏掉任意一次 、 等少数元素可拖拽。要让 、 等可拖,必须显式设置 draggable="true" 属性:
拖我
dragstart 中绑定事件并设置数据:document.getElementById('item-1').addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', 'item-1');
// 可选:隐藏拖拽时的半透明影子
e.dataTransfer.setDragImage(new Image(), 0, 0);
});
dragenter 和 dragover 并 preventDefault(),否则拖进去就跳默认行为(比如打开新标签页)。preventDefault()(尤其是 dragenter),整个拖放流程就卡死在“悬停但无法释放”状态——这不是 bug,是规范强制设计。