handsontable简介
handsontable是一个类似Excel表格编辑器,支持丰富的展现和交互,有多样的单元格类型供配置。核心是由原生JavaScript构建。
除了核心表格渲染(实质就是js操作table,计算元素位置,自定义绑定事件处理),大部分功能以插件提供。可以灵活构建插拔,自定义添加新功能插件。
1.常用属性
1.1 data
:初始化组件表格数据,data可以有两种格式。
第一种是二维数组:
data: [
['日期','销售地点','销售商品','单价','销量'],
['2017-01', '北京', '冰箱', '3399', 530],
['2017-01', '天津', '空调', '4299', 522],
['2017-01', '上海', '洗衣机', '1299', 544],
['2017-01', '广州', '彩电', '4599', 562],
['2017-01', '深圳', '热水器', '1099', 430],
['2017-02', '重庆', '笔记本电脑', '4999', 666],
['2017-02', '厦门', '油烟机', '2899', 438],
['2017-02', '青岛', '饮水机', '899', 620],
['2017-02', '大连', '手机', '1999', 500]
第二种是对象数组:如果使用该方式渲染数据,则不能使用插入列方法:insert_col和remove_col
data: [
{'date': '2017-01', 'place':'北京', 'goods':'冰箱', 'price':3399, '销量':530},
{'date': '2017-01', 'place':'天津', 'goods':'空调', 'price':4299, '销量':522},
{'date': '2017-01', 'place':'上海', 'goods':'洗衣机', 'price':1299, '销量':544},
{'date': '2017-01', 'place':'广州', 'goods':'彩电', 'price':4599, '销量':562},
{'date': '2017-01', 'place':'深圳', 'goods':'热水器', 'price':1099, '销量':430},
{'date': '2017-02', 'place':'重庆', 'goods':'笔记本电脑', 'price':4999, '销量':666},
{'date': '2017-02', 'place':'厦门', 'goods':'油烟机', 'price':2899, '销量':438},
{'date': '2017-02', 'place':'青岛', 'goods':'饮水机', 'price':1099, '销量':620},
{'date': '2017-02', 'place':'大连', 'goods':'手机', 'price':1999, '销量':500}
1.2 colHeaders:显示列头数据(false/true/数组),默认值false,设置true则按A、B、C...显示,还可以自定义数组作为列头
colHeaders: ['日期', '地点', '商品', '单价', '销量']
1.3 rowHeaders:显示行头数据(false/true/数组),默认值false,设置true则按顺序1、2、3...显示,跟colHeaders一样可使用自定义数组作为行头
rowHeaders: [1, 2, 3, 4, 5, 6]
1.4 stretchH:自适应列宽,默认值none,last 将最后一列拉伸到最大,all 将所有列均匀拉伸
1.5 colWidths:设置每一列的宽度,数据可为数字、数字数组,stretchH与colWidths一起使用时,将先适配colWidths,然后在适配stretchH进行列宽拉伸
colWidths: 200 // 所有列宽都为200像素
colWidths: [100, 200, 300, 200, 100]
1.6 className:容器单元格的class属性(htCenter,htLeft,htRight,htJustify,htTop,htMiddle,htBottom),默认值undefined,这些属性将作为容器单元格内容的对齐方式
1.7 cell:指定单元格的某些属性(数组),如下:
cell: [
{row:0, col:0, className: 'htRight htMiddle', editor: false}, // 右对齐垂直居中,只读
{row:1, col:1, className: 'htLeft'} // 左对齐
1.8 contextMenu:是否启用右键菜单(true:启用默认配置,false:禁用右键菜单,数组),默认undefined,或者可用数组自定义那些操作可用
context:Menu: ["row_above", "row_below", "col_left", "col_right", "remove_row", "remove_col", "---------", "undo", "redo", "Read Only", "alignment", "Merge Cells"]
1.9 mergeCells:合并单元格(true允许合并单元格,对象数组),默认值false禁止合并单元格,若使用对象数组,将会合并对象数组中提供的单元格
mergeCells: [
{row:0, col:0, rowspan:5, colspan:1},
{row:5, col:0, rowspan:4, colspan:1}
1.10 startRows:初始行数
1.11 startCols:初始列数
1.12 customBorders:自定义单元格边框,可以用range指定一个范围,或者直接使用row、col指定单元格位置,用top、right、bottom、left分别设置单元格上下左右边框的属性。
customBorders: [
range: {
from: {row: 1, col:1},
to: {row: 3, col:3}
top: {width: 2, color: '#25e825'},
right: {width: 2, color: '#25e825'},
bottom: {width: 2, color: '#25e825'},
left: {width: 2, color: '#25e825'}
row: 2,
col: 2,
top: {width: 2, color: '#7687c5'},
right: {width: 2, color: '#7687c5'},
bottom: {width: 2, color: '#7687c5'},
left: {width: 2, color: '#7687c5'}
2. 核心方法
接下来将写一个简单的例子,并使用一些方法进行讲解,下面先附上基础代码
<!DOCTYPE html>
<title>handsontable demo</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/handsontable.full.css">
<script src="js/jquery.js"></script>
<script src="js/handsontable.full.js"></script>
</head>
<div id="example"></div>
<script>
var data = [
['2017-01', '北京', '冰箱', '3399', 530],
['2017-01', '天津', '空调', '4299', 522],
['2017-01', '上海', '洗衣机', '1299', 544],
['2017-01', '广州', '彩电', '4599', 562],
['2017-01', '深圳', '热水器', '1099', 430],
['2017-02', '重庆', '笔记本电脑', '4999', 666],
['2017-02', '厦门', '油烟机', '2899', 438],
['2017-02', '青岛', '饮水机', '899', 620],
['2017-02', '大连', '手机', '1999', 500]
var hot = new Handsontable(document.getElementById('example'),{
data: data,
colHeaders: ['日期', '地点', '商品', '单价', '销量'], // 使用自定义列头
rowHeaders: true,
editor: false, // 禁用所有单元格编辑
colWidths: 150, // 设置所有列宽为150像素
contextMenu: false, // 禁用右键菜单
mergeCells: [
{row:0, col:0, rowspan:5, colspan:1},
{row:5, col:0, rowspan:4, colspan:1}
cell: [
{row: 0, col: 0, className: "htCenter htMiddle"}, // 设置下标为0,0的单元格样式 水平居中、垂直居中
{row: 5, col: 0, className: "htCenter htMiddle"}
</script>
</body>
</html>
先附上页面
上面我们已经将右键菜单禁用掉,现在将使用js对容器插入行列,并初始化插入列的数据
2.1 alter(action, index, amount, source, keepEmptyRows):alter方法用于改变表格结构,即插入或删除行列数据。
action:可用改变表格结构操作insert_row、insert_col、remove_row、remove_col
index:行列索引值,从0开始,insert操作将插入到该索引值的前一行/列
amount(可选,默认1):将要插入/删除的行列数
source(可选):行或列对象
keepEmptyRows(可选):防止删除空行,true/false
2.2 setDataAtCell(row, col, value, source):设置某个单元格的数据。
row:行号索引
col:列号索引
value:将要设置的单元格数据
source(可选):字符串标识中描述这一变化将如何改变数组(用于onAfterChange或onBeforeChange回调)
也可使用数组参数,如下:
hot.setDataAtCell([
[9, 0, 'a'], // row col value
[9, 1, 'b']
2.3 setDataAtRowProp(row, prop, value, source):设置某个单元格的数据,与setDataAtCell不同的是数据源格式,setDataAtCell是使用二维数组做数据源,setDataAtRowProp是以对象数组做数据源,两个的功能实际上是一样的。
结合alter与setDataAtCell方法我们将在下面做一个简单的例子:
// 在索引9行之前插入2个空行
hot.alter('insert_row', 9, 2);
// 对2个空行进行填充数据
hot.setDataAtCell([
[9, 0, '2017-03'],
[9, 1, '武汉'],
[9, 2, '路由器'],
[9, 3, 149],
[9, 4, 692],
[10, 0, '2017-03'],
[10, 1, '杭州'],
[10, 2, '移动电源'],
[10, 3, 99],
[10, 4, 785]
hot.alter('remove_row', 6); // 移除索引为6的行
由于我们刚开始设置了合并列,现在删除第6行后,后面几行数据将会追加上去,所以新添加的一行(日期列)会被合并掉
2.4 clear():清空表格数据
2.5 colToProp(col):返回与给定列索引相对应的属性名。如果数据源是二维数组,将返回列索引。
2.6 countCols():返回表格总列数。
countRows():返回表格总行数。
2.7 countRenderedCols():统计并返回被渲染的列数
countRenderedRows():统计并返回被渲染的行数
2.8 countVisibleCols():统计并返回可见的列数,当返回-1时,表格不可见
countVisibleRows():统计并返回可见的行数,当返回-1时,表格不可见
2.9 deselectCell():取消当前选中的单元格
2.10 getCell(row, col, topmost):获取单元格td元素,topmost为true它将从top最多覆盖返回TD元素
2.11 getCellMeta(row, col):返回指定行和坐标的单元格属性对象
2.12 getCellRenderer(row, col):返回指定单元格的渲染函数
2.13 getColHeader(col):根据列索引获取列头名称
getRowHeader(row):根据行索引获取行头名称
2.14 getData(r, c, r2, c2):返回某个范围内的数据
2.15 getDataAtCell(row, col):返回指定单元格的数据
2.16 getDataAtCol(col)/getDataAtProp(prop):返回某一列的数据
getDataAtRow(row):返回某一行的数据
2.17 getValue():返回当前选择单元格的数据
2.18 loadData():动态加载本地数据,此方法将会覆盖原有的数据
2.19 render():重新渲染表格
2.20 setCellMeta(row, col, key, val):设置单元格属性
方法太多,这边就没有一一列举出来,更多详细的方法介绍可以到官网上去看,下面我们还是通过几个例子来说明吧。
3. 案例解析
使用案例我们将结合一些事件操作,在这些事件触发时我们将对单元格做一些样式渲染。
3.1 选择完单元格后进行渲染
首先我们要知道单元格选择完成后将触发事件afterSelectionEnd,所以我们要在这个事件完成之后干点小事。
我们先在这里加点样式,后面将通过改变单元格class属性进行样式渲染
.selected-td{
background: #8ef98e;
在这里我们先给网格对象在单元格选择完之后添加一个触发事件,执行一个函数打印出参数列表
hot.addHook('afterSelectionEnd', function(){
console.log(arguments);
由此可见,我们得到的参数有哪些,我们现在能用到的最主要就是前面四个索引值,然后对该事件进行修改如下:
hot.addHook('afterSelectionEnd', function(r, c, r2, c2){
// 清除所有扩展的样式
for(var i = 0; i < hot.countRows(); i++){
for(var j = 0; j < hot.countCols(); j++){
// 在这里只需移除扩展样式selected-td就行,保留表格原有样式
var className = hot.getCellMeta(i, j).className;
if(className && className.lastIndexOf('selected-td') > 0){
var index = className.indexOf('selected-td');
hot.setCellMeta(i, j, 'className', className.substring(0, index) + className.substring(index+1, className.length));
// 给选择范围的单元格添加样式
for(var i = r; i <= r2; i++){
for(var j = c; j <= c2; j++){
hot.setCellMeta(i, j, 'className', hot.getCellMeta(i, j).className + ' selected-td');
// 重新渲染网格
hot.render();
这种方法去渲染网格的话,它的样式不会被改变,之前在做一个项目的时候,用到了类似这样的东西,那时候还不知道可以给网格渲染样式,只会用jquery动态给表格里的某个单元格添加样式,但是这样做的话,样式并没有被加载到单元格的属性对象中,由于handsontable页面数据是实时加载的,所以当表格带有滚动条的时候,一旦滚动由外部添加的样式马上会被清除掉,在这里是有setCellMeta方法进行设置就不会出现那种问题了。通过方法hot.getCellMeta(r, c).className我们就可以直接看到某个单元格被渲染的class样式。
3.2 单击单元格事件
跟3.1一样,我们要给表格对象添加操作事件,就得先找出事件触发点在哪,由此我们得到afterOnCellMouseDown 事件,在这个事件之后我们还是一样会做一些操作,这里如果不知道有什么参数,还是可以将arguments打印出来查看,在这就不重复说了。
为了减少代码冗余,我们将上面清除扩展样式的代码包装一下,封装到hot对象里面
* 移除所有单元格的某个样式
* @param {[type]} classVal 要移除的样式值
hot.removeClass = function(classVal){
for(var i = 0; i < hot.countRows(); i++){
for(var j = 0; j < hot.countCols(); j++){
var className = hot.getCellMeta(i, j).className;
if(className && className.lastIndexOf(classVal) > 0){
var index = className.indexOf(classVal);
hot.setCellMeta(i, j, 'className', className.substring(0, index) + className.substring(index+1, className.length));
所以我们这次就可以简化方法了,如下:
// 鼠标按下时触发
hot.addHook('afterOnCellMouseDown', function(event, coords){
this.removeClass('clk-td');
hot.setCellMeta(coords.row, coords.col, 'className', hot.getCellMeta(coords.row, coords.col).className + ' clk-td');
以上基本上都是很基础的操作,还有很多东西不常用也没有写出来,大家如果需要的可以进入官网查看。
本次分享就到这里了,如果有哪里不清楚的可以留言哦,第一次写博客,还得请各位朋友多多关照(^-^)