draggable属性仅是拖拽开关,跨窗口数据封装依赖dataTransfer对象;必须用application/json格式+JSON.stringify序列化,text/plain因不解析结构、编码不一致、截断风险等必然失败。

draggable 属性本身不参与跨窗口数据封装,它只是启动拖拽的“开关”;真正负责报文封装的是 dataTransfer 对象,且跨窗口场景下必须用 application/json 格式 + 字符串化,否则 JSON 会丢失结构、中文乱码、嵌套对象截断。
为什么 text/plain 在跨窗口拖拽 JSON 时必然失败
浏览器对 text/plain 的处理极其简单:只保留字符串首尾空白截断后的纯文本,不解析结构。一旦 JSON 含换行、引号、中文或嵌套对象(如 {"id":1,"meta":{"name":"张三"}}),dataTransfer.getData("text/plain") 返回的往往是截断片段或乱码——尤其在 Safari 和旧版 Edge 中更明显。
-
text/plain不保证编码一致性,跨窗口时源页面 UTF-8、目标页面 ISO-8859-1 就直接崩 - JSON 中的双引号会被浏览器转义或丢弃,
JSON.parse()直接抛SyntaxError - Chrome 允许
text/plain传长字符串,但 Firefox 会静默截断到约 4KB,毫无提示
setData("application/json", ...) 的正确写法与兼容性陷阱
必须显式调用 JSON.stringify() 并确保目标端用 JSON.parse() 解包;但注意:不是所有浏览器都原生支持 application/json MIME 类型识别,尤其 Safari 16.6 之前会忽略该类型并 fallback 到空字符串。
- 安全写法:同时设置
application/json和text/plain双格式,后者作为降级兜底 - 示例:
e.dataTransfer.setData("application/json", JSON.stringify(payload)); e.dataTransfer.setData("text/plain", JSON.stringify(payload)); - 读取时优先尝试
application/json,失败则 fallback 到text/plain再JSON.parse() - 避免使用
event.dataTransfer.types判断可用类型——Safari 返回空数组,Firefox 返回["text/plain"]即使你设了 JSON
跨窗口拖拽时 dragstart 事件里必须立即读取数据源
跨窗口场景下,源窗口可能被快速切换、最小化甚至失焦,e.target 在 dragstart 触发后几毫秒内就可能无法稳定访问 DOM 属性(尤其滚动中或 Vue/React 动态渲染的节点)。
- 禁止在
dragstart回调里异步读取e.target.dataset或调用getAttribute()—— 时机稍晚就取到空值或旧值 - 推荐做法:拖拽前就将完整 payload 序列化并缓存在元素上,例如
el._dragPayload = {type: "card", id: 123, props: {...}} - 或用
data-*属性预存字符串:,dragstart中直接e.target.dataset.payload- 切勿依赖
mouseenter/hover状态判断——跨窗口拖拽根本不会触发这些事件跨窗口拖拽的真正难点不在
draggable属性,而在于数据生命周期管理:源窗口要确保 payload 在dragstart瞬间可确定、不可变;目标窗口要能容忍 MIME 类型缺失、字符串截断、编码错位——这些都不是规范问题,而是现实浏览器的行为差异。 - 切勿依赖
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











