draggable="true"仅开启拖拽流程起点,不自带逻辑;必须手动绑定dragstart setdata()、dragover preventdefault()等事件并规范mime类型,否则流程中断。

draggable="true"只是开关,不是拖拽逻辑
设了 draggable="true" 却没反应?不是浏览器问题,是误以为它自带行为。这个属性只做两件事:让鼠标悬停时变成抓手光标、允许浏览器进入拖拽流程起点。它不绑定事件、不传数据、不控制投放——这些全得手动补。
常见错误现象:dragstart 根本不触发;拖动时没半透明拖影;松开后目标区毫无响应。本质是流程卡在第一步:浏览器检查到 draggable="true" 后,发现没 dataTransfer.setData(),就静默终止整个拖拽链。
- 必须显式写
draggable="true",draggable(空值)虽等价但易被误删,draggable="auto"在 Chrome/Firefox 中对<div> 无效 <li> <code><input>、<textarea></textarea>硬加该属性也无效,浏览器强制禁用 - 空
<div> 没宽高或 padding,浏览器判定“不可交互”,<code>dragstart压根不会派发 - 样式拦截:如果父级有
pointer-events: none或自身设了user-select: none,鼠标按下阶段就被拦住 - 推荐写法:
e.dataTransfer.setData('text/plain', row.id)—— 用 ID 比索引可靠,重排后索引会变 - 结构化数据先
JSON.stringify()再塞进去,避免自定义类型兼容问题 - 顺手设
e.dataTransfer.effectAllowed = 'move',部分浏览器(如 Safari)依赖这个控制光标样式 - 别传 DOM 节点或对象:
setData()只接受字符串 - 除了
dragover,drop事件里也要再写一次e.preventDefault(),双重保险 - Safari 对
effectAllowed和dropEffect匹配更敏感:建议dragstart设effectAllowed = 'move',dragover设dropEffect = 'move' - 别给
<table> 或 <code><tbody> 加 <code>draggable="true",否则整块被拖,失去行级控制移动端原生 drag/drop 完全不可用
iOS Safari 和 Android Chrome 均不触发
dragstart、drop等事件,这不是兼容性 bug,是平台级缺失。检测'ontouchstart' in window后必须降级处理,照搬桌面代码只会白忙活。常见错误是:在手机上测试,控制台没报错,事件就是不触发,还以为是逻辑写错了。其实根本没进任何回调。
- 降级方案一:用
touchstart/touchmove/touchend+CSS transform手动模拟拖拽位移 - 降级方案二:引入轻量库如
interact.js或sortablejs,它们内部已做平台适配 - 坚持用原生 API?至少长按激活模式:
touchstart后延时 500ms 触发拖拽态,避免误触 - 别在
drop里依赖event.target—— 它可能是子元素,要用event.composedPath()[0]或向上遍历找绑定节点
draggable="true"开始,到dragstart里setData(),再到目标端dragover的preventDefault(),最后是drop里的位置计算与 DOM 更新——漏掉任意一环,整个流程就断在那儿。 - 降级方案一:用
dragstart 必须调用 setData(),且 type 要规范
dragstart 是拖拽的真正起点,但它只在你往 dataTransfer 里塞了东西之后才算“正式启动”。传空字符串都比不传强——Safari 甚至要求不能是纯空串,至少填 '0' 或 ''。
type 参数不是随便起的别名,是 MIME 类型,大小写敏感,跨浏览器兼容首选 text/plain 或 text/html。Text/Plain、mydata、json 这类写法在 Firefox 中会静默失败,Chrome 可能返回空字符串。
dragover 不 preventDefault,drop 就永远不会来
dragover 是拖拽过程中的“许可闸门”。不调 e.preventDefault(),浏览器就默认你要下载文件或跳转链接,直接拦截 drop,控制台也不报错,只看到鼠标变成圆圈斜杠。
这个 preventDefault() 必须写在 dragover 回调里,而且要针对实际接收拖拽的目标元素监听——比如表格行排序,得绑在 <tr> 或 <code><tbody> 上,不能只绑在 <code><table> 上,否则拖到空白处就失效。
<ul><li>目标容器若为空 <code><div>,确认没被 <code>display: none 或 visibility: hidden 隐藏











