CSS 的"格式化上下文"(Formatting Context)可以理解为:浏览器进行布局时使用的一套规则。
它发生在渲染管线的布局阶段:
DOM + CSSOM
→ 样式计算
→ 布局(根据格式化上下文排列元素)
→ 绘制
→ 合成
格式化上下文决定:
- 子元素横着排列还是竖着排列;
- 是否换行;
- 宽高如何计算;
- 外边距是否合并;
- 浮动元素如何影响其他元素;
- 元素如何对齐;
- 剩余空间如何分配。
常见格式化上下文
| 名称 | 全称 | 常见创建方式 |
|---|---|---|
| BFC | Block Formatting Context | display: flow-root |
| IFC | Inline Formatting Context | 文本、span 等行内内容 |
| FFC | Flex Formatting Context | display: flex |
| GFC | Grid Formatting Context | display: grid |
| TFC | Table Formatting Context | display: table |
可以简单记忆:
BFC:块级元素纵向排列
IFC:文字和行内元素按行排列
FFC:按照主轴、交叉轴排列
GFC:按照行列网格排列
TFC:按照表格规则排列
一、BFC:块级格式化上下文
BFC 全称:
scss
Block Formatting Context
在普通块级布局中,元素通常从上往下排列:
ini
<div class="container">
<div class="item">第一项</div>
<div class="item">第二项</div>
<div class="item">第三项</div>
</div>
┌───────────────┐
│ 第一项 │
├───────────────┤
│ 第二项 │
├───────────────┤
│ 第三项 │
└───────────────┘
不过需要注意:
css
display: block;
表示元素以块级方式参与父级布局,但不一定意味着它创建了一个新的、独立的 BFC。
创建独立 BFC 最推荐的方式是:
css
.container {
display: flow-root;
}
BFC 的常见创建方式
css
/* 推荐 */
display: flow-root;
/* 浮动 */
float: left;
float: right;
/* 绝对定位 */
position: absolute;
position: fixed;
/* 行内块 */
display: inline-block;
/* overflow 不是 visible 或 clip */
overflow: hidden;
overflow: auto;
overflow: scroll;
其中最推荐:
css
display: flow-root;
因为它表达的含义非常明确:
为内部内容创建一个独立的块级格式化上下文
使用 overflow: hidden 虽然也能创建 BFC,但可能会意外裁剪内容。
BFC 特性一:包含浮动元素
假设子元素全部浮动:
ini
<div class="parent">
<div class="child"></div>
</div>
css
.child {
float: left;
width: 100px;
height: 100px;
}
浮动元素脱离普通文档流,父元素可能无法正确计算高度。
可以给父元素创建 BFC:
css
.parent {
display: flow-root;
}
此时父元素会把浮动子元素计算进自己的高度。
完整示例:
ini
<div class="parent">
<div class="child">浮动内容</div>
</div>
css
.parent {
display: flow-root;
background: gray;
}
.child {
float: left;
width: 100px;
height: 100px;
background: skyblue;
}
这也是以前所谓"清除浮动"的一种方式。
BFC 特性二:限制外边距合并
普通块级元素的垂直外边距可能发生合并。
例如:
ini
<div class="parent">
<div class="child">子元素</div>
</div>
css
.parent {
background: gray;
}
.child {
margin-top: 30px;
}
在某些条件下,子元素的 margin-top 会和父元素发生外边距合并,看起来像父元素整体向下移动。
创建 BFC:
css
.parent {
display: flow-root;
}
可以阻止父元素和子元素之间的这种外边距合并。
但是,同一个 BFC 中相邻块级元素的垂直外边距仍然可能合并:
ini
<div class="first"></div>
<div class="second"></div>
css
.first {
margin-bottom: 30px;
}
.second {
margin-top: 20px;
}
最终间距通常不是:
ini
30px + 20px = 50px
而是取较大的值:
scss
max(30px, 20px) = 30px
所以不能简单理解为"BFC 内部永远不会发生外边距合并"。
BFC 特性三:减少浮动元素的影响
例如左边有一个浮动元素:
css
.left {
float: left;
width: 100px;
}
右边内容可能环绕浮动元素。
给右边内容创建 BFC:
css
.right {
display: flow-root;
}
可以使右侧内容形成相对独立的布局区域,避免内容覆盖浮动区域。
不过现代项目通常更推荐直接使用:
css
display: flex;
或者:
css
display: grid;
二、IFC:行内格式化上下文
IFC 全称:
scss
Inline Formatting Context
当一个块级容器中主要包含文本和行内元素时,浏览器会按照行内格式化规则进行布局。
例如:
css
<p>
这是一段文字
<span>这是 span</span>
<strong>这是加粗文字</strong>
</p>
这些内容会从左到右排列:
css
这是一段文字 这是 span 这是加粗文字
空间不足时自动换行:
css
这是一段文字 这是
span 这是加粗文字
常见行内元素:
css
<span>
<a>
<strong>
<em>
<img>
IFC 中的重要概念是"行盒":
rust
Line Box
浏览器会把一行中的文字和行内元素放入同一个行盒中。
IFC 主要受到这些属性影响:
arduino
font-size
line-height
vertical-align
text-align
white-space
word-break
line-height
css
p {
line-height: 30px;
}
它控制每一行行盒的高度。
vertical-align
css
img {
vertical-align: middle;
}
它控制行内元素在当前行盒中的垂直对齐方式。
需要注意:
css
vertical-align: middle;
主要用于行内元素、行内块元素或表格单元格,不是普通块级元素的通用垂直居中方案。
三、FFC:Flex 格式化上下文
当设置:
css
.container {
display: flex;
}
容器就创建了 Flex 格式化上下文。
它的直接子元素会成为 Flex 项目:
xml
<div class="container">
<div>项目1</div>
<div>项目2</div>
<div>项目3</div>
</div>
默认沿主轴排列:
项目1 项目2 项目3
主要概念:
css
主轴 main axis
交叉轴 cross axis
常见属性:
css
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10px;
例如水平垂直居中:
css
.container {
display: flex;
justify-content: center;
align-items: center;
}
在 Flex 格式化上下文中:
- 子元素成为 Flex 项目;
- 浮动对 Flex 项目通常不再按照普通浮动规则生效;
vertical-align对 Flex 项目不起普通行内布局作用;- Flex 项目的外边距不会按照普通块级布局方式发生垂直合并;
- 剩余空间可以通过
flex分配。
需要注意,只有直接子元素才是 Flex 项目:
xml
<div class="container">
<div class="child">
<span>孙元素</span>
</div>
</div>
这里:
css
child 是 Flex 项目
span 不是 container 的 Flex 项目
span 如何布局,由 child 自己的格式化上下文决定。
四、GFC:Grid 格式化上下文
设置:
css
.container {
display: grid;
}
容器就建立 Grid 格式化上下文。
Grid 按照行和列进行二维布局:
css
.container {
display: grid;
grid-template-columns: 200px 1fr 200px;
gap: 10px;
}
表示三列:
200px | 剩余空间 | 200px
例如:
xml
<div class="container">
<div>左侧</div>
<div>中间</div>
<div>右侧</div>
</div>
Grid 特别适合:
- 页面整体结构;
- 卡片网格;
- 同时控制行和列;
- 仪表盘;
- 复杂表单布局。
Flex 更擅长一维布局:
一行或者一列
Grid 更擅长二维布局:
同时控制行和列
五、TFC:表格格式化上下文
表格布局使用表格格式化规则:
css
<table>
<tr>
<td>姓名</td>
<td>年龄</td>
</tr>
</table>
也可以通过 CSS 模拟:
css
.table {
display: table;
}
.row {
display: table-row;
}
.cell {
display: table-cell;
}
浏览器会根据单元格内容、列宽、跨行跨列等规则计算布局。
现代页面布局一般优先使用 Flex 或 Grid,而不是使用表格布局模拟普通页面结构。
display 的外部和内部布局
现代 CSS 可以把 display 理解成两部分:
外部显示类型
内部格式化方式
例如:
css
display: flex;
可以理解为:
scss
外部:block
内部:flex
也就是:
- 容器本身像块级元素一样参与父级布局;
- 容器内部使用 Flex 布局。
而:
arduino
display: inline-flex;
表示:
scss
外部:inline
内部:flex
也就是:
- 容器自身像行内元素一样排列;
- 容器内部仍然使用 Flex 布局。
同理:
css
display: grid;
通常可以理解为:
scss
外部 block
内部 grid
arduino
display: inline-grid;
可以理解为:
scss
外部 inline
内部 grid
inline-block 则可以理解为:
scss
外部 inline
内部 flow-root
它在外面跟文字一样横向排列,但内部可以拥有独立的块级布局区域。
格式化上下文和其他概念不要混淆
格式化上下文
决定:
元素如何排列
例如:
css
display: flex;
display: grid;
display: flow-root;
层叠上下文
决定:
元素在 Z 轴上的遮挡顺序
例如可能由下面的属性创建:
css
position: relative;
z-index: 1;
transform: translateX(0);
opacity: 0.9;
包含块
决定:
百分比尺寸和绝对定位以谁为参考
例如:
css
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
}
这里 child 的定位包含块通常是 parent。
这三个概念分别是:
格式化上下文:怎么排
层叠上下文:谁盖住谁
包含块:尺寸和定位参考谁
最后可以这样记忆:
普通块布局 → BFC
文字行内布局 → IFC
一行或一列的弹性布局 → FFC
行列二维布局 → GFC
表格布局 → TFC
在现代项目中最常用的是:
css
display: flow-root;
display: flex;
display: grid;