这个标题名字还真不好取,一般想要这种功能的还真不好搜到这篇内容,只能静待有缘人了。(有好的标题,可以留言,我改还不行么?)
最近公司有一个新的项目需求,要求的是合并单元格,看似也没什么难度,但实际操作起来,确实不好做。(但理解了里面的逻辑,也就几行代码的问题)
然后前几天在一次看群里面聊天的时候,发现其他人也遇到了这样的需求。
要求是这样的,先来张图片

就是这种效果,将红色框部分合并起来,看似很简单的,但是注意,我们的数据是动态的,数据量是非常庞大的。完成后的效果如下

以为我们现在的项目是用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