清除浮动(Clearfix)

在 CSS 的上古时代,Flexbox 和 Grid 这些现代布局利器还未诞生,前端工程师们为了把块级元素(比如 <li> 或者 <div>)横向排列,最主要的手段就是使用 浮动(float

然而,凡是有浮动的地方,几乎都会伴随着一个让人抓狂的幽灵 Bug:父元素高度塌陷

为了解决这个问题,一代又一代的前端人总结出了一套名为 clearfix(清除浮动)的内功心法。虽然今天纯粹为了布局去使用 float 的场景已经非常罕见了,但理解这段历史以及背后的 BFC 机制,依然是每一位合格前端的必修课。

什么是“浮动高度塌陷”?

在常规的文档流中,一个父容器的高度,是由它内部的子元素自然撑开的(假设你没有写死固定高度)。 但是,当你给子元素设置了 float: left 或者 float: right 后,这些子元素就会“起飞”,脱离了原本的普通文档流

这就导致了一个灾难性的后果:父盒子环顾四周,发现(在最初的文档流层面上)自己肚子里空空如也,于是它的高度瞬间变成了 0。 父盒子这么一瘪,原本紧跟在父盒子里面的后面的内容,或者跟在父盒子外面的兄弟元素,全都会挤上来,页面布局瞬间乱作一团。

这就是著名的“浮动高度塌陷”。

经典解药:Clearfix Hack

为了抵御这种塌陷,业界总结出了多种应对方案,其中经受了岁月考验并被奉为圭臬的,就是利用伪元素实现的 .clearfix 方案。

当年几乎每一个经过考验的前端框架(例如早期的 Bootstrap ),底层都会有一段这样的 CSS 代码:

/* 给需要清除浮动的父元素加上 clearfix 类名 */
.clearfix::after {
  content: "";
  display: block; /* 必须是块级元素 */
  clear: both; /* 核心大招:不允许左右两边有浮动元素 */
  height: 0; /* 为了不占据实际空间 */
  visibility: hidden; /* 隐身 */
}

clear: both 的核心作用是:强行让当前元素移动到它前面所有浮动元素(不管是左浮动还是右浮动)的下方。

它是如何精确包裹且不引发“部分塌陷”的?

这段代码就像是在那些脱缰起飞的浮动子元素之后,悄悄派出了一个看不见、也没有高度的“压舱石”(::after伪元素)。为什么这个压舱石能够分毫不差地把父元素撑回到原来应有的高度?

核心逻辑在于浏览器计算父元素高度的规则与 clear: both 的物理位置相互碰撞:

  1. 高度计算底线:在常规文档流中,如果父元素没有固定高度,它的总高度其实是由内部没有脱离文档流的最底端一个元素决定的。
  2. 压舱石的位置:我们在父盒子内部的最后方,利用伪元素生成了一个正常的块级元素(display: block)。
  3. 被迫疯狂下沉:因为这块压舱石带有 clear: both 属性,它遵循一条铁律——我的左右两边绝对不允许被任何浮动元素遮挡。如果上面的浮动子元素参差不齐(比如左边高 500px,右边高 200px),压舱石为了避开所有的浮动元素,只能委屈自己,硬生生地被迫下沉到最高的那个浮动元素(500px)的正下方
  4. 瞬间撑开:父盒子开始算账了,虽然它对脱离了文档流的浮动元素视而不见,但它看到了在正常文档流里的最底端,这块压舱石正处于 500px 的深度。为了包裹住它,父盒子只好乖乖把自己的高度向下拉伸到了 500px

至于会不会出现部分塌陷?绝对不会。因为只要有了 clear: both,压舱石就肯定会被强行推挤到所有浮动元素的绝对最下方。而我们的代码又给伪元素写了 height: 0;content: "";,说明这块压舱石不占据任何垂直体积,纯粹只是一个“占位底线坐标”。最终父元素的底部恰好就贴合到了浮动元素的底部,毫厘不差,完美闭合。

新时代的优雅方案:触发 BFC

除了强行塞一个伪元素去“闭合”浮动之外,CSS 其实还有另一套更为底层的逻辑来包容浮动元素:BFC(Block Formatting Context,块级格式化上下文)

BFC 是一个独立渲染的区域,它有一个极其重要的特性:计算 BFC 的高度时,浮动元素也会参与计算。 也就是说,只要我们想办法把那个塌陷的父元素触发为 BFC 结界,它就能把起飞的浮动子元素强行罩在里面,高度自然就恢复了。

常见触发父元素 BFC 的手段包括(但不限于):

  1. overflow: hidden:这曾经是最流行的偷懒做法。简单有效,但它有巨大的副作用——如果子元素真的需要超出父容器显示(比如绝对定位的下拉菜单),就会被无情截断。
  2. display: flow-root:这是 W3C 在看到了开发者们长期遭受“清除浮动”之苦后,特地在现代 CSS 中加入的一个专门用来触发 BFC 的无副作用属性
/* 新时代的理想清除方案,不需要写落长的 clearfix */
.parent-with-floats {
  display: flow-root;
}

高频面试题剖析

Q:什么是浮动塌陷?除了使用伪元素的 clearfix 原理,还有哪些现代手法来清除浮动?

回答思路:

  1. 解释现象本质:点明子元素使用 float 后脱离常规文档流,导致未设置固定高度的父元素无法计算内部高度从而收缩为 0,这会引起后续元素的错位。
  2. 拆解经典 Hacker(得分点):解释老派神仙方案 .clearfix::after。核心在于利用伪元素生成一个块级盒子(display: block),并利用 clear: both 属性强行将其自身排布在浮动元素之下,从而利用其身躯撑开父容器。
  3. 拔高视野(引出 BFC):提到从 CSS 渲染层面上,还可以通过触发父容器成为 BFC 来包裹住浮动元素。举例常见的 overflow: hidden/auto(会说明其截断内容的副作用)。
  4. 展示前沿积累:最后带出目前标准委员会给出的完美纯净方案 display: flow-root,它旨在不产生任何额外副作用地创建一块新的 BFC,是现代清除浮动的最佳实践之一。
  5. 结语(展现架构能力):可以补充一句,在现代工程开发中,横向布局已经绝大部分被 Flex 甚至 Grid 替代,float 已经退化回它最初的设计目标——实现文字环绕图片的效果。因此,全局使用 clearfix 的历史包袱正在逐渐卸下。