实用的拖放排序列表
使用拖放 API 构建可重新排序的列表
实用的拖放排序列表 是 CoddyKit 上的免费 HTML Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 HTML Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 HTML Academy 课程共包含 4 节课。
模式概览
要构建拖动排序列表,请使每个项目都可拖动,在 dragstart 时将被拖动项目的 id 存入 dataTransfer,并在 drop 时将被拖动元素插入目标之前或之后。如果每次放置后都根据 DOM 重新构建顺序,DOM 与模型就能保持同步。
标记
请渲染一个扁平列表,使每个 li 都带有 draggable="true" 和 data-id。将 id 保留在元素上,意味着拖动处理函数无需查询单独的状态存储来识别项目——元素本身就是事实来源。
<ul id="list">
<li draggable="true" data-id="1">Item 1</li>
<li draggable="true" data-id="2">Item 2</li>
<li draggable="true" data-id="3">Item 3</li>
</ul>dragstart 记录源
在 dragstart 时,将被拖动的 id 存入 dataTransfer,并应用 "dragging" class 以提供视觉反馈。effectAllowed="move" 告诉浏览器这是重新排序而不是复制,因此光标和 dropEffect 会反映移动操作。
list.addEventListener("dragstart", (e) => {
const li = e.target.closest("li");
if (!li) return;
e.dataTransfer.setData("text/plain", li.dataset.id);
e.dataTransfer.effectAllowed = "move";
li.classList.add("dragging");
});dragover 允许放置
请在列表上监听事件(使用事件委托),调用 preventDefault,并将 dropEffect 设置为 "move"。使用 e.target.closest("li") 查找鼠标悬停的行,这样同一个处理函数就能适用于整个列表。
计算插入位置
要决定被拖动项目应落在悬停行的上方还是下方,请将光标的 Y 坐标与该行的垂直中点进行比较:const before = (e.clientY - rect.top) < rect.height / 2。before=true 表示使用 insertBefore,false 表示使用 insertAfter。
function getInsertBefore(target, clientY) {
const rect = target.getBoundingClientRect();
return clientY < rect.top + rect.height / 2;
}drop 重新排序 DOM
在 drop 处理函数中,根据 id 查找被拖动的元素,并查找光标下方的目标行。根据悬停位置位于目标行的哪一半,调用 list.insertBefore(dragged, target) 或 list.insertBefore(dragged, target.nextSibling)。
dragend 清理样式
无论放置成功,还是用户按下了 Escape,源元素上都会触发 dragend。请使用它移除 "dragging" class 以及所有放置目标高亮,使列表恢复到闲置状态。
list.addEventListener("dragend", () => {
list.querySelectorAll(".dragging, .over").forEach((el) => {
el.classList.remove("dragging", "over");
});
});可视化放置指示器
请使用悬停行上的 CSS 伪元素显示项目将要落下位置的细线,并由 dragover 处理函数根据光标位置切换 "over-top" 或 "over-bottom" class。这样用户在释放之前就能看到插入位置。
同步模型
放置后,请从 DOM 推导新顺序:const order = Array.from(list.children).map((li) => li.dataset.id)。请将该数组发送到服务器或存入状态中——绝不要尝试在 DOM 之外维护并行索引,因为快速拖动时两者会逐渐偏离。
键盘替代方案
请添加上移/下移处理函数,使用 insertBefore 移动获得焦点的项目。通过 aria-live 区域播报移动结果(“项目已上移至第 2 位”)。键盘用户无需进行视觉拖动也能获得相同的功能,这对无障碍访问至关重要。
触摸友好的备用方案
在移动设备上,请使用 Pointer Events 替代原生拖动:在 pointerdown 时捕获行,在 pointermove 时使用 CSS transform 移动它,在 pointerup 时对下方的行进行命中测试并重新排序。SortableJS 等库封装了这一模式,可适用于不同的输入类型。
知识检查
为什么要根据光标相对于悬停行垂直中点的位置来计算插入位置?
总结
健壮的排序列表由 draggable="true" + dragstart(记录 id,effectAllowed=move)+ dragover(preventDefault + 指示器位置)+ drop(在根据中点推导出的插槽处使用 insertBefore)+ dragend(清理)组成。放置后始终从 DOM 推导新顺序,并为无障碍访问提供键盘备用方案。
常见问题解答
「实用的拖放排序列表」课时是免费的吗?
是的 — 「实用的拖放排序列表」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 HTML Academy 课程的其余内容,请升级到 CoddyKit PRO。 HTML Academy 课程共包含 4 节课。
「实用的拖放排序列表」这节课中我会学到什么?
使用拖放 API 构建可重新排序的列表 你通过在浏览器中直接运行的动手代码来练习 HTML Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 HTML Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 HTML Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「实用的拖放排序列表」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 HTML Academy 课中编写并运行代码吗?
能。每节 HTML Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。