盒模型

CSS 中的每个元素都带有一个框(盒子),理解这些框对于使用 CSS 创建更复杂的布局或对齐元素至关重要。在本课程中,我们将深入探讨 CSS 盒模型。您将了解它的工作原理以及与之相关的术语。

预备知识 HTML 基础(学习 基本 HTML 语法
学习成果
  • 块级和行内元素
  • 构成元素的各种框以及如何设置它们的样式——内容、外边距、边框、内边距。
  • 替代盒模型(通过 box-sizing: border-box 访问)以及它与常规盒模型的区别。
  • 外边距折叠。
  • 基本的显示值及其对盒行为的影响——blockinlineinline-blocknone

块级和行内框

在 CSS 中,我们有几种类型的框,通常分为块级框行内框。类型指的是框在页面流以及与页面上其他框的关系方面的行为方式。框具有内部显示类型外部显示类型

通常,您可以使用 display 属性为显示类型设置各种值。

如果一个框的显示值为 block,那么

  • 该框将换行。
  • widthheight 属性受尊重。
  • 内边距、外边距和边框将导致其他元素被推离该框。
  • 如果未指定 width,则该框将在行内方向上延伸以填充其容器中可用的空间。在大多数情况下,该框将变得与其容器一样宽,填充 100% 的可用空间。

一些 HTML 元素,例如 <h1><p>,默认使用 block 作为其外部显示类型。

如果一个框的显示类型为 inline,那么

  • 该框不会换行。
  • widthheight 以及上下外边距将不起作用。
  • 上下内边距和边框将改变框的大小,而不影响周围内容的位置,这可能导致重叠。
  • 左右内边距、外边距和边框将影响周围行内内容的位置。

一些 HTML 元素,例如 <a><span><em><strong>,默认使用 inline 作为其外部显示类型。

块级和行内布局是网页上元素的默认行为方式。默认情况下,在没有任何其他指令的情况下,框内的元素也会以正常流方式布局,并表现为块级或行内框。

内部和外部显示类型

blockinline 显示值被称为外部显示类型——它们影响框相对于周围其他框的布局方式。框还具有内部显示类型,它决定了该框内部元素的布局方式。

您可以通过设置内部显示值来改变内部显示类型,例如 display: flex;。该元素仍将使用外部显示类型 block,但这会将内部显示类型更改为 flex。该框的任何直接子元素都将成为弹性项,并根据 弹性盒(Flexbox)规范进行行为。

当您更详细地学习 CSS 布局时,您会遇到 flex 以及您的框可以拥有的各种其他内部值,例如 grid

暂时不必太担心内部和外部的术语;这是内部发生的事情,我们在这里提到它以防您在其他地方遇到它。通常,您只需处理单个 display 值,而无需过多考虑它。

不同显示类型的示例

下面的示例有三个不同的 HTML 元素,它们都具有 block 的外部显示类型。

  • 一个在 CSS 中添加了边框的段落。浏览器将其渲染为块级框。该段落从新行开始,并水平延伸以填充整个可用宽度。

  • 一个列表,使用 display: flex 布局。这为容器的子元素建立了弹性布局,子元素是默认按行布局的弹性项。列表本身是一个块级框,并且——像段落一样——扩展到整个容器宽度并换行。

  • 一个块级段落,其中包含两个 <span> 元素。这些元素通常是 inline,但是,其中一个元素具有 block 类,并设置为 display: block。结果是,那个单词从新行开始,并横跨其父元素的整个宽度。

html
<p>I am a paragraph. A short one.</p>
<ul>
  <li>Item One</li>
  <li>Item Two</li>
  <li>Item Three</li>
</ul>
<p>
  I am another paragraph. Some of the <span class="block">words</span> have been
  wrapped in a <span>span element</span>.
</p>
css
body {
  font-family: sans-serif;
}
p,
ul {
  border: 2px solid rebeccapurple;
  padding: 0.2em;
}

.block,
li {
  border: 2px solid blue;
  padding: 0.2em;
}

ul {
  display: flex;
  list-style: none;
}

.block {
  display: block;
}

在下一个示例中,我们可以看到 inline 元素的行为方式。

  • 第一个段落中的 <span> 元素默认是行内的,因此不会强制换行。

  • 设置为 display: inline-flex<ul> 元素创建一个包含一些弹性项的行内框。

  • 这两个段落都设置为 display: inline。行内弹性容器和段落都一起在一行上运行,而不是换行(如果它们显示为块级元素,它们就会换行)。

要在显示模式之间切换,您可以将 display: inline 更改为 display: block 或将 display: inline-flex 更改为 display: flex

html
<p>
  I am a paragraph. Some of the
  <span>words</span> have been wrapped in a <span>span element</span>.
</p>
<ul>
  <li>Item One</li>
  <li>Item Two</li>
  <li>Item Three</li>
</ul>
<p class="inline">I am a paragraph. A short one.</p>
<p class="inline">I am another paragraph. Also a short one.</p>
css
body {
  font-family: sans-serif;
}
p,
ul {
  border: 2px solid rebeccapurple;
}

span,
li {
  border: 2px solid blue;
}

ul {
  display: inline-flex;
  list-style: none;
  padding: 0;
}

.inline {
  display: inline;
}

目前需要记住的关键是:更改 display 属性的值可以改变框的外部显示类型是块级还是行内。这会改变它在布局中与其他元素一起显示的方式。

什么是 CSS 盒模型?

CSS 盒模型作为一个整体适用于块级框,并定义了框的不同部分——外边距、边框、内边距和内容——如何协同工作以创建您可以在页面上看到的框。行内框只使用盒模型中定义的部分行为。

为了增加复杂性,存在标准盒模型和替代盒模型。默认情况下,浏览器使用标准盒模型。

框的组成部分

在 CSS 中构成一个块级框,我们有

  • 内容框:显示内容的区域;使用 widthheight 等属性调整其大小。
  • 内边距框:内边距围绕内容作为空白;使用 padding 和相关属性调整其大小。
  • 边框框:边框框包裹内容和任何内边距;使用 border 和相关属性调整其大小。
  • 外边距框:外边距是最外层,包裹内容、内边距和边框作为此框与其他元素之间的空白;使用 margin 和相关属性调整其大小。

下图显示了这些层

Diagram of the box model

标准 CSS 盒模型

在标准盒模型中,如果您在框上设置 widthheight 属性值,这些值定义了内容框widthheight。然后将任何内边距和边框添加到这些尺寸中,以获得框所占用的总大小(请参见下图)。

如果我们假设一个框具有以下 CSS

css
.box {
  width: 350px;
  height: 150px;
  margin: 10px;
  padding: 25px;
  border: 5px solid black;
}

该框实际占用的空间将是 410px 宽(350 + 25 + 25 + 5 + 5)和 210px 高(150 + 25 + 25 + 5 + 5)。

Showing the size of the box when the standard box model is being used.

注意: 外边距不计入框的实际大小——当然,它会影响框在页面上占用的总空间,但仅限于框外部的空间。框的区域止于边框——它不会延伸到外边距中。

替代 CSS 盒模型

在替代盒模型中,任何宽度都是页面上可见框的宽度。内容区域宽度是该宽度减去内边距和边框的宽度(参见下图)。这很方便,因为无需将边框和内边距加起来即可获得框的实际大小。

要为元素开启替代模型,请在其上设置 box-sizing: border-box

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

如果我们假设该框具有与上面相同的 CSS

css
.box {
  width: 350px;
  height: 150px;
  margin: 10px;
  padding: 25px;
  border: 5px solid black;
}

该框实际占用的空间现在将是行内方向 350px,块级方向 150px

Showing the size of the box when the alternate box model is being used.

要为所有元素使用替代盒模型(这是开发人员的常见选择),请在 <html> 元素上设置 box-sizing 属性,并将所有其他元素设置为继承该值

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

*,
*::before,
*::after {
  box-sizing: inherit;
}

要理解其基本思想,您可以阅读 CSS Tricks 关于 box-sizing 的文章

玩转盒模型

在下面的示例中,您可以看到两个框。两者都具有 .box 类,这使得它们具有相同的 widthheightmarginborderpadding。唯一的区别是第二个框已设置为使用替代盒模型。您能否更改第二个框的大小(通过向 .alternate 类添加 CSS)以使其在宽度和高度上与第一个框匹配?

html
<div class="box">I use the standard box model.</div>
<div class="box alternate">I use the alternate box model.</div>
css
.box {
  border: 5px solid rebeccapurple;
  background-color: lightgray;
  padding: 40px;
  margin: 40px;
  width: 300px;
  height: 150px;
}

.alternate {
  box-sizing: border-box;
}

注意: 您可以在我们的 css-examples 仓库中找到此任务的解决方案:css-examples 仓库

使用浏览器开发工具查看盒模型

您的浏览器开发工具可以使理解盒模型变得容易得多——它们可以向您显示元素的大小以及其外边距、内边距和边框。以这种方式检查元素是了解您的框是否确实是您认为的大小的好方法!

Inspecting the box model of an element using Firefox DevTools

外边距、内边距和边框

您已经在上面的示例中看到了 marginpaddingborder 属性的作用。该示例中使用的属性是简写,允许我们一次设置框的所有四个侧面。这些简写也有等效的长手属性,允许单独控制框的不同侧面。

让我们更详细地探讨这些属性。

外边距

外边距是框周围的不可见空间。它将其他元素推离框。外边距可以具有正值或负值。在框的一侧设置负外边距可能会导致它与页面上的其他内容重叠。无论您使用的是标准盒模型还是替代盒模型,外边距始终在计算可见框的大小之后添加。

我们可以使用 margin 属性一次控制元素的所有外边距,或者使用等效的长手属性单独控制每个侧面

玩转外边距

编辑下面的示例。尝试更改外边距值,看看由于外边距在此元素和包含元素之间创建或移除空间(如果是负外边距)而导致框如何被推来推去。

html
<div class="container">
  <div class="box">Change my margin.</div>
</div>
css
.container {
  border: 5px solid blue;
  margin: 40px;
}

.box {
  border: 5px solid rebeccapurple;
  background-color: lightgray;
  padding: 10px;
  height: 100px;
  /* try changing the margin properties: */
  margin-top: -40px;
  margin-right: 30px;
  margin-bottom: 40px;
  margin-left: 4em;
}

外边距折叠

根据两个外边距接触的元素是正外边距还是负外边距,结果将有所不同

  • 两个正外边距将合并成一个外边距。其大小将等于最大的单个外边距。
  • 两个负外边距将折叠,并使用最小的(离零最远)值。
  • 如果一个外边距是负数,其值将从总数中减去

在下面的示例中,我们有两个段落。顶部段落的 margin-bottom 为 50 像素,另一个段落的 margin-top 为 30 像素。外边距已折叠,因此框之间的实际外边距为 50 像素,而不是两个外边距的总和。

您可以通过将第二个段落的 margin-top 设置为 0 来测试这一点。两个段落之间可见的外边距不会改变——它保留了第一个段落 margin-bottom 中设置的 50 像素。如果您将其设置为 -10px,您将看到总外边距变为 40px——它从 50px 中减去。

html
<div class="container">
  <p class="one">I am paragraph one.</p>
  <p class="two">I am paragraph two.</p>
</div>
css
.container {
  border: 5px solid blue;
  margin: 40px;
}

p {
  border: 5px solid rebeccapurple;
  background-color: lightgray;
  padding: 10px;
}
.one {
  margin-bottom: 50px;
}

.two {
  margin-top: 30px;
}

许多规则规定了外边距何时折叠以及何时不折叠。有关更多信息,请参阅关于掌握外边距折叠的详细页面。要记住的主要事情是,如果您正在使用外边距创建空间但没有得到预期的空间,那么外边距折叠是会发生的事情。

边框

边框绘制在框的外边距和内边距之间。如果您使用的是标准盒模型,则边框的大小会添加到内容框的 widthheight 中。如果您使用的是替代盒模型,则边框越大,内容框就越小,因为边框会占用元素框可用 widthheight 的一部分。

为了设置边框样式,有大量的属性——有四个边框,每个边框都有我们可能想要操作的样式、宽度和颜色。

您可以使用 border 属性一次设置所有四个边框的宽度、样式或颜色。

要单独设置每个侧面的属性,请使用

要设置所有侧面的宽度、样式或颜色,请使用

要设置单个侧面的宽度、样式或颜色,请使用更细粒度的长手属性之一

玩转边框

在下面的示例中,我们使用了各种简写和长手属性来创建边框。编辑不同的属性以检查您是否理解它们的工作原理。MDN 边框属性页面提供了有关不同可用边框样式的信息。

html
<div class="container">
  <div class="box">Change my borders.</div>
</div>
css
body {
  font-family: sans-serif;
}
.container {
  margin: 40px;
  padding: 20px;
  border-top: 5px dotted green;
  border-right: 1px solid black;
  border-bottom: 20px double rgb(23 45 145);
}

.box {
  padding: 20px;
  background-color: lightgray;
  border: 1px solid #333333;
  border-top-style: dotted;
  border-right-width: 20px;
  border-bottom-color: hotpink;
}

内边距

内边距位于边框和内容区域之间,用于将内容推离边框。与外边距不同,您不能有负内边距。应用于元素的任何背景都将显示在内边距后面。

padding 属性控制元素所有侧面的内边距。要单独控制每个侧面,请使用这些长手属性

玩转内边距

在下面的示例中,编辑类 .box 的内边距值,看看这如何改变文本相对于框的起始位置。您还可以更改类 .container 的内边距,以在容器和框之间创建空间。您可以更改任何元素的内边距,以在其边框和元素内部的内容之间创建空间。

html
<div class="container">
  <div class="box">Change my padding.</div>
</div>
css
body {
  font-family: sans-serif;
}
.box {
  border: 5px solid rebeccapurple;
  background-color: lightgray;
  padding-top: 0;
  padding-right: 30px;
  padding-bottom: 40px;
  padding-left: 4em;
}

.container {
  border: 5px solid blue;
  margin: 40px;
  padding: 20px;
}

盒模型和行内框

以上所有内容完全适用于块级框。其中一些属性也可以应用于行内框,例如由 <span> 元素创建的行内框。

在下面的示例中,我们有一个段落中的 <span>。我们已对其应用 widthheightmarginborderpadding。您可以看到宽度、高度以及上下外边距不影响 <span>。上下内边距和边框会改变行内框的大小,但不会影响周围内容的位置。相反,上下内边距和边框会重叠段落中的其他单词。只有左右内边距、外边距和边框会影响 <span> 周围文本的位置。

html
<p>
  I am a paragraph and this is a <span>span</span> inside that paragraph. A span
  is an inline element and so does not respect width and height.
</p>
css
body {
  font-family: sans-serif;
}
p {
  border: 2px solid rebeccapurple;
  width: 200px;
}
span {
  margin: 20px 30px;
  padding: 10px 20px;
  width: 80px;
  height: 150px;
  background-color: lightblue;
  border: solid blue;
  border-width: 7px 1px;
}

使用 display: inline-block

display: inline-blockdisplay 的一个特殊值,它提供了 inlineblock 之间的中间地带。如果您不希望某个项换行,但又希望它遵守 widthheight 并避免上面看到的重叠,请使用它。

具有 display: inline-block 的元素会执行我们已经知道的块级事物的一个子集

  • widthheight 属性受尊重。
  • paddingmarginborder 将导致其他元素被推离该框。

但是,它不会换行,并且只有在您明确添加 widthheight 属性时才会变得大于其内容。

玩转 inline-block

在下一个示例中,我们已将 display: inline-block 添加到我们的 <span> 元素。尝试将其更改为 display: block 或完全删除该行以查看显示模型的差异

html
<p>
  I am a paragraph and this is a <span>span</span> inside that paragraph. A span
  is an inline element and so does not respect width and height.
</p>
css
body {
  font-family: sans-serif;
}
p {
  border: 2px solid rebeccapurple;
  width: 300px;
}

span {
  margin: 20px;
  padding: 20px;
  width: 80px;
  height: 50px;
  background-color: lightblue;
  border: 2px solid blue;
  display: inline-block;
}

这在您希望通过添加 padding 为链接提供更大的点击区域时非常有用。<a> 是一种行内元素,就像 <span> 一样;您可以使用 display: inline-block 允许在其上设置内边距,使用户更容易点击链接。

您在导航栏中经常看到这种情况。下面的导航使用弹性盒(flexbox)以行形式显示,我们已向 <a> 元素添加了内边距,因为我们希望在 <a> 悬停时能够更改 background-color。内边距似乎与 <ul> 元素上的边框重叠。这是因为 <a> 是一个行内元素。

在带有 .links-list a 选择器的规则中添加 display: inline-block;,您将看到它如何通过使其他元素尊重内边距来解决此问题

html
<nav>
  <ul class="links-list">
    <li><a href="">Link one</a></li>
    <li><a href="">Link two</a></li>
    <li><a href="">Link three</a></li>
  </ul>
</nav>
css
ul {
  font-family: sans-serif;
  display: flex;
  list-style: none;
  border: 1px solid black;
}

li {
  margin: 5px;
}

.links-list a {
  background-color: rgb(179 57 81);
  color: white;
  text-decoration: none;
  padding: 1em 2em;
}

.links-list a:hover {
  background-color: rgb(66 28 40);
  color: white;
}

总结

这就是您需要了解的盒模型大部分内容。如果您将来对布局中盒子的大小感到困惑,您可能需要回到这节课。

在下一篇文章中,我们将为您提供一些测试,您可以用来检查您对我们提供的 CSS 盒模型信息的理解和记忆程度。