Skip to content

04|CSS 盒模型与显示模式

CSS 把每个 HTML 元素都视为一个矩形盒子。理解这个盒子的构成——内容区、内边距、边框、外边距——是控制元素尺寸和间距的基础。两个元素为什么挨得太近或太远、设了宽度为什么实际占用空间更大,根子都在盒模型上。

一、盒模型的四个部分

一个元素在页面上占据的空间由四层组成:

┌─────────────────────────────┐
│         margin(外边距)      │  ← 元素与其他元素的距离
│   ┌─────────────────────┐   │
│   │     border(边框)   │   │  ← 边框线
│   │   ┌─────────────┐   │   │
│   │   │  padding(内边距)│   │   │  ← 内容与边框的距离
│   │   │   ┌─────┐   │   │   │
│   │   │   │content│   │   │   │  ← 实际内容区域
│   │   │   └─────┘   │   │   │
│   │   └─────────────┘   │   │
│   └─────────────────────┘   │
└─────────────────────────────┘

用代码展示:

css
.box {
  width: 200px;      /* 内容区宽度 */
  height: 100px;     /* 内容区高度 */
  padding: 20px;     /* 内边距 */
  border: 1px solid #ccc;  /* 边框 */
  margin: 10px;      /* 外边距 */
}

这个元素在页面上实际占用的水平空间是:

margin-left(10) + border-left(1) + padding-left(20) +
width(200) +
padding-right(20) + border-right(1) + margin-right(10) = 262px

二、box-sizing

上面的计算方式(width 只指内容区)是 CSS 的默认行为,称为 content-box。但这种方式不直观:设了 width: 200px,实际宽度却受 paddingborder 影响变成 262px。

box-sizing: border-box 改变了这个行为:

css
* {
  box-sizing: border-box;
}

.box {
  width: 200px;
  padding: 20px;
  border: 1px solid #ccc;
}

此时 width: 200px 指的是内容区 + 内边距 + 边框的总宽度。内容区实际宽度自动计算为 200 - 20 - 1 - 20 - 1 = 158px

含义width 包含
content-box默认值仅内容区
border-box推荐值内容区 + padding + border

现代项目通常在全局重置中统一设置 border-box。这也是 CSS 框架(Bootstrap、Tailwind 等)的标准做法。

三、外边距折叠

垂直方向相邻的两个块级元素,它们的外边距不会相加,而是取较大值:

html
<style>
  .a { margin-bottom: 20px; }
  .b { margin-top: 30px; }
</style>

<div class="a">元素 A</div>
<div class="b">元素 B</div>

A 和 B 之间的间距不是 20 + 30 = 50px,而是 30px(取较大值)。这个现象称为外边距折叠(margin collapsing)。

发生条件:

  • 相邻兄弟元素之间
  • 父元素的第一个/最后一个子元素与父元素的边界之间
  • 空元素自身的上下外边距之间

阻止折叠的方法:

  • 给其中一个元素加 paddingborder
  • 给父元素创建 BFC(如 overflow: hidden
  • 使用 flexgrid 布局

四、display 属性

display 决定元素在页面上的显示模式。HTML 标签有默认的 display 值,但可以通过 CSS 修改。

block(块级)

css
div, p, h1, ul, li {
  display: block;
}

特性:

  • 独占一行,前后产生换行
  • 可以设置 widthheightmarginpadding
  • 默认宽度填满父容器

inline(行内)

css
span, a, strong, em {
  display: inline;
}

特性:

  • 不独占一行,与其他行内元素并排
  • 不能设置 widthheight(由内容决定)
  • 水平方向的 marginpadding 有效,垂直方向不影响布局
css
/* 无效:inline 元素设宽高不会生效 */
span {
  width: 100px;
  height: 50px;
}

inline-block(行内块)

css
.button {
  display: inline-block;
  width: 120px;
  height: 40px;
  line-height: 40px;
  text-align: center;
}

特性:

  • 对外表现像 inline:不独占一行,可以与其他元素并排
  • 对内表现像 block:可以设置 widthheightmarginpadding

适合需要并排显示但又需要控制尺寸的场景,如按钮、卡片缩略图。

none

css
.hidden {
  display: none;
}

元素从页面中完全移除,不占据任何空间。与 visibility: hidden 不同——后者隐藏元素但保留其占据的空间。

其他显示值

现代布局还会用到 flexgridtable 等值,这些在专门的布局篇中详细讲。

五、宽度和高度计算

百分比宽度

css
.container {
  width: 800px;
}

.sidebar {
  width: 25%;      /* 200px */
  float: left;
}

.main {
  width: 75%;      /* 600px */
  float: left;
}

百分比基于父元素的内容区宽度计算。如果父元素有 paddingborder,子元素的百分比不会自动扣除这些值——这就是 border-box 重要的原因。

最小/最大宽度

css
.card {
  width: 100%;
  min-width: 300px;   /* 再窄也不能小于 300px */
  max-width: 600px;   /* 再宽也不能超过 600px */
}

适合响应式设计:在小屏幕上不缩得太窄,在大屏幕上不拉得太宽。

height 的陷阱

css
.container {
  height: 100%;   /* 常常不生效 */
}

百分比高度要求父元素有明确的高度。如果父元素高度由内容撑开(默认行为),height: 100% 无法计算。要让元素占满视口高度,用:

css
.full-height {
  height: 100vh;   /* vh = viewport height,100vh = 视口高度 */
}

六、常见错误

忘记 box-sizing

css
/* 错误:实际宽度变成 242px,布局失控 */
.box {
  width: 200px;
  padding: 20px;
  border: 1px solid #ccc;
}

/* 正确 */
.box {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 1px solid #ccc;
}

inline 元素设垂直 margin

css
/* 无效:inline 元素的 margin-top/bottom 不影响布局 */
span {
  display: inline;
  margin-top: 20px;
}

/* 正确:改用 inline-block 或 block */
span {
  display: inline-block;
  margin-top: 20px;
}

外边距折叠导致间距不符合预期

css
/* 期望间距 40px,实际只有 20px */
.header { margin-bottom: 20px; }
.content { margin-top: 20px; }

/* 解决:给 content 加上 padding-top 替代 margin-top */
.content { padding-top: 20px; }