引言
一天哈比比突然冒出一个毫无理头的一个问题:

本文就该问题进行展开...
原文链接: 昆仑虚F2E
一、需求说明
大致需求如下, 当然这里做了些简化
有如下初始代码:
- 一个自适应的表格
- 每个单元格的宽度固定
200px
- 每个单元格高度则是自适应
- 每个单元格内是一个
div
标签,div
标签内包裹了一段文本, 文本内容不定
下面是初始代码(为了方便演示和美观, 代码中还加了些背景色、边距、圆角, 这些都是可以忽略):
html
<table>
<tr>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
</tr>
</table>
<style>
table {
background: #f5f5f5;
}
td {
background: #ffccc7;
}
table, tr, td {
padding: 12px;
border-radius: 4px;
}
td > div {
padding: 12px;
border-radius: 4px;
background: #f4ffb8;
}
</style>
上面代码的整体效果如下:

上面是哈比比目前的现状, 然后需求就是希望, 黄色部分也就是 div
标签能够高度撑满单元格(td
), 也就是如下图所示:

二、关键问题
这里我第一反应就是, 既然 td
高度是对的(自适应)的那么 div
高度直接设置 100%
不就好了吗? 事实是这样的吗? 我们可以试下:
diff
<table>
...
</table>
<style>
td > div {
+ height: 100%;
}
</style>
实际效果肯定是没有用的, 要不然也就不会有这篇文章了 🐶🐶🐶

主要问题: 在 CSS
中如果父元素没有一个明确的高度, 子元素设置 100%
是无法生效的, 至于为啥就不能生效呢, 因为如果可以, 那么必然会进入死循环这里可以参考张鑫旭大大的文章《从 height:100% 不支持聊聊 CSS 中的 "死循环"》
三、方案一(定位)
通过定位来实现, 也是哈比比最初采用的一个方案:
td
设置相对定位即:position: relative;
td
下的子元素通过相对定位(position: absolute;
)撑满
diff
....
<style>
...
+ td {
+ position: relative;
+ }
+ td:not(:first-child) > div {
+ position: absolute;
+ inset: 0;
+ }
</style>
整体效果如下:

上面代码其实我并没有给所有 td
中的 div
设置 position: absolute;
目的是为了留一个内容最多的块, 来将 tr
、 td
撑开, 如果不这么做就会出现下面这种情况:

所以, 严格来说该方案是不行的, 但是可能哈比比情况比较特殊, 他只有空值和有内容两种情况, 所以他完全可以通过判断内容是否为空来设置 position: absolute;
即可
四、方案二(递归设置 height 100%)
第二个方案就是给 table
、tr
、td
设置一个明确的高度即 100%
, 这样的话 td
中的子元素 div
再设置高度 100%
就可以生效了
diff
<style>
+ table, tr, td, td > div {
+ height: 100%;
}
</style>
效果如下:

上面第一个单元格高度其实还是有点问题, 目前也没找到相关研究可以结束这个现象, 要想达到我们要的效果解决办法有两个:
- 移除代码中所有
padding
, 有关代码和效果图如下:
diff
<style>
table, tr, td {
- padding: 12px;
}
td > div {
- padding: 12px;
}
</style>

- 修改
td
中div
的box-sizing
属性为border-box
, 有关代码和效果图如下:
diff
<style>
...
td > div {
+ box-sizing: border-box;
}
</style>

五、方案三(利用 td 自增加特效, 推荐)
方案三是比较推荐的做法, 其利用了 td
自增加的一个特效, 那么何谓自增加呢? 假设我们给 td
设置可一个高度 1px
但是呢它实际高度实际上是会根据 tr
的高度进行自适应(自动增长), 那么在这种情况下我们给 td
下子元素 div
设置高度 100%
则会奏效, 因为这时的 td
高度是明确的
html
<table>
<tr>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
</tr>
</table>
<style>
table {
background: #f5f5f5;
}
td {
height: 1px; /* 关键代码 */
background: #ffccc7;
}
table, tr, td {
padding: 12px;
border-radius: 4px;
}
td > div {
height: 100%; /* 关键代码 */
padding: 12px;
border-radius: 4px;
background: #f4ffb8;
}
</style>
效果如下:

六、补充: td 下 div 内容顶对齐
几天后, 哈比比又来找我了 🐶🐶🐶

这次需求就比较简单了, 就是 td
中默认情况下子元素(p
)都是居中呈现的, 现想要的就是能否居上(置顶)展示
这里初始代码和上面是一样的:
html
<table>
<tr>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
<td width="400">
<div>
路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面路由器管理页面
</div>
</td>
</tr>
</table>
<style>
table {
background: #f5f5f5;
}
td {
background: #ffccc7;
}
table, tr, td {
padding: 12px;
border-radius: 4px;
}
td > div {
padding: 12px;
border-radius: 4px;
background: #f4ffb8;
}
</style>
默认效果就是 div
都居中展示:

这里我第一反应是用 vertical-align
但是该属性在很多人印象中只针对 行内元素
(或文本
)才能生效, 但这里是 div
是 块元素
所以哈比比自然就忽略了该 vertical-align
属性
但实际上如果查阅文档会发现 vertical-align
实际用途有两个:
- 用来指定行内元素(
inline
)的垂直对齐方式 - 表格单元格(
table-cell
)元素的垂直对齐方式
所以这个问题就简单了, 一行 CSS
就解决了:
diff
<style>
...
td {
+ vertical-align: top;
}
</style>
完美实现(最终效果):

七、参考
