本文正在参加「金石计划」
起因
在使用 antd 的组件 Table,使用嵌套表格时,希望能做到内外表格宽度能够对齐,于是设想将内外表格的宽度保持一致,结果发现对于 table 元素而言,设置单元格的宽度并不容易。
这和今天要说的table-layout
属性有什么联系呢?
table-layout 表格布局算法
可以看到表格布局主要就两种:
- auto 自动表格布局
- fixed 固定表格布局
table-layout 默认值就是auto
,也就是自动布局,并且只对table
和inline-table
元素起作用。
布局会影响表格中单元格宽度的渲染,当表格按照指定的布局方式渲染以后,只有了解其中的布局原理,才能知道如何更细粒度的控制表格布局。
以下面这个例子详细了解这个属性:
<html>
<head>
<title>Table</title>
<style>
table {
border-collapse: collapse;
}
tr td,
tr th {
border: 1px solid #000;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
<th>sex</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mary</td>
<td>20</td>
<td>male</td>
</tr>
<tr>
<td>June</td>
<td>15</td>
<td>male</td>
</tr>
<tr>
<td>Seven</td>
<td>24</td>
<td>female</td>
</tr>
</tbody>
</table>
</body>
</html>
自动表格布局
首先自动表格布局table-layout: auto
会根据浏览器用户代理,自行实现布局,此时表格的宽度就是所有列宽的和,而且为了确定最终的布局,用户代理会多次访问表格的内容,所以auto
布局的效率是比较低的。
auto
布局的表格默认长这样,
乍一看,单元格的宽度好像是由其内容所决定的。可以尝试将某个单元格的内容增加:
第一列单元格的宽度也变大了,是不是就说明了auto
布局下,单元格的宽度由其内容决定的呢?
现在将第一行第一列的单元格设置一个宽度:
tr:first-child td:first-child {
width: 50px;
}
这个宽度并没有起作用。但是如果将宽度增加到超过 显示的194px
,则该宽度设置就生效了:
所以实际上,auto
布局的宽度是由单元格的最小内容宽度
所决定,所谓最小内容宽度
就是width
值为auto
时的宽度,如果设置的width
大于最小内容宽度,则单元格的宽度才会由width
值决定。
那有没有办法强制设置一个小于最小内容宽度
的宽度呢?
有。可以使用max-width
来设置,还是上面的代码,修改一下:
tr:first-child td:first-child {
max-width: 50px;
}
成功将第一个单元格宽度设置成了50像素,并且发生了溢出。但是如果将max-width
属性设置大于上面说的最小内容宽度
,那么表格宽度始终会表现为最小内容宽度
。
tr:first-child td:first-child {
max-width: 200px;
}
和上面是一样的。同理,min-width
属性应该也是一样的效果:当min-width
的值大于194.99像素时,宽度才会被设置为min-width
的值,否则就还是最小内容宽度
。
回到上面溢出的情况,对于溢出,通常的做法要么就是裁剪掉溢出的部分,要么进行换行。可能你会想到white-space
属性,但是这里却无法换行,因为white-space
属性是针对存在空白符的情况进行换行,这里连续字符换行可以使用work-break: break-all
,这也是 antd 中 Table 组件换行配置的属性。
width
属性作为盒模型的一部分,padding
、border
同样也是盒模型的一部分,但是border
却受单元格宽度的约束:
tr:first-child td:first-child {
padding: 0 50px;
border-right: 20px solid red;
}
border
虽然发生了“溢出
”,但是单元格的宽度还是并没有受到其影响,并且这里border
的溢出也无法使用overflow
来截掉。
最后,如果给 table 元素设置具体的宽度,则宽度会被均分给每一列:
table {
width: 300px;
}
tr:first-child td:first-child {
width: 250px;
}
这里设置的250px
的单元格并没有生效,但是如果将width
属性换成min-width
或max-width
,则结果又不一样了:
min-width
会打破 table 元素的width
设置,但是max-width
属性却不会:
可以看到 table 的宽度还是和设置的一样,单元格宽度也发生了变化,但是长度并不是设置的最大宽度50,而是浏览器自动进行了宽度调整。
与此相比较,固定布局table-layout: fixed
要简单不少。
固定表格布局
来一个最简单的例子:
table {
table-layout: fixed;
border-collapse: collapse;
}
和auto
布局是一致的,先给 table 元素添加一个宽度:
table {
table-layout: fixed;
border-collapse: collapse;
width: 300px;
}
这里变成了等宽布局,也就是每列是均分300的宽度的。注意到这里给第一个单元格添加了几个单词,表格会自动换行,但是对于连续的字母却不会换行,要想换行的话,还需要work-break
属性。
回到上面的CSS,再给单元给添加一个宽度:
tr:first-child td {
width: 250px;
}
并没有变化?再修改一下:
/* 注意这里单元格的宽度设置在表头上,而不是tbody */
tr:first-child th {
width: 250px;
}
单元格的设置生效了!
这是 CSS 规范 table-layout 中提到的:当所有列相加得到的宽度,与 table 本身设置的宽度作对比,会取较大值作为表格的最终宽度。这里各列设置的宽度之和已经是大于300,所以最终的宽度应该是 250 * 3。
注意,这里的单元格宽度是由第一行中的单元格所决定,所以上面的例子中,宽度是设置在th
元素上,如果设置到td
元素上,则该宽度不会起作用。这也是和auto
布局一个重要的区别:fixed
布局决定单元格宽度的只有第一行,当第一行各列的宽度决定以后,表格就开始进行渲染了,所以布局的速度会更快。
修改一下上面的 CSS:
tr:first-child th:first-child {
width: 250px;
}
这里就是规范中说到另外一点:表格布局为固定布局时,各列的宽度会由宽度不为auto
的值所决定,意思就是第一个单元格的宽度就是设置好的250,剩下的宽度都是默认的auto
。如果指定了 table 的宽度,则auto
值的列会均分剩下的宽度。
可以看到最后两列宽度为auto
的单元格的宽度是一样的。
对于固定布局来说,是否可以使用上面提到的min-width
和max-width
呢?这里就直接说答案了,那就是这两个属性在固定布局中并不能改变单元格的宽度,它们就相当于width: auto
。
至此,对于table-layout
的两个属性值,应该会有一个更深的了解。
总结
那么对于开发者来说,使用 table 布局,只需要关注以下两点:
-
如果对单元格的宽度没有要求,直接使用默认布局就可以了。如果需要在自动布局的情况下,设置单元格的宽度,可以使用
min-width
结合max-width
去实现。 -
如果单元格的宽度有要求,可以将布局变成固定布局
table-layout: fixed
,将宽度设置在表头单元格上就能指定各行的宽度了。
参考资料
原文链接:https://juejin.cn/post/7220775341727318071 作者:qiugu