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是以对象数组做数据源,两个的功能实际上是一样的。

结合altersetDataAtCell方法我们将在下面做一个简单的例子:

// 在索引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');

以上基本上都是很基础的操作,还有很多东西不常用也没有写出来,大家如果需要的可以进入官网查看。

本次分享就到这里了,如果有哪里不清楚的可以留言哦,第一次写博客,还得请各位朋友多多关照(^-^)