作为一名每天和 DOM 打交道的前端程序员,我深知在项目中实现拖拽排序功能的痛点 ------ 自己手写不仅要处理复杂的鼠标 / 触摸事件,还要考虑兼容性和性能问题。直到我接触到了SortableJS,这个轻量级且功能强大的库彻底改变了我处理拖拽需求的方式。今天就从实际开发角度,带大家全面了解 SortableJS,并通过代码实例演示其核心用法。
一、SortableJS 是什么?
SortableJS 是一款无依赖(不依赖 jQuery、Vue 等框架)的 JavaScript 拖拽排序库,支持 PC 端和移动端,兼容所有现代浏览器,甚至包括 IE11。它的核心优势在于:
-
轻量:压缩后仅 30KB 左右,不会给项目带来额外负担;
-
灵活:支持列表、表格、网格等多种布局的拖拽,可自定义拖拽样式、触发条件和回调函数;
-
易用:API 设计简洁,只需几行代码就能实现基础拖拽功能;
-
生态好:有专门针对 Vue、React、Angular 等框架的适配版本(如 vue-sortablejs),无缝融入现有项目。
在实际工作中,我曾用它实现过 "任务看板拖拽""表格列顺序调整""多列表数据迁移" 等需求,效率比手写方案提升了至少 50%。
二、基础使用:从一个简单列表开始
首先我们来看最常见的场景 ------ 单列表拖拽排序。假设项目中有一个待办事项列表,需要支持拖拽调整顺序,步骤如下:
1. 引入 SortableJS
有两种引入方式,可根据项目环境选择:
- CDN 引入(适合快速测试或小型项目):
xml
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
- npm 安装(适合工程化项目):
css
npm install sortablejs --save
在 Vue/React 组件中引入:
javascript
import Sortable from 'sortablejs';
2. 基础 HTML 结构
先创建一个简单的待办列表 DOM:
xml
<!-- 待办列表 -->
<ul id="todoList" class="todo-list">
<li class="todo-item">完成SortableJS文档阅读</li>
<li class="todo-item">编写拖拽排序示例代码</li>
<li class="todo-item">测试移动端兼容性</li>
<li class="todo-item">提交代码到GitHub</li>
</ul>
3. 初始化 Sortable
只需一行核心代码即可初始化拖拽功能,再通过配置项自定义体验:
javascript
// 获取列表DOM元素
const todoList = document.getElementById('todoList');
// 初始化Sortable
const sortable = new Sortable(todoList, {
// 1. 基础配置
animation: 150, // 拖拽时的动画时长(毫秒),提升视觉体验
handle: '.todo-item', // 拖拽触发区域(这里整个列表项都可拖拽)
ghostClass: 'todo-item-ghost', // 拖拽时"幽灵"元素的类名(用于自定义样式)
// 2. 回调函数:处理拖拽后的逻辑(如更新数据)
onEnd: (evt) => {
// evt包含拖拽相关信息:
// oldIndex:元素原来的位置索引
// newIndex:元素新的位置索引
// item:被拖拽的DOM元素
console.log(`待办项从位置 ${evt.oldIndex} 移动到 ${evt.newIndex}`);
// 实际项目中,这里需要更新数据(如接口请求保存新顺序)
// 示例:假设我们有一个todoData数组,更新其顺序
const movedItem = todoData.splice(evt.oldIndex, 1)[0];
todoData.splice(evt.newIndex, 0, movedItem);
console.log('更新后的待办数据:', todoData);
}
});
4. 自定义拖拽样式
通过 CSS 美化拖拽过程中的视觉效果:
css
.todo-list {
list-style: none;
padding: 0;
width: 300px;
}
.todo-item {
padding: 12px 16px;
margin-bottom: 8px;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 4px;
cursor: move; /* 鼠标悬浮时显示"移动"光标 */
transition: all 0.2s;
}
/* 拖拽时"幽灵"元素的样式(半透明、灰色) */
.todo-item-ghost {
opacity: 0.5;
background-color: #f3f4f6;
}
/* 拖拽过程中,被拖拽元素的样式(可选) */
.todo-item.sortable-ghost {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
到这里,一个基础的拖拽排序列表就完成了。在实际开发中,我会根据需求调整handle(比如只允许通过 "拖拽图标" 触发)、animation时长等配置,让交互更符合产品设计。
三、进阶场景:表格拖拽与多列表交互
SortableJS 的能力远不止单列表排序,下面两个场景在工作中也非常常见,我们结合代码实例来看。
场景 1:表格列拖拽调整顺序
在数据表格中,用户常需要自定义列的显示顺序,SortableJS 可以轻松实现:
xml
<!-- 数据表格 -->
<table id="dataTable" class="data-table">
<thead>
<tr>
<th class="table-header">ID</th>
<th class="table-header">姓名</th>
<th class="table-header">部门</th>
<th class="table-header">入职时间</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>张三</td><td>产品部</td><td>2023-01-15</td></tr>
<tr><td>2</td><td>李四</td><td>研发部</td><td>2023-03-22</td></tr>
<!-- 更多行... -->
</tbody>
</table>
ini
// 初始化表格列拖拽(注意:目标是thead中的tr)
const tableHeader = document.querySelector('#dataTable thead tr');
new Sortable(tableHeader, {
animation: 150,
ghostClass: 'header-ghost',
// 关键:只允许横向拖拽(表格列不需要纵向移动)
axis: 'x',
onEnd: (evt) => {
const columnIndex = evt.newIndex;
console.log(`表格列调整到第 ${columnIndex + 1} 位`);
// 实际项目中:需要同步调整tbody中对应列的数据
const tbodyRows = document.querySelectorAll('#dataTable tbody tr');
tbodyRows.forEach(row => {
const cells = row.querySelectorAll('td');
const movedCell = cells[evt.oldIndex];
row.removeChild(movedCell);
row.insertBefore(movedCell, cells[evt.newIndex]);
});
}
});
场景 2:多列表之间的拖拽(如看板)
在 "任务看板" 场景中,常需要将任务从 "待办" 拖拽到 "进行中" 或 "已完成",SortableJS 通过group配置实现多列表交互:
xml
<!-- 看板容器 -->
<div class="kanban-board">
<!-- 待办列 -->
<div class="kanban-column" id="todoColumn">
<h3>待办</h3>
<div class="task">任务1:需求分析</div>
</div>
<!-- 进行中列 -->
<div class="kanban-column" id="doingColumn">
<h3>进行中</h3>
<div class="task">任务2:UI设计</div>
</div>
<!-- 已完成列 -->
<div class="kanban-column" id="doneColumn">
<h3>已完成</h3>
</div>
</div>
javascript
// 多列表拖拽的核心:所有列使用相同的group名称
const createKanbanSortable = (columnId) => {
const column = document.getElementById(columnId);
return new Sortable(column, {
animation: 150,
group: {
name: 'kanban-group', // 相同group名称的列表可互相拖拽
pull: true, // 允许从当前列表拖拽出去
put: true // 允许从其他列表拖拽进来
},
handle: '.task',
onEnd: (evt) => {
// 区分"同列表排序"和"跨列表拖拽"
if (evt.from !== evt.to) {
// 跨列表拖拽:更新任务状态(如从"待办"到"进行中")
const taskName = evt.item.textContent;
const fromColumn = evt.from.querySelector('h3').textContent;
const toColumn = evt.to.querySelector('h3').textContent;
console.log(`任务「${taskName}」从「${fromColumn}」移动到「${toColumn}」`);
// 实际项目中:这里需要调用接口更新任务状态
} else {
// 同列表排序:仅更新顺序
console.log(`任务在「${evt.to.querySelector('h3').textContent}」中调整顺序`);
}
}
});
};
// 初始化三个看板列
createKanbanSortable('todoColumn');
createKanbanSortable('doingColumn');
createKanbanSortable('doneColumn');
四、框架适配:以 Vue3 为例
如果你的项目基于 Vue3 开发,可使用专门的适配库vue-sortablejs,用法更贴合 Vue 的语法:
- 安装依赖:
css
npm install vue-sortablejs sortablejs --save
- 在组件中使用:
xml
<template>
<ul v-sortable="sortableOptions" class="todo-list">
<li v-for="(item, index) in todoData" :key="index" class="todo-item">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
import { vSortable } from 'vue-sortablejs';
// 响应式数据
const todoData = ref([
'完成SortableJS文档阅读',
'编写拖拽排序示例代码',
'测试移动端兼容性'
]);
// Sortable配置项
const sortableOptions = {
animation: 150,
ghostClass: 'todo-item-ghost',
onEnd: (evt) => {
// 直接操作响应式数据,Vue会自动更新DOM
const movedItem = todoData.value.splice(evt.oldIndex, 1)[0];
todoData.value.splice(evt.newIndex, 0, movedItem);
}
};
</script>
五、工作中的避坑指南
在实际使用 SortableJS 时,我踩过几个小坑,分享给大家:
- 拖拽元素内有输入框 :如果列表项包含或,拖拽时可能会触发输入框聚焦,需要通过filter配置排除:
less
new Sortable(list, {
filter: '.input', // 过滤掉输入框元素,点击输入框时不触发拖拽
onFilter: (evt) => {
// 可选:处理过滤后的逻辑(如聚焦输入框)
if (evt.item.querySelector('.input')) {
evt.item.querySelector('.input').focus();
}
}
});
- 移动端拖拽不生效:确保没有禁止触摸事件,且touch-action样式未被覆盖,可添加:
css
.todo-item {
touch-action: none; /* 允许移动端拖拽 */
}
- 大数据列表性能:如果列表项超过 100 个,建议开启scroll配置,优化滚动时的性能:
arduino
new Sortable(list, {
scroll: true, // 拖拽时自动滚动容器
scrollSpeed: 20, // 滚动速度
scrollSensitivity: 30 // 距离容器边缘多少像素时开始滚动
});
六、总结
作为前端程序员,高效解决业务需求是核心目标。SortableJS 凭借其轻量、灵活、易用的特点,成为我处理拖拽排序需求的首选工具。从简单的单列表排序,到复杂的多列表看板,再到框架内的无缝集成,它都能胜任。
欢迎jym点赞关注加评论~