draggable="true"仅启用拖拽入口,必须在dragstart中调用dataTransfer.setData()传合法字符串ID,否则流程静默中断;动态渲染需事件委托;跨列拖放应基于DOM结构定位而非坐标;React/Vue中须用唯一key和ID映射确保状态同步;移动端应降级为touch模拟或长按排序。

draggable="true" 在大规模看板里不是“设了就能用”,而是个高危开关——它默认不传数据、不保状态、不跨列、不兼容移动端,设错位置或漏掉任一事件处理,整个拖拽链就静默中断。
dragstart 里没调 dataTransfer.setData() 就等于没拖
浏览器看到 draggable="true" 只会启用拖拽入口,但认为“没东西可拖”,直接跳过后续流程。常见表现是:光标变手形、能抓起、松手后卡片原地不动,控制台也无报错。
- 必须在
dragstart事件中调用e.dataTransfer.setData("text/plain", cardId),cardId应来自element.dataset.id,不能是空字符串或undefined - 动态渲染的卡片(如 Vue 的
v-for或 React 的map)容易漏绑监听器,建议用事件委托:给看板容器加addEventListener("dragstart", handleDragStart),再用e.target.matches("[draggable='true']")过滤目标 - 别传 DOM 节点或 JSON 字符串到
dataTransfer—— Firefox 和跨 iframe 场景下getData()极可能返回空字符串
drop 时靠 clientY 算插入位置,在 Flex + 滚动容器里必翻车
用 event.clientY 对比每个卡片的 getBoundingClientRect().top,在真实看板中极易偏移:卡片高度不一、列有 padding/margin、滚动偏移、缩放、字体渲染差异都会让坐标失真。
- 正确做法是基于 DOM 结构定位:在
drop回调里,先确认event.target是目标列内的卡片或空白处 - 用
Array.from(column.children).indexOf(event.target)获取插入索引;若event.target === column(拖到空白),则插入末尾,索引取column.children.length - 务必确保列容器本身设
draggable="false",否则它可能抢走dragstart,导致跨列失败
React/Vue 中拖完状态和 DOM 不同步,根源在 key 和更新时机
直接 setState([...cards.slice(0, i), draggedCard, ...cards.slice(i)]) 后,下一次拖拽的 event.target 拿到的仍是旧 DOM 节点,indexOf 返回错误索引 —— 这不是框架 bug,是渲染滞后于事件回调。
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
立即学习“前端免费学习笔记(深入)”;
-
v-for必须用稳定唯一 ID 绑定:key,禁用数组 index - React 中
useEffect依赖数组必须包含全部影响渲染的状态项,漏掉columns就会跳过重渲染 - 拖拽结束时,优先从
event.currentTarget(即列 DOM)反查 state:通过dataset.columnId找到对应列数据,再用卡片 ID 查原数组索引 - 别依赖 DOM 顺序推导 state 顺序,要靠 ID 映射做双向锚定
localStorage 存的不是 HTML,而是列 ID 到卡片 ID 数组的映射
只设 draggable="true" 不参与状态持久化,刷新后所有卡片默认回到初始父容器。常见错误是存整个列的 innerHTML 或只存卡片 ID 列表但没存归属列。
- 应存轻量、稳定、与 DOM 解耦的数据结构,例如:
{ "todo": ["task-1","task-3"], "doing": ["task-2"], "done": ["task-4"] } - 列名(如
todo)对应真实 DOM 的data-column-id="todo",不依赖 class 名或顺序 - 拖拽结束时,用
dragstart记下的原始列 ID 和drop拿到的目标列 ID 更新该对象,再localStorage.setItem('kanbanState', JSON.stringify(newState)) - 加载时不要遍历子节点实时抓顺序,而应按列清空 + 批量插入,避免动画帧干扰或事件残留
最易被忽略的一点:WeakMap 存拖拽上下文这事,不是“可选优化”,而是跨列、动态渲染、组件复用场景下的刚性需求;不这么做,Firefox 下几乎必然丢 ID,移动端更得整套 touch 模拟来兜底。


















