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>

完美实现(最终效果):

七、参考

相关推荐
蜗牛快跑21311 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy12 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇1 小时前
ES6进阶知识一
前端·ecmascript·es6
前端郭德纲2 小时前
浏览器是加载ES6模块的?
javascript·算法
JerryXZR2 小时前
JavaScript核心编程 - 原型链 作用域 与 执行上下文
开发语言·javascript·原型模式