Sortable.js使用教程:轻量级JS拖拽排序库快速上手
本文是一篇详尽实用的Sortable.js快速上手教程。从环境引入、容器初始化、常用配置项详解,到跨列表拖拽、生命周期事件捕获及数据后端持久化,帮助开发者快速掌握这个轻量级JavaScript拖拽排序库。
假设你在开发后台管理系统的自定义菜单、商品图片上传面板或项目任务看板,需要让用户通过直观的拖动来调整显示顺序。若直接编写原生 HTML5 拖拽 API(dragstart、dragover、drop),不仅要手动计算鼠标指针与元素边界坐标、绘制幽灵占位块,还需额外编写触控事件以兼顾移动端设备。Sortable.js 正是为了解决这一繁复问题而诞生的经典工具:它不依赖任何外部框架,压缩体积仅十余千字节,仅需一行核心脚本即可让任意 DOM 列表具备丝滑平移的拖拽排序体验。
一、准备工作与快速引入
在项目中引入 Sortable.js 主要有两种方式:使用 HTML 页面常见的 CDN 链接直接加载,或者在工程化打包环境中使用 npm / yarn 进行模块依赖管理。读者可以根据当前项目的构建方式选择对应方案。
1. CDN 引入(原生 HTML 或传统项目)
如果当前正在维护不需要打包构建的原生页面或后端模板页面,在 <head> 或 <body> 底部加入以下 <script> 标签即可直接使用全局变量 Sortable:
<!-- 引入 Sortable.js 最新稳定版 CDN -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
2. NPM 依赖安装(Vue / React / Vite 工程)
如果是使用 Vite、Webpack 或各大现代前端框架开发的应用,在终端工作目录下执行安装命令:
# 使用 npm 安装
npm install sortablejs --save
# 或使用 pnpm / yarn
pnpm add sortablejs
安装完成后,在需要启用拖拽的业务组件中导入即可:
import Sortable from 'sortablejs';
二、编写第一个拖拽排序列表
完成引入后,只需在 DOM 树中选择目标列表父节点,调用 new Sortable(el, options) 或 Sortable.create(el, options),即可在当前容器上激活拖拽能力。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Sortable.js 快速示例</title>
<style>
.sortable-list {
width: 320px;
list-style: none;
padding: 0;
margin: 20px 0;
}
.sortable-item {
padding: 12px 16px;
margin-bottom: 8px;
background: #f1f5f9;
border: 1px solid #cbd5e1;
border-radius: 6px;
cursor: grab;
user-select: none;
}
/* 拖拽时的占位槽样式 */
.my-ghost {
opacity: 0.4;
background-color: #77B9AE !important;
border: 2px dashed #142537 !important;
}
/* 鼠标按住选中的元素样式 */
.my-chosen {
background-color: #e2e8f0;
}
</style>
</head>
<body>
<ul id="my-list" class="sortable-list">
<li class="sortable-item" data-id="1">项目一:首页轮播图配置</li>
<li class="sortable-item" data-id="2">项目二:热门推荐模块</li>
<li class="sortable-item" data-id="3">项目三:底部导航栏设置</li>
</ul>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
const listEl = document.getElementById('my-list');
// 初始化 Sortable 实例
const sortable = Sortable.create(listEl, {
animation: 150, // 排序过渡动画时长(单位:毫秒)
ghostClass: 'my-ghost', // 占位项自定义样式类名
chosenClass: 'my-chosen', // 鼠标抓取选中项自定义样式类名
onEnd: function (evt) {
console.log('拖拽前索引:', evt.oldIndex);
console.log('拖拽后新索引:', evt.newIndex);
}
});
</script>
</body>
</html>
options,Sortable.js 也会以默认参数运行。但默认情况下 animation 为 0(无过渡动画,元素会瞬移),建议在实际业务中始终将其显式设置为 150 到 200 毫秒,以获得自然的平滑流动视觉体验。
三、常用核心参数全解
Sortable.js 拥有丰富的配置项,能够满足不同界面的交互定制需求。下表列出了日常开发中使用频率最高的核心配置:
| 参数名 | 类型 | 默认值 | 功能与使用场景 |
|---|---|---|---|
animation |
Number | 0 | 元素移动排序时的过渡动画时长(毫秒)。设为 150~200 体验最佳。 |
handle |
String | "" | 拖拽手柄 CSS 选择器。限制只有点击该子元素时才能拖动,常用于列表项内有表单或按钮的场景。 |
filter |
String | "" | 禁止拖动的元素选择器。匹配该选择器的子项将无法被拖拽。 |
ghostClass |
String | "" | 占位符样式类。当元素被拖出原位置时,留在原地的虚影所附加的 class。 |
chosenClass |
String | "" | 鼠标按下选中元素时立刻附加的 class。 |
dragClass |
String | "" | 跟随鼠标光标移动的浮动镜像元素附加的 class。 |
disabled |
Boolean | false | 动态禁用或恢复当前容器的拖拽功能。 |
delay |
Number | 0 | 按下后需要等待多少毫秒才触发拖拽,常搭配 delayOnTouchOnly: true 用于移动端防止滚动冲突。 |
使用拖动手柄(Handle)避免点击误触
当列表项内部包含输入框、超链接或操作按钮时,随意点击整行容易误触发拖拽。此时可以通过 handle 限制仅允许抓取专门的图标:
// HTML 结构
<ul id="handle-list">
<li><span class="drag-handle">☰</span> 菜单设置 <button>删除</button></li>
<li><span class="drag-handle">☰</span> 权限管理 <button>删除</button></li>
</ul>
// JavaScript 初始化
Sortable.create(document.getElementById('handle-list'), {
handle: '.drag-handle', // 只有点击包含 .drag-handle 的元素时才触发拖拽
animation: 150
});
四、进阶特性:跨列表互通与看板拖拽
在看板系统(如“待办 - 进行中 - 已完成”)或商品分类分配场景中,经常需要把一个列表的卡片拖拽移动到另一个列表中。Sortable.js 提供了极其简洁的 group 分组机制来实现这一能力。
跨列表配置代码示例
只需为两个列表指定相同的 group 名称,两者的子项便可自由相互拖拽:
// 列表 1:待办箱
const todoList = Sortable.create(document.getElementById('todo-list'), {
group: 'kanban-group',
animation: 150
});
// 列表 2:进行中
const inProgressList = Sortable.create(document.getElementById('progress-list'), {
group: 'kanban-group',
animation: 150
});
如果业务需要更细粒度的控制(例如:左侧是“素材库”,只能向右侧拖出,但不能把右侧的内容拖回左侧,或者拖出时采用“复制副本”而不是“剪切移动”),可以为 group 传入对象:
// 只能拖出且保留原卡片副本(克隆模式)
Sortable.create(sourceEl, {
group: {
name: 'photo-gallery',
pull: 'clone', // 拖拽时克隆副本
put: false // 不允许其他列表的元素放进此列表
},
animation: 150
});
五、事件监听与业务数据持久化
在实际生产业务中,拖拽调整顺序只是第一步,关键是将用户拖拽后的最新顺序持久化提交给后端 API 保存。Sortable.js 提供了完善的事件生命周期和快捷获取序列的 API。
1. 核心回调事件
onChoose(evt):用户指针按下,选定元素时触发。onStart(evt):拖拽开始移动时触发。onMove(evt, originalEvent):拖拽过程中指针移动经过其他列表项时触发,返回false可阻止本次位置交换。onEnd(evt):拖拽结束,DOM 节点已完成重排时触发。核心属性包括:evt.item:被拖动的 DOM 元素。evt.to:目标容器。evt.from:来源容器。evt.oldIndex:拖拽前的原位置索引(从 0 开始)。evt.newIndex:拖拽后落地的新位置索引(从 0 开始)。
onAdd(evt):跨列表拖拽时,目标容器收到新元素时触发。onRemove(evt):跨列表拖拽时,源容器失去该元素时触发。
2. 使用 toArray() 获取当前 ID 序列并保存
Sortable.js 内置了 toArray() 方法。当你的 HTML 子节点上设置了 data-id 属性时,该方法会自动按照当前 DOM 顺序返回一个包含所有 ID 的字符串数组:
const sortable = Sortable.create(document.getElementById('category-list'), {
animation: 150,
dataIdAttr: 'data-id', // 默认即为 data-id
onEnd: async function (evt) {
// 只有当实际位置发生变动时才发起网络请求
if (evt.oldIndex === evt.newIndex) {
return;
}
// 获取当前列表最新的 ID 数组,例如 ["103", "101", "102"]
const sortedIds = sortable.toArray();
console.log('提交最新排序顺序:', sortedIds);
try {
const response = await fetch('/api/categories/reorder', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: sortedIds })
});
if (!response.ok) {
throw new Error('网络响应异常');
}
console.log('排序已成功保存');
} catch (err) {
console.error('保存失败:', err);
// 可在此处调用后端回滚或还原 DOM 顺序
}
}
});
六、现代前端框架集成注意事项
当在 Vue 3 或 React 等基于响应式数据驱动的前端框架中使用 Sortable.js 时,必须注意以下关键陷阱:
- 真实 DOM 与虚拟 DOM(Virtual DOM)脱节:Sortable.js 是直接操作底层真实 DOM 节点的,而 Vue/React 依靠内部虚拟 DOM 树进行渲染。如果在拖拽后不及时更新底层的组件数据数组(如响应式 state),下一次组件重新渲染时,DOM 会立刻被重置回旧状态甚至产生混乱。
- Vue 方案:推荐直接使用生态中基于 Sortable.js 封装的成熟组件(Vue 3 使用
sortablejs-vue3或vuedraggable),它们会在内部自动接管事件并同步双向绑定数据数组。 - React 方案:在 React 中使用时,一般在
useEffect内部初始化 Sortable 实例并在组件卸载时调用sortable.destroy()。在onEnd回调中,通过计算oldIndex和newIndex执行数组位置交换,再调用setItems([...newItems])触发状态更新。
七、总结
Sortable.js 凭借小巧的体积、零第三方依赖以及出色的移动触控兼容性,成为前端开发中实现列表拖拽与看板排序的首选类库。在日常项目中,通过掌握基础容器初始化、animation 动画设置、group 跨列表协作以及 onEnd 数据提取这几个核心知识点,就能在短时间内搭建出交互平滑、稳定可靠的拖拽排序功能。
JavaScript零基础入门:从环境搭建到运行第一段代码
2026-11-05 15:29:58
HTML链接中javascript:void(0)的作用及现代替代方案
2026-11-05 10:07:47
在线调试 JS 时怎么引入 jQuery 或 Vue 等第三方库
2026-11-05 08:53:56
原生JavaScript实现简易颜色选择器原理与代码
2026-11-03 23:22:19
JS原生表单序列化:FormData与URLSearchParams用法详解
2026-11-03 23:13:21
相关标签:








