Sortable.js 拖拽排序插件使用详解与配置指南
本文详细介绍轻量级开源 JavaScript 插件 Sortable.js 的使用方法。该插件基于原生 HTML5 拖放 API,支持单列表内部及多列表间拖拽排序,兼容主流浏览器与触屏设备。文章涵盖核心配置项、事件回调、常用方法及动画效果设置,并提供完整代码示例,帮助开发者快速实现高效的列表排序交互。
Sortable.js 简介与特性
本文通过具体实例解析 JavaScript 拖拽排序插件 Sortable.js 的使用方式,供读者参考学习。
因项目功能需求,需实现表格行的拖拽排序功能。经过筛选,发现开源库 Sortable.js 能完美契合需求,故整理本文以记录其使用心得。
Sortable.js 具备以下主要特性:
- 体积小巧且功能丰富。
- 列表项移动时支持平滑动画效果。
- 兼容触屏设备以及绝大多数现代浏览器(IE9 及以下版本除外)。
- 既支持单一列表容器内部的拖拽排序,也支持多个列表容器之间的相互拖拽排序。
- 兼容拖放操作与文本选择功能。
- 提供优秀的自动滚动体验。
- 基于原生 HTML5 拖放 API 构建。
- 兼容多种前端框架,包括 Angular、Vue、React 等。
- 完全支持各类 CSS 框架,例如 Bootstrap。
- API 设计简洁,调用便捷。
- 提供 CDN 加速支持。
- 无需依赖 jQuery 即可独立运行。
插件下载与获取
获取方式:访问 GitHub 仓库 https://github.com/RubaXa/Sortable 下载,或点击此处本站链接下载。
运行效果演示
运行效果如图所示:

单个列表容器内部的拖拽排序演示
<head>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/Sortable-master/Sortable.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var ul = document.getElementById("infos");
var sortable = new Sortable(ul, {
handle: "input,li",//设置li、input支持拖拽
animation: 150,//设置动画时长
// Element dragging started
onStart: function (/**Event*/evt) {
evt.oldIndex; // element index within parent,此处指的是li的索引值
},
// Element dragging ended
onEnd: function (/**Event*/evt) {
var lis = $("#infos li");
//拖拽完毕后重新设置序号,使其连续
for (var i = 0; i < lis.length; i++) {
var no = $(lis[i]).find("input:eq(0)");
no.val(i + 1);
}
}
});
});
</script>
<style>
li{
cursor:pointer;
padding-bottom:5px;
list-style:none;
}
</style>
</head>
<body>
<ul id="infos">
<li>
<input type="text" value="1" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">objective</option>
<option value="2">proposition</option>
</select>
</li>
<li>
<input type="text" value="2" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">eliminate</option>
<option value="2">alige</option>
</select>
</li>
<li>
<input type="text" value="3" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">optimize</option>
<option value="2">deploy</option>
</select>
</li>
</ul>
</body>
两个列表容器之间相互拖拽排序演示
<head>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/Sortable-master/Sortable.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var infosOne = document.getElementById("infosOne");
var sortableOne = new Sortable(infosOne, {
group: "guo",//若需要在两个列表容器间拖拽排序,那group的值必须相同
handle: "input,li",//设置li、input支持拖拽
animation: 150,//设置动画时长
// Element dragging started
onStart: function (/**Event*/evt) {
evt.oldIndex; // element index within parent,此处指的是li的索引值
},
// Element dragging ended
onEnd: function (/**Event*/evt) {
var lis = $("#infosOne li");
for (var i = 0; i < lis.length; i++) {
var no = $(lis[i]).find("input:eq(0)");
no.val(i + 1);
}
lis = $("#infosTwo li");
for (var i = 0; i < lis.length; i++) {
var no = $(lis[i]).find("input:eq(0)");
no.val(i + 1);
}
}
});
var infosTwo = document.getElementById("infosTwo");
var sortableTwo = new Sortable(infosTwo, {
group:"guo",//若需要在两个列表容器间拖拽排序,那group的值必须相同
handle: "input,li",//设置li、input支持拖拽
animation: 150,//设置动画时长
// Element dragging started
onStart: function (/**Event*/evt) {
evt.oldIndex; // element index within parent,此处指的是li的索引值
},
// Element dragging ended
onEnd: function (/**Event*/evt) {
var lis = $("#infosOne li");
for (var i = 0; i < lis.length; i++) {
var no = $(lis[i]).find("input:eq(0)");
no.val(i + 1);
}
lis = $("#infosTwo li");
for (var i = 0; i < lis.length; i++) {
var no = $(lis[i]).find("input:eq(0)");
no.val(i + 1);
}
}
});
});
</script>
</head>
<body>
<ul id="infosOne">
<li>
<input type="text" value="1" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">objective</option>
<option value="2">proposition</option>
</select>
</li>
<li>
<input type="text" value="2" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">eliminate</option>
<option value="2">alige</option>
</select>
</li>
<li>
<input type="text" value="3" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">optimize</option>
<option value="2">deploy</option>
</select>
</li>
</ul>
<hr />
<ul id="infosTwo">
<li>
<input type="text" value="1" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">目标任务</option>
<option value="2">论题议题</option>
</select>
</li>
<li>
<input type="text" value="2" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">消除排除</option>
<option value="2">机敏的敏捷的</option>
</select>
</li>
<li>
<input type="text" value="3" />
<input type="text" value="hierarchy" />
<select>
<option value="">--请选择--</option>
<option value="1">优化使最优化</option>
<option value="2">发布部署</option>
</select>
</li>
</ul>
</body>
配置项与事件回调
常见配置项及事件回调说明
var sortable = new Sortable(el, {
group: "name", // or { name: "...", pull: [true, false, clone], put: [true, false, array] }
sort: true, // sorting inside list
delay: 0, // time in milliseconds to define when the sorting should start
touchStartThreshold: 0, // px, how many pixels the point should move before cancelling a delayed drag event
disabled: false, // Disables the sortable if set to true.
store: null, // @see Store
animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
handle: ".my-handle", // Drag handle selector within list items
filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function)
preventOnFilter: true, // Call `event.preventDefault()` when triggered `filter`
draggable: ".item", // Specifies which items inside the element should be draggable
ghostClass: "sortable-ghost", // Class name for the drop placeholder
chosenClass: "sortable-chosen", // Class name for the chosen item
dragClass: "sortable-drag", // Class name for the dragging item
dataIdAttr: 'data-id',
forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in
fallbackClass: "sortable-fallback", // Class name for the cloned DOM Element when using forceFallback
fallbackOnBody: false, // Appends the cloned DOM Element into the Document's Body
fallbackTolerance: 0, // Specify in pixels how far the mouse should move before it's considered as a drag.
scroll: true, // or HTMLElement
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
scrollSpeed: 10, // px
setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
},
// Element is chosen
onChoose: function (/**Event*/evt) {
evt.oldIndex; // element index within parent
},
// Element dragging started
onStart: function (/**Event*/evt) {
evt.oldIndex; // element index within parent
},
// Element dragging ended
onEnd: function (/**Event*/evt) {
var itemEl = evt.item; // dragged HTMLElement
evt.to; // target list
evt.from; // previous list
evt.oldIndex; // element's old index within old parent
evt.newIndex; // element's new index within new parent
},
// Element is dropped into the list from another list
onAdd: function (/**Event*/evt) {
// same properties as onEnd
},
// Changed sorting within list
onUpdate: function (/**Event*/evt) {
// same properties as onEnd
},
// Called by any change to the list (add / update / remove)
onSort: function (/**Event*/evt) {
// same properties as onEnd
},
// Element is removed from the list into another list
onRemove: function (/**Event*/evt) {
// same properties as onEnd
},
// Attempt to drag a filtered element
onFilter: function (/**Event*/evt) {
var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event.
},
// Event when you move an item in the list or between lists
onMove: function (/**Event*/evt, /**Event*/originalEvent) {
// Example: http://jsbin.com/tuyafe/1/edit?js,output
evt.dragged; // dragged HTMLElement
evt.draggedRect; // TextRectangle {left, top, right и bottom}
evt.related; // HTMLElement on which have guided
evt.relatedRect; // TextRectangle
originalEvent.clientY; // mouse position
// return false; — for cancel
},
// Called when creating a clone of element
onClone: function (/**Event*/evt) {
var origEl = evt.item;
var cloneEl = evt.clone;
}
});
核心配置属性详解:
group:字符串或数组。sort:布尔值,定义列表单元是否可在容器内拖拽排序;delay:数值,定义触发拖动的延迟时间(毫秒);disabled:布尔值,设为 true 时禁用排序功能,设为 false 时启用,相当于开关;animation:数值(毫秒),定义排序动画持续时间;handle:CSS 选择器字符串,指定触发拖动的元素(如标题或按钮),仅按住该区域可拖动;filter:CSS 选择器字符串,定义不可拖动的元素,支持多个选择器以逗号分隔;draggable:CSS 选择器字符串,定义允许拖动的列表单元;ghostClass:CSS 选择器字符串,为拖动时生成的影子副本添加类名,便于自定义样式;chosenClass:CSS 选择器字符串,选中列表单元时添加的类名;forceFallback:布尔值,设为 true 时不使用原生 HTML5 拖放,允许自定义样式;fallbackClass:字符串,forceFallback 为 true 时鼠标附着单元的样式类;scroll:布尔值,默认为 true,当容器可滚动时,拖放操作可触发区域滚动。
事件回调函数:
onChoose:列表单元被选中时的回调;onStart:拖动开始时的回调;onEnd:拖放结束后的回调;onAdd:列表单元添加到当前容器的回调;onUpdate:列表容器内排序发生变化后的回调;onRemove:列表元素移至另一个容器时的回调;onFilter:尝试选中被 filter 过滤掉的单元时的回调;onMove:在单个或多列表容器中移动单元时的回调;onClone:创建列表单元副本时的回调。
事件对象属性:
各事件中的对象属性略有差异,可通过控制台输出查看。主要属性包括:
to:HTMLElement,目标列表容器;from:HTMLElement,源列表容器;item:HTMLElement,被移动的列表单元;clone:HTMLElement,副本单元;oldIndex:数值或 undefined,原序号;newIndex:数值或 undefined,新序号。
常用方法说明
常用方法:
option(name[,value])
用于获取或设置项参数。用法类似 jQuery,无第二个参数时获取指定选项的值,有第二个参数时则重新赋值。
closest
返回满足指定 selector 条件的元素集合中的第一个项。
toArray()
序列化可排序列表单元的 data-id(可通过配置项 dataIdAttr 修改)并返回包含这些 ID 的数组。
sort()
通过自定义列表单元的 data-id 数组对列表单元进行排序。
save()
destroy()
相关工具与资源
此外,推荐一款排序算法演示工具供参考:
在线动画演示插入/选择/冒泡/归并/希尔/快速排序算法过程工具: http://tools.jb51.net/aideddesign/paixu_ys
更多 JavaScript 相关内容可查阅本站专题:《JavaScript表格(table)操作技巧大全》、《JavaScript操作DOM技巧总结》、《JavaScript遍历算法与技巧总结》、《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》。
希望本文所述内容对大家学习 JavaScript 程序设计有所帮助。
D3.js 比例尺与坐标轴的配置详解
2026-11-05 16:54:46
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19
