draggable 属性必须显式设为 true 才能跨浏览器稳定启用拖拽,auto 值因浏览器实现不一致而不可靠;需在 dragstart 中调用 dataTransfer.setData() 传数据,并在 dragover 中 preventDefault() 才能触发 drop。

draggable 属性必须显式设为 true,否则绝大多数自定义元素(如 div、span、section)默认不可拖拽。
为什么 draggable="auto" 不可靠
浏览器对 auto 的实现不一致:Chrome 和 Safari 默认允许 img、a 拖拽,但 div 仍被禁用;Firefox 对非原生可拖元素直接忽略 auto;Edge/IE 更早版本甚至完全不响应。实际项目中,只要你想拖某个元素,就别依赖 auto。
-
draggable="false"会彻底禁用拖拽,哪怕它本来支持(比如一张img) -
draggable="true"是唯一跨浏览器稳定的启用方式 - 注意:设置后只是“可拖”,不代表能放——后续还要处理
dragstart、dragover、drop事件
ondragstart 里必须调用 dataTransfer.setData()
只设 draggable="true" 只是让鼠标能抓起元素,但没数据,目标区域收不到任何信息。常见错误是漏掉这步,导致 drop 时 getData() 返回空字符串。
- 类型名建议用
"text/plain"或"text/html",避免用过时的"Text"(部分旧文档误写) - 值可以是 ID:
ev.dataTransfer.setData("text/plain", ev.target.id) - 也可以是结构化数据:
ev.dataTransfer.setData("application/json", JSON.stringify({type: "card", id: 123})) - 不要在
dragstart外提前调用setData(),它只在该事件中有效
目标容器必须监听 dragover 并立即 preventDefault()
这是最容易卡住的一步:没有 preventDefault(),浏览器会按默认行为处理(比如把拖进来的链接打开成新页面),drop 事件根本不会触发。
- 仅
ondrop里调preventDefault()不够,dragover必须先拦住 - 不能只在
dragenter里拦一次,dragover是持续触发的,每次都要拦 - 如果目标区域需要高亮反馈,可在
dragenter加 class,dragleave移除,但核心逻辑仍在dragover
移动端拖拽基本不可用,别硬套原生 API
截至 2026 年,iOS Safari 和 Android Chrome 均未完整支持 draggable + dataTransfer 在触摸场景下的行为。手指长按不会触发 dragstart,drop 也极不稳定。
- 真实项目中,移动端排序、卡片拖拽等需求,应改用
touchstart/touchmove+ CSStransform模拟 - 若坚持用原生 API,至少加降级提示:
if (!("draggable" in document.createElement("span"))) { /* 启用备用方案 */ } - 桌面端测试务必用真实鼠标,Chrome 开发者工具的“设备模拟”对拖拽事件支持极差

















