先记住这个答案
浮动元素会脱离正常文档流,也就是说它不再占据父元素里的那一块位置。普通块级元素在 height: auto 时,只根据流内子元素计算高度,浮动的子元素不算。所以一个父元素里只有浮动子元素时,高度就是 0,只剩 padding 和 border。修法有两类。第一类是 clearfix,在父元素末尾用 ::after 插入一个 clear: both 的块,它被推到浮动下面,父元素为了包住它就被撑高了。第二类是让父元素建立 BFC,BFC 的自动高度会把浮动算进去,最干净的写法是 display: flow-root。
- 普通块的自动高度不计算浮动子元素
- 末尾清除浮动可撑开父级
- BFC 的自动高度会把浮动包进去
- 新布局可用
display: flow-root
塌陷和两种修法放在一起看
下面三个容器里都是两个浮动的方块,唯一的区别是父元素的样式。
.float-item {
float: left;
width: 100px;
height: 80px;
}
/* 1. 什么都不加:父元素高度 0,边框缩成一条线 */
.box { border: 2px solid #333; }
/* 2. clearfix:末尾伪元素清除浮动,父元素高 80px */
.box-clearfix::after {
content: '';
display: block; /* 伪元素默认是 inline,clear 对行内元素不生效 */
clear: both;
}
/* 3. 建立 BFC:父元素的自动高度会包含浮动,同样是 80px */
.box-bfc { display: flow-root; }第一种情况里,浮动方块画在了父元素边框的外面,父元素下方的内容会往上顶,贴着浮动绕排。第二种和第三种效果一样,父元素把两个方块完整包住。
为什么 BFC 能包住浮动
BFC 是块格式化上下文,可以理解成一个独立的布局小区:里面的浮动只在小区内部起作用,影响不到外面,外面的浮动也影响不到里面。为了做到这一点,规范规定建立 BFC 的元素在计算自动高度时,要把内部浮动的底部也算进去,否则浮动就会溢出这个小区。
能建立 BFC 的方式很多:overflow 不是 visible 或 clip、display: flow-root、display: inline-block、float 不是 none、position: absolute 或 fixed,以及 flex、grid 容器。但除了 flow-root,其它方式都会顺带改变别的东西。
- 各种修法的副作用
overflow: hidden最常见,但它会裁掉超出父元素的内容,比如子元素的阴影、绝对定位的下拉菜单、transform放大的图片。overflow: auto可能冒出滚动条。给父元素也加float会把问题往上一层传递。display: inline-block会让父元素不再占满整行。clearfix 没有这些问题,但要多记一个类名。display: flow-root是规范专门为「建立 BFC 又不产生副作用」加的值,现代浏览器都支持,新代码直接用它。
容易答错的地方
- 说浮动元素完全不占空间
- 浮动元素脱离了正常流,但它仍然会影响同一 BFC 里的行内内容,后面兄弟元素里的文字会绕开它排。所以塌陷时后面的文字会挤到浮动旁边,而不是被浮动盖住。
- clearfix 里漏写 display: block
::after伪元素默认是行内的,clear只对块级元素生效,漏写display: block就等于没清除。老写法里用display: table,是为了顺便阻止父元素和第一个子元素的外边距合并,效果上也是块级。- 在父元素后面的兄弟元素上加 clear 就算修好了
- 给后面的兄弟加
clear: both能让它排到浮动下面,看起来布局正常了,但父元素的高度依然是 0,背景和边框还是没有。高度塌陷得在父元素自己身上解决。
面试官还会怎么问?
为什么早期的 clearfix 还要写 ::before?
经典的 micro clearfix 同时写 ::before 和 ::after,并且用 display: table。::before 生成的是一个匿名表格盒,它能阻止第一个子元素的 margin-top 和父元素的外边距合并。只为解决高度塌陷,::after 一个就够了。
给父元素写死高度可以吗?
可以让边框和背景显示出来,但内容一变高就溢出,一变矮就留空。响应式页面和动态内容里基本不可用,只适合高度确实固定的小组件。
现在做两栏、三栏布局还用浮动吗?
基本不用了,flex 和 grid 天然会让容器包住子项,不存在高度塌陷。float 现在主要保留给图片左浮、文字环绕的图文排版,这种场景下再用 display: flow-root 包住就行。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。