draggable="true"仅是拖拽开关,必须配合事件绑定、setdata()、dragover.preventdefault()等完整链路才能生效,缺一不可。

draggable="true"只是开关,不配事件、不传数据、不阻止默认行为,拖拽一定失败。
draggable="true"必须写在被拖元素上,且元素得有尺寸和可交互样式
空 <div></div> 即使加了 draggable="true",浏览器也认为“不可点击”,dragstart 根本不会触发。常见解决方式:
- 给元素设
min-height: 24px或padding: 4px,确保有渲染尺寸 - 避免
user-select: none或pointer-events: none——它们会直接拦掉鼠标按下阶段 -
<input>、<textarea></textarea>硬加draggable="true"无效,这是浏览器强制限制 - 属性值必须是显式的
draggable="true",不要用draggable="auto"或空字符串
dragstart 必须绑定 + setData + effectAllowed
dragstart 不是自动触发的,必须手动 addEventListener('dragstart', ...)。漏掉任意一项都会导致后续链路中断:
- 必须调
event.dataTransfer.setData('text/plain', 'some-id')—— 哪怕只传空字符串也要写,否则getData()在drop里返回空 - MIME 类型严格区分大小写:
'text/plain'可用,'Text/Plain'或'mydata'在 Firefox 中静默失败 - 建议同步设
event.dataTransfer.effectAllowed = 'move',避免 Safari 对光标样式匹配过于敏感 - 别在
dragstart里尝试存 DOM 节点(如event.dataTransfer.setData('text/html', el.outerHTML)),跨浏览器兼容性差;优先传 ID 或 JSON 字符串
目标区必须监听 dragover 并 preventDefault()
没这一步,drop 永远不会触发——浏览器在 dragover 阶段就决定是否允许投放,不是可选项。
- 目标容器(比如
<div id="drop-zone">)必须绑定 <code>dragover事件,并在回调中执行event.preventDefault() -
return false或event.stopPropagation()都不能替代preventDefault() - 目标区若为空,记得加
min-height: 60px,否则视觉不可见、悬停无法命中 - 如果目标区有内边距或子元素,
event.target可能是子节点,要用event.currentTarget或向上找容器 - 检测
'ontouchstart' in window,一旦为真,立刻禁用原生拖拽逻辑 - 降级方案:用
touchstart/touchmove+transform手动模拟位移,或引入interact.js这类已做兼容封装的库 - 别指望
draggable="true"在手机上“凑合能用”——它连事件都不派发,控制台也不会报错
移动端原生 drag/drop 完全不可用,必须降级
iOS Safari 和 Android Chrome 均不触发 dragstart/drop,这不是 bug,是平台级缺失。照搬桌面代码必失效。
最容易被忽略的是:拖拽不是“设个属性就完事”,而是一条强依赖顺序和精确配置的链路——draggable="true" → dragstart 绑定 → setData() 执行 → dragover.preventDefault() → drop 处理。任一环出错,整条链就断了。











