原生html+javascript可实现可拖拽任务看板,需设draggable="true"、dragover中preventdefault()、用data-id配合text/plain传输标识符,移动端需降级处理。

用原生 HTML + JavaScript 实现可拖拽任务列的看板墙
纯 HTML 无法实现拖拽排序或跨列移动,必须配合 DragEvent API 和 DOM 操作。现代浏览器(Chrome 12+、Firefox 100+、Edge 110+)已稳定支持 dragstart/dragover/drop,无需第三方库也能做出可用的任务墙。
关键:给每个任务卡片加 draggable="true" 并阻止默认行为
很多人卡在拖拽没反应——根本原因是没设 draggable="true",或没在 dragover 里调 event.preventDefault()。这个阻止动作不是可选的,是浏览器强制要求,否则 drop 事件根本不会触发。
实操建议:
- 所有任务卡片(
<li>或<div class="task">)必须显式写 <code>draggable="true" - 目标容器(每列的
<ul class="column"></ul>)监听dragover,且只做一件事:event.preventDefault() - 不要在
dragover里改样式或读取dataTransfer,那会拖慢响应;样式变化统一交给dragenter/dragleave - 给每个任务卡片加唯一
data-id,例如<li data-id="task-42"> - 在
dragstart中:event.dataTransfer.setData('text/plain', 'task-42') - 在
drop事件中:用event.dataTransfer.getData('text/plain')拿到 ID,再用document.querySelector('[data-id="task-42"]')找节点并appendChild到当前列 - 避免用
innerHTML或outerHTML传输,序列化成本高,还可能丢失事件监听器 - 用
matchMedia('(hover: none) and (pointer: coarse)')检测是否为触屏无悬停设备 - 检测成立时,隐藏拖拽提示(如“拖动排序”文字),改用点击弹出操作菜单(
move to column A/B/C) - 不模拟拖拽手势(比如监听
touchmove自己实现),体验割裂且易误操作 - 如果项目必须全端拖拽,直接上
SortableJS,它内部对移动端做了 touch 事件封装,但注意它会重写整个列表结构,和原生 DOM 操作逻辑不兼容
dataTransfer 只能传字符串,别想直接传 DOM 节点
常见错误是试图在 dragstart 中写 event.dataTransfer.setData('text/html', taskElement) —— 这会失败,因为 setData 只接受字符串,且多数浏览器会忽略非 'text/plain' 类型的值。正确做法是传一个轻量标识符。
推荐方案:
移动端不支持原生 DragEvent,得降级处理
iOS Safari 和 Android Chrome 的 WebView 基本不响应 draggable="true",连 dragstart 都不会触发。这不是 bug,是规范未强制要求移动端实现该 API。强行适配只会浪费时间。
务实做法:
拖拽看似简单,真正难的是边界情况:快速连续拖两次、拖到空白区域、跨窗口释放……这些不用库的话,得靠 dragend 清理状态、用 setTimeout 防抖 drop 区域判断。先跑通单列内排序,再扩展跨列,别一上来就堆功能。











