这个标题名字还真不好取,一般想要这种功能的还真不好搜到这篇内容,只能静待有缘人了。(有好的标题,可以留言,我改还不行么?)
最近公司有一个新的项目需求,要求的是合并单元格,看似也没什么难度,但实际操作起来,确实不好做。(但理解了里面的逻辑,也就几行代码的问题)
然后前几天在一次看群里面聊天的时候,发现其他人也遇到了这样的需求。
要求是这样的,先来张图片
就是这种效果,将红色框部分合并起来,看似很简单的,但是注意,我们的数据是动态的,数据量是非常庞大的。完成后的效果如下
以为我们现在的项目是用vue框架写的,刚开始尝试用vue的思想来解决,发现没法下手。最终还是操作dom来完成的。
首先从后端的数据,是要有一定规范的,其实我们可以把他看成是一种树状结构。但是后端给的数据,其实还是标准的结构,表格一个循环就能解析出来的
我这里的标题数据,跟内容数据是分开来的。
我对后端要求的数据结构是这样的。
标题部分:
TableTitle: ['标题1', '标题2', '标题3','标题1', '标题2', '标题3'],
内容部分:
TableBody: [ { value : [ { title: 't1', val: 'a1', }, { title: 't2', val: 'b1', }, { title: 't3', val: 'c1', }, { title: 't4', val: 'd1', }, { title: 't5', val: 'e1', }, { title: 't6', val: 'f1', }, ] }]
这是前端拿到的数据结构,
接下来是渲染数据,其实就一个循环就可以了(这里用的是基于vue的渲染形式)
<table width="100%" border="0" id="table1"> <thead class="tableTitle"> <tr> <th v-for="item in TableTitle" :key="item">{{item}}</th> </tr> </thead> <tbody> <tr v-for="(items, index) in TableBody" :key="index"> <td v-for="item in items.value" :key="item.title">{{item.val}}</td> </tr> </tbody> </table>
到这里,渲染出来的效果就是最上面的第一张图的效果;
那么如何完成我们想要的合并单元格的目的呢。接下来,就是本篇的核心代码的(其实也就几行)
MergeCells (tableId, startRow, endRow, col) { const tb = document.getElementById(tableId); if (col >= tb.rows[0].cells.length) { return; } if (col == 0) { endRow = tb.rows.length-1; } for (var i = startRow; i < endRow; i++) { if (tb.rows[startRow].cells[col].innerHTML == tb.rows[i + 1].cells[col].innerHTML){ tb.rows[i + 1].cells[col].className = "hide"; tb.rows[startRow].cells[col].rowSpan = (tb.rows[startRow].cells[col].rowSpan | 0) + 1; if(this.colorSet && col == 0) { tb.rows[i].className = "lightColor" tb.rows[i+1].className = "lightColor" } if (i == endRow - 1 && startRow != endRow) { this.MergeCells(tableId, startRow, endRow, col + 1); } } else { this.MergeCells(tableId, startRow, i, col+1); this.colorSet = !this.colorSet; startRow = i + 1; } } },
调用它。只需要执行
this.MergeCells('table1',0,0,0);
其实用过canvas的小伙伴,应该是知道的,canvas是有x,y坐标这样的概念。其实在表格里面,也是有这样的概念,这才是我们操作合并单元格的核心
tb.rows[0].cells[0],表示的就是该表格第一行,第一列的那个单元格。
在这里面,我还遇到一个巨大的坑。(受html合并单元格思想的影响,但我们这使用js操作dom来完成的。)
普通合并单元格是这么玩的。
<table> <tr> <td width= "25% "rowspan="2"> </td> <td width= "25% "> </td> <td width= "25% "> </td> <td width= "25% "> </td> </tr> <tr> <td width= "25% "> </td> <td width= "25% "> </td> <td width= "25% "> </td> </tr> </table>
但是我们在用js操作dom的时候,最好不要remove里面的<td>,不然,就会导致整个表格的 rows cells 位置全部混乱,很难定位到自己想要的单元格。移除掉一种一个单元格,它后面的单元格 rows cells 的坐标位置肯定发生变化
这个坑,我踩了半天
<table> <tr> <td width= "25% "rowspan="2"> </td> <td width= "25% "> </td> <td width= "25% "> </td> <td width= "25% "> </td> </tr> <tr> <td width= "25% " class="hide"> </td> <td width= "25% "> </td> <td width= "25% "> </td> <td width= "25% "> </td> </tr> </table> css部分 .hide{ display: none; }
只需要将他给隐藏掉就好了,这样既不影响占位,也不影响其他单元格的下标。
其实到这里就结束了。
其实在实际操作中,要用到好些功能,比如,单元格加个斑马线的样式,让用户一眼就能区分开。
还有我们的数据量非常庞大,还要做滚动触底加载的功能等等,这些都是很简单的了,就不写了。
来源:https://www.cnblogs.com/chuyunshi/p/9645073.html