Safari要求dragstart中必须显式设置effectAllowed且与dragover中的dropEffect严格一致,否则显示禁止光标并静默拦截drop;setDragImage在Firefox需图像已加载且宽高>0,Safari仅支持img或canvas;高亮应避免dragenter/leave,改用dragover+requestAnimationFrame节流;移动端需用touchstart+requestAnimationFrame模拟拖拽。

dragstart里effectAllowed不设,Safari直接变禁止光标
只要没在dragstart里显式写e.dataTransfer.effectAllowed = 'move',Safari就会把拖拽光标变成带斜杠的圆圈(?),且drop事件被静默丢弃——Chrome和Firefox可能还容忍,但Safari认死理。
-
effectAllowed不能依赖默认值,必须在dragstart回调第一行就赋值 - 类型必须是
'move'、'copy'或'link';'all'和'uninitialized'在Safari里无效 - 目标区
dragover里也得同步写e.dataTransfer.dropEffect = 'move',大小写、拼写、空格全要一致,差一个字符就失败
setDragImage在Firefox和Safari行为完全不同
setDragImage()不是“传个元素就能用”,各浏览器对参数合法性的判断差异极大:Firefox要求图像已加载且宽高>0,Safari只认<img>或<canvas>,Chrome最宽松但偏移计算又不统一。
- 别传
document.body或未渲染的div——Firefox静默忽略,Safari直接报错 - 推荐方案:创建一个
<canvas>,用ctx.fillText()画简略文字标识,再调setDragImage(canvas, 0, 0) -
x、y是相对于图像左上角的偏移,不是鼠标坐标;统一设为0, 0可避免跨浏览器错位
用dragenter/dragleave控制高亮,Safari和Firefox会疯狂闪烁
很多代码在dragenter加class、dragleave删class来高亮目标区,但在Safari/Firefox里,只要目标容器内有子元素,鼠标稍微一晃就反复进出,样式来回切换卡顿明显。
- 改用
dragover+requestAnimationFrame节流判断“是否真正悬停” -
pointer-events: none会彻底禁用dragover,别给目标容器加这个 - 高亮优先用
outline而非border,避免触发重排;空容器至少设min-height: 24px
移动端原生draggable基本等于不存在
iOS Safari和Android Chrome根本不触发dragstart/drop事件,这不是兼容性问题,是平台级缺失。你看到的“能拖”只是开发者工具模拟,真机上连事件监听器都不会进。
立即学习“前端免费学习笔记(深入)”;
- 检测
'ontouchstart' in window后必须降级,不能靠draggable="true"硬扛 - 手动模拟时,
touchmove必须节流+requestAnimationFrame,否则高频触发直接掉帧 - 如果项目允许引入轻量库,
interact.js或sortablejs内部已处理了这些边界情况
effectAllowed漏写、一张canvas没等加载完成、一次dragover.preventDefault()少调,整条链就断在不同浏览器里。最麻烦的是,它往往不报错,只是“没反应”。



















