- 前端
- UI组件
【免费下载链接】ce
Jspreadsheet is a lightweight JavaScript data grid component for creating interactive data grids with advanced spreadsheet controls.
本文是 Jspreadsheet CE(轻量级 JavaScript 数据网格组件)列拖拽功能的实战指南。你将掌握如何在原生 JavaScript、React、Vue 三种环境中开启columnDrag,理解拖拽起始、目标定位、落点判定与moveColumn底层调用链的完整实现,并学会如何结合合并单元格、右键菜单与onmovecolumn事件安全地使用这一交互能力。
一、功能概览:默认行为与配置开关
Jspreadsheet CE 的列拖拽允许用户用鼠标按住表头并左右拖动,从而改变整列(表头、单元格数据、列宽、脚注等)的位置。该交互能力的开关由工作表级配置项columnDrag: boolean控制:
| 配置项 | 说明 | 默认值 |
|---|---|---|
columnDrag: boolean | 是否允许通过拖拽改变列位置 | 见下文版本差异说明 |
需要特别注意的是版本间的默认值差异:
- 在 v4 及更早版本中,列拖拽默认是禁用的,必须显式设置
columnDrag: true才能开启(参见 v4 示例 的说明); - 在 v5(当前 CE 主版本)中,该属性的默认值已调整为
true,这一变更记录在 升级指南 中; - 官方 列配置文档 同样标注该属性默认值为
true。
因此,本文关联文档中“默认禁用、需显式开启”的表述针对的是 v4 行为;如果你升级到 v5,即便不写columnDrag,列拖拽也会默认可用。在下面的示例中我们仍统一显式传入columnDrag: true,以便在任何版本中都能得到确定的行为。
二、原生 JavaScript(HTML)示例
在纯浏览器环境中,通过jspreadsheet()函数初始化即可。核心是在某个worksheets条目(即工作表)内设置columnDrag: true:
<html> <script src="https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js"></script> <script src="https://jsuites.net/v5/jsuites.js"></script> <link rel="stylesheet" href="https://jsuites.net/v5/jsuites.css" type="text/css" /> <link rel="stylesheet" href="https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css" type="text/css" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" /> <div id="spreadsheet"></div> <script> jspreadsheet(document.getElementById('spreadsheet'), { worksheets: [{ data: [ ['BR', 'Cheese', 1], ['CA', 'Apples', 0], ['US', 'Carrots', 1], ['GB', 'Oranges', 0], ], columns: [ { type: 'autocomplete', title: 'Country', width: '300', url: '/jspreadsheet/countries.json' }, { type: 'dropdown', title: 'Food', width: '150', source: ['Apples','Bananas','Carrots','Oranges','Cheese'] }, { type: 'checkbox', title: 'Stock', width:'100' }, ], columnDrag: true, }] }); </script> </html>示例中的三个列类型都支持拖拽:autocomplete(国家列,通过url异步加载候选项)、dropdown(食品列,通过source提供静态选项)、checkbox(库存列)。拖拽功能与列类型无关,任何类型的列都可以被拖动。
三、React 示例
在 React 中使用官方封装组件@jspreadsheet-ce/react,将columnDrag作为属性传给<Worksheet>即可:
import React, { useRef } from "react"; import { Spreadsheet, Worksheet } from "@jspreadsheet-ce/react"; import "jsuites/dist/jsuites.css"; import "jspreadsheet-ce/dist/jspreadsheet.css"; export default function App() { // Spreadsheet array of worksheets const spreadsheet = useRef(); // Tabs const data = [ ['BR', 'Cheese', 1], ['CA', 'Apples', 0], ['US', 'Carrots', 1], ['GB', 'Oranges', 0], ]; const columns = [ { type: 'autocomplete', title: 'Country', width: '300', url: '/jspreadsheet/countries.json' }, { type: 'dropdown', title: 'Food', width: '150', source: ['Apples','Bananas','Carrots','Oranges','Cheese'] }, { type: 'checkbox', title: 'Stock', width:'100' }, ]; return ( <Spreadsheet ref={spreadsheet}> <Worksheet data={data} columns={columns} columnDrag={true} /> </Spreadsheet> ); }四、Vue 示例
在 Vue 3 中使用@jspreadsheet-ce/vue时,同样把columnDrag绑定到<Worksheet>组件上。注意data、columns与columnDrag在模板中都以属性形式传入:
<template> <Spreadsheet ref="spreadsheet"> <Worksheet :data="data" :columns="columns" :columnDrag="true" /> </Spreadsheet> </template> <script> import { ref } from 'vue'; import { Spreadsheet, Worksheet } from "@jspreadsheet-ce/vue"; import "jsuites/dist/jsuites.css"; import "jspreadsheet-ce/dist/jspreadsheet.css"; export default { components: { Spreadsheet, Worksheet }, setup() { // Spreadsheet reference const spreadsheet = ref(null); // Data for the worksheet const data = ref([ ['BR', 'Cheese', 1], ['CA', 'Apples', 0], ['US', 'Carrots', 1], ['GB', 'Oranges', 0], ]); // Columns definition for the worksheet const columns = ref([ { type: 'autocomplete', title: 'Country', width: '300', url: '/jspreadsheet/countries.json' }, { type: 'dropdown', title: 'Food', width: '150', source: ['Apples','Bananas','Carrots','Oranges','Cheese'] }, { type: 'checkbox', title: 'Stock', width: '100' }, ]); return { spreadsheet, data, columns }; } } </script>五、源码级原理:从按下表头到列移动完成
仅仅知道“打开开关”还不够,理解底层实现能帮你预判各种边界行为(例如与合并单元格、历史记录、表格公式引用的交互)。Jspreadsheet CE 的列拖拽在 src/utils/events.js 和 src/utils/columns.js 中实现,整体分为四个阶段。
1. 启动阶段:标记表头容器为可拖拽
工作表初始化时,worksheets.js 会根据配置给表头容器追加样式类:
if (obj.options.columnDrag != false) { obj.thead.classList.add('draggable'); }draggable类一方面用于 CSS 视觉提示,另一方面在mousemove的悬停检测中用于决定是否显示move光标(见 events.js)。
2. 起始判定:鼠标按住表头底部边缘
在mousedown处理器中(events.js),组件会先判断按下的位置是否满足拖拽触发条件——表头单元格底部 6 像素区域内:
} else if (libraryBase.jspreadsheet.current.options.columnDrag != false && info.height - e.offsetY < 6) { if (isColMerged.call(libraryBase.jspreadsheet.current, columnId).length) { console.error('Jspreadsheet: This column is part of a merged cell.'); } else { // Reset selection libraryBase.jspreadsheet.current.resetSelection(); // Drag helper libraryBase.jspreadsheet.current.dragging = { element: e.target, column: columnId, destination: columnId, }; // Border indication libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging'); ... } }这里有三个值得注意的细节:
- 触发区域只有表头底部 6px 高,避免与列宽调整(右侧 6px 区域触发
columnResize,见同文件第 230 行)和表头单击选中/重命名(第 273-288 行,单击后延时 800ms 进入setHeader重命名)冲突; - 若被拖动的列属于合并单元格(
isColMerged命中),组件会直接向控制台输出错误Jspreadsheet: This column is part of a merged cell.并拒绝开启拖拽; - 拖拽开始时
dragging助手对象记录column(源列)与destination(目标列,初始等于源列),同时给表头和数据单元格添加dragging类做视觉高亮。
3. 拖拽过程:实时计算目标列
在mousemove处理器中(events.js),组件根据鼠标在目标表头内的横向位置计算落点:
if (e.target.clientWidth / 2 > e.offsetX) { // 鼠标位于目标列左半侧 if (libraryBase.jspreadsheet.current.dragging.column < columnId) { libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) - 1; } else { libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId); } libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-left'); } else { // 鼠标位于目标列右半侧 if (libraryBase.jspreadsheet.current.dragging.column < columnId) { libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId); } else { libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) + 1; } libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-right'); }规则可概括为:以被掠过表头的中线为分界,鼠标在左半侧则目标列号向源列方向收敛(-1),在右半侧则向远离方向推进(+1);同时通过dragging-left/dragging-right类给出插入位置的视觉指示。这里同样会对合并单元格做拦截检查。
4. 落点执行:moveColumn的完整副作用链
鼠标松开时(mouseup处理器,events.js),若源列与目标列不同,组件调用moveColumn(源列号, 目标列号)。真正完成移动的是 columns.js 中的moveColumn函数,它依次执行:
- 合并单元格保护:若工作表中存在合并单元格且拖拽涉及合并列,弹出确认框
This action will destroy any existing merged cells. Are you sure?,用户取消则返回false并中止(第 306-321 行); - DOM 重排:分别对表头容器、列宽容器以及每一行的数据单元格执行
insertBefore,把源列节点插入到目标位置(第 326-340 行); - 内部数组同步:同步
options.columns、headers、cols、options.data[j]、records[j]五个数组,并重写受影响范围内每个单元格的x坐标(第 342-362 行); - 脚注同步:若配置了
footers,脚注数组同步移动(第 365-369 行); - 历史记录:写入
{ action: 'moveColumn', oldValue: o, newValue: d }到历史栈,使 Ctrl+Z 撤销可以还原本次移动(第 372-376 行); - 公式引用更新:调用
updateTableReferences修正表格中公式对移动后列位置的引用(第 379 行); - 事件派发:触发
onmovecolumn回调(第 382 行)。
5. 关联事件:onmovecolumn
moveColumn的最后一步会派发onmovecolumn(instance, oldColumn, newColumn, numOfColumnsMoved)事件。在 v5 中该事件新增了第四个参数表示一次移动涉及的列数量,见 升级指南 的说明。你可以在工作表配置中监听它,例如在列被拖动后执行额外的数据同步或界面更新:
jspreadsheet(document.getElementById('spreadsheet'), { worksheets: [{ data: [['BR', 'Cheese', 1], ['CA', 'Apples', 0]], columnDrag: true, onmovecolumn: function (instance, oldColumn, newColumn) { console.log('列已从 ' + oldColumn + ' 移动到 ' + newColumn); } }] });六、与相邻交互能力的协同与取舍
列拖拽并非孤立功能,它与表头区域的多个交互共享同一个鼠标事件处理入口(events.js),使用时需留意以下协同关系:
- 列宽调整(
columnResize):表头右侧 6px 内按下触发缩放,表头底部 6px 内按下触发拖拽,两者互不干扰; - 列排序(
columnSorting):默认开启,右键表头菜单提供“升序/降序”排序(见 events.js),拖拽改变的是列序,排序改变的是行序,二者用途不同; - 列重命名(
allowRenameColumn):单击表头 800ms 后进入重命名模式,拖拽不会误触重命名; - 合并单元格:涉及合并列的拖拽会被拦截或需用户确认,这是为了防止破坏合并区域的结构;
- 撤销重做(history):列移动会被记录进历史栈,可通过撤销快捷键恢复原位置。
若你需要完全禁止拖拽,例如在只读报表场景下,显式设置columnDrag: false即可——源码中的判定都是!= false,因此false是唯一能关闭该能力的取值(undefined、null等都会被当作开启处理)。
七、小结与延伸阅读
- 配置要点:
columnDrag是工作表级配置;v4 默认关闭,v5 默认开启;显式传值可以规避版本差异。 - 三种接入方式:原生 HTML 在
worksheets[i]中配置,React/Vue 作为<Worksheet>的属性传入。 - 交互细节:拖拽从表头底部边缘触发;合并列会被拦截;移动后会同步 DOM、数据、脚注、历史与公式引用,并触发
onmovecolumn事件。
想进一步深入列相关能力,可继续阅读:
- 列配置总览(columns.md):
columnDrag、columnResize、columnSorting、allowRenameColumn等完整属性表及moveColumn/insertColumn/deleteColumn方法说明; - v4 列拖拽示例:了解旧版 API(顶层
data/columns,而非worksheets包裹)的写法差异; - v4 到 v5 升级指南:查看
columnDrag默认值变更及onmovecolumn参数更新的完整记录; - 合并单元格文档:其中多个示例展示了与
columnDrag同时配置的典型场景。
- 前端
- UI组件
【免费下载链接】ce
Jspreadsheet is a lightweight JavaScript data grid component for creating interactive data grids with advanced spreadsheet controls.
相关推荐
Granite Guardian 3.0-2B核心功能解析:从风险检测到幻觉识别
Granite Guardian 3.0 2B核心功能解析:从风险检测到幻觉识别 Granite Guardian 3.0 2B是一款基于Granite架构的轻
前端UI组件TanStack Table Ember 列排序(Column Ordering)完整指南:从状态配置到拖拽重排的实现原理
TanStack Table Ember 列排序(Column Ordering)完整指南:从状态配置到拖拽重排的实现原理 导读 本文基于 @tanstack/
前端UI组件react-dnd useDragLayer Hook 完全指南:从自定义拖拽层到源码级原理
react dnd useDragLayer Hook 完全指南:从自定义拖拽层到源码级原理 useDragLayer 是 react dnd Hooks AP
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考