用Draggable快速实现可排序列表:Sortable拖拽排序实战指南
【免费下载链接】draggableThe JavaScript Drag & Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable
Draggable是一款轻量级 JavaScript 拖拽库,内置 Sortable 模块,让你几行代码就能实现可排序列表与拖拽排序,支持鼠标、触摸、原生拖拽和 Force Touch。本文带你从零上手 Sortable 拖拽排序,并掌握事件监听与排序动画等进阶技巧。
为什么选择 Draggable 实现拖拽排序?
拖拽排序是任务看板、待办列表、商品管理后台里的高频需求。市面上方案不少,但 Draggable(npm 包名@shopify/draggable,gzip 后仅 16KB 左右)有几个突出的优点:
- ✅API 简洁:一个构造器 + 一组事件回调,无需学习复杂概念
- ✅多端输入:默认内置
MouseSensor与TouchSensor,天然支持触屏拖拽 - ✅模块化:
Draggable负责底层拖拽,Sortable只管排序,职责清晰 - ✅可插拔插件:排序动画、尺寸镜像等能力按需挂载,如 src/Plugins/
核心源码位于 src/Sortable/Sortable.js,它继承自Draggable,并额外追踪拖动项的起始索引与容器,这正是"排序"能力的来源。
一键安装:npm / yarn / CDN 三种方式
npm:
npm install @shopify/draggable --saveyarn:
yarn add @shopify/draggable不装依赖也行,浏览器直接以 ES Module 或 UMD 方式引入即可,具体写法见根目录 README.md 的 Install 章节。
最快配置方法:3 行代码让列表可拖拽排序
Sortable的构造逻辑很直白:传入容器选择器,再用draggable选项指定可拖拽项的选择器。
import {Sortable} from '@shopify/draggable'; const sortable = new Sortable(document.querySelectorAll('ul'), { draggable: 'li', });就这么简单。官方示例 src/Sortable/README.md 给出的就是这个最小用法。
⚠️关键前提:可拖拽元素必须是容器的直接子元素,这是 Sortable 的硬性要求(见 src/Sortable/README.md)。
Sortable 拖拽排序的 4 个核心事件
拖拽过程中,Sortable 会在底层drag:*事件之外再抛出 4 个排序专属事件(定义见 src/Sortable/SortableEvent/SortableEvent.ts):
| 事件 | 触发时机 | 可取消 | 取消效果 |
|---|---|---|---|
sortable:start | 拖拽开始 | ✅ | 阻止拖拽开始 |
sortable:sort | 排序即将发生 | ✅ | 阻止排序(如容量限制) |
sortable:sorted | 元素已在 DOM 中完成排序 | ❌ | — |
sortable:stop | 拖拽结束 | ❌ | 携带oldIndex/newIndex |
监听事件的推荐写法(参考 src/Sortable/README.md):
sortable.on('sortable:stop', (evt) => { console.log(`从第 ${evt.oldIndex} 位移动到第 ${evt.newIndex} 位`); // 在这里把新顺序同步给后端 });💡实战建议:持久化排序结果时优先监听sortable:stop,此时oldIndex、newIndex、oldContainer、newContainer都已就绪,无需自己计算。
多容器拖拽排序:看板场景怎么做?
把多个容器都传给构造器即可实现跨容器移动(源码 src/Sortable/Sortable.js 的move函数会自动处理"同容器移动"与"跨容器移动"两种情况)。
官方多容器示例位于 examples/src/content/Sortable/MultipleContainers/index.js,其中有一个很实用的技巧——用sortable:sort的取消机制做容量限制:
sortable.on('sortable:sort', (evt) => { if (!capacityReached) return; // 目标容器已满,且拖拽源不是它自己 → 取消排序 if (evt.dragEvent.overContainer === sortable.containers[1]) { evt.cancel(); } });对应 HTML 模板见 examples/src/content/Sortable/MultipleContainers/MultipleContainers.html。
进阶:为拖拽排序加丝滑动画
默认排序是"瞬移"的,稍显生硬。挂载SortAnimation插件后,被挤开的列表项会通过translate3d平滑位移(插件源码 src/Plugins/SortAnimation/SortAnimation.js):
import {Sortable, Plugins} from '@shopify/draggable'; const sortable = new Sortable(document.querySelectorAll('ul'), { draggable: 'li', sortAnimation: { duration: 200, // 动画时长(毫秒) easingFunction: 'ease-in-out', }, plugins: [Plugins.SortAnimation], });两点注意(见 src/Plugins/SortAnimation/README.md):
- 该插件只与
Sortable配合,目前只支持同一容器内排序; - 不要与
SwapAnimation插件同时使用,两者会冲突。
常用配置项速查清单
以下选项对Sortable全部生效(完整说明见 src/Draggable/README.md):
| 选项 | 作用 | 默认值 |
|---|---|---|
draggable | 可拖拽项的 CSS 选择器 | .draggable-source |
handle | 限定只有某个"手柄"区域可触发拖拽 | null(整项可拖) |
distance | 指针移动多少像素后才开始拖拽,适合可点击的列表项 | 0 |
delay | 拖拽延迟,解决触屏滚动冲突 | {mouse: 0, touch: 100} |
mirror | 拖拽镜像的行为配置,如constrainDimensions锁定尺寸 | — |
classes | 自定义各状态类名,方便写 CSS 高亮 | 内置默认值 |
🎯 想让列表项既能点击又能拖?把distance设为 5~10,即可优雅区分两种手势。
常见问题:拖拽排序不生效怎么办?
- 元素不是容器的直接子元素:最常见的原因,参考 examples/src/content/Sortable/SimpleList/SimpleList.html 中的
ul > li结构; - 选择器不匹配:
draggable选项的类名必须与 HTML 中可拖拽项一致,可对照 examples/src/content/Sortable/SimpleList/index.js 检查; - 容器查找为空:示例代码里都有
containers.length === 0的保护判断,初始化时机(如 DOM 未就绪)要留意。
写在最后
Draggable 的 Sortable 模块用最小的心智成本换来了完整的拖拽排序能力:单列表重排 3 行代码搞定,跨容器、容量限制、排序动画都有现成的事件与插件可用。更多可运行的场景(简单列表、多容器、变换布局)都可以参考 examples/src/content/Sortable/ 目录,本地运行示例只需在根目录执行yarn && yarn start(见 README.md)。
【免费下载链接】draggableThe JavaScript Drag & Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考