跳到主要内容
预计阅读 30 分钟

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-boxborder-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 = 270px
  • border-box:实际占据宽度 = width + 左右 margin = 200 + 20 = 220px(因为 padding 和 border 被包含在 width 里了)

第2题: 间距是 30px,不是 50px。这就是 margin 合并(也叫 margin 塌陷),垂直方向相邻的 margin 会取较大值。

第3题: widthheight 不生效(内联元素不能设置宽高);margin 只有左右生效,上下不生效;padding 上下左右都生效,但上下 padding 不会撑开行高,可能会和其他内容重叠。

如果三道题都答对了,说明你基础不错!继续阅读可以巩固细节。如果有答错的,别担心,看完本文你就全明白了。

什么是盒模型?

在 CSS 中,每一个元素都可以看作是一个矩形的”盒子”。这个盒子由四个部分组成,从内到外依次是:

  1. Content(内容区):元素的实际内容,比如文字、图片
  2. Padding(内边距):内容区与边框之间的空间
  3. Border(边框):围绕 padding 和内容的边框
  4. Margin(外边距):盒子与其他元素之间的空间
┌──────────────────────────────────────┐
│              margin                  │
│  ┌────────────────────────────────┐  │
│  │           border               │  │
│  │  ┌──────────────────────────┐  │  │
│  │  │        padding           │  │  │
│  │  │  ┌──────────────────┐   │  │  │
│  │  │  │     content      │   │  │  │
│  │  │  └──────────────────┘   │  │  │
│  │  └──────────────────────────┘  │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘

这四层结构,就是 CSS 盒模型的全部。你可以在浏览器 DevTools 中选中任意元素,看到这个经典的盒模型示意图。

标准盒模型 vs IE 盒模型

历史上,浏览器对”width 到底包含什么”这个问题有两种不同的理解,由此产生了两种盒模型。

标准盒模型(W3C 盒模型)

W3C 规范规定,widthheight 只指 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 包含contentcontent + padding + border
元素实际宽度width + padding + borderwidth
内容区宽度等于 widthwidth - 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 而设计,没有副作用。

内联元素的盒模型特点

块级元素(divp 等)的盒模型很直观,但内联元素(spanaem 等)的盒模型有自己的”脾气”。

width 和 height 不生效

内联元素的宽高由内容决定,你没办法用 widthheight 来控制。

span {
  width: 200px;  /* 无效 */
  height: 100px; /* 无效 */
}

margin 上下不生效

内联元素的 margin-leftmargin-right 正常工作,但 margin-topmargin-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;
}

为什么?

  1. 心智模型一致:设置 width: 300px 就是 300px,不需要心算
  2. 百分比布局更可控width: 50% 加上 padding 后不会超出父元素
  3. 响应式设计更方便:不会因为加了 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
  • 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 等阻隔,.innermargin-top: 50px 会和 .outermargin-top: 20px 合并。

合并后,.outer 元素表现得像是有 margin-top: 50px(取较大值),而 .inner 紧贴 .outer 的内容区顶部,两者之间没有间距。

要让 .inner 真正在 .outer 内部有 50px 的上边距,可以给 .outer 加上 overflow: hiddendisplay: 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