盒子模型

CSS 中的所有内容都包含在一个框中,理解这些框是能够使用 CSS 创建更复杂的布局或将项目与其他项目对齐的关键。在本课中,我们将了解 CSS 盒子模型。您将了解其工作原理以及相关的术语。

先决条件 已安装基本软件,了解文件处理的基本知识,HTML 基础知识(学习HTML 简介),以及对 CSS 工作原理的了解(学习CSS 入门)。
目标 了解 CSS 盒子模型,构成盒子模型的元素以及如何切换到备用模型。

块级盒子和内联盒子

在 CSS 中,我们有几种类型的盒子,通常分为块级盒子内联盒子。类型指的是盒子在页面流中以及相对于页面上其他盒子的行为方式。盒子具有内部显示类型外部显示类型

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

外部显示类型

如果盒子的外部显示类型为block,那么

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

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

如果盒子的外部显示类型为inline,那么

  • 该盒子将不会换行。
  • widthheight属性将不适用。
  • 顶部和底部填充、边距和边框将适用,但不会导致其他内联盒子远离该盒子。
  • 左侧和右侧填充、边距和边框将适用,并将导致其他内联盒子远离该盒子。

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

内部显示类型

盒子还具有内部显示类型,它决定该盒子内部的元素如何排列。

块级和内联布局是网页上内容默认的行为方式。默认情况下,如果没有其他指令,盒子内的元素也会以正常流方式排列,并表现为块级或内联盒子。

例如,您可以通过设置display: flex;来更改内部显示类型。该元素将继续使用外部显示类型block,但这会将内部显示类型更改为flex。该盒子的所有直接子元素将成为弹性项目,并根据Flexbox规范进行操作。

当您继续学习有关 CSS 布局的更多详细信息时,您将遇到flex,以及盒子的各种其他内部值,例如grid

注意:要了解更多关于 display 的值以及盒子在块级和内联布局中的工作方式,请查看 MDN 指南块级和内联布局

不同显示类型的示例

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

  • 一个添加了 CSS 边框的段落。浏览器将其呈现为块级盒子。段落从新行开始,并扩展到整个可用宽度。
  • 一个列表,它使用display: flex进行布局。这为容器的子元素(即弹性项目)建立了弹性布局。列表本身是一个块级盒子,与段落一样,扩展到整个容器宽度,并在新行换行。
  • 一个块级段落,其中包含两个<span>元素。这些元素通常是inline,但是,其中一个元素具有类名 "block",它被设置为display: block

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

  • 第一个段落中的<span>元素默认是内联的,因此不会强制换行。
  • 设置为display: inline-flex<ul>元素创建一个包含一些弹性项目的内联盒子。
  • 这两个段落都被设置为display: inline。内联弹性容器和段落都在一行上运行,而不是换行(如果它们作为块级元素显示,则会换行)。

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

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

什么是 CSS 盒子模型?

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

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

盒子的组成部分

构成 CSS 中的块级盒子的是

  • 内容盒子:显示内容的区域;使用像inline-sizeblock-sizewidthheight这样的属性对其进行大小调整。
  • 填充盒子:填充作为空白空间位于内容周围;使用padding及其相关属性对其进行大小调整。
  • 边框盒子:边框盒子包裹内容和任何填充;使用border及其相关属性对其进行大小调整。
  • 边距盒子:边距是最外层,包裹内容、填充和边框,作为该盒子与其他元素之间的空白空间;使用margin及其相关属性对其进行大小调整。

下图显示了这些层

Diagram of the box model

标准 CSS 盒子模型

在标准盒子模型中,如果您在盒子中设置inline-sizeblock-size(或widthheight)属性值,这些值将定义内容盒子inline-sizeblock-size(在水平语言中为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;
  inline-size: 350px;
  height: 150px;
  block-size: 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。唯一的区别是第二个盒子被设置为使用备用盒子模型。

您可以更改第二个盒子的大小(通过将 CSS 添加到.alternate类中)使其在宽度和高度上与第一个盒子匹配吗?

注意:您可以在此处找到此任务的解决方案。

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

您的浏览器开发者工具可以使理解盒子模型变得更加容易。如果您在 Firefox 的开发者工具中检查元素,您可以看到元素的大小及其边距、填充和边框。以这种方式检查元素是了解您的盒子是否真的如您所想的那样大小的绝佳方法!

Inspecting the box model of an element using Firefox DevTools

边距、填充和边框

您已经看到上面的示例中使用的marginpaddingborder属性。这些示例中使用的属性是简写,使我们能够一次设置盒子的所有四个边。这些简写也具有等效的完整属性,它们允许单独控制盒子的不同边。

让我们更详细地了解这些属性。

边距

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

我们可以使用margin属性一次控制元素的所有边距,或者使用等效的完整属性分别控制每个边

在以下示例中,尝试更改边距值以查看盒子如何由于边距创建或移除空间(如果是负边距)而被推到周围,在该元素与其包含元素之间创建或移除空间。

边距折叠

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

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

在下面的例子中,我们有两个段落。顶部的段落有一个margin-bottom为50像素,另一个段落有一个margin-top为30像素。这些边距合并在一起,因此盒子之间的实际边距是50像素,而不是两个边距的总和。

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

许多规则决定了何时边距会合并或不会合并。有关更多信息,请参阅有关掌握边距合并的详细页面。需要记住的最重要的一点是,如果使用边距创建空间,但没有获得预期的空间,则会发生边距合并。

边框

边框绘制在盒子的边距和填充之间。如果你使用的是标准盒子模型,边框的大小将添加到内容盒子的widthheight中。如果你使用的是备用盒子模型,那么边框越大,内容盒就越小,因为边框会占用元素盒子可用widthheight的一部分。

对于样式化边框,有很多属性——有四个边框,每个边框都有一个样式、宽度和颜色,我们可能需要操作。

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

要分别设置每个边的属性,请使用

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

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

在下面的例子中,我们使用了各种简写和长手来创建边框。尝试使用不同的属性,确保你理解它们的工作原理。边框属性的MDN页面提供了有关不同可用边框样式的信息。

填充

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

padding属性控制元素所有边的填充。要分别控制每边,请使用这些长手属性

在下面的例子中,你可以更改类.box上的填充值,以查看这会如何改变文本相对于盒子的起始位置。你也可以更改类.container上的填充,在容器和盒子之间创建空间。你可以更改任何元素上的填充,在元素的边框与其内部的内容之间创建空间。

盒子模型和内联盒子

以上所有内容都完全适用于块盒子。一些属性也可能适用于内联盒子,例如由<span>元素创建的那些。

在下面的例子中,我们有一个<span>在段落内。我们对其应用了widthheightmarginborderpadding。你可以看到宽度和高度被忽略了。顶部和底部的边距、填充和边框被尊重,但不会改变其他内容与内联盒子的关系。填充和边框与段落中的其他单词重叠。左右填充、边距和边框会将其他内容从盒子中移开。

使用 display: inline-block

display: inline-blockdisplay的一个特殊值,它提供了inlineblock之间的中间地带。如果你不希望项目换行,但希望它尊重widthheight,并避免上述重叠,请使用它。

具有display: inline-block的元素执行我们已经知道的一些块操作

  • widthheight属性被尊重。
  • paddingmarginborder会导致其他元素被推离盒子。

但是,它不会换行,并且只有在你显式添加widthheight属性时才会变得比其内容更大。

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

当你想通过添加padding来使链接具有更大的点击区域时,这将非常有用。<a>是一个内联元素,类似于<span>;你可以使用display: inline-block来允许对其设置填充,从而使用户更容易点击链接。

你经常在导航栏中看到这种方式。下面的导航使用flexbox在一行中显示,并且我们已向<a>元素添加了填充,因为我们希望能够在鼠标悬停在<a>上时更改background-color。填充似乎与<ul>元素上的边框重叠。这是因为<a>是一个内联元素。

display: inline-block添加到具有.links-list a选择器的规则中,你将看到它如何通过使其他元素尊重填充来解决这个问题。

测试您的技能!

你已经阅读完本文的结尾,但你能记住最重要的信息吗?在你继续之前,你可以找到一些进一步的测试来验证你是否保留了这些信息——请参阅测试你的技能:盒子模型.

总结

这些就是你关于盒子模型需要了解的大部分内容。如果你发现自己在布局中对盒子的大小感到困惑,你将来可能需要返回本课。

在下一篇文章中,我们将介绍如何使用背景和边框使你的普通盒子看起来更有趣。