dragover 事件必须阻止默认行为,否则 drop 永远不会触发;需在 dragover 中同步调用 preventDefault(),并在 drop 中用 closest(".drop-zone") 获取目标容器,结构化数据应使用 application/json 传输,状态更新须先改数据再渲染。

dragover 事件必须阻止默认行为,否则 drop 永远不会触发
跨组件拖拽失败最常见原因不是数据传错了,而是目标容器没正确响应 dragover。浏览器默认禁止在任意元素上 drop,event.preventDefault() 是解锁 drop 的唯一钥匙。
- 别只在
drop里调preventDefault()—— 它已经晚了,drop 根本不会进到你的回调里 -
dragover必须同步、无条件调用preventDefault(),哪怕只是空函数也要写上 - 如果目标组件是自定义封装的(比如 Vue 的
<kanban-column>或 React 的KanbanColumn),确保监听器挂载在真实 DOM 容器上,而不是虚拟节点代理层 - 多个嵌套容器时,注意事件冒泡:父容器的
dragover若没阻止,默认会向上冒泡并被外层拦截,导致内层收不到
dataTransfer 用 application/json 传结构,别用 text/plain
text/plain 只能塞字符串,中文乱码、对象序列化失败、嵌套字段截断——跨组件场景下极易出错。结构化数据必须走 application/json。
- dragstart 中:
event.dataTransfer.setData("application/json", JSON.stringify({ type: "card", id: "task-123", props: { title: "修复登录页" } })) - drop 中:
const payload = JSON.parse(event.dataTransfer.getData("application/json")) - 避免在 dragstart 里存 DOM 引用或函数——跨 shadow DOM 或跨框架时直接失效
- 如果组件间存在权限或上下文隔离(如微前端子应用),建议加
sourceApp字段做来源校验,防止误投
drop 事件的 target 往往不是你想要的容器
鼠标松手那一刻,event.target 极大概率是目标容器里的某个子元素(比如标题文字、图标、空白 padding 区),而不是你期望的列容器本身。
- 统一给目标容器加 class,比如
class="drop-zone",然后在 drop 处理函数里立刻用event.target.closest(".drop-zone")向上查找 - 不要依赖
event.currentTarget—— 如果用了事件委托,它可能是父级容器,但不一定是语义上的“接收方” - 若目标组件使用 Shadow DOM,
closest()无法穿透,需提前把 drop-zone 设为 open 模式,或改用getRootNode().host获取宿主元素 - 移动端 touch 事件模拟拖拽时,
event.target更不可靠,务必加容错判断:找不到则 fallback 到绑定监听器的原始容器
跨组件状态更新必须先改数据,再触发渲染
直接操作 DOM 移动节点,在 React/Vue/Svelte 等框架里等于绕过响应式系统,轻则丢失 input 值、焦点、滚动位置,重则触发无限 re-render 或 key 冲突。
立即学习“前端免费学习笔记(深入)”;
- 拖拽开始前,从全局状态(如 Redux store、Pinia store、useState)读取当前任务列表和源列 ID
- drop 触发后,先计算新数组:移除原项 + splice 插入目标位置,再 dispatch 更新动作
- 视觉占位逻辑(比如插入线)也得靠数据驱动:根据目标索引生成
insertBeforeIndex,而非靠 getBoundingClientRect() 动态算 Y 坐标 - 如果组件间通信走的是自定义事件(如
CustomEvent),确保事件 detail 是 plain object,不要传 function 或 DOM 节点



















