CSS篇 | 盒子模型
篇章过渡:前面 11 章我们系统拆解了 JavaScript 的核心考点——从闭包、this、Promise 到模块化。接下来进入 CSS 篇。CSS 在前端面试中占比虽然不如 JS 大,但盒模型、BFC、选择器优先级、移动端适配等知识点几乎是必考内容,也是很多候选人容易丢分的地方。让我们从最基础的盒模型开始。
前言
盒模型(Box Model)是 CSS 里最基础、也最重要的概念之一。不管你是写一个按钮的内边距,还是调一个卡片的外边距,本质上都是在和盒模型打交道。
面试中,盒模型几乎是 CSS 部分的必考题。常见的问法有:
- “请描述一下 CSS 的盒模型”
- “content-box 和 border-box 的区别是什么?”
- “什么是 margin 塌陷?怎么解决?”
- “为什么你项目里要全局设置
box-sizing: border-box?”
看似简单,但很多人在面试时讲不清楚细节,尤其是 margin 合并和内联元素的盒模型特点。今天我们就彻底搞明白。
诊断自测
在开始之前,试着回答以下问题,检测你对盒模型的理解程度:
1. 一个 div 设置了 width: 200px; padding: 20px; border: 5px solid; margin: 10px;,在 content-box 和 border-box 下,这个 div 在页面上实际占据的宽度分别是多少?
2. 以下代码中,两个 div 之间的间距是多少?
<div style="margin-bottom: 30px;">A</div>
<div style="margin-top: 20px;">B</div>
3. 给一个 <span> 设置 width: 200px; height: 100px; margin: 20px; padding: 10px; 哪些属性会生效?
点击查看答案
第1题:
content-box:实际占据宽度 = width + 左右 padding + 左右 border + 左右 margin = 200 + 40 + 10 + 20 = 270pxborder-box:实际占据宽度 = width + 左右 margin = 200 + 20 = 220px(因为 padding 和 border 被包含在 width 里了)
第2题: 间距是 30px,不是 50px。这就是 margin 合并(也叫 margin 塌陷),垂直方向相邻的 margin 会取较大值。
第3题: width 和 height 不生效(内联元素不能设置宽高);margin 只有左右生效,上下不生效;padding 上下左右都生效,但上下 padding 不会撑开行高,可能会和其他内容重叠。
如果三道题都答对了,说明你基础不错!继续阅读可以巩固细节。如果有答错的,别担心,看完本文你就全明白了。
什么是盒模型?
在 CSS 中,每一个元素都可以看作是一个矩形的”盒子”。这个盒子由四个部分组成,从内到外依次是:
- Content(内容区):元素的实际内容,比如文字、图片
- Padding(内边距):内容区与边框之间的空间
- Border(边框):围绕 padding 和内容的边框
- Margin(外边距):盒子与其他元素之间的空间
┌──────────────────────────────────────┐
│ margin │
│ ┌────────────────────────────────┐ │
│ │ border │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ └──────────────────────────┘ │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
这四层结构,就是 CSS 盒模型的全部。你可以在浏览器 DevTools 中选中任意元素,看到这个经典的盒模型示意图。
标准盒模型 vs IE 盒模型
历史上,浏览器对”width 到底包含什么”这个问题有两种不同的理解,由此产生了两种盒模型。
标准盒模型(W3C 盒模型)
W3C 规范规定,width 和 height 只指 content 区域 的宽高。
.box {
box-sizing: content-box; /* 默认值 */
width: 200px;
padding: 20px;
border: 5px solid #333;
}
这个盒子在页面上实际占据的宽度:
实际宽度 = width + padding-left + padding-right + border-left + border-right
= 200 + 20 + 20 + 5 + 5
= 250px
IE 盒模型(怪异盒模型)
早期 IE(IE6 及以下的怪异模式)认为 width 应该包含 content + padding + border。
.box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 5px solid #333;
}
这个盒子在页面上实际占据的宽度:
实际宽度 = width = 200px
内容区宽度 = 200 - 20 - 20 - 5 - 5 = 150px
直观对比
| 属性 | content-box(标准) | border-box(IE) |
|---|---|---|
| width 包含 | content | content + padding + border |
| 元素实际宽度 | width + padding + border | width |
| 内容区宽度 | 等于 width | width - padding - border |
box-sizing:content-box vs border-box
CSS3 引入了 box-sizing 属性,让我们可以自由选择使用哪种盒模型。
/* 标准盒模型(默认) */
.standard { box-sizing: content-box; }
/* IE 盒模型 */
.ie-style { box-sizing: border-box; }
来看一个直观的例子:
.box-content {
box-sizing: content-box;
width: 300px;
padding: 20px;
border: 2px solid;
/* 元素在页面上占据:300 + 40 + 4 = 344px */
}
.box-border {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 2px solid;
/* 元素在页面上占据:300px,内容区只有 300 - 40 - 4 = 256px */
}
为什么 border-box 在实际开发中更受欢迎?因为当你说”这个元素宽 300px”时,你心里想的就是它在页面上占 300px,而不是再去算 padding 和 border。border-box 让心智模型和实际效果一致。
margin 合并与塌陷
这是盒模型里最容易踩坑的部分,也是面试高频考点。
什么是 margin 合并?
垂直方向上相邻的两个 margin 会合并为一个,取两者中的较大值。
<div class="box-a">A</div>
<div class="box-b">B</div>
.box-a { margin-bottom: 30px; }
.box-b { margin-top: 20px; }
/* A 和 B 之间的实际间距是 30px,不是 50px */
注意:水平方向的 margin 不会合并。
margin 合并的三种场景
场景一:相邻兄弟元素
最常见的场景,上面已经演示过了。
场景二:父元素与第一个/最后一个子元素
<div class="parent">
<div class="child">子元素</div>
</div>
.parent {
background: lightblue;
/* 父元素没有 border、padding、overflow */
}
.child {
margin-top: 50px;
}
/* 这个 margin-top 会"穿透"到父元素外面,看起来像是父元素有了 margin-top */
这就是所谓的 margin 塌陷,子元素的 margin-top 和父元素的 margin-top 合并了。
场景三:空的块级元素
如果一个块级元素没有 border、padding、内容、高度,它自身的 margin-top 和 margin-bottom 也会合并。
.empty {
margin-top: 20px;
margin-bottom: 30px;
/* 这个空元素实际只占据 30px 的垂直空间 */
}
如何解决 margin 塌陷?
针对父子元素的 margin 塌陷,常见的解决方案有:
/* 方案一:给父元素加 overflow */
.parent { overflow: hidden; }
/* 方案二:给父元素加 border */
.parent { border-top: 1px solid transparent; }
/* 方案三:给父元素加 padding */
.parent { padding-top: 1px; }
/* 方案四:创建 BFC(后面章节会详细讲) */
.parent { display: flow-root; }
其中 display: flow-root 是最”正统”的方式,它专门为创建 BFC 而设计,没有副作用。
内联元素的盒模型特点
块级元素(div、p 等)的盒模型很直观,但内联元素(span、a、em 等)的盒模型有自己的”脾气”。
width 和 height 不生效
内联元素的宽高由内容决定,你没办法用 width 和 height 来控制。
span {
width: 200px; /* 无效 */
height: 100px; /* 无效 */
}
margin 上下不生效
内联元素的 margin-left 和 margin-right 正常工作,但 margin-top 和 margin-bottom 不会生效。
span {
margin-left: 20px; /* 有效 */
margin-right: 20px; /* 有效 */
margin-top: 50px; /* 无效 */
margin-bottom: 50px;/* 无效 */
}
padding 上下”视觉生效”但不影响布局
内联元素的 padding 四个方向都会渲染出来,但上下 padding 不会影响行高,也不会把其他行的内容推开,可能会和相邻行的内容重叠。
span {
padding: 20px;
background: lightblue;
/* 背景色会向上下扩展 20px,但不会撑开行高 */
}
如何让内联元素支持宽高?
/* 方案一:改为块级元素 */
span { display: block; }
/* 方案二:使用 inline-block */
span { display: inline-block; }
/* 方案三:使用 flex 或 grid 布局中的子元素自动成为块级 */
.parent { display: flex; }
inline-block 是最常用的方案,它让元素既能和其他内联元素排在一行,又能设置宽高和完整的 margin、padding。
实际开发中的最佳实践
全局设置 border-box
几乎所有现代前端项目都会在全局样式中加上:
*,
*::before,
*::after {
box-sizing: border-box;
}
为什么?
- 心智模型一致:设置
width: 300px就是 300px,不需要心算 - 百分比布局更可控:
width: 50%加上 padding 后不会超出父元素 - 响应式设计更方便:不会因为加了 padding 导致布局溢出
使用通配符的性能
有人担心 * 选择器的性能问题。实际上,现代浏览器对 * 的处理已经非常高效了,这点性能开销完全可以忽略不计。如果你实在介意,也可以用继承的方式:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
这样做的好处是,如果你引入了第三方组件库,它使用 content-box,可以在组件的根元素上覆盖回去,子元素会通过 inherit 继承。
调试盒模型
当布局不符合预期时,第一件事就是打开 DevTools,查看元素的盒模型。Chrome DevTools 的 Elements 面板右侧有一个可视化的盒模型图,可以直观地看到 content、padding、border、margin 各部分的尺寸。
你还可以给所有元素加上轮廓线来快速排查布局问题:
* {
outline: 1px solid red !important;
}
注意用 outline 而不是 border,因为 outline 不占据空间、不影响布局。
常见误区
误区一:margin 合并发生在所有方向
很多人以为 margin 合并是上下左右都会发生的。实际上,只有垂直方向(上下)的 margin 才会合并,水平方向的 margin 永远不会合并。
/* 水平方向不会合并 */
.left { margin-right: 20px; }
.right { margin-left: 30px; }
/* 两者之间的间距是 50px,不是 30px */
误区二:inline-block 元素的 margin 也会合并
inline-block 元素不会发生 margin 合并。margin 合并只发生在块级元素之间(display 为 block 的元素)。
.item {
display: inline-block;
margin-bottom: 20px;
}
.item + .item {
margin-top: 20px;
}
/* inline-block 元素不会 margin 合并,间距是各自 margin 的总和 */
误区三:padding 和 margin 可以设置负值都一样
padding 不能设置负值,设置了也不会生效。而 margin 可以设置负值,且负 margin 在实际开发中非常有用。
.box {
padding: -20px; /* 无效,浏览器会忽略 */
margin: -20px; /* 有效,元素会向反方向移动 */
}
误区四:border-box 包含了 margin
有些人以为 border-box 的 width 包含了 margin。不是的!无论是 content-box 还是 border-box,width 都不包含 margin。border-box 的 width 只包含 content + padding + border。
.box {
box-sizing: border-box;
width: 200px;
margin: 20px;
/* 元素在页面上占据的空间 = 200 + 40 = 240px */
/* width 200px 里不包含 margin */
}
小结
盒模型虽然是 CSS 的基础概念,但它的细节足以区分一个前端工程师对 CSS 的理解深度。
本章思维导图
- 四层结构
- Content(内容区)
- Padding(内边距)
- Border(边框)
- Margin(外边距)
- 两种盒模型
- content-box(标准盒模型,默认)
- width = content
- border-box(IE 盒模型)
- width = content + padding + border
- content-box(标准盒模型,默认)
- margin 合并
- 只在垂直方向发生
- 三种场景
- 相邻兄弟元素
- 父子元素(margin 塌陷)
- 空块级元素
- 解决方案
- overflow: hidden
- border / padding
- display: flow-root(BFC)
- 内联元素盒模型
- width / height 无效
- margin 上下无效
- padding 上下不影响布局
- 解决:display: inline-block
- 最佳实践
- 全局 border-box
- inherit 继承模式
- outline 调试法
练习挑战
挑战一:基础(⭐)
以下元素在页面上实际占据的宽度是多少?
.box {
box-sizing: border-box;
width: 100%;
padding: 0 20px;
border: 1px solid #ccc;
margin: 0 10px;
}
假设父元素宽度为 400px。
点击查看答案
元素在页面上占据的总宽度 = width + 左右 margin。
因为 box-sizing: border-box,所以 width: 100% 等于父元素宽度 400px,padding 和 border 已经包含在内。
但是 margin 不在 border-box 范围内,所以:
总宽度 = 400px + 10px + 10px = 420px。
这会导致元素溢出父元素 20px。所以在使用 width: 100% 配合 margin 时要特别小心,可以考虑用 calc(100% - 20px) 或者用 padding 代替 margin。
挑战二:进阶(⭐⭐)
以下代码中,.inner 元素距离 .outer 顶部的实际距离是多少?
<div class="outer">
<div class="inner">Hello</div>
</div>
.outer {
margin-top: 20px;
background: lightblue;
}
.inner {
margin-top: 50px;
}
点击查看答案
.inner 距离 .outer 顶部的距离是 0px。
这是经典的 margin 塌陷问题。因为 .outer 没有 border、padding、overflow 等阻隔,.inner 的 margin-top: 50px 会和 .outer 的 margin-top: 20px 合并。
合并后,.outer 元素表现得像是有 margin-top: 50px(取较大值),而 .inner 紧贴 .outer 的内容区顶部,两者之间没有间距。
要让 .inner 真正在 .outer 内部有 50px 的上边距,可以给 .outer 加上 overflow: hidden 或 display: flow-root 来创建 BFC。
挑战三:综合(⭐⭐⭐)
不改变 HTML 结构,只用 CSS 实现以下效果:三个 inline-block 元素等宽排列,每个元素之间间距 20px,整体占满容器宽度。
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
点击查看答案
这道题考察盒模型 + calc + inline-block 的综合运用。
.container {
font-size: 0; /* 消除 inline-block 之间的空白间隙 */
}
.item {
display: inline-block;
box-sizing: border-box;
width: calc((100% - 40px) / 3);
/* 3 个元素之间有 2 个间隙,每个 20px,总共 40px */
font-size: 16px; /* 恢复字体大小 */
}
.item + .item {
margin-left: 20px;
}
当然,现代开发中更推荐用 Flexbox:
.container {
display: flex;
gap: 20px;
}
.item {
flex: 1;
}
Flexbox 方案更简洁,也不需要处理 inline-block 的空白间隙问题。但面试中,能用传统方式实现,说明你对盒模型理解得很透彻。
自我检测
读完本章后,确认你能回答以下问题:
- 能画出盒模型的四层结构(content、padding、border、margin)
- 能说出 content-box 和 border-box 的区别
- 知道为什么实际开发中推荐全局设置 border-box
- 能解释 margin 合并的三种场景(相邻兄弟、父子、空元素)
- 能说出至少两种解决 margin 塌陷的方法
- 知道内联元素的 width/height 不生效,margin 上下不生效
- 知道内联元素的 padding 上下会渲染但不影响行高
- 能区分 padding 不能为负值而 margin 可以
- 知道 border-box 的 width 不包含 margin
购买课程解锁全部内容
大厂前端面试通关:71 篇构建完整知识体系
¥89.90