CSS 技巧:如何让 div 完美填充 td 高度

引言

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

本文就该问题进行展开...

一、需求说明

大致需求如下, 当然这里做了些简化

有如下初始代码:

  • 一个自适应的表格
  • 每个单元格的宽度固定 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; 目的是为了留一个内容最多的块, 来将 trtd 撑开, 如果不这么做就会出现下面这种情况:

所以, 严格来说该方案是不行的, 但是可能哈比比情况比较特殊, 他只有空值和有内容两种情况, 所以他完全可以通过判断内容是否为空来设置 position: absolute; 即可

四、方案二(递归设置 height 100%)

第二个方案就是给 tabletrtd 设置一个明确的高度即 100%, 这样的话 td 中的子元素 div 再设置高度 100% 就可以生效了

diff 复制代码
<style>
+ table, tr, td, td > div {
+   height: 100%;
  }
</style>

效果如下:

上面第一个单元格高度其实还是有点问题, 目前也没找到相关研究可以结束这个现象, 要想达到我们要的效果解决办法有两个:

  1. 移除代码中所有 padding, 有关代码和效果图如下:
diff 复制代码
<style>
  table, tr, td {
-   padding: 12px;
  }
  td > div {
-   padding: 12px;
  }
</style>
  1. 修改 tddivbox-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 实际用途有两个:

  1. 用来指定行内元素(inline)的垂直对齐方式
  2. 表格单元格(table-cell)元素的垂直对齐方式

所以这个问题就简单了, 一行 CSS 就解决了:

diff 复制代码
<style>
  ...
  td {
+   vertical-align: top;
  }
</style>

完美实现(最终效果):

七、参考

相关推荐
还是大剑师兰特几秒前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_748236119 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo61721 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489423 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356135 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-8 小时前
验证码机制
前端·后端
燃先生._.9 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js