Sortable.js 功能强大的 JavaScript 拖拽库示例详解
Sortable.js 是一款轻量级且功能强大的原生 JavaScript 拖拽排序库,无需依赖 jQuery。本文详细介绍其安装方式、核心配置项(如 group、handle、animation)、事件回调及常用方法,并通过 Vue 环境下的简单列表、多列表互拖、克隆节点及禁用排序等实战案例,展示如何快速实现高性能的拖放交互效果。
一、介绍
Sortable.js 是一款功能强大的 JavaScript 拖拽库,专门用于在网页中创建可拖放和可排序的元素。它提供了简洁而强大的 API,帮助开发人员轻松实现拖放功能,并允许用户通过拖拽操作对列表、网格及其他元素进行重新排序。
该库主要具备以下特性:
- 兼容性良好:支持触屏设备及主流现代浏览器,同时提供对旧版浏览器的向后兼容能力;
- 使用简单:API 设计简洁,通过基础的 HTML 结构和 JavaScript 配置即可快速实现拖放排序功能;
- 原生实现:基于原生 HTML5 拖放 API 构建;
- CSS 框架兼容:完美支持各类 CSS 框架,例如 Bootstrap;
- 零依赖:不依赖 jQuery 或其他第三方框架;
- SPA 支持良好:兼容多种主流前端框架,包括 Angular、Vue、React 等;
- 高度可定制:通过配置选项和回调函数,可自定义拖放行为及排序动画效果;
- 交互丰富:除基础拖放外,还提供拖放动画、排序同步等丰富的交互选项;
- 社区支持:SortableJS 拥有活跃的开发者社区,为使用者提供技术支持、插件及示例代码。
二、安装
1、npm 安装
npm install sortablejs --save2、bower 安装
bower install --save sortablejs3、script 引入
<script src="../../js/Sortable.min.js"></script>三、配置项
const sortable = new Sortable(el, {
// or { name: "...", pull: [true, false, 'clone', array], put: [true, false, array] }
group: "name",
// boolean 定义是否列表单元是否可以在列表容器内进行拖拽排序
sort: true,
// number 定义鼠标选中列表单元可以开始拖动的延迟时间;
delay: 0,
// px,在取消延迟拖动事件之前,点应该移动多少像素
touchStartThreshold: 0,
// boolean 定义是否此sortable对象是否可用,为true时sortable对象不能拖放排序等功能,为false时为可以进行排序,相当于一个开关;
disabled: false,
// @see Store
store: null,
// ms, number 单位:ms,定义排序动画的时间
animation: 150,
// 制作动画。默认为null,https://easings.net/。
easing: "cubic-bezier(1, 0, 0, 1)",
// 格式为简单css选择器的字符串,使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动
handle: ".my-handle",
// 过滤器,不需要进行拖动的元素
filter: ".ignore-elements",
// 在触发过滤器`filter`的时候调用`event.preventDefault()`
preventOnFilter: true,
// 允许拖拽的项目类名
draggable: ".item",
// drop placeholder的css类名
ghostClass: "sortable-ghost",
// 被选中项的css 类名
chosenClass: "sortable-chosen",
// 正在被拖拽中的css类名
dragClass: "sortable-drag",
dataIdAttr: 'data-id',
// 交换区的阈值
swapThreshold: 1,
// 如果设置为 true,将始终使用反向交换区
invertSwap: false,
// 反向交换阈值,默认情况下将设置为swapThreshold 值
invertedSwapThreshold: 1,
// 拖拽方向 (默认情况下会自动判断方向)
direction: 'horizontal',
// 忽略 HTML5拖拽行为,强制回调进行
forceFallback: false,
// 当使用forceFallback的时候,被复制的dom的css类名
fallbackClass: "sortable-fallback",
// 将cloned DOM 元素挂到body元素上。
fallbackOnBody: false,
// 以像素为单位指定鼠标在被视为拖动之前应移动的距离。
fallbackTolerance: 0,
// 是否启用滚动,值为true或者HTMLElement
scroll: true,
// 自定义滚动
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... },
// 鼠标必须离边缘多近才能开始滚动,单位 px
scrollSensitivity: 30,
// 滚动速度,px
scrollSpeed: 10,
// 将自动滚动应用于所有父元素,以便更轻松地移动
bubbleScroll: true,
dragoverBubble: false,
// 删除不显示的克隆元素,而不是仅仅隐藏它
removeCloneOnHide: true,
// 拖动时鼠标必须与空可排序项的距离(以像素为单位),以便将拖动元素插入到该可排序项中, 设置为0禁用此功能。
emptyInsertThreshold: 5,
setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
},
// 元素被选中
onChoose: function (/**Event*/evt) {
evt.oldIndex; // element index within parent
},
// 元素未被选中的时候(从选中到未选中)
onUnchoose: function(/**Event*/evt) {
// same properties as onEnd
},
// 开始拖拽的时候
onStart: function (/**Event*/evt) {
evt.oldIndex; // element index within parent
},
// 结束拖拽
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
evt.clone // the clone element
evt.pullMode; // when item is in another sortable: `"clone"` if cloning, `true` if moving
},
// 元素从一个列表拖拽到另一个列表
onAdd: function (/**Event*/evt) {
// same properties as onEnd
},
// 列表内元素顺序更新的时候触发
onUpdate: function (/**Event*/evt) {
// same properties as onEnd
},
// 列表的任何更改都会触发
onSort: function (/**Event*/evt) {
// same properties as onEnd
},
// 元素从列表中移除进入另一个列表
onRemove: function (/**Event*/evt) {
// same properties as onEnd
},
// 试图拖拽一个filtered的元素
onFilter: function (/**Event*/evt) {
var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event.
},
// 拖拽移动的时候
onMove: function (/**Event*/evt, /**Event*/originalEvent) {
// Example: https://jsbin.com/nawahef/edit?js,output
evt.dragged; // dragged HTMLElement
evt.draggedRect; // DOMRect {left, top, right, bottom}
evt.related; // HTMLElement on which have guided
evt.relatedRect; // DOMRect
evt.willInsertAfter; // Boolean that is true if Sortable will insert drag element after target by default
originalEvent.clientY; // mouse position
// return false; — for cancel
// return -1; — insert before target
// return 1; — insert after target
},
// clone一个元素的时候触发
onClone: function (/**Event*/evt) {
var origEl = evt.item;
var cloneEl = evt.clone;
},
// 拖拽元素改变位置的时候
onChange: function(/**Event*/evt) {
evt.newIndex // most likely why this event is used is to get the dragging element's current index
// same properties as onEnd
}
}); 1.属性
(1)group:string 或 object
- string:命名,建议使用元素 id,主要用于设置可拖放容器,具体在 array 中的 put 设置中介绍。
- object:{name, pull, put}。
name:同 string 的用法。 pull:定义从当前列表容器移动出去的设置,可选值 true/false/'clone'/function。true 表示列表单元可移出;false 表示不可移出;'clone' 表示移出时创建副本;function 用于执行复杂逻辑,通过 return false/true 判断是否允许移出。 put:定义向当前列表容器放置列表单元的设置,可选值 true/false/['foo','bar']/function。true 表示可从其他容器放入单元;false 相反;['foo','bar'] 为字符串或字符串数组,对应 group 配置中的 name 值;function 用于执行复杂逻辑,通过 return false/true 判断是否允许放入。
(2)sort:boolean,定义列表单元是否可在当前列表容器内进行拖拽排序;
(3)delay:number,定义鼠标选中列表单元后开始拖动的延迟时间;
(4)disabled:boolean,定义此 sortable 对象是否可用。为 true 时禁用拖放排序等功能,为 false 时启用,相当于开关;
(5)animation:number,单位 ms,定义排序动画的持续时间;
(6)handle:selector,格式为简单 CSS 选择器的字符串,指定列表单元中符合选择器的元素作为拖动手柄,仅按住手柄才能拖动列表单元;
(7)filter:selector,格式为简单 CSS 选择器的字符串,定义哪些列表单元不能进行拖放,可设置多个选择器,中间用“,”分隔;
(8)draggable:selector,格式为简单 CSS 选择器的字符串,定义哪些列表单元可以进行拖放;
(9)ghostClass:selector,格式为简单 CSS 选择器的字符串。当拖动列表单元时,会生成一个副本作为影子单元模拟排序情况,此配置用于给影子单元添加 class,从而编辑其样式;
(10)chosenClass:selector,格式为简单 CSS 选择器的字符串。当选中列表单元时,会给该单元增加一个 class;
(11)forceFallback:boolean。如果设置为 true,将不使用原生 HTML5 拖放,允许修改拖放过程中元素的样式等;
(12)fallbackClass:string。当 forceFallback 设置为 true 时,指定拖放过程中鼠标附着单元的样式类名;
(13)scroll:boolean,默认为 true。当排序容器为可滚动区域时,拖放操作可引起区域滚动。
2.事件
onChoose:function,列表单元被选中的回调函数
onStart:function,列表单元拖动开始的回调函数
onEnd:function,列表单元拖放结束后的回调函数
onAdd:function,列表单元添加到本列表容器的回调函数
onUpdate:function,列表单元在列表容器中的排序发生变化后的回调函数
onRemove:function,列表元素移到另一个列表容器的回调函数
onFilter:function,试图选中一个被 filter 过滤的列表单元的回调函数
onMove:function,当移动列表单元在一个或多个列表容器中时的回调函数
onClone:function,当创建一个列表单元副本时的回调函数
3.事件对象
事件对象在各个函数中略有不同,可通过输出对象查看属性,以下列举几个关键属性:
to:HTMLElement,移动到列表容器
from:HTMLElement,来源的列表容器
item:HTMLElement,被移动的列表单元
clone:HTMLElement,副本的列表单元
oldIndex:number/undefined,在列表容器中的原序号
newIndex:number/undefined,在列表容器中的新序号
4.方法
option(name[,value]) 获取或设置项参数,用法类似 jQuery。无第二个参数时获取 option 中第一个参数对应的值;有第二个参数时,将重新赋值给第一个参数对应的值;
closest 未详细解释。
toArray() 序列化可排序列表单元的 data-id(可通过配置项 dataIdAttr 修改)放入一个数组,并返回该数组;
sort() 通过自定义列表单元的 data-id 数组对列表单元进行排序;
save() destroy()
四、实战应用
1. Vue 先把数据渲染到页面上;
2. 利用 sortablejs 操作真实 DOM,得到列表。SortableJS 核心在于操作真实 DOM,此时由于 Vue 数据未更新,不会触发 DOM 重新渲染,导致 Vue 中的数据与真实 DOM 数据不一致,因此需要第 3 步;
3. 利用 sortablejs 的 onEnd 方法将数据变化映射到真实的 Vue 数据源 list 上,实现拖拽时 DOM 变化与数据同步。
环境如下:

1、简单列表拖拽
<script setup lang="ts">
import Sortable from 'sortablejs'
import { ref, onMounted, nextTick } from 'vue';
const sortableRef = ref(null)
const list = [
{
name: 'item1'
},
{
name: 'item2'
},
{
name: 'item3'
},
{
name: 'item4'
},
{
name: 'item5'
},
{
name: 'item6'
}
]
const initSortable = () => {
new Sortable(sortableRef.value, {
delay: 10,
animation: 150,
// 如果有不想排序的,在这里按照class匹配的方式写出即可
filter: '.demo-title',
// 需要拖拽的标签的class,强烈推荐写
draggable: '.demo-item',
// drop placeholder的css类名
ghostClass: 'blue-background-class'
});
}
onMounted(() => {
nextTick(() => {
initSortable()
})
})
</script>
<template>
<ul ref="sortableRef" class="demo">
<li class="demo-title">Demo</li>
<li class="demo-item" v-for="item in list" :key="item.name">{{ item.name }}</li>
</ul>
</template>
<style lang="scss" scoped>
.demo {
list-style: none;
width: 40%;
margin: 200px auto 0;
.demo-title {
text-align: center;
font-weight: 600;
font-size: 16px;
}
.demo-item {
border: 1px solid red;
margin: 10px 0;
padding: 10px 0;
text-align: center;
}
}
.blue-background-class {
background-color: #1890ff;
}
</style>效果如下:

2、多列表相互拖拽
<script setup lang="ts">
import Sortable from 'sortablejs'
import { ref, onMounted, nextTick } from 'vue';
const demo1Ref = ref(null)
const demo2Ref = ref(null)
const initSortable = () => {
new Sortable(demo1Ref.value, {
// 相互拖拽列表的组名必须一样
group: 'shared',
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});
new Sortable(demo2Ref.value, {
// 相互拖拽列表的组名必须一样
group: 'shared',
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});
}
onMounted(() => {
nextTick(() => {
initSortable()
})
})
</script>
<template>
<div class="demo-container">
<ul ref="demo1Ref" class="demo">
<li class="demo-item demo1-item" v-for="item in 6" :key="'demo1'+item">item{{ item }}</li>
</ul>
<ul ref="demo2Ref" class="demo">
<li class="demo-item demo2-item" v-for="item in 6" :key="'demo2'+item">item{{ item }}</li>
</ul>
</div>
</template>
<style lang="scss" scoped>
.demo-container {
display: flex;
justify-content: space-between;
width: 1000px;
.demo {
list-style: none;
width: 48%;
.demo-item {
margin: 10px 0;
padding: 10px 0;
text-align: center;
}
.demo1-item {
background-color: #5ac8fa;
}
.demo2-item {
background-color: #c0ca33;
}
}
}
</style> 效果如下:

3、克隆(通过拖拽复制节点)
new Sortable(demo1Ref.value, {
group: {
name: 'shared',
pull: 'clone' // To clone: set pull to 'clone'
},
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});
new Sortable(demo2Ref.value, {
group: {
name: 'shared',
pull: 'clone' // To clone: set pull to 'clone'
},
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});效果如下:

4、禁止 sorting
坐标的列表无法进行排序,因为设置了禁止 sort,但是你还是可以从左边拖拽到右边。
new Sortable(demo1Ref.value, {
group: {
name: 'shared',
pull: 'clone',
put: false // 不允许拖拽进这个列表
},
sort: false, // 设为false,禁止sort
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});
new Sortable(demo2Ref.value, {
group: 'shared',
delay: 10,
animation: 150,
// 需要拖拽的标签的class,强烈推荐写
// 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
draggable: '.demo-item'
});效果如下:

5、Handle
使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动。在面代码中只有按住白色方块才能拖动元素。
<script setup lang="ts">
import Sortable from 'sortablejs'
import { ref, onMounted, nextTick } from 'vue';
const sortableRef = ref(null)
const initSortable = () => {
new Sortable(sortableRef.value, {
handle: '.handle-draggabel',
animation: 150,
});
}
onMounted(() => {
nextTick(() => {
initSortable()
})
})
</script>
<template>
<ul ref="sortableRef" class="demo">
<li class="demo-item" v-for="item in 6" :key="item">
<span class="handle-draggabel"></span>
<span>item{{ item }}</span>
</li>
</ul>
</template>
<style lang="scss" scoped>
.demo {
list-style: none;
width: 40%;
.demo-item {
background-color: #1890ff;
margin: 10px 0;
padding: 10px 0;
color: #fff;
text-align: center;
.handle-draggabel {
display: inline-block;
width: 14px;
height: 14px;
background-color: #fff;
margin-right: 10px;
cursor: pointer;
}
}
}
</style>效果如下:

五、总结
除了基本的拖放功能外,SortableJS 还提供了许多额外的功能和选项,例如拖放动画、排序同步、多选模式等。这些功能可以帮助您创建更加丰富和用户友好的交互体验。
SortableJS 的社区非常活跃,许多开发者使用该库并为其贡献插件和示例。这意味着您可以找到许多现成的解决方案和资源,以帮助您解决在开发中遇到的问题。
总的来说,SortableJS 是一个强大、灵活且易于使用的 JavaScript 库,适用于各种类型的拖放排序需求。无论是简单的列表排序还是复杂的交互式应用程序,SortableJS 都能够帮助您快速实现所需的功能。如果您正在寻找一个可靠的拖放库来增强您的网页或应用程序的交互性,SortableJS 是一个值得考虑的选择。
文档地址:http://www.sortablejs.com/index.html
到此这篇关于Sortable.js功能强大的JavaScript 拖拽库示例详解的文章就介绍到这了,更多相关Sortable.js JavaScript 拖拽库内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
