绘制表格

之前做页面都没有画表格的经历, 最近工作中有接触到, 刚好补了一下这方面的知识

结构

一个较完整的表格结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<table>
<caption>title</caption>
<thead>
<tr>
<th>head</th>
</tr>
</thead>
<tbody>
<tr>
<td>data</td>
</tr>
</tbody>
</table>

caption是表格的标题

thead里放的是表格的头, 一个头对应一个th

tbody里放的是表格的数据, 一条数据对应一个td

thtd都应该在tr内, 一个tr就是一行

样式

  • 边框

需要同时设置th/td的边框, 同时还要设置table的边框折叠

1
2
3
4
5
6
7
table {
border-collapse: collapse; /*默认为separate 分开*/
}

th, td {
border: 1px solid blue;
}

table的html属性也有border

1
<table border="1"></table>

border属性同时设置了td/th的边框宽度, 但缺点是不能设置边框颜色及边框线条样式, 所以还是使用css来设置边框更好

border-spacing 属性可以设置单元格的边框之间的间距, 但需要border-collapse属性为separate时才生效. 一般较少使用

  • 文字

文字水平居中使用 text-align: center;

文字垂直居中使用 vertical-align: middle; Chrome下默认是垂直居中的

如果限制了单元格的宽度, 此时需要文字换行, 可以使用 word-break: break-all;

如果感觉文字太靠近边框, 可以对td/th设置内衬 padding: 2px 5px;

如果改变标题的位置, 需要对table设置 caption-side: top;caption-side: bottom; Chrome下的默认值是top

  • 跨行与跨列
    跨行需要用到属性rowspan, 跨列需要用到属性colspan, 下面是简单的示例

See the Pen table-with-cross-row-col by levy (@levy9527) on CodePen.

  • 布局算法
1
2
3
table {
table-layout: fixed; /*默认值为automatic*/
}

二者有以下区别:

  1. automatic时, 每一列的宽度是自动计算的, 由该列中最宽的内容决定; fixed时, 每一列的宽度可以用width单独设置
  2. 前者需要需要接受所有内容后, 才能确定表格布局, 耗时较多; 后者在接受到第一行表格内容时, 即可确定表格布局, 耗时较少

实践

  • 多列表格

表格有很多列时, 则宽度会很大, 如果table的父元素是body, 则水平滚动表格时body也会一起动, 尤其是手机端, 体验非常不好

解决方案是, 给table添加套一个父元素, 则可以只滚动表格, 不滚动页面的其他内容

1
2
3
4
<div class="table-container">
<table>
</table>
</div>
1
2
3
4
.table-container {
width: 100%;
overflow: scroll;
}
  • 固定列

表格的前几列在水平滚动时固定, 我的思路是这样的:

  • 把原本一个表格拆分成两个, 一个是拥有固定列的(以下称为固定表格fixedTable), 一个是正常的表格, 可以横向滚动(下面称为滚动表格scrollTable)
  • 固定列其实可以看成表格固定于视窗, 则前一个表格的position应设置为fixed
  • 因为固定表格不在文档流中, 则正常表格应设置positionrelative, 使它可以产生偏移, 使得两个表格拼接看起来像是一个表格

具体看代码

See the Pen YpmWBg by levy (@levy9527) on CodePen.

如果固定列是动态的, 则需要元素生成后取得固定表格的宽度, 再设置滚动表格的偏移, 示例代码如下:

1
2
3
4
// 滚动table设置水平偏移
let width = fixedTable.clientWidth
let left = width - 1 // 以免border重叠
scrollTable.style.left = left+'px' // 一定要记得加上'px'

如果单元格的内容也是动态的, 且单元格限制了宽度, 则文字过多时, 会产生换行, 则滚动表格的表头的高度也会变化, 需要调整固定表格的表头高度

1
2
3
4
5
6
7
// 调整固定table的thead高度
let height = scrollTable.firstChild.clientHeight // 取得thead的高度
let ths = fixedTable.getElementsByTagName('th')

for(let i = 0, len = ths.length; i < len; i++) {
ths[i].style.height = height + 'px'
}

实践表明, 这种方案并不完善, 因为以下两种情况:

  • 表格行过多
    表格有很多行时, 表格的高度超过视窗, 则需要垂直滚动才能看到下面的内容. 但因固定表格positionfixed, 无论怎么滚动, 固定表格下面的行都是看不见的, 因此还需要监听body的滚动事件, 动态修改固定表格的垂直偏移
1
2
3
4
5
6
let top = scrollTable.offsetTop

document.body.onscroll = function (e) {
let y = window.scrollY
fixedTable.style.top = top - y + 'px'
}
  • 移动端横屏显示
    移动端还有横屏显示的需求, 则监听到手机横屏时, 需要重新调整固定表格的偏移. 至于事件, 最简单的是onresize
1
2
3
let top = scrollTable.offsetTop
let y = window.scrollY
fixedTable.style.top = top - y + 'px'

因为每一次滚动都设置一下元素的偏移, 则每一次都会触发reflow, 极其影响性能, 滚动时可明显看出固定表格在上下”漂移”

另外, 在写了css的基础上, 还要写这么多的js代码来应对几种情况, 因此我觉得此方案并非最佳方案, 尤其不推荐在移动端使用

Fork me on GitHub