SortableJS 基础使用指南
1. 什么是 SortableJS
SortableJS 是一个用于列表拖拽排序的轻量 JS 库,基于原生 HTML5 Drag & Drop,支持:
- 同一列表内排序(reorder)
- 列表间拖拽(group)
- 动画/占位样式(animation/ghostClass 等)
- 触摸设备(移动端)
- 插件(如 AutoScroll、MultiDrag、Swap 等,取决于你引入的构建版本)
2. 安装与引入
2.1 NPM 安装(推荐)
npm i sortablejs
ESM 引入示例:
import Sortable from "sortablejs";
// 或更细的构建版本(取决于你的构建/插件需求)
// import Sortable from "sortablejs/modular/sortable.core.esm.js";
2.2 CDN 引入(快速验证)
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
引入后会得到全局变量 Sortable。
3. 最小可用示例(创建一个可排序列表)
HTML:
<ul id="items">
<li class="item">A</li>
<li class="item">B</li>
<li class="item">C</li>
</ul>
JS:
const el = document.getElementById("items");
Sortable.create(el, {
animation: 150,
draggable: ".item",
});
4. 常用配置项(Options)
下面是日常最常用的一组配置(不追求覆盖全部):
animation:拖拽移动时的动画时长(ms),如100/150ghostClass:拖拽占位(ghost)元素的 classchosenClass:被选中(开始拖拽前)元素的 classdragClass:正在拖拽中的元素 classdraggable:可拖拽 item 的选择器(如.item),强烈建议显式设置handle:拖拽把手选择器(如.my-handle),避免“点哪里都能拖”影响点击交互filter:禁止触发拖拽的元素选择器(如.no-drag, input, a)preventOnFilter:filter 命中时是否preventDefault()(默认true)group:跨列表拖拽(同名 group 才能互拖),可用字符串或对象disabled:禁用排序delay/delayOnTouchOnly:延迟触发拖拽(移动端防误触常用)forceFallback:强制使用 fallback(统一桌面/移动端体验时可能会用)
示例:
Sortable.create(el, {
animation: 120,
draggable: ".item",
handle: ".drag-handle",
filter: "input, a, button",
ghostClass: "sortable-ghost",
chosenClass: "sortable-chosen",
});
5. 事件(Events):在拖拽结束时更新数据
SortableJS 的核心是 DOM 排序;你的业务一般还需要“把新顺序写回数据模型”。最常用事件:
onEnd(evt):拖拽结束(最常用)onUpdate(evt):同列表内顺序变化onAdd(evt):从其他列表拖入onRemove(evt):拖出到其他列表onChoose/onStart/onUnchoose:拖拽生命周期
evt 里常用字段:
evt.oldIndex:旧索引(在原列表中的位置)evt.newIndex:新索引(在目标列表中的位置)evt.from/evt.to:源列表/目标列表 DOMevt.item:被拖拽的 DOM 元素
典型“数组重排”逻辑(同列表内):
function arrayMove(arr, from, to) {
arr.splice(to, 0, arr.splice(from, 1)[0]);
return arr;
}
Sortable.create(el, {
onEnd(evt) {
arrayMove(data, evt.oldIndex, evt.newIndex);
// TODO: 触发渲染或提交后端
},
});
6. 方法(Methods)
常用方法:
Sortable.get(el):获取某个 DOM 上绑定的 Sortable 实例sortable.option(name, value?):读/写配置sortable.toArray():按data-id(或dataIdAttr)导出顺序数组sortable.sort(order, useAnimation?):按给定顺序重排 DOMsortable.destroy():销毁实例
示例:使用 data-id 持久化顺序:
<ul id="items">
<li class="item" data-id="a">A</li>
<li class="item" data-id="b">B</li>
<li class="item" data-id="c">C</li>
</ul>
const sortable = Sortable.create(el, { dataIdAttr: "data-id" });
const order = sortable.toArray(); // ["a","b","c"]
sortable.sort(order.reverse(), true);
7. 顺序持久化(Store)
SortableJS 提供 store 钩子帮助保存/恢复排序(示例常见于 localStorage):
Sortable.create(el, {
dataIdAttr: "data-id",
store: {
get(sortable) {
const order = localStorage.getItem("my_sort_order");
return order ? order.split("|") : [];
},
set(sortable) {
const order = sortable.toArray();
localStorage.setItem("my_sort_order", order.join("|"));
},
},
});
注意:store 只处理“顺序”,不处理你的业务数据一致性;如果你的列表渲染由框架驱动(React/Vue/Owl),仍然建议在事件里更新数据源,以避免下一次重渲染把 DOM 顺序覆盖回去。
8. 常见问题与最佳实践
8.1 “点击/勾选也会触发拖拽”
优先用:
handle:只允许拖拽把手区域filter:禁用 input/button/a 等
8.2 框架渲染的列表,DOM 顺序会被覆盖
正确做法:在 onEnd/onUpdate 里把新顺序写回数据源(state/store),让框架用新顺序重新渲染。
8.3 需要跨列表拖拽
两边设置相同 group:
Sortable.create(listA, { group: "shared" });
Sortable.create(listB, { group: "shared" });
更复杂的“可拉取/可放入/克隆”行为用对象形式(详见仓库 README)。
8.4 样式建议
通常你至少需要给 ghost/chosen 设置一个明显的样式,例如:
.sortable-ghost { opacity: 0.35; }
.sortable-chosen { background: #f5f7ff; }