盒模型与 BFC
在 CSS 布局中,盒模型 (Box Model) 决定了单个元素的大小和内外部边距的计算方式,而 BFC (块级格式化上下文) 则决定了多个盒子之间如何相互作用和排列。
盒模型 (Box Model)
每一个 HTML 元素都可以看作一个矩形的盒子。这个盒子由四个部分组成(从内到外):Content (内容)、Padding (内边距)、Border (边框) 和 Margin (外边距)。
标准盒模型 vs 怪异盒模型
CSS 中通过 box-sizing 属性来控制盒模型的解析模式:
| 特性 | 标准盒模型(默认) | 怪异盒模型 |
|---|---|---|
| 关键属性 | box-sizing: content-box | box-sizing: border-box |
宽度 (width) 包含内容 | 仅包含 content | 包含 content + padding + border |
代码对比演示
/* 基础样式:给一个宽度为 200px 的盒子加上 padding 和 border */
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 5px solid black;
}
/* 标准盒模型 (默认) */
.box-standard {
box-sizing: content-box;
/* 实际渲染宽度 = width + (padding * 2) + (border * 2) = 250px */
}
/* 怪异盒模型 */
.box-quirks {
box-sizing: border-box;
/* 实际渲染宽度 = 200px (内容区域被压缩为 200 - 40(padding * 2) - 10(border * 2) = 150px) */
}最佳实践
在现代 Web 开发中,通常推荐在全局重置样式中启用 border-box。这样更符合直觉与 UI 设计稿的标注,修改 padding 或 border 时不会意外撑破外层容器布局。
块级格式化上下文 (BFC)
BFC (Block Formatting Context) 可以理解为一个完全独立的渲染容器。在这个容器内部,元素的布局有一套特定的规则,且容器内部的元素不会影响到外面的元素,外部元素也无法影响内部。
如何触发BFC?
要让一个元素成为 BFC,只需要满足以下条件之一:
- 根元素 (<html>) 默认就是一个 BFC。
- 浮动元素:float 属性不为 none。
- 绝对定位元素:position 为 absolute 或 fixed。
- 行内块元素:display 为 inline-block。
- 弹性或网格容器:display 为 flex, inline-flex, grid, inline-grid。
- 表格单元格:display 为 table-cell 等。
- overflow 值不为 visible 的块级元素:如 hidden, auto, scroll(最常用的触发方式)。
- display: flow-root:(专门用于无副作用触发 BFC 的现代属性)。
BFC的渲染规则
- 内部的 Box 会在垂直方向上一个接一个地放置。
- 属于同一个 BFC 的两个相邻 Box 的垂直 margin 会发生重叠(折叠)。
- 每个元素的左外边缘(margin-left)会与包含块的左边缘相接触(对于从右到左的格式化,则接触右边缘)。即使存在浮动也会如此。
- BFC 的区域不会与 float 盒子重叠。
- 计算 BFC 的高度时,内部的浮动元素也会参与计算。
深入探索:BFC 解决的实际痛点
理解了原理,我们来看看 BFC 在实际开发中能解决哪些“反直觉”的布局问题。
解决垂直外边距重叠 (Margin Collapse)
问题现象:相邻的两个块级元素,上方的有 margin-bottom: 20px,下方的有 margin-top: 20px,它们之间的实际间距只有 20px,而不是 40px。
解决方案:根据 BFC 规则第 2 条,只有同属于一个 BFC 的元素 margin 才会重叠。我们将其中一个元素用新的 BFC 容器包裹起来即可阻断重叠。
<div class="box1" style="margin-bottom: 20px;">Box 1</div>
<div class="bfc-wrapper" style="overflow: hidden;">
<div class="box2" style="margin-top: 20px;">Box 2</div>
</div>包含内部浮动(解决高度塌陷)
问题现象:当父元素没有设置固定高度,且其子元素全部设置为浮动 (float: left) 时,父元素的高度会变为 0,导致其下方的正常文档流元素发生错位(这被称为高度塌陷)。
解决方案:根据 BFC 规则第 5 条(计算 BFC 高度时,浮动元素也参与计算),只需将父元素触发 BFC 即可。
<div class="parent" style="overflow: hidden; border: 2px solid red;">
<div class="child" style="float: left; width: 100px; height: 100px;">浮动元素</div>
</div>实现自适应的两栏布局 (防文字环绕)
问题现象:左侧一个头像向左浮动,右侧是一长串文字。当文字行数过多超过头像高度时,文字会跑到头像正下方,形成“环绕”效果,破坏了两栏布局。
解决方案:根据 BFC 规则第 4 条(BFC 的区域不会与 float 盒子重叠),我们可以给右侧的文字容器触发 BFC。
<div class="container">
<div class="avatar" style="float: left; width: 50px; height: 50px;">头像</div>
<div class="content" style="overflow: hidden;">
这是一段很长的文字。因为触发了 BFC,这个容器会变窄,
老老实实地呆在右侧,绝对不会跑到左边头像的下方去。
</div>
</div>总结
BFC 本质上是 CSS 为元素划分的一块“独立结界”。虽然在 Flexbox 和 Grid 大行其道的今天,我们不再需要频繁地利用 BFC 特性(如浮动+BFC)去实现复杂布局,但掌握 BFC 原理,依然是解决各种奇怪的边距重叠、高度塌陷等历史遗留问题的唯一钥匙。
@keyboarder-yang