HTML无法单独实现交互式看板,需CSS+JavaScript配合:用flexbox布局三列容器,监听dragstart/drop事件实现拖拽,通过localStorage保存卡片ID序列状态。

HTML 本身不能直接“做看板面板”,它只是结构层;真正实现可拖拽、状态分组、任务卡片交互的看板(如 Trello 风格),必须配合 CSS 布局 + JavaScript 行为。纯 HTML + CSS 只能静态模拟外观,一旦涉及「拖动任务到不同列」「保存状态」「添加新卡片」,dragstart、drop、localStorage 就绕不开。
用 flexbox 实现三栏式看板容器布局
多数看板按「待办 / 进行中 / 已完成」分三列,用 display: flex 最稳妥,比 float 或 inline-block 更易控制对齐和响应式。注意别用 grid 一开始——列宽动态变化(比如某列卡片超多)时,flex 的 flex-wrap 和 min-width 组合更可控。
关键点:
-
container设display: flex; gap: 16px,不用margin手动算间距 - 每列
column设flex: 0 0 300px(不伸缩、最小宽 300px),避免小屏下挤成一列 - 卡片列表
card-list用flex-direction: column; gap: 8px垂直堆叠,比margin-bottom更干净
让卡片支持拖拽:必须监听 dragstart 和 drop
只加 draggable="true" 不够——浏览器默认只允许拖图片/链接,文字卡片需手动触发。且 drop 事件默认被阻止,不写 event.preventDefault() 就无法接收拖入。
立即学习“前端免费学习笔记(深入)”;
常见错误:
- 在
dragover里漏掉event.preventDefault()→ 拖进去时显示“禁止”图标,松手没反应 - 用
data-transfer存卡片 ID 时,没在dragstart里调用setData('text/plain', id)→drop时取不到 ID - 把
drop绑在卡片上(.card),而不是列容器(.column)→ 拖到列空白处会失败
示例片段(简化):
card.addEventListener('dragstart', e => {
e.dataTransfer.setData('text/plain', card.dataset.id);
});
column.addEventListener('dragover', e => e.preventDefault());
column.addEventListener('drop', e => {
const id = e.dataTransfer.getData('text/plain');
column.appendChild(document.querySelector(`[data-id="${id}"]`));
});
用 localStorage 保存看板状态,但别存整个 DOM
刷新页面后任务回到原位,靠的是序列化列内卡片顺序,不是把 innerHTML 整段存进去——后者一改结构就解析失败,且含事件绑定的 HTML 会失效。
正确做法是存轻量数据结构:
- 每个
column对应一个数组,如{ todo: ['task-1', 'task-3'], doing: ['task-2'] } - 卡片元素统一用
data-id标识,渲染时按数组顺序插入 - 每次拖放结束后,立刻调用
localStorage.setItem('kanban-state', JSON.stringify(state)) - 页面加载时先读取
localStorage.getItem('kanban-state'),再重建顺序 —— 不要等所有卡片 DOM 加载完才恢复
真正卡住人的地方,往往不是拖拽逻辑本身,而是「拖放目标判定」:比如任务从 A 列拖到 B 列时,B 列底部有空隙,但用户松手位置在最后一个卡片下方 20px,此时需判断是否该插入末尾——这得靠 getBoundingClientRect() 算坐标,不是简单监听 drop 就完事。



















