跨窗口拖拽复杂json数据必须用application/json格式配合json.stringify()序列化,text/plain因不解析结构、编码不可控、易截断乱码而必然失败。

跨窗口拖拽复杂 JSON 数据,draggable="true" 本身不参与数据封装,真正起作用的是 dataTransfer 对象的 MIME 类型选择和序列化时机——用 text/plain 必然失败,必须用 application/json 配合 JSON.stringify(),且需双格式兜底。
为什么 text/plain 在跨窗口传 JSON 时必然失败
浏览器对 text/plain 的处理是纯字符串截断+编码不可控,不解析结构、不保留换行、不保证长度。含中文、嵌套对象或超过 4KB 的 JSON,在 Firefox 中会被静默截断,Safari 可能返回乱码,JSON.parse() 直接抛 SyntaxError。
-
text/plain不做任何转义还原,双引号、反斜杠、Unicode 字符全靠运气保留 - 源页 UTF-8、目标页 ISO-8859-1 时,中文直接变
- Chrome 允许长文本,但 Safari 16.6 之前根本不识别
application/json,只 fallback 到空字符串
setData("application/json", ...) 的正确写法与兼容性陷阱
不能只设一种格式,也不能依赖 event.dataTransfer.types 判断可用类型——Safari 返回空数组,Firefox 即使设了 application/json 也只报 ["text/plain"]。
- 必须同时设置双格式:
e.dataTransfer.setData("application/json", JSON.stringify(payload))和e.dataTransfer.setData("text/plain", JSON.stringify(payload)) - 读取端优先尝试
getData("application/json"),失败则 fallback 到getData("text/plain")再JSON.parse() - payload 必须在
dragstart事件内立即序列化,不能异步读取e.target.dataset—— 源窗口失焦后 DOM 属性可能已失效
跨窗口场景下如何安全绑定拖拽上下文
dataTransfer.getData() 在跨 origin 或 iframe 场景下大概率为空,Firefox 尤其严格。不能靠它传 ID 或索引,得用 WeakMap 绑定上下文。
- 声明全局
const dragState = new WeakMap() -
dragstart中:dragState.set(e.dataTransfer, { cardId: el.dataset.id, type: "task" }) -
drop中:const ctx = dragState.get(event.dataTransfer),拿到即用,无需解析字符串 -
dragend后必须调用dragState.delete(e.dataTransfer),否则内存泄漏
真正难的不是设 draggable="true",而是跨窗口时数据没丢、没截断、没乱码、没拿错上下文——这些都发生在 dragstart 的那一毫秒里,晚一步,就全崩。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











