30KB 轻量王者!SortableJS 轻松搞定拖拽需求

作为一名每天和 DOM 打交道的前端程序员,我深知在项目中实现拖拽排序功能的痛点 ------ 自己手写不仅要处理复杂的鼠标 / 触摸事件,还要考虑兼容性和性能问题。直到我接触到了SortableJS,这个轻量级且功能强大的库彻底改变了我处理拖拽需求的方式。今天就从实际开发角度,带大家全面了解 SortableJS,并通过代码实例演示其核心用法。

Sortable.js中文网

一、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 的语法:

  1. 安装依赖:
css 复制代码
npm install vue-sortablejs sortablejs --save
  1. 在组件中使用:
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 时,我踩过几个小坑,分享给大家:

  1. 拖拽元素内有输入框 :如果列表项包含或,拖拽时可能会触发输入框聚焦,需要通过filter配置排除:
less 复制代码
new Sortable(list, {
  filter: '.input', // 过滤掉输入框元素,点击输入框时不触发拖拽
  onFilter: (evt) => {
    // 可选:处理过滤后的逻辑(如聚焦输入框)
    if (evt.item.querySelector('.input')) {
      evt.item.querySelector('.input').focus();
    }
  }
});
  1. 移动端拖拽不生效:确保没有禁止触摸事件,且touch-action样式未被覆盖,可添加:
css 复制代码
.todo-item {
  touch-action: none; /* 允许移动端拖拽 */
}
  1. 大数据列表性能:如果列表项超过 100 个,建议开启scroll配置,优化滚动时的性能:
arduino 复制代码
new Sortable(list, {
  scroll: true, // 拖拽时自动滚动容器
  scrollSpeed: 20, // 滚动速度
  scrollSensitivity: 30 // 距离容器边缘多少像素时开始滚动
});

六、总结

作为前端程序员,高效解决业务需求是核心目标。SortableJS 凭借其轻量、灵活、易用的特点,成为我处理拖拽排序需求的首选工具。从简单的单列表排序,到复杂的多列表看板,再到框架内的无缝集成,它都能胜任。

欢迎jym点赞关注加评论~

相关推荐
何何____28 分钟前
TypeScript 函数类型兼容性
前端·javascript·typescript
雪芽蓝域zzs38 分钟前
json数据转化成interface属性
前端·javascript·json
鬼手点金1 小时前
ReAct 循环
linux·服务器·前端·javascript·react.js·前端框架
雪芽蓝域zzs2 小时前
Vue导入方法三种方式对比
前端·javascript·vue.js
VimorphTech3 小时前
React 任务轮询,为什么不能只写一个 setInterval?
javascript·typescript
lexiangqicheng4 小时前
Docker的沙箱机制是怎样的?
前端·javascript
LCG米5 小时前
STM32 HardFault故障定位实测:压栈帧寄存器解析、CFSR故障源判读与出错地址回溯
前端·javascript·stm32·单片机·嵌入式
持敬chijing6 小时前
白盒测试与代码审计-JS 数据逆向
javascript·网络·安全·web安全·网络安全·网络攻击模型·安全威胁分析
薛一半6 小时前
Redux实战:组件状态管理全解析
开发语言·前端·javascript·react.js