draggable="true"仅启用拖拽视觉效果,不自动处理模块序列化、连接校验或执行;所有逻辑需手动实现,移动端原生失效,必须降级为触摸模拟并补充可访问性支持。

直接说结论:draggable 属性本身不能拼接代码块,更不能运行工作流;它只负责“让元素能被鼠标抓起”,后续所有逻辑——模块数据序列化、连接关系建立、执行环境调用——都得靠你手动补全。没这层认知,项目很快会卡在“拖得动但连不上、连得上但跑不了”的阶段。
draggable="true" 是开关,不是发动机
给一个代表 Dense 层的 <div> 加上 <code>draggable="true",只是让它光标变手形、松开后有半透明拖影。它不会自动记录类型、参数或输入输出端口信息。真正决定“这是个什么模块”的,是你在 dragstart 里塞进 dataTransfer 的内容。
- 必须用
event.dataTransfer.setData('text/plain', JSON.stringify({ type: 'Dense', units: 64 }))传结构化数据,别只传字符串 ID - 空
<div class="layer"></div>没宽高,dragstart根本不触发;加min-height: 28px或padding: 4px才行 -
<input>和<textarea></textarea>写了draggable="true"也无效,浏览器强制禁用 - 如果模块带子元素(比如图标+文字),
event.target在drop里大概率是子节点,要用event.currentTarget或提前绑定到容器
drop 区域不阻止 dragover,默认拒绝一切投放
目标画布 <div id="canvas"> 即使写了 <code>ondrop="handleDrop()",只要没监听 dragover 并调用 e.preventDefault(),drop 永远不会触发——这不是 bug,是 HTML5 规范硬性要求。
- 必须写
canvas.addEventListener('dragover', e => e.preventDefault()),只写return false或e.stopPropagation()不生效 - Safari 对
effectAllowed和dropEffect更敏感:在dragstart里设e.dataTransfer.effectAllowed = 'move',在dragover里设e.dataTransfer.dropEffect = 'move' - 画布若用了
transform: scale(0.8)或嵌套在overflow: hidden容器里,拖拽坐标可能错位,需在drop中用getBoundingClientRect()重新计算落点 - 别依赖
event.clientX/Y直接定位;用canvas.getBoundingClientRect()做偏移校准才可靠
移动端原生 drag/drop 全面失效,别幻想兼容
iOS Safari 和 Android Chrome 均不触发 dragstart、drop 等任何原生拖放事件。draggable="true" 在移动端等于摆设,这不是兼容性问题,是平台级限制。
- 检测
'ontouchstart' in window,真机环境下必须切换交互模式:长按激活 +touchmove+CSS transform模拟拖动 - 若用
interact.js,注意它默认启用autoScroll,在滚动容器中可能导致节点意外偏移,建议关掉或手动控制 - 键盘用户无法“拖拽”,WCAG 要求提供替代路径(如右键菜单插入、快捷键
Ctrl+Shift+D打开模块面板) - 真正在意可访问性的团队,往往在第一个 PR 评审时就被指出这个问题
最易被忽略的复杂点:模块之间连线不是视觉效果,而是拓扑关系。你拖一个 Conv2D 到 MaxPooling2D 上,系统得验证输入通道数是否匹配、维度是否对齐——这些校验逻辑不在 drop 事件里自动发生,得你在拿到 dataTransfer.getData() 后立刻执行,否则错误会一直埋到 TensorFlow 运行时报 InvalidArgumentError 才暴露。











