CSS 文档流相关:BFC
一、引言
display 属性接受两个值:
- 第一个值:外部显示类型,决定了元素在所处布局扮演什么角色(
block/inline) - 第二个值:内部显示类型,决定了元素内部的子元素按照什么方式来排列(
flow/flex/grid)
如
display: block;其实就是display: block flow;该元素对外以块级盒子身份参与布局,对内按照正常文档流排列子元素。
那什么是文档流呢?
二、文档流
文档流是 HTML 元素在网页中的排列方式,浏览器默认的排列方式叫做正常文档流(Normal Flow)
文档流具体的工作方式:
- 块级盒子独占一行,垂直方向从上到下依次排列
- 行内盒子从左到右排列,排满后自动换行
一个容器内部可能同时存在块级盒子和行内盒子,它们是如何共存的呢?
三、格式化上下文
浏览器会为这两套规则创建不同区域,他们各自在不同区域内生效,而CSS中这种有特定规则的区域,叫做格式化上下文。
如:
- 负责块级盒子排列:块级格式化上下文(BFC)
- 负责行内盒子排列:行内格式化上下文(IFC)
文档流就是 BFC 和 IFC 协同工作。
回到页面布局,当我们创建一个页面时,HTML 是页面的根元素,它会建立页面的第一个 BFC,还有什么会触发 BFC 的创建呢?
四、如何创建新的BFC
4.1 根元素自动创建
刚刚提到过 HTML 根元素(display: flow-root)创建了页面的第一个 BFC。
4.2 脱离文档流的情况
脱离文档流,需要独立格式化上下文的情况会创建 BFC:
float的值不是noneposition: absolute或fixed
4.3 弹性与网格布局容器
flex/grid 容器的子元素(内部需要独立的布局环境)会创建 BFC。
4.4 overflow 属性
overflow: hidden(本职工作是控制内容溢出时的裁剪行为,创建 BFC 是副作用)。
4.5 推荐方式:display: flow-root
- 多用
display: flow-root(display: block flow-root),元素本身是块级盒子,对外行为不变,内部显示类型是flow-root,唯一作用是创建 BFC,没有副作用。
一个特殊的方法:将
display改为display: inline(可以消除外边距折叠,但也会使元素变成行内元素。)因为其全称为display: inline flow-root,外部显示类型变为了inline,内部创建 BFC。
五、BFC的意义
1. 解决外边距折叠
在同一个 BFC 中,相邻块级元素的垂直外边距会发生折叠。以下示例展示了问题现象及通过创建 BFC 来解决问题。
1.1 问题现象:两个相邻 div 的外边距发生折叠
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
.container {
background-color: #f0f0f0;
padding: 10px;
}
.box {
height: 50px;
background-color: lightblue;
margin: 20px 0; /* 上下外边距均为 20px */
}
</style>
</head>
<body>
<div class="container">
<div class="box">Box 1</div>
<div class="box">Box 2</div>
</div>
</body>
</html>
在上面的代码中,.box 的上下外边距均为 20px。按照直觉,两个盒子之间的间距应该是 20px + 20px = 40px。但由于它们处于同一个 BFC(.container 内部)中,垂直外边距发生了折叠,实际间距只有 20px。
1.2 解决方案:为其中一个盒子创建独立的 BFC
通过为其中一个 .box 添加 display: flow-root;,使其形成独立的 BFC,从而阻止外边距折叠。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
.container {
background-color: #f0f0f0;
padding: 10px;
}
.box {
height: 50px;
background-color: lightblue;
margin: 20px 0;
}
.bfc-box {
display: flow-root; /* 创建独立的 BFC */
}
</style>
</head>
<body>
<div class="container">
<div class="box">Box 1</div>
<div class="box bfc-box">Box 2 (独立 BFC)</div>
</div>
</body>
</html>
此时,Box 1 和 Box 2 之间的外边距不再折叠,实际间距恢复为 40px。
2. 清除浮动,解决高度塌陷
当一个父元素内部包含浮动元素时,父元素的高度可能无法被撑开(高度塌陷)。创建 BFC 可以让父元素包含其内部的浮动元素。
2.1 问题现象:父元素高度塌陷
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
.parent {
background-color: #f0f0f0;
border: 2px dashed #ccc;
}
.float-child {
float: left;
width: 100px;
height: 100px;
background-color: lightcoral;
margin: 10px;
}
</style>
</head>
<body>
<div class="parent">
<div class="float-child">浮动子元素</div>
<div class="float-child">浮动子元素</div>
</div>
</body>
</html>
由于子元素浮动,父元素 .parent 的高度为 0,边框和背景色无法正常显示,这就是"高度塌陷"。
2.2 解决方案:为父元素创建 BFC
为父元素添加 display: flow-root; 或 overflow: hidden; 等属性,使其形成 BFC,从而包含内部的浮动元素。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
.parent {
background-color: #f0f0f0;
border: 2px dashed #ccc;
display: flow-root; /* 创建 BFC,清除内部浮动 */
}
.float-child {
float: left;
width: 100px;
height: 100px;
background-color: lightcoral;
margin: 10px;
}
</style>
</head>
<body>
<div class="parent">
<div class="float-child">浮动子元素</div>
<div class="float-child">浮动子元素</div>
</div>
</body>
</html>
现在,父元素 .parent 的高度被其内部的浮动子元素撑开,背景和边框正常显示。
总结 :通过 display: flow-root 创建 BFC,既能无副作用地解决外边距折叠问题,也能有效清除浮动,是处理这类布局问题的推荐方式。
六、扩展
- GFC:网格布局格式化上下文,形成二维表格(当
display为grid时触发) - FFC:自适应格式上下文,形成伸缩容器(当
display为flex/inline-flex时触发)