- 前端
- UI组件
【免费下载链接】ce
Jspreadsheet is a lightweight JavaScript data grid component for creating interactive data grids with advanced spreadsheet controls.
本指南围绕 Jspreadsheet(v2 时代名为 Jexcel)的事件机制,以官方示例“在电子表格每次变更时实时更新 Highcharts 图表”为核心,讲解 onchange 等事件钩子的用法、回调参数含义,并结合仓库源码剖析事件从单元格编辑到回调触发的完整链路。读完本文,你将掌握如何在 Jspreadsheet 中监听数据变更、编写自定义变更处理器,并将其应用于图表联动等实时数据同步场景。
示例背景:追踪电子表格的每次变更
在数据密集型应用中,电子表格常常只是数据采集的入口,真正的价值在于变更之后引发的联动动作——例如更新图表、重算报表、同步后端。Jspreadsheet 提供了一整套事件钩子(event handlers),你可以在初始化配置中直接声明它们,从而在特定操作发生时执行自己的逻辑。
本示例的场景是:一张记录四个城市逐月平均气温的表格,用户在单元格中修改数值后,右侧的 Highcharts 折线图必须同步刷新对应月份的数据点。整个联动的核心只有一行配置——onchange: update,其中update是开发者自定义的回调函数。
完整示例代码
以下是官方示例的完整实现,它同时引用了 jQuery、jexcel 与 Highcharts 三个库:
<html> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://code.highcharts.com/highcharts.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css" type="text/css" /> <div id="my"></div> <script> let data = [ ['Tokyo', 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6], ['New York', -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5], ['Berlin', -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0], ['London', 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8], ]; let update = function (obj, cel, val) { // Get the cell position x, y let id = $(cel).prop('id').split('-'); // If the related series does not exists create a new one if (! chart.series[id[1]]) { // Create a new series row let row = []; for (i = 1; i < data[id[1]].length; i++) { row.push(parseFloat(data[id[1]][i])); } // Append new series to the chart chart.addSeries({ name:data[id[1]][0], data:row }); } else { // Update the value from the chart chart.series[id[1]].data[id[0]-1].update({y:parseFloat(val)}); } } $('#my').jexcel({ data:data, onchange:update, colHeaders: [ 'Country' ], colWidths: [ 300 ] }); // Kepp it global let chart = null; $(function () { chart = Highcharts.chart('container', { title: { text: 'Monthly Average Temperature', x: -20 //center }, subtitle: { text: 'Source: WorldClimate.com', x: -20 }, xAxis: { categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] }, yAxis: { title: { text: 'Temperature (°C)' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { valueSuffix: '°C' }, legend: { layout: 'vertical', align: 'right', verticalAlign: 'middle', borderWidth: 0 }, series: [{ name: 'Tokyo', data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6] }, { name: 'New York', data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5] }, { name: 'Berlin', data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0] }, { name: 'London', data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8] }] }); }); </script> </html>代码拆解
- 数据准备:
data是一个二维数组,每行首元素是城市名(对应图表 series 的 name),后续 12 个元素是该城市 1 至 12 月的平均气温(对应图表 series 的 data)。 - 变更处理器
update:- 回调签名
(obj, cel, val)分别代表表格实例、被修改的单元格元素、新值; $(cel).prop('id').split('-')从单元格 DOM 的 id 中解析出坐标,例如row-2-5会得到['row', '2', '5'],其中id[1]是行号、id[0]是列号(注意 v2 示例中该 id 的拼接约定);- 若该行对应的 series 尚不存在,则用原始
data构造并chart.addSeries追加;否则直接调用 Highcharts 的update({y: parseFloat(val)})更新数据点,parseFloat保证字符串输入被转换为数值。
- 回调签名
- 初始化表格:
$('#my').jexcel({ data, onchange: update, colHeaders: ['Country'], colWidths: [300] })。第一列是城市名,colWidths: [300]只设置首列宽度,其余列使用默认宽度。 - 初始化图表:
chart通过$(function(){ ... })在 DOM 就绪后创建,四个初始 series 与data一一对应。示例注释中的 “Kepp it global” 提示了chart需要全局可访问,以便在update中引用。
注意:此示例基于 v2(Jexcel 2.1.0)的 API。表格初始化后可同时通过
colHeaders、colWidths快速定制列头与列宽;而onchange之外的事件钩子(如oninsertrow、ondeleterow)在 v2 事件文档 中有完整清单,完整的初始化参数与方法速查见 v2 快速参考。
onchange 事件钩子的使用要点
onchange在单元格值被修改并写回数据之后触发。它与onbeforechange、onafterchange共同构成“变更前—变更后—批量收尾”的三段式事件链,三者在 v2 文档中的定位如下:
| 事件 | 触发时机 |
|---|---|
| onbeforechange | 单元格值被修改之前,可返回新值以覆盖写入值 |
| onchange | 单元格值被修改之后 |
| onafterchange | 一批变更事件全部执行完之后 |
回调函数通过初始化参数声明:
$('#my').jexcel({ data: data, onchange: handler, // 每次单元格变更后触发 onbeforechange: beforeHandler, // 变更前触发 onafterchange: afterHandler, // 一批变更结束后触发 });v2 官方示例中的基础用法(见 v2 事件文档)展示了如何同时使用多个事件:
handler = function(obj, cell, val) { console.log('My table id: ' + $(obj).prop('id')); console.log('Cell changed: ' + $(cell).prop('id')); console.log('Value: ' + val); }; insertrow = function(obj) { alert('new row added on table: ' + $(obj).prop('id')); } deleterow = function(obj) { alert('row excluded on table: ' + $(obj).prop('id')); } data = [ ['Mazda', 2001, 2000, '2006-01-01 00:00:00'], ['Peugeot', 2010, 5000, '2005-01-01 00:00:00'], ['Honda Fit', 2009, 3000, '2004-01-01 00:00:00'], ['Honda CRV', 2010, 6000, '2003-01-01 00:00:00'], ]; $('#my').jexcel({ data:data, colHeaders: ['Model', 'Date', 'Price', 'Date'], colWidths: [ 300, 80, 100, 100 ], onchange:handler, oninsertrow:insertrow, ondeleterow:deleterow, columns: [ { type: 'text' }, { type: 'numeric' }, { type: 'numeric' }, { type: 'calendar', options: { format:'DD/MM/YYYY HH24:MI', time:1 } }, ] });回调参数
不同事件回调收到的参数并不完全一致,需要分别记忆:
| 事件 | 回调参数 |
|---|---|
| onbeforechange | (obj, cell, x, y, value) |
| onchange | (obj, cell, x, y, value) |
| oninsertrow / ondeleterow | (obj) |
| oninsertcolumn / ondeletecolumn | (obj) |
| onselection | (obj, x1, y1, x2, y2, origin) |
| onload | (obj) |
其中obj是表格实例,cell是被操作的单元格 DOM 元素,value是写入的新值。相比直接解析 DOM id,更稳妥的定位方式是使用源码中x、y坐标参数,这也是 v2 回调签名的标准约定。
从源码看变更事件的触发链路
Jspreadsheet 的事件机制在源码中有着清晰的实现,理解这条链路有助于你在复杂场景中预测回调的触发顺序与副作用。
onbeforechange:写入前的最后一道关卡
在 internal.js 的updateCell流程中,值在真正写入数据模型之前会先派发onbeforechange:
// On change const val = dispatch.call(obj, 'onbeforechange', obj, obj.records[y][x].element, x, y, value); // If you return something this will overwrite the value if (val != undefined) { value = val; }这段代码揭示了一个实用技巧:在 onbeforechange 回调中返回值,即可改写最终写入单元格的内容。例如可以对用户输入做格式化、校验或单位换算,这比在 onchange 中事后补救更高效。
onchange:变更落库后的通知
同一函数 internal.js 在数据与单元格 DOM 均已更新后派发onchange:
dispatch.call(obj, 'onchange', obj, obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null, x, y, value, record.oldValue);注意 v5 源码中 onchange 实际携带了 7 个参数(比 v2 文档示例多出x、y、oldValue),这为回调提供了完整的变更上下文:除了新值,还可以拿到变更前的旧值,用于实现变更审计、差异对比等功能。
onafterchanges:批量操作的统一收尾
当一次操作涉及多个单元格(如粘贴、填充、批量赋值)时,data.js 会在所有单元格更新完毕后统一派发onafterchanges,携带一个包含每条变更x、y、value、oldValue的数组。它在 v5 中的事件名为onafterchanges(复数形式),与 v2 文档中的onafterchange是同一能力的演进。
批量场景下优先使用onafterchanges而非onchange,可以避免回调被触发 N 次,显著降低联动逻辑(如保存到后端、刷新图表)的开销。
事件派发与全局钩子
所有事件最终都经过 dispatch.js 统一派发。它按顺序执行三类监听者:
- 全局钩子
onevent(若配置); - 具体事件的命名回调,如
config.onchange; - 所有插件的
onevent钩子。
const dispatch = function (event) { const obj = this; // Dispatch events if (!spreadsheet.ignoreEvents) { // Call global event if (typeof spreadsheet.config.onevent == 'function') { ret = spreadsheet.config.onevent.apply(this, arguments); } // Call specific events if (typeof spreadsheet.config[event] == 'function') { ret = spreadsheet.config[event].apply(this, Array.prototype.slice.call(arguments, 1)); } // Plugins if (typeof spreadsheet.plugins === 'object') { /* ... */ } } return ret; };这意味着你既可以为单个表格注册具体事件回调,也可以通过onevent实现“一切事件统一入口”的全局监听——后者非常适合日志记录、埋点统计等横切需求。当ignoreEvents被置为true(例如执行undo/redo重放历史时),事件派发会被整体跳过,这是 history.js 中的设计:重放历史不应再次触发业务回调,避免联动逻辑产生循环更新。
进阶实战:从示例到生产级变更追踪
官方示例已经展示了事件钩子的基本联动能力。在此基础上,结合源码能力可以扩展出更贴近生产的方案。
方案一:使用坐标参数替代 DOM id 解析
官方示例通过$(cel).prop('id').split('-')解析坐标,这种方式依赖单元格 id 的命名约定。更稳健的做法是直接使用回调中的x、y参数(v2 的onchange前三个参数为 obj、cell、val,坐标可从$(cel)的 data 属性获取,或升级到 v5 直接使用第 3、4 个参数):
let update = function (obj, cell, val) { // 用>赞- 前端
- UI组件
【免费下载链接】ce
Jspreadsheet is a lightweight JavaScript data grid component for creating interactive data grids with advanced spreadsheet controls.
相关推荐
Jspreadsheet 事件系统完全指南:事件清单、分发机制与图表联动实战
Jspreadsheet 事件系统完全指南:事件清单、分发机制与图表联动实战 Jspreadsheet 是一套轻量级的 JavaScript 数据表格组件,其事
前端UI组件如何实现uPlot多图表联动:掌握高效数据可视化事件通信技巧
如何实现uPlot多图表联动:掌握高效数据可视化事件通信技巧 uPlot作为一款轻量级高性能图表库,在处理时间序列数据可视化方面表现卓越。本文将详细介绍如何利用
前端图表库数据可视化banner4cj事件回调与联动指南:onChange/onGestureSwipe驱动页面数据实时更新
banner4cj事件回调与联动指南:onChange/onGestureSwipe驱动页面数据实时更新 banner4cj 是一个用仓颉语言编写的鸿蒙系统广告
OpenHarmonyUI组件移动开发