层叠、优先级与继承 - 学习 Web 开发

层叠、优先级与继承
=========

* 上一页
* Overview: Building blocks
* 下一页

本文旨在让你理解 CSS 的一些最基本的概念——层叠、优先级和继承——这些概念决定着如何将 CSS 应用到 HTML 中，以及如何解决冲突。

尽管与课程的其他部分相比，完成这节课可能看起来没有那么直接的相关性，而且更学术性一些，但是理解这些东西将为你以后节省很多痛苦！我们希望你仔细阅读本节，并在继续下一步学习之前，确保你是否理解了这些概念。

|  |  |
| --- | --- |
| 前提： |
 基本的计算机知识、安装基本的软件、文件处理基本知识、HTML
 基础知识（HTML
简介），以及 CSS 如何工作的基本常识（学习 CSS 第一步）。
  |
| 目标： | 学习层叠、优先级，以及在 CSS 中继承是如何工作的。 |

冲突规则
----

CSS 代表**层叠样式表**（Cascading Style Sheets），理解第一个词*层叠*（cascade）很重要——层叠的表现方式是理解 CSS 的关键。

在某些时候，在做一个项目过程中你会发现一些应该产生效果的样式没有生效。通常的原因是你创建了两个应用于同一个元素的规则。与**层叠**密切相关的概念是**优先级**（specificity），决定在发生冲突的时候应该使用哪条规则。设计元素样式的规则可能不是期望的规则，因此需要了解这些机制是如何工作的。

这里也有**继承**的概念，也就是在默认情况下，一些 css 属性继承当前元素的父元素上设置的值，有些则不继承。这也可能导致一些和期望不同的结果。

我们来快速地看下正在处理的关键问题，然后依次了解它们是如何相互影响的，以及如何和 CSS 交互的。虽然这些概念难以理解，但是随着不断的练习，你会慢慢熟悉它的工作原理。

### 层叠

样式表**层叠**——简单的说，就是 CSS 规则的顺序很重要；当应用两条同级别的规则到一个元素的时候，写在后面的就是实际使用的规则。

下面的示例中，我们有两个关于 `<h1>` 的规则。`<h1>` 最后显示蓝色——这两个规则来自同一个源，且具有相同的元素选择器，有相同的优先级，所以顺序在最后的生效。

### 优先级

浏览器是根据优先级来决定当多个规则有不同选择器对应相同的元素的时候需要使用哪个规则。它基本上是一个衡量选择器具体选择哪些区域的尺度：

* 一个元素选择器不是很具体，则会选择页面上该类型的所有元素，所以它的优先级就会低一些。
* 一个类选择器稍微具体点，则会选择该页面中有特定 `class` 属性值的元素，所以它的优先级就要高一点。

下面我们再来介绍两个适用于 `<h1>` 的规则。下面的 `<h1>` 最后会显示红色——类选择器 `main-heading` 有更高的优先级，因此就会被应用——即使元素选择器顺序在它后面。

稍后我们会详细解释优先级算法。

### 继承

继承也需要在上下文中去理解——一些设置在父元素上的 CSS 属性是可以被子元素继承的，有些则不能。

举一个例子，如果你设置一个元素的 `color` 和 `font-family`，每个在里面的元素也都会有相同的属性，除非你直接在元素上设置属性。

一些属性是不能继承的——举个例子如果你在一个元素上设置 `width` 为 50% ，所有的后代不会是父元素的宽度的 50% 。如果这个也可以继承的话，CSS 就会很难使用了！

**备注：** 在 MDN CSS 属性参考页面，你会发现一个名为“形式定义”的技术信息框，其中列举了属性的很多数据信息，包括能否被继承。以 color 属性的形式定义部分为例。

理解这些概念是如何协同工作的
--------------

这三个概念一起来控制 CSS 规则应用于哪个元素；在下面的内容中，我们将看到它们是如何协同工作的。有时候会感觉有些复杂，但是当你对 CSS 有更多经验的时候，你就可以记住它们，即便忘记了细节，可以在网上查到，有经验的开发人员也不会记得所有细节。

以下视频展示了如何使用 Firefox 的开发者工具来检查网页的级联和优先级等：

理解继承
----

我们从继承开始。下面的例子中我们有一个 `<ul>` 元素，里面有两个无序列表。我们已经给 `<ul>` 设置了边框（border）、内边距（padding）和字体颜色。

`color` 属性是一个继承属性。因此，`color` 属性应用在直接子元素和其后代——直接子元素 `<li>` 和第一个嵌套列表中的子项。然后添加了一个 `special` 类到第二个嵌套列表，其中使用了不同的颜色。然后通过它的子元素继承。

像 `width`（上面提到的）、`margin`、`padding` 和 `border` 不会被继承。如果 border 可以被继承，每个列表和列表项都会获得一个边框——可能就不是我们想要的结果！

尽管每个 CSS 属性页都列出了属性是否被继承，但我们通常可以通过常识来判断哪些属性属于默认继承。

### 控制继承

CSS 为控制继承提供了五个特殊的通用属性值。每个 CSS 属性都接收这些值。

`inherit`

设置该属性会使子元素属性和父元素相同。实际上，就是“开启继承”。

`initial`

将应用于选定元素的属性值设置为该属性的初始值。

`revert` (en-US)

将应用于选定元素的属性值重置为浏览器的默认样式，而不是应用于该属性的默认值。在许多情况下，此值的作用类似于 `unset`。

`revert-layer` (en-US)

将应用于选定元素的属性值重置为在上一个层叠层中建立的值。

`unset`

将属性重置为自然值，也就是如果属性是自然继承那么就是 `inherit`，否则和 `initial` 一样

**备注：** 见 CSS 声明的源有更多信息和具体是怎么工作的。

我们可以查看一个链接列表来探索这些值是如何运作的。在下面的实例中，你可以通过修改 CSS 来查看它们的功能，写代码是掌握 HTML 和 CSS 最好的办法。

示例：

1. 第二个列表项应用了类 `my-class-1`。它设置了内部元素来继承属性。如果你删除这个类，它会如何改变链接的颜色？
2. 你知道为什么第三个和第四个链接会是这样的颜色？第三个链接设置了 `initial`，这意味着它使用了属性的初始值（在本例中为黑色），而不是链接的浏览器默认样式的蓝色。第四个设置了 `unset`，这意味着链接文本会使用其父元素的颜色——绿色。
3. 如果你为 `<a>` 元素定义新的颜色（例如：`a { color: red; }`），哪些链接会改变颜色？
4. 阅读下一节有关重置所有属性的内容后，回到这里，并将 `color` 属性改为 `all` 属性。注意第二个链接是怎么出现在新的一行中，还带有一个无序列表的符号的。你认为继承了哪些属性。

### 重设所有属性值

CSS 的简写属性 `all` 可以用于同时将这些继承值中的一个应用于（几乎）所有属性。它的值可以是其中任意一个（`inherit`、`initial`、`unset` 或 `revert`）。这是一种撤销对样式所做更改的简便方法，以便回到之前已知的起点。

下面的示例中有两个块级引用元素。第一个用元素本身的样式，第二个设置 `all` 为 `unset`

试着将 `all` 改成其他可用的值然后观察有什么不一样。

理解层叠
----

我们现在明白了为什么嵌套在 HTML 结构中的段落和应用于正文中的 CSS 颜色相同，从入门课程中，我们了解了如何将文档中的任何修改应用于某个对象的 CSS，无论是把 CSS 指定某个元素还是创建一个类。现在，我们将要了解层叠如何定义在不止一个元素的时候怎么应用 CSS 规则。

有三个因素需要考虑，根据重要性排序如下，后面的更重要：

1. **资源顺序**
2. **优先级**
3. **重要程度**

我们从上往下看，看看浏览器是如何决定该应用哪个 CSS 规则的。

### 资源顺序

我们已经看到了顺序对于层叠的重要性。如果你有超过一条规则，而且都是相同的权重，那么最后面的规则会应用。可以理解为后面的规则覆盖前面的规则，直到最后一个开始设置样式。

资源顺序仅在规则的优先级相同时才体现出来，下面让我们看一下优先级：

### 优先级

你会发现在一些情况下，有些规则在最后出现，但是却应用了前面的具有冲突的规则。这是因为前面的有更高的**优先级**——它范围更小，因此浏览器就把它选择为元素的样式。

就像前面看到的，类选择器的权重大于元素选择器，因此类上定义的属性将覆盖应用于元素上的属性。

这里需要注意虽然我们考虑的是选择器，以及应用在选中对象上的规则，但不会覆盖所有规则，只覆盖相同的属性。

这样可以避免重复的 CSS。一种常见的做法是给基本元素定义通用样式，然后给不同的元素创建对应的类。举个例子，在下面的样式中我给 2 级标题定义了通用样式，然后创建了一些类只修改部分属性的值。最初定义的值应用于所有标题，然后更具体的值通过对应类来实现。

现在让我们来看看浏览器如何计算优先级。我们已经知道一个元素选择器比类选择器的优先级更低，会被其覆盖。本质上，不同类型的选择器有不同的分数值，把这些分数相加就得到特定选择器的权重，然后就可以进行匹配。

一个选择器的优先级可以说是由三个不同的值（或分量）相加，可以认为是百（ID）十（类）个（元素）——三位数的三个位数：

* **ID**：选择器中包含 ID 选择器则百位得一分。
* **类**：选择器中包含类选择器、属性选择器或者伪类则十位得一分。
* **元素**：选择器中包含元素、伪元素选择器则个位得一分。

**备注：** 通用选择器（`*`）、组合符（`+`、`>`、`~`、' '）和调整优先级的选择器（`:where()`）不会影响优先级。

否定（`:not()`）和任意匹配（`:is()`）伪类本身对优先级没有影响，但它们的参数则会带来影响。参数中，对优先级算法有贡献的参数的优先级的最大值将作为该伪类选择器的优先级。

下面有几个单独的例子，有空可以看看。试着思考下，理解为什么优先级是这样定的。我们还没有深入介绍选择器，不过你可以在 MDN 的选择器参考页面找到每个选择器的详细信息。

| 选择器 | ID | 类 | 元素 | 优先级 |
| --- | --- | --- | --- | --- |
| `h1` | 0 | 0 | 1 | 0-0-1 |
| `h1 + p::first-letter` | 0 | 0 | 3 | 0-0-3 |
| `li > a[href*="en-US"] > .inline-warning` | 0 | 2 | 2 | 0-2-2 |
| `#identifier` | 1 | 0 | 0 | 1-0-0 |
| `button:not(#mainBtn, .cta)` | 1 | 0 | 1 | 1-0-1 |

在我们继续之前，先看看这个示例。

这里发生了什么？首先，我们先看看最上面的选择器规则，你会发现，我们已经把优先级计算出来放在最前面的注释里。

* 前面两个选择器都是链接背景颜色的样式——第二个赢了使得背景变成蓝色，因为它多了一个 ID 选择器：优先级 2-0-1 vs. 1-0-1。
* 第三四个选择器都是链接文本颜色样式——后者赢了使得文本变成白色，因为它虽然少一个元素选择器，但是多了一个类选择器。所以优先级是 1-1-3 vs. 1-0-4。
* 第 5 到 7 个选择器都是鼠标悬停时链接边框样式。第六个显然输给了第五个，优先级是 0-2-3 vs. 0-2-4——少了个元素选择器。第七个，比第五第六都高——子选择器数量相同，但是有一个元素选择器变成类选择器。所以最后优先级是 0-3-3 vs. 0-2-3 和 0-2-4。

**备注：** 每一个选择器类编都有它自己的优先级等级，它们不会被具有较低优先级的选择器覆盖。例如，权重为*一百万*的**类**选择器不会覆盖权重为*一*的 **ID** 选择器。

评估优先级的最佳方法是对不同的优先级等级单独进行评分，并从最高的等级开始，必要时再计算低优先级等级的权重。即，仅当某一列的优先级权重相同时，你才需要评估下一列；否则，你可以直接忽略低等级的选择器，因为它们无法覆盖高优先级等级的选择器。

### 内联样式

内联样式，即 `style` 属性内的样式声明，优先于所有普通的样式，无论其优先级如何。这样的声明没有选择器，但它们的优先级可以理解为 1-0-0-0；即无论选择器中有多少个 ID，它总是比其他任何优先级的权重都要高。

### !important

有一个特殊的 CSS 可以用来覆盖所有上面所有优先级计算，不过需要很小心的使用——`!important`。用于修改特定属性的值，能够覆盖普通规则的层叠。

**备注：** 了解 `!important` 是为了在阅读别人代码的时候知道有什么作用。**但是，强烈建议除了非常情况不要使用它。**`!important` 改变了层叠的常规工作方式，它会使调试 CSS 问题非常困难，特别是在大型样式表中。

看看这个示例，有两个段落，其中一个有 ID。

让我们看看会发生什么——如果有什么疑问，试着删除一些属性：

1. 你会发现第三个规则 `color` 和 `padding` 的值被应用了，但是 `background-color` 没有。为什么？应该三个都应用，因为顺序规则是后面覆盖前面。
2. 无论如何，上面的规则赢了，因为类选择器比元素选择器有更高的优先级。
3. 两个元素都有 `better` `class`，但是第二个有 `id` 。因为 ID 选择器比类选择器优先级更高（一个页面只能有一个独特的 ID，但是很多元素都有相同的类——ID 对于目标非常独特），红色背景和 1px 黑色边框应该都被应用到第二个元素，第一个元素应该是灰色背景和 no border，根据类选择器。
4. 第二个元素有红色背景但是没有边框。为什么？因为 `!important` 声明在第二条规则里——在 `border: none` 后面，说明即使计算优先级低，这个属性也使用这个值。

**备注：** 覆盖 `!important` 唯一的办法就是另一个 `!important` 具有相同*优先级*而且顺序靠后，或者更高优先级。

在一种情况下，你可能不得不使用它：当你不能编辑核心的 CSS 模块，不能用任何其他方式覆盖，而你又真的想要覆盖一个样式时。但说真的，如果可以避免的话就不要用它。

CSS 位置的影响
---------

最后，也很有用，CSS 声明的优先级取决于定义它的样式表和级联层。

它让用户可以设置自定义样式表来覆盖开发人员定义的样式。例如用户可能视力受损，并想在所有网页上设置两倍的正常字体大小，以便更容易进行阅读。

也可以在级联层中声明开发人员定义的样式：你可以让非分层样式覆盖分层样式，或者你可以让后面的层中声明的样式覆盖先前的层中声明的样式。例如，作为开发人员，你可能无法编辑第三方的样式表，但你可以将外部的样式表导入级联层中，以便你自己的样式可以轻松地覆盖导入的样式，无需担心第三方选择器的优先级。

### 覆盖声明的顺序

相互冲突的声明将按以下顺序应用，后一种声明将覆盖前一种声明：

1. 用户代理样式表中的声明（例如，浏览器的默认样式，在没有设置其他样式时使用）。
2. 用户样式表中的常规声明（由用户设置的自定义样式）。
3. 作者样式表中的常规声明（这些是我们 web 开发人员设置的样式）。
4. 作者样式表中的 `!important` 声明
5. 用户样式表中的 `!important` 声明
6. 用户代理样式表中的 `!important` 声明

**备注：** 标记为 `!important` 的样式的优先级顺序是颠倒的。web 开发人员的样式表覆盖用户的样式表是有意义的，因此设计可以按预期进行，但是有时用户有充足的理由覆盖 web 开发人员的样式，正如上面提到的——这可以通过在他们的规则中使用 `!important` 来实现。

级联层的顺序
------

尽管级联层属于高级的主题，你可能不会立刻使用此特性，但了解层是如何级联的非常重要。

在级联层中声明 CSS 是，优先级的顺序由声明层的顺序来决定。在任何层之外声明的 CSS 样式会被按声明的顺序组合在一起，形成一个未命名的层，它会被当作最后声明的层。对于存在冲突的常规（没有 `!important` 声明）样式，后面的层比先前定义的层的优先级高。但对于带有 `!important` 标记的样式，其顺序相反——先前的层中的 important 样式比后面的层以及为在层中声明的 important 样式优先级要高。但内联样式比所有作者定义的样式的优先级都要高，不受级联层规则的影响。

当你在不同的层中有多个样式块，且其中提供了对于某一元素的单一属性的相互冲突的值时，声明该冲突样式的层的顺序将决定其优先级。而不是高优先级的层直接覆盖低优先级的层中的所有样式。需要注意的是单独的一个层中的样式的优先级仍旧会起作用。

让我们讨论一下上例以了解发生了什么。示例中，声明了两个级联层，按 `firstLayer` 和 `secondLayer` 的顺序声明。即使 `secondLayer` 的优先级更高，但其中声明的样式没有被使用。为什么？因为不分层的常规样式具有更高的优先级，覆盖了层中的常规样式，不论层的优先级如何，而具有 important 的样式，则是先前声明的层会覆盖后声明的层，而不是层的优先级。

如果你修改示例中 CSS 的第一行为 `@layer secondLayer, firstLayer;`，就会修改层声明的顺序，所有 `firstLayer` 中的 important 样式会被 `secondLayer` 中的冲突值覆盖。

技能测试！
-----

你已经阅读到了本文的末尾，但你记住最重要的内容了吗？在进一步学习之前，你可以寻找一些深入的测验来验证你是否掌握了这些信息，参见技能测试：层叠 (en-US)。

总结
--

如果你理解了本文的大部分内容，那么就做得很好了——你已经开始熟悉 CSS 的基本机制。接下来，我们将详细讨论层叠层。

如果你没有完全理解层叠、优先级和继承，也请不要担心！这绝对是我们到目前为止在课程中所涉及到的最复杂的事情，即使是专业的 web 开发人员有时也会觉得棘手。我们建议你在继续学习这门课程的过程中，反复阅读这篇文章几次，并继续思考它。

如果你开始遇到样式没有按照预期应用的奇怪问题，请回到这里。这可能是一个特殊的问题。

* 上一页
* Overview: Building blocks
* 下一页


层叠层 - 学习 Web 开发

层叠层
===

* 上一页
* Overview: Building blocks
* 下一页

这一课的目的是向你介绍层叠层，这是一个更高级的特性，它建立在 CSS 层叠和 CSS 优先级的基本概念之上。

如果你是 CSS 的新手，刚开始可能会觉得这部分的内容与本课程的其他部分相比不太相关，而且有些学术化。然而，了解层叠层的基本知识对于你在项目中遇到它们时会非常有帮助。随着你对 CSS 的不断使用，理解层叠层以及如何充分利用它们的功能将能够避免在处理来自不同团队、插件和开发人员的 CSS 代码库时遇到的很多问题。

当你使用来自多个源的 CSS，或当存在冲突的 CSS 选择器和竞争优先级时，又或者当你考虑使用 `!important` (en-US) 时，层叠层最为相关。

|  |  |
| --- | --- |
| 前提： |
 对 CSS 如何工作有一定的了解，包括层叠和优先级（学习
 CSS 入门和层叠和继承）。
  |
| 目标： | 学习层叠层是如何工作的。 |

应用于元素的每个 CSS 属性，只能有一个值。你可以通过在浏览器的开发者工具中检查元素来查看应用于元素的所有属性值。工具的“样式”面板显示了应用在被检查元素上的所有属性值，以及匹配的选择器和 CSS 源文件。具有优先权的来源（origin）的选择器将其值应用于匹配的元素。

除了应用的样式外，“样式”面板还显示了与选定元素匹配但由于层叠、优先级或源顺序而未应用的被划掉的值。划掉的样式可能来自具有相同优先权（precedence）来源但具有较低优先级（specificity），或者来源和优先级相同，但在代码库中较早出现。对于任何应用的属性值，可能有来自许多不同来源的几个声明被划掉。如果你看到一个划掉的样式，它有一个具有更高优先级的选择器，这意味着该值的来源或重要性较低。

通常情况下，随着网站复杂度的增加，样式表数量也会增加，这使得样式表的源顺序变得更加重要和复杂。层叠层简化了在这样的代码库维护样式表的复杂程度。层叠层是显式优先级容器，提供了对最终应用的 CSS 声明更简单和更强大的控制能力，使 web 开发者能够在不必与优先级抗争的情况下，优先考虑 CSS 的部分。

要了解层叠层，必须很好地理解 CSS 层叠。下面的小节提供了层叠的重要概念的快速回顾。

CSS 层叠概念的回顾
-----------

CSS 中的 C 代表“层叠”。这是样式层叠在一起的方法。用户代理经过几个非常明确定义的步骤来确定每个元素的每个属性的值。我们将在这里简要列出这些步骤，然后深入研究第 4 步——层叠层，就是你来到这里学习的内容：

1. **相关声明**：找到所有具有匹配每个元素的选择器的声明代码块。
2. **重要性**：根据规则是普通还是重要对规则进行排序。重要的样式是指设置了 `!important` (en-US) 标志的样式。
3. **来源**：在两个按重要性划分的分组内，按作者、用户或用户代理这几个来源对规则进行排序。
4. **层**：在六个按重要性和来源划分的分组内，按层叠层进行排序。普通声明的层顺序是从创建的第一个到最后一个，然后是未分层的普通样式。对于重要的样式，这个顺序是反转的，但保持未分层的重要样式优先权最低。
5. **优先级**：对于来源层中优先权相同的竞争样式，按优先级对声明进行排序。
6. **出现顺序**：当两个来源层的优先权相同的选择器具有相同的优先级时，最后声明的具有最高优先级的选择器的属性值获胜。

对于每一步，只有“仍在运行”的声明才会进入下一轮“竞争”。如果只有一个声明在运行，那么它就“赢了”，后续的步骤就没有意义了。

### 来源和层叠

有三种层叠来源类型：用户代理样式表、用户样式表和作者样式表。浏览器根据来源和重要性将每个声明分为六个来源分组。有八个优先权级别：六个来源分组、正在过渡的属性和正在动画的属性。优先权的顺序是从具有最低优先权的普通用户代理样式，到当前应用的动画中的样式，到具有最高优先权的重要用户代理样式，再到正在过渡的样式：

1. 用户代理普通样式
2. 用户普通样式
3. 作者普通样式
4. 正在动画的样式
5. 作者重要样式
6. 用户重要样式
7. 用户代理重要样式
8. 正在过渡的样式

“用户代理”指的是浏览器。“用户”指的是是网站访问者。“作者”指的是你，开发者。用 `<style>` 元素直接在元素上声明的样式是作者样式。不包括动画和过渡样式，用户代理普通样式具有最低优先权；用户代理重要样式具有最高优先权。

### 来源和优先级

对于每个属性，“获胜”的声明是来自基于权重（普通或重要）具有优先权的来源的声明。暂时忽略层，来自具有最高优先权的来源的值将被应用。如果获胜来源对于一个元素有多个属性声明，那么将比较这些竞争属性值的选择器的优先级。不同来源之间的选择器从不比较优先级。

在下面的例子中，有两个链接。第一个没有应用作者样式，所以只有用户代理样式被应用（以及你个人的用户样式，如果有的话）。第二个被作者样式设置了 `text-decoration` 和 `color`，即使作者样式表中的选择器具有 `0-0-0` 的优先级。作者样式“获胜”的原因是，当来自不同来源的样式发生冲突时，具有优先权的来源的规则被应用，而不管没有优先权的来源中的优先级如何。

在撰写本文时，用户代理样式表中“竞争”的选择器是 `a:any-link`，它具有 `0-1-1` 的优先级权重。虽然这大于作者样式表中 `0-0-0` 的选择器，但即使你当前的用户代理中的选择器不同，也没关系：作者和用户代理来源之间从不比较优先级权重。了解更多关于如何计算优先级权重。

来源优先权总是胜过选择器优先级。如果一个元素属性被多个来源中的普通样式声明所设置，那么作者样式表将总是覆盖用户或用户代理样式表中声明的冗余普通属性。如果样式是重要的，那么用户代理样式表将总是胜过作者和用户样式。层叠来源优先权确保了不同来源之间的优先级冲突永远不会发生。

在继续之前，还有一件事要注意：当具有优先权的来源中竞争的声明具有相同的优先级时，出现顺序才变得相关。

层叠层的概述
------

我们现在了解了“层叠来源优先权”，那么什么是“层叠层优先权”呢？我们将通过解释层叠层是什么，它们如何排序以及如何为层叠层分配样式来回答这个问题。我们将介绍普通层、嵌套层和匿名层。首先让我们讨论层叠层是什么以及它们解决了哪些问题。

### 层叠层的优先权顺序

类似于我们有六个基于来源和重要性的优先权级别，层叠层使我们能够在这些来源中创建子来源级别的优先权。

在六个来源中的每一个，都可以有多个层叠层。层创建的顺序非常重要。层创建的顺序确定了同一来源内层的优先权顺序。

在普通来源中，层按照创建的顺序排序。优先权顺序是从首个创建的层到最后一个层，然后是未分层的普通样式。

对于重要样式，这个顺序是相反的。所有未分层的重要样式会层叠在一起，形成一个隐式层，优先权高于所有非过渡的普通样式。未分层的重要样式的优先权低于任何重要分层样式。在同一来源内，先前声明的层中的重要样式优先于后续声明的层中的重要样式。

在本教程的其余部分，我们将讨论限制在作者样式上，但请记住层也可以存在于用户和用户代理样式表中。

### 层叠层可以解决的问题

大型代码库可能会有来自多个团队、组件库、框架和第三方的样式。无论包含了多少样式表，所有这些样式都会层叠在一个单一的来源中：*作者*样式表。

将许多来源的样式层叠在一起，特别是来自不同团队的样式，可能会导致问题。不同团队可能有不同的方法论；一个团队可能有自己的降低优先级的最佳实践，而另一个团队可能有自己的在每个选择器中包含 `id` 的标准。

优先级冲突可能会迅速升级。Web 开发人员可能通过添加 `!important` 标志来进行“快速修复”。虽然这可能看起来是一种简单的解决方案，但它通常只是将优先级战争从普通声明转移到重要声明。

就像层叠来源在用户、用户代理和作者样式之间提供了力量平衡一样，层叠层提供了一种结构化的方式来组织和平衡单一来源内的关注点，就好像来源中的每个层都是一个子来源。可以为每个团队、组件和第三方创建一个层，其中的样式优先权基于层的顺序。

层内的规则层叠在一起，而不会与层外的样式规则竞争。层叠层使得可以优先考虑整个样式表而不必担心这些子来源之间的优先级。

层的优先权始终高于选择器的优先级。具有优先权的层中的样式“胜出”于具有较低优先权的层。输掉的层中选择器的优先级是无关紧要的。在层内竞争的属性值的优先级仍然会受到注意，但是在层之间没有优先级的问题，因为每个属性对应的最高优先权层才会被考虑。

### 嵌套层叠层可以解决的问题

层叠层允许创建嵌套层。每个层叠层可以包含嵌套层。

例如，可以将组件库导入到 `components` 层中。常规层叠层将组件库添加到作者来源中，消除与其他作者样式的优先级冲突。在 `components` 层内部，开发人员可以选择定义各种主题，每个主题作为单独的嵌套层。这些嵌套层的顺序可以根据媒体查询（参见下面的层创建和媒体查询部分），例如视口大小或方向来定义。这些嵌套层提供了一种创建不基于优先级冲突的主题的方式。

嵌套层的能力非常适用于开发组件库、框架、第三方小部件和主题的任何人。

创建嵌套层的能力还消除了层名称冲突的担忧。我们将在嵌套层部分介绍这一点。

>
> “作者可以创建表示元素默认值、第三方库、主题、组件、覆盖和其他样式问题的层，并且能够以明确的方式对层的层叠进行重新排序，而无需更改每个层内的选择器或优先级，也无需依赖出现顺序来解决跨层的冲突。”
>
>
> ——层叠与继承规范。
>
>
>

创建层叠层
-----

可以使用以下任一方法创建层叠层：

* 使用 `@layer` 声明 at 规则，使用 `@layer` 后跟一个或多个层的名称来声明层。这将创建一个没有分配任何样式的具名层。
* 使用 `@layer` 块 at 规则，在块中的所有样式都将添加到一个命名或未命名的层中。
* 使用具有 `layer` 关键字或 `layer()` 函数的 `@import` 规则，将导入文件的内容分配到该层中。

在尚未初始化具有相同名称的层的情况下，这三种方法中的任何一种都会创建一个层。如果在 `@layer` at 规则或带有 `layer()` 的 `@import` 中没有提供层名称，则将创建一个新的匿名层。

**备注：** 层的优先顺序是它们创建的顺序。不在层中的样式，或者称为“未分层样式”，会层叠到最终的隐式标签中。

在讨论嵌套层之前，让我们对创建层的三种方法进行详细介绍。

### 用于具名层的 @layer 声明 at 规则

层的顺序由 CSS 中层出现的顺序确定。使用 `@layer` 后跟一个或多个层的名称而不分配任何样式是定义层顺序的一种方式。

`@layer` CSS at 规则用于声明层叠层，并在存在多个层叠层时定义优先权顺序。以下规则按照列出的顺序声明了三个层：

```css
@layer theme，layout，utilities;
```

通常，你需要在 CSS 的第一行声明这个 `@layer`（当然要用对你的站点有意义的层名称），以便完全控制层的顺序。

如果上述声明是站点 CSS 的第一行，那么层的顺序将是 `theme`、`layout` 和 `utilities`。如果在上述语句之前已经创建了一些层，只要同名的层还不存在，这三个层就会被创建并添加到现有层列表的末尾。但是，如果同名的层已经存在，那么上述语句只会创建两个新层。例如，如果 `layout` 已经存在，只会创建 `theme` 和 `utilities`, 但在这种情况下的层顺序将是 `layout`、`theme` 和 `utilities`。

### 用于具名层和匿名层的 @layer 块 at 规则

可以使用块 `@layer` at 规则来创建层。如果一个 `@layer` at 规则后跟一个标识符和一个块样式，则该标识符用于命名该层，该规则中的样式被添加到该层的样式中。如果具有指定名称的层不存在，则会创建一个新层。如果具有指定名称的层已经存在，则会将样式添加到先前存在的层中。如果在使用 `@layer` 创建块样式时没有指定名称，则该规则中的样式将被添加到一个新的匿名层中。

在下面的示例中，我们使用了四个块和一个内联的 `@layer` at 规则。这个 CSS 按列出的顺序执行以下操作：

1. 创建一个命名的 `layout` 层
2. 创建一个未命名的匿名层
3. 声明三个层的列表并只创建两个新层 `theme` 和 `utilities`，因为 `layout` 已经存在
4. 向已经存在的 `layout` 层添加额外的样式
5. 创建第二个未命名的匿名层

```css
/\* 文件：layers1.css \*/

/\* 未分层的样式 \*/
body {
  color: #333;
}

/\* 创建第一个层：`layout` \*/
@layer layout {
  main {
    display: grid;
  }
}

/\* 创建第二个层：一个未命名的匿名层 \*/
@layer {
  body {
    margin: 0;
  }
}

/\* 创建第三和第四个层：`theme` 和 `utilities` \*/
@layer theme，layout，utilities；
/\* 向已经存在的 `layout` 层添加样式 \*/
@layer layout {
  main {
    color: #000;
  }
}

/\* 创建第五个层：一个未命名的匿名层 \*/
@layer {
  body {
    margin: 1vw;
  }
}
```

在上面的 CSS 中，我们创建了五个层：`layout`、`<anonymous(01)>`、`theme`、`utilities` 和 `<anonymous(02)>`——按这个顺序——第六个隐含的未分层样式层包含在 `body` 样式块中。层的顺序是层的创建顺序，未分层样式的隐含层总是在最后的。一旦创建了层之后就无法改变层的顺序。

我们将一些样式分配给名为 `layout` 的层。如果指定的具名层不存在，则在 `@layer` at 规则中指定名称（无论是否向层分配样式）都会创建该层；这会将该层添加到现有层名称系列的末尾。如果指定的具名层已经存在，该命名块内的所有样式都会附加到之前存在的层中的样式——通过重用现有的层名称指定样式不会创建新层。

通过在不命名层的情况下将样式分配给层来创建匿名层。只能在创建时向未命名的层添加样式。

**备注：** 后续使用不带层名的 `@layer` 会创建额外的未命名层；它不会将样式附加到先前存在的未命名层。

`@layer` at 规则创建一个命名或未命名的层，或者如果指定的具名层已经存在，则将样式附加到该层。我们称第一个匿名层为 `<anonymous(01)>`，第二个为 `<anonymous(02)>`，这只是为了便于解释。这些实际上是未命名的层。没有办法引用它们或向它们添加额外的样式。

在层之外声明的所有样式都会加入到一个隐含的层中。在上面的示例代码中，第一条声明为 `body` 设置了 `color: #333` 属性。这是在任何层之外声明的。即使未分层的样式具有更低的优先级并且在出现顺序上排在前面，正常的未分层声明也优先于正常的分层声明。这解释了为什么即使未分层的 CSS 在代码块中先被声明，包含这些未分层样式的隐含层的优先权却如同它是最后声明的层一样。

在 `@layer theme, layout, utilities;` 这一行中，声明了一系列层，只创建了 `theme` 和 `utilities` 层；`layout` 已经在第一行中创建。请注意，此声明不会更改已经创建层的顺序。目前没有办法重新排序已声明的层。

在下面的交互式示例中，我们将样式分配给两个层，在此过程中创建并命名了它们。因为它们在首次使用时已经存在，所以在最后一行声明它们没有任何影响。

试着将最后一行 `@layer site, page;` 移到第一行。会发生什么？

#### 层创建和媒体查询

如果你使用媒体或特性查询来定义层，且媒体不匹配或特征不被支持，则不会创建该层。下面的示例展示了改变设备或浏览器的尺寸可能会改变层的顺序。在这个示例中，我们只在更宽的浏览器中创建 `site` 层。然后我们按顺序为 `page` 和 `site` 层分配样式。

在宽屏上，`site` 层在第一行被声明，这意味着 `site` 的优先权低于 `page`。否则在窄屏上，`site` 的优先权高于 `page`，因为它在后面被声明。如果不起作用，请将媒体查询中的 `50em` 改为 `10em` 或 `100em`。

### 使用 @import 将样式表导入具名层和匿名层

`@import` 规则允许用户直接从其他样式表导入样式规则到 CSS 文件或 `<style>` 元素中。

导入样式表时，必须在样式表或 `<style>` 块中的任何 CSS 样式之前定义 `@import` 语句。`@import` 语句必须出现在最前面，在任何样式之前，但可以在创建一个或多个层而不向这些层分配任何样式的 `@layer` 规则之后（`@import` 也可以在 `@charset` 规则之后）。

你可以将样式表导入具名层、嵌套具名层或匿名层。以下层分别将样式表导入 `components` 层、`components` 层中的嵌套 `dialog` 层和一个未命名层：

```css
@import url("components-lib.css") layer(components);
@import url("dialog.css") layer(components.dialog);
@import url("marketing.css") layer();
```

你可以将多个 CSS 文件导入到单个层中。以下声明将两个单独的文件导入到单个 `social` 层中：

```css
@import url(comments.css) layer(social);
@import url(sm-icons.css) layer(social);
```

你可以使用媒体查询和特性查询根据特定条件导入样式并创建层。以下将样式表导入到 `international` 层，但前提是浏览器支持 `display: ruby`，而且被导入的文件取决于屏幕的宽度。

```css
@import url("ruby-narrow.css") layer(international) supports(display: ruby) and
 (width < 32rem);
@import url("ruby-wide.css") layer(international) supports(display: ruby) and
 (width >= 32rem);
```

**备注：** 没有与链接样式表的 `<link>` 方法相对应的方法。当你无法在样式表内部使用 `@layer` 时，请使用 `@import` 将样式表导入到层中。

### 嵌套层的概述

嵌套层是具名层或匿名层中的子层。每个层叠层（即使是匿名的）都可以包含嵌套层。导入到另一个层中的层会成为该层中的嵌套层。

### 嵌套层的优点

嵌套层的能力使团队可以创建层叠层，而不用担心其他团队会将它们导入到一个层中。同样，嵌套使你可以将第三方样式表导入到一个层中，而不用担心该样式表本身是否具有层。因为层可以嵌套，所以你不必担心外部和内部样式表之间会有冲突的层名称。

### 创建嵌套层叠层

嵌套层可以使用与常规层相同的方法创建。例如，可以使用点表示法，在 `@layer` 规则后跟一个或多个层名称来创建。多个点和层名称表示多重嵌套。

如果你在一个块 `@layer` at 规则内嵌套另一个块 `@layer` at 规则，无论是否命名，嵌套的块都会成为一个嵌套层。类似地，当一个样式表被使用包含 `layer` 关键字或 `layer()` 函数的 `@import` 声明导入时，样式会被分配到该具名或匿名层。如果 `@import` 语句包含层，那么这些层会成为该匿名或具名层中的嵌套层。

让我们看下面的例子：

```css
@import url("components-lib.css") layer(components);
@import url("narrowtheme.css") layer(components.narrow);
```

在第一行中，我们将 `components-lib.css` 导入 `components` 层。如果该文件包含任何层，无论命名与否，这些层都会成为 `components` 层中的嵌套层。

第二行将 `narrowtheme.css` 导入 `narrow` 层，`narrow` 是 `components` 的子层。嵌套的 `components.narrow` 会作为 `components` 层中的最后一个层创建，除非 `components-lib.css` 已经包含一个 `narrow` 层，在这种情况下，`narrowtheme.css` 的内容会被附加到 `components.narrow` 嵌套层。可以使用 `components.<layerName>` 模式向 `components` 层添加更多命名嵌套层。如前所述，可以创建未命名层，但随后无法访问它们。

让我们看另一个例子，其中我们使用以下语句将 `layers1.css` 导入一个具名层：

```css
@import url(layers1.css) layer(example);
```

这将创建一个单一的具名层 `example`，其中包含一些声明和五个嵌套层——`example.layout`、`example.<anonymous(01)>`、`example.theme`、`example.utilities` 和 `example.<anonymous(02)>`。

要向命名的嵌套层添加样式，应使用点表示法：

```css
@layer example.layout {
  main {
    width: 50vw;
  }
}
```

根据层的顺序确定优先权
-----------

层的顺序决定了它们的优先权顺序。因此，层的顺序非常重要。与层叠根据来源和重要性进行排序的方式相同，层叠也会根据来源层和重要性对每个 CSS 声明进行排序。

### 常规层叠层的优先权顺序

```css
@import url(A.css) layer(firstLayer);
@import url(B.css) layer(secondLayer);
@import url(C.css);
```

上述代码创建了两个具名层和一个未命名层。假设这三个文件（`A.css`、 `B.css` 和 `C.css`）本身不包含任何额外的层。以下列表显示了在这些文件内外声明的样式将以从最低（1）优先权到最高（10）优先权进行排序。

1. firstLayer 普通样式（`A.css`）
2. secondLayer 普通样式（`B.css`）
3. 未分层普通样式（`C.css`）
4. 内联普通样式
5. 动画样式
6. 未分层重要样式（`C.css`）
7. secondLayer 重要样式（`B.css`）
8. firstLayer 重要样式（`A.css`）
9. 内联重要样式
10. 过渡样式

在层中声明的普通样式具有最低的优先权，并按照创建层的顺序进行排序。在最先创建的层中声明的普通样式具有最低的优先权，而在最后创建的层中声明的普通样式在所有层中具有最高的优先权。换句话说，如果存在冲突的话，在 `firstLayer` 中声明的普通样式将被列表中任何后续的样式覆盖。

接下来是在层外声明的任何样式。`C.css` 中的样式没有导入到层中，并将覆盖任何来自 `firstLayer` 和 `secondLayer` 的冲突样式。在层外声明的普通样式总是比层内的普通样式具有更高的优先权。

内联样式是使用 `style`属性声明的。以这种方式声明的普通内联样式将优先于在未分层和分层样式表中找到的普通样式（`firstLayer - A.css`，`secondLayer - B.css` 和 `C.css`）。

动画样式比所有普通样式都具有更高的优先权，包括内联普通样式。

重要样式，即包含 `!important` 标志的属性值，优先于我们列表中前面提到的任何样式。它们的排序顺序与普通样式的顺序相反。在层外声明的任何重要样式的优先权都低于在层内声明的样式。在层中找到的重要样式也按层的创建顺序进行排序。对于重要样式，最后创建的层具有最低的优先权，而首先创建的层在声明的层中具有最高的优先权。

内联重要样式再次优先于在其他地方声明的重要样式。

过渡样式具有最高的优先权。当正在过渡普通属性值时，它优先于所有其他属性值声明，甚至是内联重要样式；但是只在过渡时。

在这个例子中，有两个没有样式的内联层 `A` 和 `B`，一块不分层样式，以及两个具名层 `A` 和 `B` 中的样式块。

使用 `style` 属性在 `h1` 元素上添加的内联样式，设置了普通的 `color` 和重要的 `background-color`。普通内联样式覆盖所有分层和未分层普通样式。重要内联样式覆盖所有分层和未分层，普通和重要的作者样式。作者样式无法覆盖重要的内联样式。

普通的 `text-decoration` 和重要的 `box-shadow` 不是 `style` 内联样式的一部分，因此可以被覆盖。对于普通的非内联样式，未分层样式具有优先权。对于重要样式，分层顺序也很重要。虽然普通未分层样式覆盖层中设置的所有普通样式，但对于重要样式，优先权顺序被反转了；未分层重要样式的优先权低于分层重要样式。

仅在层内声明的两个样式是具有普通重要性的 `font-style`，和具有 `!important` 标志的 `font-weight`。对于普通样式，后声明的 `B` 层覆盖了先声明的 `A` 层中的样式。对于普通样式，后面的层优先于前面的层。重要样式的优先权顺序相反。对于重要的 `font-weight` 声明，被先声明的 `A` 层优先于后声明的 `B` 层。

你可以通过将第一行从 `@layer A, B;` 更改为 `@layer B, A;` 来反转层顺序。试一试。这会改变哪些样式，哪些保持不变？为什么？

层的顺序由层在 CSS 中出现的顺序设置。在第一行中，我们使用 `@layer` 后跟层的名称声明了层，而没有分配任何样式，以分号结束。如果我们省略这一行，结果将是相同的。为什么？我们按顺序 A 然后 B 在命名的 @layer 块中分配了样式规则。这两层是在第一行中创建的。如果不是这样，这些规则块将按该顺序创建它们。

我们包含第一行的原因有两个：第一，这样你可以轻松编辑该行并切换顺序，第二，因为大多数时候你会发现首先声明顺序层是你的层顺序管理的最佳实践。

总结：

* 层的优先权顺序是创建层的顺序。
* 一旦创建，就无法更改层顺序。
* 普通样式的层优先权是创建层的顺序。
* 未分层普通样式优先于有层普通样式。
* 重要样式的层优先权被反转，早期创建的层具有优先权。
* 所有有层的重要样式都优先于未分层的重要（和普通）样式。
* 普通内联样式优先于所有普通样式，无论是否分层。
* 重要内联样式优先于所有其他样式，正在过渡的样式除外。
* 作者样式无法覆盖重要内联样式（过渡除外，但这是临时的）。
### 嵌套层叠层的优先权顺序

嵌套层的层叠优先权顺序与常规层类似，但包含在层内。优先权顺序基于嵌套层创建的顺序。层中的非嵌套样式优先于嵌套的普通样式，对于重要样式则相反。嵌套层之间的优先级权重并不重要，但它在嵌套层内的冲突样式中确实很重要。

下面创建并向 `components` 层和 `components.narrow` 嵌套层添加样式，然后创建并追加样式到新的 `components.wide` 层：

```css
@import url("components-lib.css") layer(components);
@import url("narrowtheme.css") layer(components.narrow);

@layer components {
  :root {
    --theme: red;
    font-family: serif !important;
  }
}
@layer components.narrow {
  :root {
    --theme: blue;
    font-family: sans-serif !important;
  }
}
@layer components.wide {
  :root {
    --theme: purple;
    font-family: cursive !important;
  }
}
```

因为未分层普通样式优先于分层普通样式，并且在层内，非嵌套样式优先于普通嵌套样式，所以 `red` 优先于其他 `theme` 颜色。

对于重要样式，分层样式优先于未分层样式，并且早期声明的层中的重要样式优先于后来声明的层。在这个例子中，嵌套层的创建顺序是 `components.narrow`，然后是 `components.wide`，所以 `components.narrow` 中的重要样式优先于 `components.wide` 中的重要样式，这意味着 `sans-serif` 获胜。

技能测试！
-----

你已经阅读完本文，但是你还记得最重要的信息吗？在继续学习之前，你可以找到一些进一步的测试来验证你已经掌握了这些信息——请参阅技能测试：层叠—任务 2 (en-US)。

总结
--

如果你理解了本文中的大部分内容，那么恭喜——你现在已经熟悉了 CSS 层叠层的基本机制。接下来，我们将详细了解盒模型。

* 上一页
* Overview: Building blocks
* 下一页


技能测试：背景与边框 - 学习 Web 开发

技能测试：背景与边框
==========

本技能测试的目的是评估你是否完全理解了 CSS 中的背景和边框教程的内容。

**备注：** 你可以在下面的互动编辑器中尝试解决方案。然而，下载代码并使用在线工具，如 CodePen、jsFiddle 或 Glitch 来完成任务可能会有所帮助。

如果你遇到了困难，那就向我们寻求帮助吧——见本页面底部的评估或进一步帮助部分。

任务 1
----

在这个任务中，我们希望你能为一个页面的标题添加背景、边框和一些简单的样式：

1. 令这个盒子的边框宽度为 5px，且为黑色实心（solid）的，圆角半径为 10px。
2. 添加背景图片（使用 `balloons.jpg` URL），调整它的大小，令其覆盖整个盒子。
3. 给予 `<h2>` 一个半透明的黑色背景颜色，并使文本为白色。

你的最终结果看起来应该如下图所示：

![图片显示了一个有照片背景的盒子，圆形的边框和半透明的黑色背景上的白色文字](/zh-CN/docs/Learn/CSS/Building_blocks/Test_your_skills_backgrounds_and_borders/backgrounds-task1.png)

尝试更新下面的实时代码，重现完成的示例：

下载这项任务的初始代码，在你自己的编辑器或在线编辑器中完成测试。

任务 2
----

在这个任务中，我们希望你为一个装饰性的盒子添加背景图片、边框和其他一些样式：

* 给这个盒子加一个 5px 的淡蓝色（lightblue）边框，设定左上角圆角半径为 20px，右下角圆角半径为 40px。
* 标题使用 `star.png` 图像作为背景图像，左边是一颗居中的星星，右边是重复的星星图案。
* 确保标题文本不覆盖图像，并且居中——你将需要使用在以前的课程中学到的技术来实现这一点。

你的最终结果看起来应该如下图所示：

![图片显示了一个在左上角和右下角有圆角蓝色边框的盒子。文字的左边有一颗星星，右边有 3 颗星星](/zh-CN/docs/Learn/CSS/Building_blocks/Test_your_skills_backgrounds_and_borders/backgrounds-task2.png)

尝试更新下面的实时代码，重现完成的示例：

下载这项任务的初始代码，在你自己的编辑器或在线编辑器中完成测试。

评估或进一步帮助
--------

可以在互动编辑器中完成上面的练习。

如果你期望对练习结果进行评估，或者遇到困难需要帮助，可以：

1. 可借助 CodePen、jsFiddle、Glitch 等在线编程实用工具完成任务。在那里你可以自行编写代码，也可以使用上文中所给的初始代码。
2. 在 MDN 论坛学习区发帖寻求他人的评估和帮助。你发表的文章应包括：
    * 一个描述性的标题，比如“Assessment wanted for backgrounds and borders skill test 1”。
    * 讲清你已经做出的努力，以及需要我们做什么，比如你遇到了困难并需要帮助，或者期望得到评估。
    * 一个链接指向你需要得到评估或帮助的实例，步骤 1 中三个在线编程工具之一均可。我们对希望得到帮助但由不提供代码的求助也很无奈。
    * 一个链接指向 MDN 测试的原始页面，在那里我们可以找到你需要获得帮助的原始信息。


技能测试：图片和表格元素 - 学习 Web 开发

技能测试：图片和表格元素
============

该任务的目的是帮助你检查对我们在本课程Images, Media and Form elements(图像，媒体及表格元素）中了解的一些值和单位的理解。

**备注：** 你可以在下面的交互式编辑器中尝试解决方案，但是下载代码并使用诸如CodePen, jsFiddle, Glitch 之类的在线工具来完成任务可能会更加有所帮助。

如果你遇到困难，联系我们获得帮助 — 参见页面底部的评价以及更多帮助

任务一
---

在此任务中，你有一张溢出盒子的图像。我们希望图像缩小到适合盒子的大小，而没有多余的空白，并不介意某些图像是否被裁剪。

![An image in a box](/zh-CN/docs/Learn/CSS/Building_blocks/Images_tasks/mdn-images-object-fit.png)

在下面的例子中看看你能否能符合上面的图片

**备注：** 为了为了评估或进一步完成任务，请下载此任务的起点，以便在你自己的编辑器或在线编辑器中工作。

任务二
---

在此任务中，你会获得一个简单的表格。你的任务是对该表单的外观进行以下更改：

* 使用属性选择器定位.myform 中的\*\*搜索 (search)\*\*字段和按钮。
* 使表单字段和按钮使用与表单其余部分相同的文本大小。
* 给表单字段和按钮设置 10 像素的内边距。
* 为按钮提供紫色背景，白色前景，无边框和 5px 的圆角。

![A single line form](/zh-CN/docs/Learn/CSS/Building_blocks/Images_tasks/mdn-images-form.png)

尝试更改下面的实时代码以重新创建图像中显示的示例：

**备注：** 为了为了评估或进一步完成任务，请下载此任务的起点，以便在你自己的编辑器或在线编辑器中工作。

评价以及更多帮助
--------

你可以在上面提到的交互式编辑器中练习这些示例。

如果你希望对自己的工作进行评估，或者遇到困难并希望寻求帮助，请执行以下操作：

1. 将你的工作上传到在线共享编辑器中，例如 CodePen, jsFiddle, Glitch.。你可以自己编写代码，也可以使用以上部分中链接到的起点文件。
2. 在MDN Discourse forum Learning category. “学习”类别中写一篇帖子，要求评估帮助。你的帖子应包括：

* 描述性标题，例如“**Assessment wanted for Images skill test 1**”。
* 你已经尝试过的内容以及你希望我们做什么的详细信息，例如 **如果你陷入困境并需要帮助**，或者**需要评估**。
* 指向你要评估或需要帮助的示例的链接（如上面的步骤 1 中所述）。这是很好好习惯 - 如果看不到代码，很难帮助存在问题的人。
* 指向实际任务或评估页面的链接，因此我们可以找到你需要帮助的问题。


创建精美的信纸 - 学习 Web 开发

创建精美的信纸
=======

* 上一页
* Overview: Styling boxes
* 下一页

如果你想给人留下好印象，把信写在一张精美的信纸上会是个不错的开始，在这个评估里我们希望你能创建一个在线模版来达到这样的效果。

|  |  |
| --- | --- |
| 前提： | 在开始这个评估之前你应该已经学习过这个模块里的所有其他文章。 |
| 目标： | 测试对 CSS 盒模型和其他盒相关特性的掌握程度，比如实现背景等。 |

起点
--

在开始评估之前，你需要：

* 复制一份HTML和CSS代码，并在一个新的目录下把它们保存为`index.html` 和 `style.css`。
* 在同一个目录下下载这几张图片：顶部、底部和标志。

**备注：** 或者你也可以用JSBin或Glitch这样的网站来做这个评估，把链接里的 HTML 和 CSS 代码贴到这些在线编辑器里就行。如果你在用的在线编辑器没有独立的 CSS 面板的话，把 CSS 代码放到 HTML 文档头部的`<style>`元素里就好。

项目简介
----

你已经有了创建一个信纸模版所需的所有文件，只需把它们放到一起就好。为了达到目标，你需要：

### 信纸主体

* 把 CSS 链接到 HTML 文档里。
* 给信纸添加这样一个背景：
    + 把之前下载的顶部图片固定到信纸顶部。
    + 把之前下载的底部图片固定到信纸底部。
    + 为了给信纸一点纹理，在前面背景的基础上添加一个半透明的渐变，使其在顶部和底部附近稍微变暗，但中间的大部分完全透明。
* 多添加一个`background`声明，仅添加顶部图片到信纸顶部，以此作为不支持之前那种声明方式的浏览器的后备方案。
* 给信纸添加一个白色的背景颜色。
* 给信纸添加一个 1mm 的上下实线边框，选一个符合信纸的颜色主题的边框颜色。
### 标志

* 把之前下载的标志图片添加为`<h1>` (en-US)的背景图片。
* 给标志添加一个过滤器，使它有一个隐隐约约的阴影。
* 现在把添加的过滤器注释掉，尝试用其他（更跨浏览器兼容）的方式实现一个相同的阴影，阴影需要同样符合圆形图片的形状。
提示和技巧
-----

* 记住，你可以这样为较旧的浏览器创建一个后备方案：先写一个较旧的浏览器支持的后备声明，然后再接着写只有较新的浏览器才支持的声明。这样比较旧的浏览器会应用第一个声明而忽略掉第二个不支持的声明，与此同时比较新的浏览器会先应用第一个声明，然后用第二个声明把它覆盖掉。
* 如果想的话你可以随意地用自己的图片来做这个评估。
范例
--

完成之后的信纸可能会像下面的截图这样：

![](/zh-CN/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper/letterhead.png)

评估
--

如果这个评估是一系列课程的一部分，你应该可以让你的老师或导师为你批改。如果你是自学，可以很容易地在 discussion thread for this exercise 或Mozilla IRC的#mdn IRC 频道回复得到批改指南。请先自己试着做——作弊学不到任何东西！

* 上一页
* Overview: Styling boxes
* 下一页


高级区块效果 - 学习 Web 开发

高级区块效果
======

* 上一页
* Overview: Styling boxes
* 下一页

这篇文章展示了盒子的小技巧，提供了一些高级特性的介绍，这些特性不适合其他类别的样式，比如盒子阴影、混合模式和滤镜。

|  |  |
| --- | --- |
| 前提： |
 HTML 基础 (学习
 Introduction to HTML) ，了解 CSS 工作原理 (学习
 Introduction to CSS.)
  |
| 目标： | 要了解如何使用高级的盒子效果，并了解一些在 CSS 语言中出现的新样式工具。 |

盒子阴影
----

回到我们的样式化文本模块，我们查看了`text-shadow`属性，它允许你将一个或多个阴影应用到元素的文本上。对于盒子来说，存在一个等价的属性——`box-shadow`允许你将一个或多个阴影应用到一个实际的元素盒子中。和文本阴影一样，盒子的阴影在各种浏览器中也得到了很好的支持，但只有在 IE9+（IE9 及更新版本）中可用。你的旧 IE 版本的用户可能只需要应付没有阴影的情况，所以只要测试一下你的设计，确保你的内容在没有他们的情况下是清晰可见的。

你可以 box-shadow.html在这部分找到例子 (见源码)。

### 一个简单的盒子阴影

让我们看一个简单的例子来起步。首先，一些 HTML：

```html
<article class="simple">
  <p>
    <strong>Warning</strong>: The thermostat on the cosmic transcender has
    reached a critical level.
  </p>
</article>
```

现在是 CSS:

```css
p {
  margin: 0;
}

article {
  max-width: 500px;
  padding: 10px;
  background-color: red;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0),
    rgba(0, 0, 0, 0.25)
  );
}

.simple {
  box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.7);
}
```

结果如下：

你会看到，我们在`box-shadow`属性值中有 4 个项：

1. 第一个长度值是水平偏移量（**horizontal offset** ）——即向右的距离，阴影被从原始的框中偏移 (如果值为负的话则为左)。
2. 第二个长度值是垂直偏移量（**vertical offset**）——即阴影从原始盒子中向下偏移的距离 (或向上，如果值为负)。
3. 第三个长度的值是模糊半径（**blur radius**）——在阴影中应用的模糊度。
4. 颜色值是阴影的基本颜色（**base color**）。

你可以使用任何长度和颜色单位来定义这些值。

### 多个盒子阴影

还可以在单个`box-shadow`声明中指定多个框阴影，用逗号分隔：
```

<article class="multiple">
  <p>
    <strong>Warning</strong>: The thermostat on the cosmic transcender has
    reached a critical level.
  </p>
</article>
```

```css
p {
  margin: 0;
}

article {
  max-width: 500px;
  padding: 10px;
  background-color: red;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0),
    rgba(0, 0, 0, 0.25)
  );
}

.multiple {
  box-shadow:
    1px 1px 1px black,
    2px 2px 1px black,
    3px 3px 1px red,
    4px 4px 1px red,
    5px 5px 1px black,
    6px 6px 1px black;
}
```

结果如下：

我们在这里做了一些有趣的事情，创建了一个带有多个颜色图层的凸起的盒子，但是你可以用任何你想要的方式来使用它，例如，用基于多个光源的阴影来创建一个更加真实的外观。

### 其他盒子阴影特点

与`text-shadow`不同，`box-shadow`有一个`inset`关键字可用——把它放在一个影子声明的开始，使它变成一个内部阴影，而不是一个外部阴影。让我们看一看。

首先，我们将为这个例子使用一些不同的 HTML：

```html
<button>Press me!</button>
```

```css
button {
  width: 150px;
  font-size: 1.1rem;
  line-height: 2;
  border-radius: 10px;
  border: none;
  background-image: linear-gradient(to bottom right, #777, #ddd);
  box-shadow:
    1px 1px 1px black,
    inset 2px 3px 5px rgba(0, 0, 0, 0.3),
    inset -2px -3px 5px rgba(255, 255, 255, 0.5);
}

button:focus,
button:hover {
  background-image: linear-gradient(to bottom right, #888, #eee);
}

button:active {
  box-shadow:
    inset 2px 2px 1px black,
    inset 2px 3px 5px rgba(0, 0, 0, 0.3),
    inset -2px -3px 5px rgba(255, 255, 255, 0.5);
}
```

结果如下：

在这里我们将 focus/hover/active 这些声明一起设置了按钮样式。这个按钮的默认状态下设置了一个简单的黑色盒阴影，并且加上了一对 inset 阴影，一个明的，一个暗的，位于按钮的两个对角上，以此给按钮一种很棒的阴影效果。

当按钮被按下时，这里的 active 声明将第一个盒阴影换成一个非常暗的 inset 阴影。给人一种按钮被按下的样子。

**备注：** 还有一个可以在 box-shadow 中设置的值 — 另外一个位于颜色值前面可选的长度值，即**spread radius**，如果设置了这个值，将会导致阴影变得比原始的阴影更大，这个值不是很常用，但是值得一提。

Filters（滤镜）
-----------

CSS 滤镜提供了一种过滤元素的方法，就好比你在诸如 Photoshop 这样的平面设计程序中过滤元素一样。有大量的不同的选项可以使用，你可以在`filter` 参考页面阅读所有相关的更多细节。在这篇文章中，我们将会向你介绍它的语法，并且向你展示将会发生多么有趣的结果。

基本上，滤镜可以应用在任何元素上，块元素（block）或者行内元素（inline）——你只需要使用`filter`属性，并且给他一个特定的过滤函数的值。有些可用的滤镜选项和其他 CSS 特性做的事情十分相似，例如`drop-shadow()`的工作方式以及产生的效果和 `box-shadow` 或`text-shadow`十分相似。然而滤镜真正出色的地方在于，它们作用于盒（box）内内容（content）的确切形状，而不仅仅将盒子本身作为一个大的块，这看起来会更棒，即使他们可能不会总是变成你想要的模样。让我们来举一个简单的例子来阐明我们的意思：

首先，一些简单的 HTML:

```html
<p class="filter">Filter</p>

<p class="box-shadow">Box shadow</p>
```

现在是一些 CSS，用来给它们各自一个下降的阴影：

```css
p {
  margin: 1rem auto;
  padding: 20px;
  width: 100px;
  border: 5px dashed red;
}

.filter {
  -webkit-filter: drop-shadow(5px 5px 1px rgba(0, 0, 0, 0.7));
  filter: drop-shadow(5px 5px 1px rgba(0, 0, 0, 0.7));
}

.box-shadow {
  box-shadow: 5px 5px 1px rgba(0, 0, 0, 0.7);
}
```

这给了我们一个如下的结果：

正如你所看到的，drop-shadow 滤镜跟随着文本和 border dashes 的确切形状。而盒阴影（box-shadow）仅仅跟随着盒的四方。

其他需要注意的事：

* 滤镜很新——它们可以被大多数的现代的浏览器支持，包括 Microsoft Edge，但它们一点也不能被 IE 浏览器支持。因此如果你在你的设计中使用滤镜，你需要确保你的内容即使没有滤镜也是可用的。
* 你会看到我们在`filter`属性中通过`-webkit-`前缀包含了一个版本信息，这被称为一个 Vendor Prefix，有时会被浏览器使用，以在一个新特性完整实现之前，当它与无前缀版本没有冲突的时候支持并实验这个特性。Vendor prefixes 永远都不被指望着被 web 开发人员使用，但是它们有时候确实会被在产品页面中使用，即当实验性的特性确实被需要时。在这个实例中，Chrome/Safari/Opera 目前要求这些属性的`-webkit-`版本，而 Edge 和 Firefox 则使用后者，无前缀版本。

**备注：** 如果你确实决定在你的代码中使用前缀，确保你包括了所有需要的前缀以及无前缀的版本，这样才会有尽可能多的浏览器能够使用这些特性，并且如果浏览器落下了前缀，它们也能够使用无前缀的版本。另外需要注意的是这些实验性的特性可能会有改变，这可能会导致你的代码被破坏，在前缀被去除之前，最好还是仅仅实验这些特性。

你可以看到更多关于滤镜的例子，在 filters.html (也可以看 source code).

Blend modes（混合模式）
-----------------

CSS 混合模式允许我们为元素添加一个混合模式，以当两个元素重叠时，指定一个混合的效果——最终每个像素所展示的颜色将会是原来像素中颜色和其下面一层相组合之后的结果，对于像 Photoshop 这样的图形程序的用户来说，混合模式应该也非常熟悉。

这里有两个在 CSS 中用到的属性：

* `background-blend-mode`, 用来将单个元素的多重背景图片和背景颜色设置混合在一起。
* `mix-blend-mode`, 用来将一个元素与它覆盖的那些元素各自所设置的背景（background）和内容 (content) 混合在一起。

你可以找到比这里用到的更多的例子，在我们的blend-modes.html 示例页面 (查看 source code), 或者在 `<blend-mode>` 参考页面。

**备注：** 混合模式（Blend modes）同样也很新，而且略微不如滤镜（filter）的被支持度。至今也没有没 Edge 支持，并且 Safari 也仅仅支持部分混合模式选项。

### background-blend-mode

让我们再来看一些例子以帮助我们更好的理解这一点。首先，`background-blend-mode`——在这里我们将展示一对简单的`<div>`s，这样你就可以比较原始版本和混合版本：

```html
<div></div>
<div class="multiply"></div>
```

现在来点 CSS — 我们正在给`<div>`添加一个背景图像和一个绿色的背景色：

```css
div {
  width: 250px;
  height: 130px;
  padding: 10px;
  margin: 10px;
  display: inline-block;
  background: url(colorful-heart.png) no-repeat center 20px;
  background-color: green;
}

.multiply {
  background-blend-mode: multiply;
}
```

我们得到的结果是这样的——你可以看到左边的原始版本，以及右边的多层混合版本：

### mix-blend-mode

现在让我们看一看 `mix-blend-mode`。这里我们将给出两个相同的`<div>`，但是每个都位于一个有着紫色背景的简单的`<div>`上，来向你展示元素们将会怎样混合在一起：

```html
<article>
  No mix blend mode
  <div></div>
  <div></div>
</article>

<article>
  Multiply mix
  <div class="multiply-mix"></div>
  <div></div>
</article>
```

这是我们将用来装饰它的 CSS:

```css
article {
  width: 300px;
  height: 180px;
  margin: 10px;
  position: relative;
  display: inline-block;
}

div {
  width: 250px;
  height: 130px;
  padding: 10px;
  margin: 10px;
}

article div:first-child {
  position: absolute;
  top: 10px;
  left: 0;
  background: url(colorful-heart.png) no-repeat center 20px;
  background-color: green;
}

article div:last-child {
  background-color: purple;
  position: absolute;
  bottom: -10px;
  right: 0;
  z-index: -1;
}

.multiply-mix {
  mix-blend-mode: multiply;
}
```

结果如下：

你可以看到，多层混合（mix-blend）不仅混合了两种背景图像，还混合了在`<div>`下面的颜色。

**备注：** 如果你不理解上面的一些布局属性，请不要担心，像 `position`、`top`、`bottom`、`z-index` 等。我们将在 CSS Layout 模块中详细地介绍这些内容。

-webkit-background-clip: text
-----------------------------

另一个我们认为在继续之前会提到的新特性 (目前支持 Chrome、Safari 和 Opera，和 Firefox 正在实现) 是`background-clip`的 `text` 值。当与专有 `-webkit-text-fill-color: transparent;` 特性一起使用时，这允许你将背景图像剪贴到元素文本的形状，从而产生一些不错的效果。这不是一个正式的标准，但是已经在多个浏览器中实现了，因为它很流行，并且被开发人员广泛使用。在这种情况下，这两种属性都需要一个`-webkit-`供应商前缀，甚至对于非 webkit/Chrome-based 的浏览器来说也是如此。

```css
.text-clip {
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
```

那么为什么其他浏览器会实现一个 `-webkit-` 前缀？主要是为了浏览器兼容性——许多 web 开发人员已经开始使用 `-webkit-` 前缀来实现 web 站点，它开始看起来像其他的浏览器一样被破坏了，而实际上他们遵循的是标准。因此，他们被迫实施了一些这样的功能。这就凸显了在你的工作中使用非标准和/或带前缀的 CSS 特性的危险——这不仅会导致浏览器兼容性问题，而且还会发生变化，因此你的代码随时可能崩溃。坚持标准要好得多。

如果你确实希望在你的生产工作中使用这些特性，请确保在浏览器中进行彻底的测试，并检查这些特性不工作的地方，站点仍然可用。

**备注：** 对于一个完整的 `-webkit-background-clip: text` 代码示例，见background-clip-text.html（也可以见源码）。

自主学习：尝试一些效果
-----------

现在轮到你了。对于这种自主学习，我们希望你使用下面所提供的代码来试验上面所读到的一些效果。

如果你犯了一个错误，你可以用 *Reset* 按钮来重置这个例子。
```

<div
 class="body-wrapper"
 style="font-family: 'Open Sans Light',Helvetica,Arial,sans-serif;">
  <h2>HTML Input</h2>
  <textarea
 id="code"
 class="html-input"
 style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;">
<div class="style-me">
</div></textarea
 >

  <h2>CSS Input</h2>
  <textarea
 id="code"
 class="css-input"
 style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;">
.style-me {
  width: 280px;
  height: 130px;
  padding: 10px;
  margin: 10px;
  display: inline-block;
  background-color: red;
  background: url(colorful-heart.png) no-repeat center 20px,
              linear-gradient(to bottom right, #f33, #a33);
} </textarea
 >

  <h2>Output</h2>
  <div
 class="output"
 style="width: 90%;height: 15em;padding: 10px;border: 1px solid #0095dd;overflow:hidden;"></div>
  <div class="controls">
    <input
 id="reset"
 type="button"
 value="Reset"
 style="margin: 10px 10px 0 0;" />
  </div>
</div>
```

```
var htmlInput = document.querySelector(".html-input");
var cssInput = document.querySelector(".css-input");
var reset = document.getElementById("reset");
var htmlCode = htmlInput.value;
var cssCode = cssInput.value;
var output = document.querySelector(".output");

var styleElem = document.createElement("style");
var headElem = document.querySelector("head");
headElem.appendChild(styleElem);

function drawOutput() {
  output.innerHTML = htmlInput.value;
  styleElem.textContent = cssInput.value;
}

reset.addEventListener("click", function () {
  htmlInput.value = htmlCode;
  cssInput.value = cssCode;
  drawOutput();
});

htmlInput.addEventListener("input", drawOutput);
cssInput.addEventListener("input", drawOutput);
window.addEventListener("load", drawOutput);
```

总结
--

我们希望这篇文章被证明是很有趣的——玩着闪亮的玩具通常是很有趣的，而且看看什么样的工具在尖端的浏览器中是可以得到的是我们很感兴趣的。你已经到达了样式盒文章的末尾，因此，接下来你将通过我们的评估来测试你的 box syling 技能。

* 上一页
* Overview: Styling boxes
* 下一页


溢出的内容 - 学习 Web 开发

溢出的内容
=====

* 上一页
* Overview: Building blocks
* 下一页

本节课，我们来了解一下 CSS 中另外一个重要的概念——**溢出**。溢出是在盒子无法容纳下太多的内容的时候发生的。在这篇教程里面，你将会学习到什么是溢出，以及如何控制它。

|  |  |
| --- | --- |
| 前提： |
 基础的电脑知识，安装了基本的软件，处理文件的基础知识，HTML 基础（学习
 HTML 简介），对 CSS 的工作原理有所了解（学习
 CSS 入门概述）。
  |
| 目标： | 理解溢出和控制溢出的方法。 |

什么是溢出？
------

我们知道，CSS 中万物皆盒，因此我们可以通过给 `width` 和 `height`（或者 `inline-size` 和 `block-size`）赋值的方式来约束盒子的尺寸。溢出是在你往盒子里面塞太多东西的时候发生的，所以盒子里面的东西也不会老老实实待着。CSS 给了你好几种工具来控制溢出，在学习的早期理解这些概念是很有用的。在你写 CSS 的时候你经常会遇到溢出的情形，尤其是当你以后更加深入到 CSS 布局的时候。

CSS 尽力减少“数据损失”
--------------

我们从两个展示了在碰到溢出的时候，CSS 默认会如何处理的例子开始吧。

第一个例子是，一个盒子，在块方向上已经受到 `height` 的限制。然后我们已经加了过多的内容，以至于盒子里面没有空间容纳。内容正在从盒子里面溢出，并让自己把盒子下面的段落弄得一团糟。

第二个例子是一个单词，位于在内联方向上受到限制的盒子里面。盒子已经被做得小到无法放置那个单词的地步，于是那个单词就突破了盒子的限制。

你也许会好奇，为什么 CSS 默认会采取如此不整洁的方式，让内容这么凌乱地溢出出来呢？为何不把多余的内容隐藏起来，或者让盒子变大呢？

只要有可能，CSS 就不会隐藏你的内容，隐藏引起的数据损失通常会造成困扰。在 CSS 的术语里面，这会导致一些内容消失，你的访客可能不会注意到这一点，如果消失的是表格上的提交按钮，没有人能填完这个表格，这是很麻烦的事情！所以 CSS 反而会把它以可见的形式溢出出去。这样做的结果就是，你会看到错误的 CSS 导致的一片混乱，或者最坏的情况也只是你的网站的访客会告诉你有些内容冒了出来，你的网站需要修缮。

如果你已经用 `width` 或者 `height` 限制住了一个盒子，CSS 假定，你知道你在做什么，而且你已经控制住了溢出的隐患。总之，在盒子里面需要放置文本的时候，限制住块方向的尺寸是会引起问题的，因为可能会有比你在设计网站的时候所预计的文本更多的文本，或者文本变大了——比如用户增加字体大小的时候。

在下面的几节课里，我们会看一下各种不同的控制尺寸的方式，以减少溢出的影响。但是，如果你需要固定的尺寸，你也可以控制溢出表现的形式。那么让我们接着读下去吧！

overflow 属性
-----------

`overflow` 属性是你控制一个元素溢出的方式，它告诉浏览器你想怎样处理溢出。`overflow` 的默认值为 `visible`，这就是我们的内容溢出的时候，我们在默认情况下看到它们的原因。

如果你想在内容溢出的时候把它裁剪掉，你可以在你的盒子上设置 `overflow: hidden`。这就会像它表面上所显示的那样作用——隐藏掉溢出。这可能会很自然地让东西消失掉，所以你只应该在判断隐藏内容不会引起问题的时候这样做。

也许你还会想在有内容溢出的时候加个滚动条？如果你用了 `overflow: scroll`，那么你的浏览器总会显示滚动条，即使没有足够多引起溢出的内容。你可能会需要这样的样式，它避免了滚动条在内容变化的时候出现和消失。

**如果你移除了下面的盒子里的一些内容，你可以看一下，滚动条是否还会在没有能滚动的东西的时候保留。**

在以上的例子里面，我们仅仅需要在 `y` 轴方向上滚动，但是我们在两个方向上都有了滚动条。你可以使用 `overflow-y` 属性，设置 `overflow-y: scroll` 来仅在 `y` 轴方向滚动。

你也可以用 `overflow-x`，以在 x 轴方向上滚动，尽管这不是处理长英文词的好办法！如果你真的需要在小盒子里面和长英文词打交道，那么你可能要了解一下 `word-break` 或者 `overflow-wrap` 属性。除此以外，一些在 CSS 里面调整大小这节课里面讨论过的方式可能会帮助你创建可以和有变化容量的内容相协调的盒子。

和 `scroll` 一样，在无论是否有多到需要 用滚动条的内容的时候，页面上都会显示一个滚动条。

**备注：** 你可以用 `overflow` 属性指定 x 轴和 y 轴方向的滚动，同时使用两个值进行传递。如果指定了两个关键字，第一个对 `overflow-x` 生效而第二个对 `overflow-y` 生效。否则，`overflow-x` 和 `overflow-y` 将会被设置成同样的值。例如，`overflow: scroll hidden` 会把 `overflow-x` 设置成 `scroll`，而 `overflow-y` 则为 `hidden`。

如果你只是想让滚动条在有比盒子所能装下更多的内容的时候才显示，那么使用 `overflow: auto`。此时由浏览器决定是否显示滚动条。桌面浏览器一般仅仅会在有足以引起溢出的内容的时候这么做。

**在下面的例子里面，移除一些内容，直到能够装在盒子里面，你还会看到滚动条消失了。**

溢出建立了区块格式化上下文
-------------

CSS 中有所谓**区块格式化上下文**（Block Formatting Context，BFC）的概念。现在你不用太过在意，但是你应该知道，在你使用诸如 `scroll` 或者 `auto` 的时候，你就建立了一个块级排版上下文。结果就是，你改变了 `overflow` 的值的话，对应的盒子就变成了更加小巧的状态。在容器之外的东西没法混进容器内，也没有东西可以突出盒子，进入周围的版面。激活了滚动动作，你的盒子里面所有的内容会被收纳，而且不会遮到页面上其他的物件，于是就产生了一个协调的滚动体验。

网页设计时不需要的溢出
-----------

现代网页布局的方式（正如 CSS 布局模块中所介绍的那些）可以很好地处理溢出。我们不一定能预料到网页上会有多少内容，人们很好地设计它们，使得它们能与这种现状协调。但是在以往，开发者会更多地使用固定高度，尽力让毫无关联的盒子的底部对齐。这是很脆弱的，在旧时的应用里面，你偶尔会遇到一些盒子，它们的内容遮到了页面上的其他内容。如果你看到了，那么你现在应该知道，这就是溢出，理论上你应该能重新排布这些布局，使得它不必依赖于盒子尺寸的调整。

在开发网站的时候，你应该一直把溢出的问题挂在心头，你应该用或多或少的内容测试设计，增加文本的字号，确保你的 CSS 可以正常地协调。改变溢出属性的值，来隐藏内容或者增加滚动条，会是你仅仅在少数特别情况下需要的，例如在你确实需要一个可滚动盒子的时候。

技能测试！
-----

你已经到了本文的结尾，但你能记住其中重要的信息吗？你可以在继续前进之前进行一些测试来验证你是否记住了这些内容——见技能测试：溢出 (en-US)。

小结
--

这节短短的课已经介绍了溢出的概念，你现在明白，CSS 会尽力不让溢出的内容不可见，因为这会造成数据损失。你已经发现，你可以控制住潜在的溢出，同样，你也应该测试你的作品，确保你不会一下子就弄出令人困扰的溢出。

* 上一页
* Overview: Building blocks
* 下一页


CSS 选择器 - 学习 Web 开发

CSS 选择器
=======

* 上一页
* Overview: Building blocks
* 下一页

CSS中，选择器用来指定网页上我们想要样式化的HTML元素。有 CSS 选择器提供了很多种方法，所以在选择要样式化的元素时，我们可以做到很精细的地步。本文和本文的子篇中，我们将会详细地讲授选择器的不同使用方式，并了解它们的工作原理。

|  |  |
| --- | --- |
| 学习前提： |
 计算机的基本知识，
 安装了基础软件，处理文件的基本知识，HTML 基础（学习HTML 介绍）以及对 CSS 工作方式的了解（学习CSS 初步）
  |
| 目标： | 详细学习 CSS 选择器的工作方式。 |

选择器是什么？
-------

你也许已经见过选择器了。CSS 选择器是 CSS 规则的第一部分。它是元素和其他部分组合起来告诉浏览器哪个 HTML 元素应当是被选为应用规则中的 CSS 属性值的方式。选择器所选择的元素，叫做“选择器的对象”。

![Some code with the h1 highlighted.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/selector.png)

前面的文章中，你也许已经遇到过几种不同的选择器，了解到选择器可以以不同的方式选择元素，比如选择诸如`h1`的元素，或者是根据 class (类) 选择例如`.special`。

CSS 中，选择器由 CSS 选择器规范加以定义。就像是 CSS 的其他部分那样，它们需要浏览器的支持才能工作。你会遇到的大多数选择器都是在CSS 3中定义的，这是一个成熟的规范，因此你会发现浏览器对这些选择器有良好的支持。

选择器列表
-----

如果你有多个使用相同样式的 CSS 选择器，那么这些单独的选择器可以被混编为一个“选择器列表”，这样，规则就可以应用到所有的单个选择器上了。例如，如果我的`h1`和`.special`类有相同的 CSS，那么我可以把它们写成两个分开的规则。

```css
h1 {
  color: blue;
}

.special {
  color: blue;
}
```

我也可以将它们组合起来，在它们之间加上一个逗号，变为选择器列表。

```css
h1, .special {
  color: blue;
}
```

空格可以在逗号前或后，你可能还会发现如果每个选择器都另起一行，会更好读些。

```css
h1,
.special {
  color: blue;
}
```

在下面的实时示例中，试着把两个有相同声明的选择器组合起来。外观在组合起来以后应该还是一样的。

当你使用选择器列表时，如果任何一个选择器无效 (存在语法错误)，那么整条规则都会被忽略。

在下面的示例中，无效的 class 选择器会被忽略，而`h1`选择器仍会被样式化。

```css
h1 {
  color: blue;
}

..special {
  color: blue;
}
```

但是在被组合起来以后，整个规则都会失效，无论是`h1`还是这个 class 都不会被样式化。

```css
h1,
..special {
  color: blue;
}
```

选择器的种类
------

有几组不同的选择器，知道了需要哪种选择器，你会更容易正确使用它们。在本文的子篇中，我们将会来更详细地看下不同种类的选择器。

### 类型、类和 ID 选择器

这个选择器组，第一个是指向了所有 HTML 元素 `<h1>`。

```css
h1 {
}
```

它也包含了一个 class 的选择器：

```css
.box {
}
```

亦或，一个 id 选择器：

```css
#unique {
}
```

### 标签属性选择器

这组选择器根据一个元素上的某个标签的属性的存在以选择元素的不同方式：

```css
a[title] {
}
```

或者根据一个有特定值的标签属性是否存在来选择：

```css
a[href="https://example.com"]
{
}
```

### 伪类与伪元素

这组选择器包含了伪类，用来样式化一个元素的特定状态。例如`:hover`伪类会在鼠标指针悬浮到一个元素上的时候选择这个元素：

```css
a:hover {
}
```

它还可以包含了伪元素，选择一个元素的某个部分而不是元素自己。例如，`::first-line`是会选择一个元素（下面的情况中是`<p>`）中的第一行，类似`<span>`包在了第一个被格式化的行外面，然后选择这个`<span>`。

```css
p::first-line {
}
```

### 运算符

最后一组选择器可以将其他选择器组合起来，更复杂的选择元素。下面的示例用运算符（`>`）选择了`<article>`元素的初代子元素。

```css
article > p {
}
```

接下来要做的事情
--------

你可以看下下面的选择器参考表，可以获得到这个学习章节——或者总体来说是 MDN 上——的各种选择器的直接链接；你也可以继续下去，开始你的了解类型、类和 ID 选择器的旅程。

* 上一页
* Overview: Building blocks
* 下一页


属性选择器 - 学习 Web 开发

属性选择器
=====

* 上一页
* Overview: Building blocks
* 下一页

从 HTML 的学习中，你已经知道，元素可以带有属性，它提供了关于如何标记的更详细信息。CSS 中，你能用属性选择器来选中带有特定属性的元素。本节课中，我们将会为你展示如何使用这些很有用的选择器。

|  |  |
| --- | --- |
| 学习前提： | 基础电脑知识，安装了基本的软件，处理文件的基本知识，HTML 基础（学习HTML 介绍），以及对 CSS 工作原理的了解（学习CSS 初步） |
| 目标： | 学习属性选择器是什么，如何使用。 |

存否和值选择器
-------

这些选择器允许基于一个元素自身是否存在（例如`href`）或者基于各式不同的按属性值的匹配，来选取元素。

| 选择器 | 示例 | 描述 |
| --- | --- | --- |
| `[*attr*]` | `a[title]` | 匹配带有一个名为*attr*的属性的元素——方括号里的值。 |
| `[*attr*=*value*]` | `a[href="https://example.com"]` | 匹配带有一个名为*attr*的属性的元素，其值正为*value*——引号中的字符串。 |
| `[*attr*~=*value*]` | `p[class~="special"]` | 匹配带有一个名为*attr*的属性的元素，其值正为*value*，或者匹配带有一个*attr*属性的元素，其值有一个或者更多，至少有一个和*value*匹配。
注意，在一列中的好几个值，是用空格隔开的。 |
| `[*attr*|=*value*]` | `div[lang|="zh"]` | 匹配带有一个名为*attr*的属性的元素，其值可正为*value*，或者开始为*value*，后面紧随着一个连字符。 |

下面的示例中，你可以看到这些选择器是怎样使用的。

* 使用`li[class]`，我们就能匹配任何有 class 属性的选择器。这匹配了除了第一项以外的所有项。
* `li[class="a"]`匹配带有一个`a`类的选择器，不过不会选中一部分值为`a`而另一部分是另一个用空格隔开的值的类，它选中了第二项。
* `li[class~="a"]`会匹配一个`a`类，不过也可以匹配一列用空格分开、包含`a`类的值，它选中了第二和第三项。

子字符串匹配选择器
---------

这些选择器让更高级的属性的值的子字符串的匹配变得可行。例如，如果你有`box-warning`和`box-error`类，想把开头为“box-”字符串的每个物件都匹配上的话，你可以用`[class^="box-"]`来把它们两个都选中。

| 选择器 | 示例 | 描述 |
| --- | --- | --- |
| `[attr^=value]` | `li[class^="box-"]` | 匹配带有一个名为*attr*的属性的元素，其值开头为*value*子字符串。 |
| `[attr$=value]` | `li[class$="-box"]` | 匹配带有一个名为*attr*的属性的元素，其值结尾为*value*子字符串 |
| `[attr*=value]` | `li[class*="box"]` | 匹配带有一个名为*attr*的属性的元素，其值的字符串中的任何地方，至少出现了一次*value*子字符串。 |

下个示例展示了这些选择器的用法：

* `li[class^="a"]`匹配了任何值开头为`a`的属性，于是匹配了前两项。
* `li[class$="a"]`匹配了任何值结尾为`a`的属性，于是匹配了第一和第三项。
* `li[class*="a"]`匹配了任何值的字符串中出现了`a`的属性，于是匹配了所有项。

大小写敏感
-----

如果你想在大小写不敏感的情况下，匹配属性值的话，你可以在闭合括号之前，使用`i`值。这个标记告诉浏览器，要以大小写不敏感的方式匹配 ASCII 字符。没有了这个标记的话，值会按照文档语言对大小写的处理方式，进行匹配——HTML 中是大小写敏感的。

下面的示例中，第一个选择器将会匹配一个开头为`a`的值，这样它只匹配了第一项，因为另外两项开头是大写的 A。第二个选择器使用了大小写不敏感的标记，于是匹配了所有项。

>
> \*\*备注：\*\*此外还有一个更加新的`s`值，它会强制在上下文的匹配正常为大小写不敏感的时候，强行要求匹配时大小写敏感。不过，在浏览器中它不太受支持，而且在上下文为 HTML 时也没啥用。
>
>
>

接下来……
-----

现在我们完成了属性选择器的学习，你可以继续读下篇文章——关于伪类和伪元素选择器的事情。

* 上一页
* Overview: Building blocks
* 下一页


技能测试：选择器 - 学习 Web 开发

技能测试：选择器
========

这个任务的目的是帮助你理解 CSS 里的选择器。

**备注：** 你可以在下面的交互式编辑器中尝试解决问题，然而把代码下载然后使用一个在线工具比如 CodePen、jsFiddle 或 Glitch 去解决这些问题可能对你更有作用。

如果你卡住了，可以向我们寻求帮助——请参阅本页底部的打分评估或进一步帮助部分。

选择器一
----

在没有改变 HTML 的情况下，使用 CSS 去完成下面的要求：:

* 使一级标题的字体颜色为蓝色
* 使二级标题有一个蓝色背景且白色文本。
* 使跨距中换行的文本的字体大小为 200%。

![Text with the CSS applied for the solution to task 1.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Selectors_Tasks/selectors1.jpg)

尝试更新下面的实时代码，以重现完整的示例：

如需提交测评或进一步练习，下载本任务的初始文件，然后在本地或在线编辑器中练习。

选择器二
----

在没有改变 HTML 的情况下，对本例中内容的外观进行以下更改：

* 让 id 为 `special` 的元素有一个黄色背景。
* 让使用类 `alert` 的元素有一个 1px 的灰色边框。
* 如果一个元素使用了 `alert` 类还有 `stop`类，让它的背景变为红色。
* 如果一个元素使用 `alert` 类还有 `go`类，让它的背景变为绿色。

![Text with the CSS applied for the solution to task 2.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Selectors_Tasks/selectors2.jpg)

尝试更新下面的实时代码，以重现完整的示例：

如需提交测评或进一步练习，下载本任务的初始文件，然后在本地或在线编辑器中练习。

选择器三
----

在本例中，尝试在不添加 HTML 的情况下进行以下更改。

* 链接文本的样式：使链接为橘色，被访问后变为绿色，当被 hover 时，移除链接文本的下划线。
* 让容器里的第一个元素的字体大小为:150%，并且让这个元素的第一行是红色的。
* 让表格中每隔一行条带化，分别给它们一个颜色为#333 的背景和一个白色前景。

![Text with the CSS applied for the solution to task 3.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Selectors_Tasks/selectors3.jpg)

尝试更新下面的实时代码，以重现完整的示例：

如需提交测评或进一步练习，下载本任务的初始文件，然后在本地或在线编辑器中练习。

选择器四
----

在这个任务中尝试下面这个任务：

* 让任何直接跟随二级标题的段落颜色为红色。
* 移除使用了 list 类的无序列表的子元素前面的圆点，并给他们添加一个 1px 的灰色下边框。

![Text with the CSS applied for the solution to task 4.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Selectors_Tasks/selectors4.jpg)

尝试更新下面的实时代码，以重现完整的示例：

如需提交测评或进一步练习，下载本任务的初始文件，然后在本地或在线编辑器中练习。

选择器五
----

在最后一个任务中，使用属性选择器添加 CSS 以执行以下操作：

* 选择带有 `title` 属性的 `<a>` 元素，将其边框颜色设置为粉色（`border-color: pink`）。
* 选择带有 `href` 属性且属性值中包含 `contact` 的 `<a>` 元素，将其边框颜色设置为橙色（`border-color: orange`）。
* 选择 `href` 属性值以 `https` 开头的 `<a>` 元素，将其边框颜色设置为绿色（`border-color: green`）。

![Four links with different color borders.](/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Selectors_Tasks/selectors-attribute.png)

尝试更新下面的实时代码，以重现完整的示例：

如需提交测评或进一步练习，下载本任务的初始文件，然后在本地或在线编辑器中练习。

打分评估或进一步帮助
----------

你可以在上面的交互式编辑器中练习示例这些示例。

如果你想得到任务评价，或者遇到了问题需要帮助，请按如下操作进行：

1. 将你的作业上传到可共享的在线编辑器上，比如 CodePen、jsFiddle 或 Glitch。你可以自己编写代码，也可以使用上面给出的初始文件。
2. 在 MDN 论坛的学习板块上发帖以寻求打分或帮助。你的帖子中应包含：
    * 一个简述问题的标题。如“Assessment wanted for Selectors skill test 1”（请使用英文发帖）。
    * 详细描述你的尝试和你预期的网页效果。比如你在哪个位置需要帮助，或者你需要打分评估。
    * 你需要打分或帮助的作业链接（通过上述的在线编辑器上传）。只有让别人看到你的代码，你才能得到更好的帮助。
    * 本题的链接。让别人知道你所问的具体题目。


类型、类和 ID 选择器 - 学习 Web 开发

类型、类和 ID 选择器
============

* 上一页
* Overview: Building blocks
* 下一页

本节课中，我们看下可用的最简单的选择器，在你的工作中，它们很有可能会是最常用到的。

|  |  |
| --- | --- |
| 学习前提： | 基本电脑知识，安装了基础的软件，处理文件的基础知识，HTML 基础（学习HTML 介绍），以及对 CSS 工作原理的理解（学习CSS 初步） |
| 目标： | 学习不同的 CSS 选择器，通过它们将 CSS 应用到文档中。 |

类型选择器
-----

**类型选择器**有时也叫做“标签名选择器\*”\*或者是”元素选择器“，因为它在文档中选择了一个 HTML 标签/元素的缘故。在下面的示例中，我们已经用了 span、em 和 strong 选择器，`<span>`、`<em>`和`<strong>`元素的所有实例这样就都被样式化了。

**试着加上一条 CSS 规则，选择`<h1>`元素，把它的颜色变为蓝色。**

全局选择器
-----

全局选择器，是由一个星号（`*`）代指的，它选中了文档中的所有内容（或者是父元素中的所有内容，比如，它紧随在其他元素以及邻代运算符之后的时候）。下面的示例中，我们已经用全局选择器，移去了所有元素上的外边距。这就是说，和浏览器以外边距隔开标题和段的方式默认加上的样式不同的是，每个物件都紧紧地挨在一起，我们不能那么容易就看清楚不同的段。

这种行为有时能在“重置样式表”中看到，其中所有浏览器所做的样式化都会被忽略。这些一度很受欢迎，但是把所有的样式化忽略掉的话，通常就是指，你必须做把这些样式带回来的工作！因此我们应小心使用全局选择器，以处理诸如下面所述之类的很特殊的情况。

### 使用全局选择器，让选择器更易读

全局选择器的一种用法是让选择器更易读，更明显地表明它们的作用。例如，如果我想选中任何`<article>`元素的第一子元素，不论它是什么元素，都给它加粗，我可以将`:first-child`选择器（我们将会在伪类和伪元素课中进一步了解）用作`<article>`元素选择器的一个后代选择器：

```css
article :first-child {
}
```

但是这会和`article:first-child`混淆，而后者选择了作为其他元素的第一子元素的`<article>`元素。

为了避免这种混淆，我们可以向`:first-child`选择器加入全局选择器，这样选择器所做的事情很容易就能看懂。选择器正选中`<article>`元素的*任何*第一子元素：

```css
article \*:first-child {
}
```

类选择器
----

类选择器以一个句点（`.`）开头，会选择文档中应用了这个类的所有物件。在下面的实时示例中，我们已经建立了一个名为`.highlight`的类，把它应用到了我的文档中的几个地方。所有包含此类的元素都被高亮了。

### 指向特定元素的类

你能建立一个指向应用一个类的特定元素。在下一个示例里面，我们将会用不同方式高亮一个带有`highlight`类的`<span>`和带有 `highlight`类的`<h1>`标题。我们通过使用附加了类的欲选元素的选择器做到这点，其间没有空格。

这种方式使 CSS 没那么可复用，因为类现在只会应用到那个特定元素，在认为规则也该应用到其他元素的时候，你会需要另外加上一个选择器。

### 多个类被应用的时候指向一个元素

你能对一个元素应用多个类，然后分别指向它们，或者仅仅在选择器中存在所有这些类的时候选择这一元素。在你的站点上，构建可以以不同方式组合起来的组件的时候，这会有用。

在下面的示例中，有一个包含了一条笔记的`<div>`。灰色的边框在盒子带有`notebox`类的时候应用。如果它还有一个`warning`或是`danger`类，我们改变`border-color`。

为了告诉浏览器我们只想匹配带有所有这些类的元素，我们可以将这些类不加空格地连成一串。

ID 选择器
------

ID 选择器开头为`#`而非句点，不过基本上和类选择器是同种用法。可是在一篇文档中，一个 ID 只会用到一次。它能选中设定了`id`的元素，你可以在 ID 前面加上类型选择器，只指向元素和 ID 都匹配的类。在下面的示例里，你可以看看这两种用法。

>
> \*\*备注：\*\*正如我们在和特定性相关的课里面学到的那样，ID 所指特定，会优先于大多数其他选择器。所以很难处理它们。大多数情况下，给一个元素加个类，而不是使用 ID，会更好。不过要是 ID 是唯一一种指定这个元素的方式的话——也许是因为你没法访问标记标记因此不能编辑——这种方式可行。
>
>
>

下一篇
---

来看属性选择器，继续探索选择器。

* 上一页
* Overview: Building blocks
* 下一页


关系选择器 - 学习 Web 开发

关系选择器
=====

* 上一页
* Overview: Building blocks
* 下一页

我们要了解的最后一种选择器被命名为关系选择器（Combinator），这是因为它们在其他选择器之间和其他选择器与文档内容的位置之间建立了一种有用的关系的缘故。

|  |  |
| --- | --- |
| 学习前提： | 基础电脑知识、安装了基本的软件、文件处理基本知识、HTML 基础（学习 HTML 介绍），以及对 CSS 工作原理的了解（学习CSS 初步） |
| 目标： | 了解 CSS 里面可用的不同关系选择器。 |

后代选择器
-----

后代选择器——典型用单个空格（" "）字符——组合两个选择器，比如，第二个选择器匹配的元素被选择，如果他们有一个祖先（父亲，父亲的父亲，父亲的父亲的父亲，等等）元素匹配第一个选择器。选择器利用后代组合符被称作后代选择器。

```css
body article p
```

下面的示例中，我们只会匹配处于带有`.box`类的元素里面的`<p>`元素。

子代关系选择器
-------

子代关系选择器是个大于号（`>`），只会在选择器选中直接子元素的时候匹配。继承关系上更远的后代则不会匹配。例如，只选中作为`<article>`的直接子元素的`<p>`元素：

```css
article > p
```

下个示例中，我们弄了个有序列表，内嵌于另一个无序列表里面。我用子代关系选择器，只选中为`<ul>`的直接子元素的`<li>`元素，给了它们一个顶端边框。

如果你移去指定子代选择器的`>`的话，你最后得到的是后代选择器，所有的`<li>`会有个红色的边框。

邻接兄弟
----

邻接兄弟选择器（`+`）用来选中恰好处于另一个在继承关系上同级的元素旁边的物件。例如，选中所有紧随`<p>`元素之后的`<img>`元素：

```css
p + img
```

常见的使用场景是，改变紧跟着一个标题的段的某些表现方面，就像是我下面的示例那样。这里我们寻找一个紧挨`<h1>`的段，然后样式化它。

如果你往`<h1>`和`<p>`之间插入其他的某个元素，例如`<h2>`，你将会发现，段落不再与选择器匹配，因而不会应用元素邻接时的前景和背景色。

通用兄弟
----

如果你想选中一个元素的兄弟元素，即使它们不直接相邻，你还是可以使用通用兄弟关系选择器（`~`）。要选中所有的`<p>`元素后*任何地方*的`<img>`元素，我们会这样做：

```css
p ~ img
```

在下面的示例中，我们选中了所有的 `<h1>`之后的`<p>`元素，虽然文档中还有个 `<div>`，其后的`<p>`还是被选中了。

使用关系选择器
-------

你能用关系选择器，将任何在我们前面的学习过程中学到的选择器组合起来，选出你的文档中的一部分。例如如果我们想选中为`<ul>`的直接子元素的带有“a”类的列表项的话，我可以用下面的代码。

```css
ul > li[class="a"] {
}
```

不过，建立一长列选中你的文档中很明确的部分的选择器的时候，小心一些。这些 CSS 规则难以复用，因为你让选择器在表示标记文本中的元素的相对位置上过于明确。

建立简单的一个类，然后把它应用到有需求的元素上，经常会是更好的做法。不过话说回来，如果你需要让你的文档变换一下样式，但是没法编辑 HTML（也许是因为它由 CMS 生成）的话，你的关系选择器的知识会派上用场。

技能测试！
-----

我们已经在这篇文章里讲了许多内容，不过你能记住最重要的信息吗？你可以找些更深入的测试，在继续下去之前，验证你是否已经获取了这些信息——见技能测试：选择器。

继续学习
----

这是我们选择器这节课的最后一节了。下面我们将会继续前进，学习 CSS 的另一个重要部分——层叠与继承。

* 上一页
* Overview: Building blocks
* 下一页


伪类和伪元素 - 学习 Web 开发

伪类和伪元素
======

* 上一页
* Overview: Building blocks
* 下一页

下一组我们将了解的选择器被称为**伪类**和**伪元素**。这一类选择器的数量众多，通常用于很明确的目的。一旦你了解了如何使用它们，你便可以通过查阅列表来寻找合适的那一项以完成你想要的选择。与之前一样，每个选择器相关的 MDN 页面都将帮助你了解各浏览器的支持情况。

|  |  |
| --- | --- |
| 学习前提： | 基础电脑知识，安装了基本的软件，处理文件的基本知识，HTML 基础（学习HTML 介绍），以及对 CSS 工作原理的了解（学习CSS 初步） |
| 目标： | 了解伪类和伪元素选择器 |

什么是伪类？
------

伪类是选择器的一种，它用于选择处于特定状态的元素，比如当它们是这一类型的第一个元素时，或者是当鼠标指针悬浮在元素上面的时候。它们表现得会像是你向你的文档的某个部分应用了一个类一样，帮你在你的标记文本中减少多余的类，让你的代码更灵活、更易于维护。

伪类就是开头为冒号的关键字：
```

:pseudo-class-name
```

### 简单伪类示例

让我们看下一个简单的示例。如果我们想要让一篇文章中的第一段变大加粗，可为此段加上一个类，然后为那个类添加 CSS，正如下面的示例展示的这样：

不过，这在维护的时候可能会很恼人——要是文档的头部又加上一段的话呢？我们会需要把这个类挪到新加的这段上。假如我们不加类，我们可以使用`:first-child`伪类选择器——这将*一直*选中文章中的第一个子元素，我们将不再需要编辑 HTML（编辑 HTML 并不总是可行，也许是因为它是由一个 CMS 生成的）。

所有的伪类以同样的方式实现。它们选中你的文档中处于某种状态的那部分，表现得就像是你已经向你的 HTML 加入类一样。看下 MDN 上的另外几个示例：

* `:last-child`
* `:only-child`
* `:invalid`
### 用户行为伪类

一些伪类只会在用户以某种方式和文档交互的时候应用。这些**用户行为伪类**，有时叫做**动态伪类**，表现得就像是一个类在用户和元素交互的时候加到了元素上一样。案例包括：

* `:hover`——上面提到过，只会在用户将指针挪到元素上的时候才会激活，一般就是链接元素。
* `:focus`——只会在用户使用键盘控制，选定元素的时候激活。

伪元素是啥？
------

伪元素以类似方式表现，不过表现得是像你往标记文本中加入全新的 HTML 元素一样，而不是向现有的元素上应用类。伪元素开头为双冒号`::`。
```

::pseudo-element-name
```

**备注：** 一些早期的伪元素曾使用单冒号的语法，所以你可能会在代码或者示例中看到。现代的浏览器为了保持后向兼容，支持早期的带有单双冒号语法的伪元素。

例如，如果你想选中一段的第一行，你可以把它用一个`<span>`元素包起来，然后使用元素选择器；不过，如果包起来的单词/字符数目长于或者短于父元素的宽度，这样做会失败。由于我们一般不会知道一行能放下多少单词/字符——因为屏幕宽度或者字体大小改变的时候这也会变——通过改变 HTML 的方式来可预测地这么做是不可能的。

`::first-line`伪元素选择器会值得信赖地做到这件事——即使单词/字符的数目改变，它也只会选中第一行。

这表现得就像是`<span>`神奇地包在第一个被格式化的行一样，每当行长改变的时候还会更新。

你可以看到它把两段的第一行都选中了。

把伪类和伪元素组合起来
-----------

如果你想让第一段的第一行加粗，你需要把`:first-child`和`::first-line`选择器放到一起。试着编辑前面的实时示例，让它使用下面的 CSS。这里的意思是，我们想选择一个`<article>`元素里面的第一个`<p>`元素的第一行。

```css
article p:first-child::first-line {
  font-size: 120%;
  font-weight: bold;
}
```

生成带有::before 和::after 的内容
-------------------------

有一组特别的伪元素，它们和`content`属性一同使用，使用 CSS 将内容插入到你的文档中中。

你能用这些插入一个文本字符串，和在下面的实时示例里那样。试着改变`content`属性的文本值，看看输出是怎么改变的。你也能改变`::before`伪元素为`::after`，看到这段文本插入到了元素的末尾而不是开头。

从 CSS 插入文本字符串，我们并不会在 Web 浏览器上经常这么做，因为对于一些屏幕阅读器来说，文本是不可见的，而且对于未来别人的查找和编辑也不是很方便。

这些伪元素的更推荐的用法是插入一个图标，例如下面的示例加入的一个小箭头，作为一个视觉性的提示，而且我们并不希望屏幕阅读器读出它。

这些伪元素经常用于插入空字符串，其后可以像页面上的其他元素被样式化。

下个示例，我们已经用 `::before`伪元素加入了个空字符串。我们把它设为了`display: block`，以让它可以用 width 和 height 进行样式化。然后我们可以用 CSS 像任何元素那样样式化。你可以摆弄 CSS，改变它的外观和行为。

`::before`和`::after`伪元素与`content`属性的共同使用，在 CSS 中被叫做“生成内容”，而且你会见到这种技术被用于完成各种任务。CSS Arrow Please网站就是一个著名的示例，它帮你用 CSS 生成一个箭头。在你创建你的箭头的时候看下 CSS，你将会看到实际使用的`::before`和`::after`伪元素。无论什么时候你看到了这些选择器，都要看下`content`属性，以了解文档中添加了什么。

参考节
---

有很多伪类和伪元素，所以有一个用于参考的列表会有用。下面是列出它们的表格，链接到了 MDN 上它们的参考页。把这作为参考，看看你能选中什么。

### 伪类

| 选择器 | 描述 |
| --- | --- |
| `:active` | 在用户激活（例如点击）元素的时候匹配。 |
| `:any-link` | 匹配一个链接的`:link`和`:visited`状态。 |
| `:blank` | 匹配空输入值的`<input>`元素。 |
| `:checked` | 匹配处于选中状态的单选或者复选框。 |
| `:current` (en-US) | 匹配正在展示的元素，或者其上级元素。 |
| `:default` | 匹配一组相似的元素中默认的一个或者更多的 UI 元素。 |
| `:dir` | 基于其方向性（HTML`dir`属性或者 CSS`direction`属性的值）匹配一个元素。 |
| `:disabled` | 匹配处于关闭状态的用户界面元素 |
| `:empty` | 匹配除了可能存在的空格外，没有子元素的元素。 |
| `:enabled` | 匹配处于开启状态的用户界面元素。 |
| `:first` | 匹配分页媒体的第一页。 |
| `:first-child` | 匹配兄弟元素中的第一个元素。 |
| `:first-of-type` | 匹配兄弟元素中第一个某种类型的元素。 |
| `:focus` | 当一个元素有焦点的时候匹配。 |
| `:focus-visible` | 当元素有焦点，且焦点对用户可见的时候匹配。 |
| `:focus-within` | 匹配有焦点的元素，以及子代元素有焦点的元素。 |
| `:future` (en-US) | 匹配当前元素之后的元素。 |
| `:hover` | 当用户悬浮到一个元素之上的时候匹配。 |
| `:indeterminate` | 匹配未定态值的 UI 元素，通常为复选框。 |
| `:in-range` | 用一个区间匹配元素，当值处于区间之内时匹配。 |
| `:invalid` | 匹配诸如`<input>`的位于不可用状态的元素。 |
| `:lang` | 基于语言（HTMLlang属性的值）匹配元素。 |
| `:last-child` | 匹配兄弟元素中最末的那个元素。 |
| `:last-of-type` | 匹配兄弟元素中最后一个某种类型的元素。 |
| `:left` | 在分页媒体中，匹配左手边的页。 |
| `:link` | 匹配未曾访问的链接。 |
| `:local-link` (en-US) | 匹配指向和当前文档同一网站页面的链接。 |
| `:is()` | 匹配传入的选择器列表中的任何选择器。 |
| `:not` | 匹配作为值传入自身的选择器未匹配的物件。 |
| `:nth-child` | 匹配一列兄弟元素中的元素——兄弟元素按照*an+b*形式的式子进行匹配（比如 2n+1 匹配元素 1、3、5、7 等。即所有的奇数个）。 |
| `:nth-of-type` | 匹配某种类型的一列兄弟元素（比如，`<p>`元素）——兄弟元素按照*an+b*形式的式子进行匹配（比如 2n+1 匹配元素 1、3、5、7 等。即所有的奇数个）。 |
| `:nth-last-child` | 匹配一列兄弟元素，从后往前倒数。兄弟元素按照*an+b*形式的式子进行匹配（比如 2n+1 匹配按照顺序来的最后一个元素，然后往前两个，再往前两个，诸如此类。从后往前数的所有奇数个）。 |
| `:nth-last-of-type` | 匹配某种类型的一列兄弟元素（比如，`<p>`元素），从后往前倒数。兄弟元素按照*an+b*形式的式子进行匹配（比如 2n+1 匹配按照顺序来的最后一个元素，然后往前两个，再往前两个，诸如此类。从后往前数的所有奇数个）。 |
| `:only-child` | 匹配没有兄弟元素的元素。 |
| `:only-of-type` | 匹配兄弟元素中某类型仅有的元素。 |
| `:optional` | 匹配不是必填的 form 元素。 |
| `:out-of-range` | 按区间匹配元素，当值不在区间内的的时候匹配。 |
| `:past` (en-US) | 匹配当前元素之前的元素。 |
| `:placeholder-shown` | 匹配显示占位文字的 input 元素。 |
| `:playing` | 匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的，且正在“播放”的元素。 |
| `:paused` | 匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的，且正在“暂停”的元素。 |
| `:read-only` | 匹配用户不可更改的元素。 |
| `:read-write` | 匹配用户可更改的元素。 |
| `:required` | 匹配必填的 form 元素。 |
| `:right` | 在分页媒体中，匹配右手边的页。 |
| `:root` | 匹配文档的根元素。 |
| `:scope` | 匹配任何为参考点元素的的元素。 |
| `:valid` | 匹配诸如`<input>`元素的处于可用状态的元素。 |
| `:target` | 匹配当前 URL 目标的元素（例如如果它有一个匹配当前URL 分段的元素）。 |
| `:visited` | 匹配已访问链接。 |

### 伪元素

| 选择器 | 描述 |
| --- | --- |
| `::after` | 匹配出现在原有元素的实际内容之后的一个可样式化元素。 |
| `::before` | 匹配出现在原有元素的实际内容之前的一个可样式化元素。 |
| `::first-letter` | 匹配元素的第一个字母。 |
| `::first-line` | 匹配包含此伪元素的元素的第一行。 |
| `::grammar-error` | 匹配文档中包含了浏览器标记的语法错误的那部分。 |
| `::selection` | 匹配文档中被选择的那部分。 |
| `::spelling-error` | 匹配文档中包含了浏览器标记的拼写错误的那部分。 |

* 上一页
* Overview: Building blocks
* 下一页


定位实例练习 - 学习 Web 开发

定位实例练习
======

* 上一页
* Overview: CSS layout
* 下一页

掌握了上一篇文章中的定位的基础知识，我们将着眼于实现一些现实中的例子，来演示你能用定位来做什么。

|  |  |
| --- | --- |
| 前提： | HTML 基础 (学习Introduction to HTML),和 CSS 怎么工作的 (学习Introduction to CSS.) |
| 目标： | 了解定位的实例 |

列表消息盒子
------

我们研究的第一个例子是一个经典的选项卡消息框，你想用一块小区域包括大量信息时，一个非常常用的特征。这包括含有大信息量的应用，比如策略战争游戏，比如从移动版的网页，屏幕狭小、空间有限；比如你可能想要放置许多信息的紧凑消息框，不用就会充满整个 UI。我们简单的例子完成后就会像下面这样：

![](/zh-CN/docs/Learn/CSS/CSS_layout/Practical_positioning_examples/tabbed-info-box.png)

**备注：** 你能看完整的示例，可运行在 info-box.html (source code)。检出它以理解你在本文章里要建立什么。

你可能会想：”为什么不仅仅做独立的选项卡为一个独立的网页，然后通过点击不同的标签来在不同的页面跳转来达到这个效果？“这样代码可能会简单一些，是的。但是这样每个独立的”页面“视图将会实际上是一个新加载的网页，跨视图更难保存信息，并把这个特征融入一个更大的 UI 设计。另外，所谓的”单页应用“正在变得非常流行——尤其是移动网页 UI——因为把一切的服务放在一个单独的文件上可以减少 HTTP 请求的数量来浏览所有内容，从而提高性能。

**备注：** 一些网络开发者甚至更超前，每次只加载一页的信息，并且使用 JavaScript 诸如 XMLHttpRequest特征动态改变信息显示。但是，在你此时的学习中，我们希望尽可能保持简单。接下来会有一些 JavaScript，但是只有一点。

在开始之前，我们需要你拷贝文件到本地，当作起始的 HTML 文件——info-box-start.html。保存到你的计算机合适的位置，然后在你的编辑器里打开。让我们看看包含在 body 里的 HTML 代码：

```html
<section class="info-box">
  <ul>
    <li><a href="#" class="active">Tab 1</a></li>
    <li><a href="#">Tab 2</a></li>
    <li><a href="#">Tab 3</a></li>
  </ul>
  <div class="panels">
    <article class="active-panel">
      <h2>The first tab</h2>

      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque
        turpis nibh, porttitor nec venenatis eu, pulvinar in augue. Vestibulum
        et orci scelerisque, vulputate tellus quis, lobortis dui. Vivamus varius
        libero at ipsum mattis efficitur ut nec nisl. Nullam eget tincidunt
        metus. Donec ultrices, urna maximus consequat aliquet, dui neque
        eleifend lorem, a auctor libero turpis at sem. Aliquam ut porttitor
        urna. Nulla facilisi.
      </p>
    </article>
    <article>
      <h2>The second tab</h2>

      <p>
        This tab hasn't got any Lorem Ipsum in it. But the content isn't very
        exciting all the same.
      </p>
    </article>
    <article>
      <h2>The third tab</h2>

      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque
        turpis nibh, porttitor nec venenatis eu, pulvinar in augue. And now an
        ordered list: how exciting!
      </p>

      <ol>
        <li>dui neque eleifend lorem, a auctor libero turpis at sem.</li>
        <li>Aliquam ut porttitor urna.</li>
        <li>Nulla facilisi</li>
      </ol>
    </article>
  </div>
</section>
```

这样我们已经得到了一个`<section>`元素带有类（`class`）为 `info-box`。此元素又包含一个 `<ul>` 和一个 `<div>`。无序列表包含三个列表项，列表项有链接在内，实际上将成为用于点击后显示内容面板的选项卡。 `div` 包含三个`<article>` 元素，构成对应于每个选项卡的内容面板。每个面板包含一些示例内容。

这里的思路是我们将样式化选项卡看起来是一个标准的水平导航菜单，使用绝对定位样式化面板互相坐落其顶上。我们也给你一点 JavaScript 包含到你的页面上，当选项卡被按下时，显示对应的面板，并且样式化选项卡本身。你不需要在这个阶段了解 JavaScript 本身，但是你应该尽快学习一些基本的 JavaScript——你的用户界面越复杂，越需要一些 JavaScript 来实现你渴望的功能。

### 一般设置

开始前，在`<style>`开闭标签之间添加下面的代码：

```css
html {
  font-family: sans-serif;
}

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

body {
  margin: 0;
}
```

这只是一些一般设置，在我们页面上设置了无衬线的字体、使用 `box-sizing` 模型，去掉 `<body>` 默认外边距。

下一步，在你早先的 CSS 下面加入如下代码：

```css
.info-box {
  width: 450px;
  height: 400px;
  margin: 0 auto;
}
```

这对内容设置具体的高度和宽度、在屏幕居中使用老把戏 `margin: 0 auto` 。在早先的课程中我们建议尽可能不固定内容容器的高度。这个情况下是可以的，因为我们是在选项卡中固定的内容，如果每个标签都有不同的高度，看起来也有些不和谐。

### 样式化我们的选项卡

现在我们希望样式化选项卡看上去像选项卡——基本，这些是一个水平的导航标签，但不是点击之后加载不同的网页，和我们之前在课程中见到的不同，相反，他们在同一页面上显示不同的面板。首先，在你的 CSS 底部添加下列规则，从无序列表中移除默认的`padding-left`和`margin-top`值：

```css
.info-box ul {
  padding-left: 0;
  margin-top: 0;
}
```

**备注：** 我们正在使用后代选择器，整个例子 `.info-box` 都位于链的开始——这样页面已经带有其他内容时，我们可以插入这个特征，不害怕与应用于页面其他部分的样式冲突。

接下来，我们将样式化水平选项卡——列表项都要左浮动确保他们一行合起来，他们的`list-style-type`被设置为 `none` 用以去除项目符号，宽度（`width`）设置为 `150px` 以便于适应这个 info-box。链接（ `<a>` ）元素设置为`display` inline-block，这样他们将在一行显示，仍然保持样式可设置，他们会被样式化合适的选项卡按钮，通过一系列的其他属性。

添加下列的 CSS：

```css
.info-box li {
  float: left;
  list-style-type: none;
  width: 150px;
}

.info-box li a {
  display: inline-block;
  text-decoration: none;
  width: 100%;
  line-height: 3;
  background-color: red;
  color: black;
  text-align: center;
}
```

最后，对于本节，我们将会在链接状态上设置一些样式。首先，我们要设置标签的 `:focus` 和 `:hover` 状态，让他们在获得焦点/鼠标悬浮的时候看起来不同，给用户提供一些可视化反馈。其次，当某个选项卡的类（ `class` ）出现 `active` 时，我们为其设置一条相同的样式规则。我们将通过使用 JavaScript 来设置，当一个标签被点击时。把这些 CSS 放置在你的其他样式后面：

```css
.info-box li a:focus,
.info-box li a:hover {
  background-color: #a60000;
  color: white;
}

.info-box li a.active {
  background-color: #a60000;
  color: white;
}
```

### 样式化面板

下一步工作是样式化我们的面板，现在开始吧！

首先，添加下列的规则去样式化 `.panels` `<div>` 容器。我们简单的设置一个固定的高度，确保面板包含在 info-box 里面，`position` `relative` 设置 `<div>` 为定位上下文，这样你能然后相对自身放置定位子元素，而不是相对`<html>`元素，最后我们清除（`clear`）浮动，设置在上面 CSS 中，这样避免影响布局的剩余部分。

```css
.info-box .panels {
  height: 352px;
  position: relative;
  clear: both;
}
```

在本节的最后，我们将对包含我们面板的单独 `<article>` 元素设置样式。我们添加的第一条规则就是绝对定位面板，让他们都位于`<div>`容器的左上角——这一部分对整个布局特性是关键的，就像使面板互相位于顶部。规则设置面板为和容器同样的高度，给内容一些内边距，设置字体颜色（`color`），和背景颜色（`background-color`）。

我们将添加的第二条规则，对带有类（ `class` ）为（ `active-panel` ）的面板，设置`z-index` 为 1，会让他位于其他的面板之上（默认定位元素 `z-index` 的值是 0，会使默认元素位于下面）。同样的，我们会在合适的时候用 JavaScript 来添加这个类。

```css
.info-box article {
  position: absolute;
  top: 0;
  left: 0;
  height: 352px;
  padding: 10px;
  color: white;
  background-color: #a60000;
}

.info-box .active-panel {
  z-index: 1;
}
```

### 添加我们的 JavaScript

让这些特性工作的最后一步是添加一些 JavaScript。添加下列一块代码，准确的写在你的开始和结束的`<script>`标签之间（在接下来的 HTML 内容中你将会发现这些）：

```js
var tabs = document.querySelectorAll(".info-box li a");
var panels = document.querySelectorAll(".info-box article");

for (i = 0; i < tabs.length; i++) {
  var tab = tabs[i];
  setTabHandler(tab, i);
}

function setTabHandler(tab, tabPos) {
  tab.onclick = function () {
    for (i = 0; i < tabs.length; i++) {
      if (tabs[i].getAttribute("class")) {
        tabs[i].removeAttribute("class");
      }
    }

    tab.setAttribute("class", "active");

    for (i = 0; i < panels.length; i++) {
      if (panels[i].getAttribute("class")) {
        panels[i].removeAttribute("class");
      }
    }

    panels[tabPos].setAttribute("class", "active-panel");
  };
}
```

这些代码做了如下工作：

* 首先我们保存所有的选项卡和所有的面板引用到两个变量中，名为 `tabs` 和 `panels`，这样此后我们可以容易地为它们做事。
* 然后我们使用 `for` 循环遍历所有的选项卡，并且在每一个上运行叫做`setTabHandler()` 的函数，此函数建立当每个选项卡被点击时应该发生的功能。运行时，函数会被传递选项卡，和一个索引数`i` 指明选项卡在`tabs` 数组中的位置。
* 在 `setTabHandler()` 函数中，这个标签创建了一个 `onclick` 事件来处理点击，所以当标签被点击的时候，接下来会发生：
    + 用一个 `for` 循环清除所有标签当前存在的类。
    + 当点击的时候在标签上创建了一个 `active` 类——从相关联的元素中继承了 CSS 的一些属性，具有和 panels 的样式相同的 `color` 和 `background-color`。
    + 用一个 `for` 循环清除所有面板当前存在的类。
    + 当标签被点击的时候在和标签相对应的面板上创建了一个 `active-panel` 类——从相关联的元素中继承了 CSS 的一些属性，使其 `z-index` 属性被设置为 1，让它能位于所有的面板的上面。

这是第一个例子，保持你的代码打开，我们将会在第二个例子中继续添加。

一个固定位置的列表消息盒子
-------------

在我们的第二个例子中，我们将会采用我们的第一个例子——我们的消息盒子——把她加到一个满的网页之中去。但是不仅仅是这样——我们将固定她的位置，以便于他能待在浏览器窗口的同一个位置。当主要内容滚动时，这个消息盒子将会待在屏幕的同一个位置。完成的例子：

![](/zh-CN/docs/Learn/CSS/CSS_layout/Practical_positioning_examples/fixed-info-box.png)

**备注：** 你可以点击蓝字预览完成后的效果，看看哪些部分是你在这篇文章里你要制作的。

在开始的时候，你可以使用第一部分中完成的例子，或者从我们的 Github 仓库中拷贝 info-box.html 到本地。

### 添加 HTML

首先，我们需要一些额外的内容在当前的主内容中。添加下列`<section>` 在你的 `<body>` 标签之中，放在在已存在的部分之中：

```html
<section class="fake-content">
  <h1>Fake content</h1>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
  <p>
    This is fake content. Your main web page contents would probably go here.
  </p>
</section>
```

**备注：** 你可以随意更改假的内容，替换为你想要的真的内容。

### 更改存在的 CSS

接下来我们需要对之前的 CSS 进行一些小修改，让消息盒子放置和定位更好。删除 margin: 0 auto（不需要居中显示），添加 fixed 定位；调整 top 属性把它粘在浏览器的视域。

它应该看起来像下面这样：

```css
.info-box {
  width: 450px;
  height: 400px;
  position: fixed;
  top: 0;
}
```

### 对主内容样式设计

对于这个例子来说唯一剩下的事情就是给主内容提供一些样式设计。添加下面的规则到你剩下的 CSS 的下面：

```css
.fake-content {
  background-color: #a60000;
  color: white;
  padding: 10px;
  height: 2000px;
  margin-left: 470px;
}
```

开始我们给这个内容和消息盒子面板一样的背景颜色，颜色，内边距。然后给他一个大的 margin-left 使他移动到右边，为消息盒子在左边腾出位置，以便于各个部分不重叠。

这标志着第二个例子的结束；我们希望你能感到第三个例子，完全是因为兴趣。

一个滑动隐藏的面板
---------

最后一个我们在这里介绍的例子是通过按图标使面板滑动出现或者消失——就像前面提到的，这种场景在移动端的布局很流行，因为移动端的屏幕很小，所以你不希望使用大部分界面来显示一个有用的内容而是用消息面板或者菜单代替。

我们完工后的例子长这样：

![](/zh-CN/docs/Learn/CSS/CSS_layout/Practical_positioning_examples/hidden-sliding-panel.png)

**备注：** 你可以点击蓝字预览完成后的效果，仔细看看哪些部分是你在这篇文章里你要制作的。

在一开始，老规矩在我们的 GitHub 代码仓库拷贝 hideen-info-panel-start.html（源码）。这个例子并没有用先前的例子，所以我们需要一个新的开始文件。让我们来仔细观察一下这个 HTML 文件：

```css
<label for="toggle">❔</label>
<input type="checkbox" id="toggle">
<aside>

  ...

</aside>
```

开始，我们看到了一个 `<label>` 元素和 `<input>` 元素——`<label>`元素普遍用来联系文字标签和表单，目的是能更好的理解表单（允许屏幕阅读器用户查看表单元素的描述）。这里通过`for`属性绑定`id`到了`<input>`标签的 checkbox 元素。

**备注：** 我们已经设置了一个特殊的问题标记特性到我们的 HTML 中，来当作我们的信息图标——这代表着这个按钮将可以按下显示或隐藏面板。

现在我们将这些元素用于稍微不同的目的——另一个 `<label>` 标签有用的副作用使你能通过点击 checkbox 的 label 标签来选择这个 checkbox，就好像点击了这个 checkbox 自己一样。这就会实现有名的 checkbox hack 技术，可以提供无 JavaScript 的方法来通过按钮来控制一个元素。我们将通过其他两个元素控制 `<aside>` 元素。（为了简洁起见，我们已将其内容从上述代码列表中删除）。

在下面的部分我们将解释这一切如何运作。

### 设置表单元素样式

首先让我们处理表单元素 - 在 `<style>` 标签之间添加以下 CSS：

```css
label[for="toggle"] {
  font-size: 3rem;
  position: absolute;
  top: 4px;
  right: 5px;
  z-index: 1;
  cursor: pointer;
}

input[type="checkbox"] {
  position: absolute;
  top: -100px;
}
```

第一条规则样式化 `<label>` 标签；我们：

* 设置更大的 `font-size` 使图标更大更美观。
* 设置 `position` 为 `absolute`，使用 `top` 属性和 `right` 属性来让他能很合适的位于右上角。
* 设置其 `z-index` 为 1——因此当信息面板被赋予样式和显示的时候，不会覆盖我们的图标；相反图标依然会位于最上层能够再次被按下来隐藏信息平板。
* 使用 `cursor` 属性来改变鼠标的指针，当鼠标悬浮在图标上面的时候变成一个手形指针（就像你看到的当悬浮在链接上一样），作为一个额外的可视化线索告诉用户这个图标可以做一些有趣的事情。

第二条规则是在实际的 `<input>` 元素的 checkbox 上设置`position` 属性为 `absolute`，并从屏幕上方隐藏掉它，我们并不希望在我们的用户界面里看到它。

### 设置面板的样式

现在是时候为实际的滑动面板设计风格了。在你的 css 底部添加下列规则：

```css
aside {
  background-color: #a60000;
  color: white;

  width: 340px;
  height: 98%;
  padding: 10px 1%;

  position: fixed;
  top: 0;
  right: -370px;

  transition: 0.6s all;
}
```

这里有很多项——让我们一点一点讨论：

* 首先，我们在信息盒子中设置了一些简单的 `background-color` 和 `color`。
* 然后，我们在面板上设置一个固定的 `width` ，让它的 `height` 充满整个浏览器窗口的视口。
* 我们同样包括一些 `padding` 来组成我们想要的总的高度和宽度的值（如果我们没有设置 `box-sizing: border-box;` ，那就很有必要，正如这个例子）
* 然后，我们在面板上设置 `position``: fixed;`，即使页面的内容在滚动，也总是显示在同一个位置。我们设置 `top` 属性，使其粘在视口顶部，设置 `right` 属性使其默认情况下位于屏幕的右边。
* 最后我们在元素上设置 `transition` 属性，Transitions 是一个有意思的特性，允许你在状态改变的时候平滑的过渡，而不是粗暴的“开”或“关”。在这个例子中我们尝试在 checkbox 被选中时让面板平滑的滚动。（或者换句话说，当问题标记图标被点击以后——记住，点击 `<label>` 标签也会选择相对应的 checkbox！我们已经告诉你这是一种 hack 了）你也将会学到更多……
### 设置选择后的状态

这是最后要添加的一点 CSS——把这些放到你的 CSS 底部：

```css
input[type="checkbox"]:checked + aside {
  right: 0px;
}
```

这里的选择器是复杂的——我们选择与 `<input>` 元素邻接的 `<aside>` 元素，但是仅仅在它被选择时（请注意使用 `:checked` 伪类来实现此目的），在这种情况下，我们将 `<aside>` 的 `right` 属性设置为 0px，会造成面板再次出现在屏幕上（由于过渡属性会平滑的出现）。再一次点击这个标签会取消选中 checkbox，面板将会跟着再一次消失。

所以你有——一个相当巧妙的避免使用 JavaScript 来创建一个切换按钮效果方式。这将在 IE9 及以上版本中起作用（平滑过渡将在 IE10 及更高版本中起作用）。这种效果确实有一些问题——这是有点滥用表单元素（它们不是为了这个目的），并且在无障碍方面效果不是很好——标签在默认情况下不可聚焦，并且表单元素的非语义使用可能会导致屏幕朗读器出现问题。JavaScript 和链接或按钮可能更合适，但这样的实验仍然很有趣。

总结
--

这样完成了我们对定位的关注——现在，你应该理解基本机制的工作原理，同样理解了怎样应用这些知识去构建一些有趣的用户界面功能，不要由于你还未完全理解所有的知识而担心——定位是一个相当高级的话题，你可以随时读这篇文章来帮助你的理解。下一个主题我们将转向 Flexbox。

* 上一页
* Overview: CSS layout
* 下一页


技能测试：网格布局 - 学习 Web 开发

技能测试：网格布局
=========

此任务的目的是让你使用 CSS 网格布局，并测试你是否了解网格和网格项的行为方式。你将会完成三个包括不同的元素小任务。

**备注：** 你可以在下面的交互式编辑器中试用解决方案，不过，下载代码并使用在线工具 (如 CodePen、jsFiddle 或 Glitch) 处理这些任务可能会更有帮助。

如果你遇到困难，请向我们寻求帮助 — 参阅本页底部的 评估或进一步帮助 部分。

网格布局 一
------

在此任务中，你需要创建一个网格，要求其中的四个子元素能自动排布。网格内要有三列并且将可用空间等分，列和行的间距均为 20px。

在三列网格布局中有四个物体放入其中。

尝试更新下面的实时代码以复现上面的示例：

**备注：** 评估或进一步的工作目的，可以下载此任务的源文件在你自己的编辑器或使用线编辑器。

网格布局 二
------

在此例中，我们已经定义好了一个 grid, 请通过修改下面两个子元素的 CSS 规则，导致它们跨过彼此的网格轨道; 第二个 item 应该在第一个 item 之上 (如下图所示).

![盒子中的两个元素，其中一个元素覆盖在另一个元素上。](/zh-CN/docs/Learn/CSS/CSS_layout/Grid_skills/grid-task2.png)

尝试更新下面的实时代码以复现上面的示例：

附加问题：

* 你可以把第一个元素覆盖到第二个元素，而不通过改变 html 元素的顺序吗？

**备注：** 评估或进一步的工作目的，可以 下载此任务的源文件 在你自己的编辑器或使用线编辑器。

网格布局 三
------

此 grid 中 4 个子元素，初始状态是显示的是 auto-placement. 请通过使用 grid-area 和 grid-template-areas 属性对照下图放置元素的布局。

![网格中显示的四个项目。](/zh-CN/docs/Learn/CSS/CSS_layout/Grid_skills/grid-task3.png)

尝试更新下面的实时代码以复现上面的示例：

**备注：** 评估或进一步的工作目的，可以 下载此任务的源文件 在你自己的编辑器或使用线编辑器。

网格布局 四
------

此例中，你需要同时使用 Grid Layout 和 Flexbox 来完成下图所示的任务。这个过程中你不需要通过改变 HTML 来完成。

![两行卡片，每个卡片有一个图像和一组标签。](/zh-CN/docs/Learn/CSS/CSS_layout/Grid_skills/grid-task4.png)

尝试更新下面的实时代码以复现上面的示例：

**备注：** 评估或进一步的工作目的，可以 下载此任务的源文件 在你自己的编辑器或使用线编辑器。

评估 或者 帮助
--------

你可以在上述的交互编辑器上练习。

如果你的作业需要评估，或者有困难想要帮助：

1. 把你的作业放在一个在线可分享的编辑器上，比如 CodePen, jsFiddle, 或者 Glitch. 你可以自己重头开始写，或者使用上面章节提供的源文件。
2. Write a post asking for assessment and/or help at the MDN Discourse forum Learning category. Your post should include:
 发布一个评估的帖子 或者 帮助在MDN Discourse forum Learning category你的发布应该涵盖以下几点：
    * A descriptive title such as "Assessment wanted for Grid layout 1 skill test".
    * Details of what you have already tried, and what you would like us to do, e.g. if you are stuck and need help, or want an assessment.
    * A link to the example you want assessed or need help with, in an online shareable editor (as mentioned in step 1 above). This is a good practice to get into — it's very hard to help someone with a coding problem if you can't see their code.
    * A link to the actual task or assessment page, so we can find the question you want help with.


弹性盒子 - 学习 Web 开发

弹性盒子
====

* 上一页
* Overview: CSS layout
* 下一页

弹性盒子是一种用于按行或按列布局元素的一维布局方法。元素可以膨胀以填充额外的空间，收缩以适应更小的空间。本文将解释所有的基本原理。

|  |  |
| --- | --- |
| 前提： |
 HTML 基础（学习
 HTML 介绍），和了解 CSS 如何工作的（学习
 CSS 介绍）。
  |
| 目标： | 学会如何使用弹性盒子布局系统来创建 Web 布局。 |

为什么是弹性盒子？
---------

长久以来，CSS 布局中唯一可靠且跨浏览器兼容的创建工具只有 floats 和 positioning。这两个工具大部分情况下都很好使，但是在某些方面它们具有一定的局限性，让人难以完成任务。

以下简单的布局需求是难以或不可能用这样的工具（floats 和 positioning）方便且灵活的实现的：

* 在父内容里面垂直居中一个块内容。
* 使容器的所有子项占用等量的可用宽度/高度，而不管有多少宽度/高度可用。
* 使多列布局中的所有列采用相同的高度，即使它们包含的内容量不同。

正如你将在后面的章节中看到的一样，弹性盒子使得很多布局任务变得更加容易。让我们继续吧！

一个简单的例子
-------

在本文中，我们将通过一系列练习来帮助你了解弹性盒子的工作原理。开始前，你应该拷贝 mozilla github 仓库的 弹性盒子 0.html 到本地。在现代浏览器里打开它（比如 Firefox 或 Chrome），然后打开你的编辑器看一眼它的代码。你可以看它的线上实例。

![显示 Flexbox 教程开始的图像](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/bih741v.png)

你可以看到这个页面有一个含有顶级标题的 `<header>` 元素，和一个包含三个 `<article>` 的 `<section>`。我们将使用这些来创建相当标准的三列布局。

指定元素的布局为 flexible
-----------------

首先，我们需要选择将哪些元素将设置为弹性的盒子。我们需要给这些 flexible 元素的父元素 `display` 设置一个特定值。在本例中，我们想要设置 `<article>` 元素，因此我们给 `<section>`（变成了 flex 容器）设置 display：

```css
section {
  display: flex;
}
```

结果如下：

![两行容器，包括第一行的单列和第二行的三列布局，显示如何根据内容将网页划分为不同的布局](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example2.png)

所以，就这样一个简单的声明就给了我们所需要的一切—非常不可思议，对吧？我们的多列布局具有大小相等的列，并且列的高度都是一样。这是因为这样的 flex 项（flex 容器的子项）的默认值是可以解决这些的常见问题的。后面还有更多内容。

为了更清晰的表述，让我们重申这里发生的事情。关于它如何与页面其余部分交互，我们给的 `display` 值为 `flex` 的元素就像一个块级元素，但是它的子元素布局为 flex 项。下个部分将更详细地描述这些意味着什么。请注意，如果你希望该元素的子元素作为 flex 项，你也可以使用 `display` 值为 `inline-flex`，但是该元素的行为类似于行内元素。

flex 模型说明
---------

当元素表现为 flex 框时，它们沿着两个轴来布局：

![在从左到右的语言中，三个 flex 项并排放置在 flex 容器中。主轴——弹性容器布置 flex 方向上的轴——是水平的。主轴的两端是开始端和结束端，分别位于左侧和右侧。交叉轴是垂直的；垂直于主轴。交叉轴的开始端和结束端分别位于顶部和底部。flex 项沿着主轴排列，在这种情况下，宽度称为主轴尺寸，flex 项沿交叉轴排列，在这种情况下，高度称为交叉尺寸。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flex_terms.png)

* **主轴**（main axis）是沿着 flex 元素放置的方向延伸的轴（比如页面上的横向的行、纵向的列）。该轴的开始和结束被称为 **main start** 和 **main end**。
* **交叉轴**（cross axis）是垂直于 flex 元素放置方向的轴。该轴的开始和结束被称为 **cross start** 和 **cross end**。
* 设置了 `display: flex` 的父元素（在本例中是 `<section>`）被称之为 **flex 容器（flex container）。**
* 在 flex 容器中表现为弹性的盒子的元素被称之为 **flex 项**（**flex item**）（本例中是 `<article>` 元素。

了解这些术语以便你阅读后续章节。如果你对使用的任何术语感到困惑，你可以随时返回这里。

列还是行？
-----

弹性盒子提供了 `flex-direction` 这样一个属性，它可以指定主轴的方向（弹性盒子子类放置的地方）——它默认值是 `row`，这使得它们在按你浏览器的默认语言方向排成一排（在英语/中文浏览器中是从左到右）。

尝试将以下声明添加到 `<section>` 元素的 css 规则里：

```css
flex-direction: column;
```

你会看到，这会将那些元素设置为列布局，就像我们添加这些 CSS 之前。在继续之前，请从示例中删除此规则。

**备注：** 你还可以使用 `row-reverse` 和 `column-reverse` 值反向排列 flex 项。用这些值试试看吧！

换行
--

当你在布局中使用定宽或者定高的时候，可能会出现问题即处于容器中的弹性盒子子元素会溢出，破坏了布局。你可以看一下弹性盒子——wrap0.html 示例（你也可以拷贝到本地），如下所示：

![简单弹性盒子示例，将所有 flex 项都布置在弹性容器的一行中。第八个 flex 项在浏览器窗口溢出，页面具有可见的水平和垂直滚动条，因为它无法容纳在窗口的宽度内，因为前七个 flex 项占用了视口中的可用空间。默认情况下，如果将弹性方向设置为行，浏览器会尝试将所有 flex 项放在一行中；如果弹性方向设置为列，则会尝试将所有 flex 项置于单列中。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example3.png)

在这里我们看到，子代确实超出了它们的容器。解决此问题的一种方法是将以下声明添加到 section css 规则中：

```css
flex-wrap: wrap;
```

同时，把以下规则也添加到 `<article>` 规则中：

```css
flex: 200px;
```

现在尝试一下吧；你会看到布局比原来好多了：

![flex 项在弹性容器中分多行排列。flex-wrap 属性在弹性容器中设置为“wrap”，如果上一行中的 flex 项在弹性容器外溢出，则弹性容器中显示新行中的 flex 项。每个 flex 项的宽度为 200 像素。所有物品都被拉伸到相同的高度，与内容最多的 flex 项一样高。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example4.png)

我们现在有多个行。每行都安装了尽可能多的 flex 项。任何溢出都会向下移动到下一行。在 article 上设置的 `flex：200px` 声明意味着每个声明将至少为 200px 宽。我们稍后会更详细地讨论这个属性。你可能还注意到，最后一行上的最后几个项每个都变得更宽，以便把整个行填满。

但是这里我们可以做得更多。首先，改变 `flex-direction` 属性值为 `row-reverse`——你会看到仍然有多行布局，但是每一行元素排列的方向和原来是相反的了。

flex-flow 缩写
------------

到这里，应当注意到存在着 `flex-direction` 和 `flex-wrap`——的缩写 `flex-flow`。比如，你可以将

```css
flex-direction: row;
flex-wrap: wrap;
```

替换为

```css
flex-flow: row wrap;
```

flex 项的动态尺寸
-----------

现在让我们回到第一个例子，看看是如何控制 flex 项占用空间的比例的。打开你本地的 弹性盒子 0.html，或者拷贝 弹性盒子 1.html 作为新的开始（查看线上）。

第一步，将以下规则添加到 CSS 的底部：

```css
article {
  flex: 1;
}
```

这是一个无单位的比例值，表示每个 flex 项沿主轴的可用空间大小。本例中，我们设置 `<article>` 元素的 flex 值为 1，这表示每个元素占用空间都是相等的，占用的空间是在设置 padding 和 margin 之后剩余的空间。因为它是一个比例，这意味着将每个 flex 项的设置为 400000 的效果和 1 的时候是完全一样的。

现在在上一个规则下添加：

```css
article:nth-of-type(3) {
  flex: 2;
}
```

现在当你刷新，你会看到第三个 `<article>` 元素占用了两倍的可用宽度和剩下的一样——现在总共有四个比例单位可用。前两个 flex 项各有一个，因此它们占用每个可用空间的 1/4。第三个有两个单位，所以它占用 2/4 或者说是 1/2 的可用空间。

你还可以指定 flex 的最小值。尝试修改现有的 article 规则：

```css
article {
  flex: 1 200px;
}

article:nth-of-type(3) {
  flex: 2 200px;
}
```

这表示“每个 flex 项将首先给出 200px 的可用空间，然后，剩余的可用空间将根据分配的比例共享”。尝试刷新，你会看到分配空间的差别。

![简单的 flexbox 示例，flex 容器中有三个 flex 项。所有 flex 项的最低宽度为 200 像素——使用“flex”设置。前两个 flex 项的 flex 值为 1，第三个项的 flex 值为 2。这会将弹性容器中的剩余空间分成 4 个比例单位。前两个 flex 项各分配一个单元，第三个 flex 项分配两个单元，使第三个 flex 项比宽度相同的其他两个更宽。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example1.png)

弹性盒子的真正价值可以体现在它的灵活性/响应性，如果你调整浏览器窗口的大小，或者增加一个 `<article>` 元素，这时的布局仍旧是好的。

flex：缩写与全写
----------

`flex` 是一个可以指定最多三个不同值的缩写属性：

* 第一个就是上面所讨论过的无单位比例。可以单独指定全写 `flex-grow` 属性的值。
* 第二个无单位比例——`flex-shrink`——一般用于溢出容器的 flex 项。这指定了从每个 flex 项中取出多少溢出量，以阻止它们溢出它们的容器。这是一个相当高级的弹性盒子功能，我们不会在本文中进一步说明。
* 第三个是上面讨论的最小值。可以单独指定全写 `flex-basis` 属性的值。

我们建议不要使用全写属性，除非你真的需要（比如要去覆盖之前写的）。使用全写会多写很多的代码，它们也可能有点让人困惑。

水平和垂直对齐
-------

还可以使用弹性盒子的功能让 flex 项沿主轴或交叉轴对齐。让我们一起看一下新例子——flex-align0.html（在线浏览）——我们将会有一个整洁，灵活的按钮/工具栏。此时，你看到了一个水平菜单栏，其中一些按钮卡在左上角，就像下面这样：

![带有 Smile、Laugh、Wink、Shrug & Blush 标签的五个按钮排列在一个弹性容器中。按钮卡在左上角，看起来不整洁。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example5.png)

首先，拷贝一份到本地。

然后，将下面的 CSS 添加到例子的底部：

```css
div {
  display: flex;
  align-items: center;
  justify-content: space-around;
}
```

![带有 Smile、Laugh、Wink、Shrug & Blush 标签的五个按钮排在一个弹性容器中。通过将对齐项属性设置为中心，flex 项位于横轴的中心。通过将 justify-content 属性设置为 space-around，flex 项沿着主轴均匀间隔。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox_center_space-around.png)

刷新一下页面，你就会看到这些按钮很好的垂直水平居中了。我们是通过下面所说的两个新的属性做到的。

`align-items` 控制 flex 项在交叉轴上的位置。

* 默认的值是 `stretch`，其会使所有 flex 项沿着交叉轴的方向拉伸以填充父容器。如果父容器在交叉轴方向上没有固定宽度（即高度），则所有 flex 项将变得与最长的 flex 项一样长（即高度保持一致）。我们的第一个例子在默认情况下得到相等的高度的列的原因。
* 在上面规则中我们使用的 `center` 值会使这些项保持其原有的高度，但是会在交叉轴居中。这就是那些按钮垂直居中的原因。
* 你也可以设置诸如 `flex-start` 或 `flex-end` 这样使 flex 项在交叉轴的开始或结束处对齐所有的值。查看 `align-items` 了解更多。

你可以用 `align-self` 属性覆盖 `align-items` 的行为。比如，你可以这样：

```css
button:first-child {
  align-self: flex-end;
}
```

![带有 Smile、Laugh、Wink、Shrug & Blush 标签的五个按钮排在一个弹性容器中。除第一个项外，所有 flex 项都通过将对齐项属性设置为中心，位于十字轴的中心或垂直居中。第一项与交叉轴末端的弹性容器底部齐平，对齐自属性设置 flex 端。flex 项沿着容器的主轴或宽度均匀间隔。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox_first-child_flex-end.png)

去看看它产生的效果，然后删除它。

`justify-content` 控制 flex 项在主轴上的位置。

* 默认值是 `flex-start`，这会使所有 flex 项都位于主轴的开始处。
* 你也可以用 `flex-end` 来让 flex 项到结尾处。
* `center` 在 `justify-content` 里也是可用的，可以让 flex 项在主轴居中。
* 而我们上面用到的值 `space-around` 是很有用的——它会使所有 flex 项沿着主轴均匀地分布，在任意一端都会留有一点空间。
* 还有一个值是 `space-between`，它和 `space-around` 非常相似，只是它不会在两端留下任何空间。

在继续下面之前，多多使用提到过的属性吧，看看它们的效果。

flex 项排序
--------

弹性盒子也有可以改变 flex 项的布局位置的功能，而不会影响到源顺序（即 dom 树里元素的顺序）。这也是传统布局方式很难做到的一点。

代码也很简单，将下面的 CSS 添加到示例代码下面。

```css
button:first-child {
  order: 1;
}
```

刷新下，然后你会看到“Smile”按钮移动到了主轴的末尾。下面我们谈下它实现的一些细节：

* 所有 flex 项默认的 `order` 值是 0。
* order 值大的 flex 项比 order 值小的在显示顺序中更靠后。
* 相同 order 值的 flex 项按源顺序显示。所以假如你有四个元素，其 order 值分别是 2，1，1 和 0，那么它们的显示顺序就分别是第四，第二，第三，和第一。
* 第三个元素显示在第二个后面是因为它们的 order 值一样，且第三个元素在源顺序中排在第二个后面。

你也可以给 order 设置负值使它们比值为 0 的元素排得更前面。比如，你可以设置“Blush”按钮排在主轴的最前面：

```css
button:last-child {
  order: -1;
}
```

flex 嵌套
-------

弹性盒子也能创建一些颇为复杂的布局。设置一个元素为 flex 项，那么他同样成为一个 flex 容器，它的孩子（直接子节点）也表现为弹性盒子。看一下 复杂弹性盒子.html（在线浏览）。

![简单 Flexbox 示例，连续列出三个 flex 项的子元素。前两个宽度相同，第三个略宽。第三个 flex 项也是一个弹性容器。它有一组两行的按钮，后跟文本。第一行按钮有 4 个按钮，排列成一排；按钮的宽度相同，占据了容器的全部宽度。第二行只有一个按钮，可以单独占据该行的整个宽度。这是复杂的布局，很少有 flex 项继续被视为弹性容器。](/zh-CN/docs/Learn/CSS/CSS_layout/Flexbox/flexbox-example7.png)

这个例子的 HTML 是相当简单的。我们用一个 `<section>` 元素包含了三个 `<article>`元素。第三个 `<article>` 元素包含了三个 `<div>`：
```

section - article
          article
          article - div - button
                    div   button
                    div   button
                          button
                          button
```

现在让我们看一下布局用到的代码。

首先，我们设置 `<section>` 的子节点布局为弹性盒子。

```css
section {
  display: flex;
}
```

下面我们给 `<article>` 元素设置 flex 值。特别注意这里的第二条 CSS 规则——我们设置第三个 `<article>` 元素的子节点的布局同样为 flex，但是属性值为列布局。

```css
article {
  flex: 1 200px;
}

article:nth-of-type(3) {
  flex: 3 200px;
  display: flex;
  flex-flow: column;
}
```

接下来，我们选择了第一个 `<div>`。首先使用 `flex: 1 100px;` 简单的给它一个最小的高度 100px，然后设置它的子节点（`<button>` 元素）为 flex 项。在这里我们将它们放在一个包装行（wrap row）中，使它们居中对齐，就像我们在前面看到的单个按钮示例中做的那样。

```css
article:nth-of-type(3) div:first-child {
  flex: 1 100px;
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  justify-content: space-around;
}
```

最后，我们给按钮设置大小，有意思的是我们给它一个值为 1 的 flex 属性。如果你调整浏览器窗口宽度，你会看到这是一个非常有趣的效果。按钮将尽可能占用最多的空间，尽可能多的堆在同一条线上，但是当它们不再适合在同一条线上，他们中的一些会到下一行去。

```css
button {
  flex: 1;
  margin: 5px;
  font-size: 18px;
  line-height: 1.5;
}
```

跨浏览器兼容性
-------

大多数浏览器都支持弹性盒子，诸如 Firefox、Chrome、Opera、Microsoft Edge 和 IE 11，较新版本的 Android/iOS 等等。但是你应该要意识到仍旧有被人使用的老浏览器不支持弹性盒子（或者支持，但是只是支持非常非常老版本的弹性盒子）。

虽然你只是在学习和实验，这不太要紧; 然而，如果你正在考虑在真实网站中使用弹性盒子，则需要进行测试，并确保在尽可能多的浏览器中你的用户体验仍然可以接受。

弹性盒子相较其他一些 CSS 特性可能更为棘手。例如，如果浏览器缺少 CSS 阴影，则该网站可能仍然可用。但是假如不支持弹性盒子功能就会完全打破布局，使其不可用。

我们在跨浏览器测试模块中讨论了克服跨浏览器支持问题的策略。

技能测试
----

我们在文章里面覆盖了很多内容，但你是否能记住最重要的知识？在你继续学习前，你可以进行测试 (en-US)来验证你是否掌握了这些知识。

总结
--

到这里，介绍弹性盒子的基础知识就结束了。我们希望你体会到乐趣，并且玩的开心，能随着你的学习与你一起向前。接下来，我们将看到 CSS 布局的另一个重要方面：CSS 网格系统

* 上一页
* Overview: CSS layout
* 下一页
参见
--

* CSS 弹性盒子技巧——一篇以视觉吸引人的方式解释弹性盒子所有内容的文章
* 弹性盒子青蛙游戏——学习和更好地了解弹性盒子基础知识的教育游戏
* 上一页
* Overview: CSS layout
* 下一页


传统的布局方法 - 学习 Web 开发

传统的布局方法
=======

* 上一页
* Overview: CSS layout
* 下一页

在 CSS 布局中，网格系统是一种非常常见的布局方式，并且在 CSS 网格布局之前，它们倾向于由浮动和其他的布局功能实现。假想你的布局是一组数字标注的列（例如 4、6 或者 12），然后把你的内容填充到这些想象的列中。这篇文章将要探讨这种早期的方法是怎么实现的，来帮助你在旧项目工作时更好地理解他们。

|  |  |
| --- | --- |
| 学习前提： |
 HTML 基础 (学习
 Introduction to HTML),并且了解 CSS 是怎么工作的 (学习
 Introduction to CSS
 and Styling boxes.)
  |
| 目标： | 了解浏览器 CSS 网格布局系统的基本概念。 |

CSS 网格布局之前的布局与网格系统
------------------

一个来自设计领域的人或许会惊讶，CSS 直到最近才有网格系统，不仅如此，我们还用了许多次优方法来建立类网格设计。我们现在把这些称为“古老”的方法。

对于新项目来说，大多数情况下 CSS 网格布局（CSS Grid Layout）被用来和其他一个或多个现代的布局方法结合，以形成布局的基础。但是你会时不时的遇到采用这种古老方法的“网格系统”。值得了解它们是如何工作的，以及为什么它们和 CSS 网格布局不同。

这个课程将会解释基于 float 和 flexbox 的网格系统和网格框架是如何工作的。学习过网格布局之后，你可能会惊讶，这些看起来真的好复杂！如果你需要为不支持新技术的老浏览器上创建后备代码的话，这些知识将会变的十分有用，而且你也可以在使用这些类别系统的已有工程上工作。

值得铭记在心的是，在我们探索这些系统时，它们里面没有哪个的建立方式是像通过 CSS 网格布局创建网格那样，真的建立一个网格。他们通过给目标一个大小，然后推动它们，让它们**看起来**像网格一样排列成一条线。

两列布局
----

让我们从最简单的实例开始——一个两列布局。你可以按照步骤在你的电脑上创建一个新的 `index.html`，先用一个简单 HTML 模板填充它，然后在适当的位置填充下面的代码。在这节底部，你可以看到一个展示最终代码样貌的实时实例。

首先，我们需要在我们的栏中放入一些内容。把现在在 body 中的内容替换成下面的代码：

```html
<h1>2 column layout example</h1>
<div>
  <h2>First column</h2>
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
    aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci,
    pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at
    ultricies tellus laoreet sit amet. Sed auctor cursus massa at porta. Integer
    ligula ipsum, tristique sit amet orci vel, viverra egestas ligula. Curabitur
    vehicula tellus neque, ac ornare ex malesuada et. In vitae convallis lacus.
    Aliquam erat volutpat. Suspendisse ac imperdiet turpis. Aenean finibus
    sollicitudin eros pharetra congue. Duis ornare egestas augue ut luctus.
    Proin blandit quam nec lacus varius commodo et a urna. Ut id ornare felis,
    eget fermentum sapien.
  </p>
</div>

<div>
  <h2>Second column</h2>
  <p>
    Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
    ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
    est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
    tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies
    lectus sed lobortis finibus. Vivamus eu urna eget velit cursus viverra quis
    vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis natoque
    penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  </p>
</div>
```

每一列都需要一个上一级元素来包含内容，这样我们可以一次性操作所有内容。在这个例子中我们选择了`<div>`，但是你可以选择其他更合适的，例如`<article>`, `<section>`, 和 `<aside>`，或者是任何别的元素。

现在我们来看 CSS。首先，应用以下的代码来对 HTML 进行基本设置：

```css
body {
  width: 90%;
  max-width: 900px;
  margin: 0 auto;
}
```

body 将会占据 90% 的视口宽度，直到达到 900 像素，在这种情况下，它将固定并保持在视口正中。默认情况下，它的子项（the `<h1>` (en-US) 和两个 `<div>`）将会达到正文宽度的 100%。如果我们希望两个`<div>`，一个浮在窗口的一边，另一个浮动在另一边的话，我们需要将它们的宽度设置为其父元素的 100% 或者更小，以便他们可以并排放置。将下面的代码加在 CSS 的底部：

```css
div:nth-of-type(1) {
  width: 48%;
}

div:nth-of-type(2) {
  width: 48%;
}
```

这里我们将它们都设置为了父元素宽度的 48%——总共是 96%，在两栏之间留 4% 的空隙，为它们提供一些宽松的空间。现在我们只需要将让列浮动，像这样：

```css
div:nth-of-type(1) {
  width: 48%;
  float: left;
}

div:nth-of-type(2) {
  width: 48%;
  float: right;
}
```

将这些都放在一起，会得到这样的结果：

你有没有注意到我们在宽度的表示上都用的是百分比——这是一个很好的策略，这创建了一个\*\*流动布局（liquid layout），\*\*能够适应不同的屏幕大小，在小一些的屏幕上也能使列保持一样的比例。试一试自己来调整浏览器窗口的宽度，这是响应式网页非常有价值的一个工具。

**备注：** 你可以在 0\_two-column-layout.html 实时查看这个实例（另见源代码）。

创建简单的传统网格框架
-----------

大多数传统的框架使用`float`属性来使列相邻排列，让它们看起来像是一个网格。以用 float 创建网格的流程工作，可以向你展示它们工作的原理，并介绍一些更高级的概念，并在浮动和清除这节课中学到的内容之上搭建更多的东西。

最简单的一类网格创建是固定宽度的——我们只需要计算设计中总的宽度、列的数目、每一列和间隔的宽度。但是，如果我们决定设计的网格是可以根据浏览器宽度缩放的，我们则需要计算每一列和间距的所占的宽度的百分比。

下一部分我们将学习如何创建这两种方式的网格。我们会构建一个有 12 列的表格——我们选择了 12 这个常见的数字，来看它对不同情景的适应情况，因为 12 可以被 6，4，3，和 2 完全整除。

### 一个简单的固定宽度网格

让我们先来创建一个固定列宽度的网格系统吧。

首先，把 simple-grid.html 下载储存下来，其 body 中包含以下的标记：

```html
<div class="wrapper">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col span1">13</div>
    <div class="col span6">14</div>
    <div class="col span3">15</div>
    <div class="col span2">16</div>
  </div>
</div>
```

我们的目标是把它变成一个有两行十二列的演示网格——第一行显示各列的大小，第二行显示网格上不同大小的区域。

![](/zh-CN/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods/simple-grid-finished.png)

在`<style>`中，加入下面的代码，使容器右侧的内边距为 20 像素，总的宽度变为 980 像素。这样给我们留出 960 像素可以放置列和它们的间隔——这种情况下，内边距会被从总的内容宽度中减去，因为我们在`box-sizing`中将站点上所有的元素设置成了`border-box` （可以查看完全改变盒模型，获得更多解释）。

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

body {
  width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 20px;
}
```

现在使用包装了网格每行的列容器，清除网格中每行的浮动，在你前面的规则里加入下面的规则：

```css
.row {
  clear: both;
}
```

应用这条清除规则，意味着我们不用在每行上都填充 12 列元素。行与行之间不会互相干扰，并保持分隔。

列与列之间保持 20 像素的间隔。我们使用每列元素的左外边框来实现这个间隔。然后我们一共有 12 个间隔 — 12 x 20 = 240。

我们需要从 960px 的总宽减去这个间隔，然后剩下 720 像素给我们的列。如果用 720 除以 12，我们知道每列有 60 个像素宽。

接下来我们给`.col`类写一个规则，让它向左浮动，给它设置 20 像素的`margin-left`来实现一个间隔，再设置 60 像素的`width`。把下面的规则加到你的 CSS 底部：

```css
.col {
  float: left;
  margin-left: 20px;
  width: 60px;
  background: rgb(255, 150, 150);
}
```

单个列的最上面一行现在铺开成为了一个排列整齐的网格。

**备注：** 我们也已经让每列变成亮红色，这样你就能准确看到每列占据了多少空间。

如果我们想让布局容器横跨多列，必须要给它设置特殊的类，来适应列的数量的`width` 值（加上间隔的值）。我们需要新建额外的类来允许横跨 2-12 列。每个宽度是将该列数的列宽加上间隔宽度相加的结果，这些宽度总是比列数少一个。

在你的 CSS 底部加入下面的内容：

```css
/\* Two column widths (120px) plus one gutter width (20px) \*/
.col.span2 {
  width: 140px;
}
/\* Three column widths (180px) plus two gutter widths (40px) \*/
.col.span3 {
  width: 220px;
}
/\* And so on... \*/
.col.span4 {
  width: 300px;
}
.col.span5 {
  width: 380px;
}
.col.span6 {
  width: 460px;
}
.col.span7 {
  width: 540px;
}
.col.span8 {
  width: 620px;
}
.col.span9 {
  width: 700px;
}
.col.span10 {
  width: 780px;
}
.col.span11 {
  width: 860px;
}
.col.span12 {
  width: 940px;
}
```

创建了这些类以后，我们可以在网格上布局不同宽度的列。试试保存并在你的浏览器上加载这个页面，来查看效果。

**备注：** 如果你在让上面的示例实现的时候正遇到麻烦，尝试将它和我们在 GitHub 上的完成版进行比较（再看下实时的示例）。

试试修改这些类，甚至添加、删除一些容器，来看看你能怎么改变这个布局。例如，你可以把第二行写成这样：

```html
<div class="row">
  <div class="col span8">13</div>
  <div class="col span4">14</div>
</div>
```

现在你的网格布局生效了。你可以简单的定义这些行，和每行的列数，然后给他们添加你想要的内容。真棒！

### 创建液态网格

这个网格表现的不错，但是它长度固定。我们实际却想要一个弹性（流体）的网格，它可以随着浏览器的viewport大小的变化自动伸缩。为了达成这个目标，我们需要把相应的像素的长度变为百分比长度。

把固定宽度转为伸缩的基于百分比宽度的算式在下面：
```

target / context = result
```

在我们的列宽里，我们的**目标列长度**是 60 像素，我们的**上下文**是 960 像素的包装。我们可以这么计算百分比：
```

60 / 960 = 0.0625
```

然后我们挪动小数点两位，得到百分数 6.25%。所以在 CSS 里面，我们可以用 6.25% 代替 60 像素。

我们需要同样这么算间隔：
```

20 / 960 = 0.02083333333
```

所以我们需要用 2.08333333% 代替`.col`里`margin-left`的 20 像素，和`.wrapper`里`padding-right`的 20 像素。

#### 更新我们的网格

创建一个之前例子网页的副本。然后开始这个章节，或者制作一个simple-grid-finished.html代码的本地副本，以将其作为入手点。

更新第二个 CSS 规则（有着`.wrapper`选择器），像下面这样：

```css
body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}
```

我们不仅给它一个百分比的`width`，还添加了`max-width`属性，来确保布局不过于宽。

下一步，更新第四条 CSS 规则（有`.col`选择器），像这样：

```css
.col {
  float: left;
  margin-left: 2.08333333%;
  width: 6.25%;
  background: rgb(255, 150, 150);
}
```

现在做些稍微麻烦的事 — 我们需要更新所有 `.col.span` 规则来把像素变为百分比。这需要点时间计算；为节省你的功夫，我们在下面替你做了。

像下面这样更新 CSS 规则的底部块：

```css
/\* Two column widths (12.5%) plus one gutter width (2.08333333%) \*/
.col.span2 {
  width: 14.58333333%;
}
/\* Three column widths (18.75%) plus two gutter widths (4.1666666) \*/
.col.span3 {
  width: 22.91666666%;
}
/\* And so on... \*/
.col.span4 {
  width: 31.24999999%;
}
.col.span5 {
  width: 39.58333332%;
}
.col.span6 {
  width: 47.91666665%;
}
.col.span7 {
  width: 56.24999998%;
}
.col.span8 {
  width: 64.58333331%;
}
.col.span9 {
  width: 72.91666664%;
}
.col.span10 {
  width: 81.24999997%;
}
.col.span11 {
  width: 89.5833333%;
}
.col.span12 {
  width: 97.91666663%;
}
```

现在保存你的代码，从浏览器里加载它，尝试改变视口长度——你应该可以看到网格完美地适配了。

>
> \*\*备注：\*\*如果你在让上面的示例实现的时候正遇到困难，试着把它和我们GitHub 上的完成版比较（另见实时的示例）。
>
>
>

### 使用 calc() 函数的更简单计算

你可以用 `calc()` 函数来在 CSS 里面做数学方面的计算——这允许你在 CSS 里插入简单的算式，来计算那些值。这个会很有用，特别当你有个复杂计算的时候，甚至你还可以在算式里用不同的单位，比如“我想要这个元素一直比它父元素少 50 像素”。看下这个来自 MediaRecorder API 教程的示例。

言归正传，来讲我们的网格！我们网格里跨越超过一列的列，它的总长是 6.45% 乘跨越的列数加 2.08333333%，乘间隔数（间隔数总等于行数减一）。`calc()` 函数允许我们就在宽度值里面这么计算，所以对跨越 4 列的列我们可以这么算：

```css
.col.span4 {
  width: calc((6.25% * 4) + (2.08333333% * 3));
}
```

试着用下面的内容替换你底部的规则块，然后在浏览器中重载，看看你是否能得到相同的结果：

```css
.col.span2 {
  width: calc((6.25% * 2) + 2.08333333%);
}
.col.span3 {
  width: calc((6.25% * 3) + (2.08333333% * 2));
}
.col.span4 {
  width: calc((6.25% * 4) + (2.08333333% * 3));
}
.col.span5 {
  width: calc((6.25% * 5) + (2.08333333% * 4));
}
.col.span6 {
  width: calc((6.25% * 6) + (2.08333333% * 5));
}
.col.span7 {
  width: calc((6.25% * 7) + (2.08333333% * 6));
}
.col.span8 {
  width: calc((6.25% * 8) + (2.08333333% * 7));
}
.col.span9 {
  width: calc((6.25% * 9) + (2.08333333% * 8));
}
.col.span10 {
  width: calc((6.25% * 10) + (2.08333333% * 9));
}
.col.span11 {
  width: calc((6.25% * 11) + (2.08333333% * 10));
}
.col.span12 {
  width: calc((6.25% * 12) + (2.08333333% * 11));
}
```

**备注：** 你能在 fluid-grid-calc.html 里看到我们的完成版（另见实时版）。

>
> \*\*备注：\*\*如果你不能让这个正常工作，可能是你的浏览器不支持`calc()`函数，尽管各浏览器对它的支持相当好——远至 IE9 那样老。
>
>
>

### 语义 vs.“无语义”网格系统

在标记中添加类以定义布局，意味着你的内容和标记与你的可视化表示相关联。你将会偶尔听到，这种使用 CSS 类的方式，被描绘成“无语义”：描述了内容的外观，而不是描述内容的语义性的类的使用。这是我们的`span2`、`span3`等类所面临的情况。

这些并不是唯一的方法，你或许会转头决定使用网格，然后向已有的语义类里面加入尺寸信息。例如，如果你有一个带有`content`类的`<div>`，而且你想让它横跨 8 列，你可以从`span8`类里面复制整个关于宽度的规则，结果是像这样的一条规则：

```css
.content {
  width: calc((6.25% * 8) + (2.08333333% * 7));
}
```

**备注：** 如果你要用预处理工具，如Sass，你可以建立一个简单的类（mixin）以插入那个值。

### 在我们的网格里启用偏移容器

我们创造的网格很有效。只要我们想把所有容器都从网格的左手边对齐。如果我们想在第一个容器前来个空列，或者容器之间来个空列，我们需要新建一个偏移类，为站点加上左外边距，来可见地推动网格。再来点数学计算！

让我们实际试试吧。

从你以前的代码开始，或者把我们的fluid-grid.html文件用作起始点。

我们在 CSS 上搞一个类，它会给一个容器元素来个一列宽度的偏移。将下面的内容加到你的 CSS 的底部：

```css
.offset-by-one {
  margin-left: calc(6.25% + (2.08333333% * 2));
}
```

或者假如你更愿意自己算百分比，用下面这个：

```css
.offset-by-one {
  margin-left: 10.41666666%;
}
```

想要给一个容器的左边加个有一列宽的空白的话，你可以在容器上添加这个类。例如，如果你在 HTML 中有这个内容的时候：

```html
<div class="col span6">14</div>
```

试着用下面的替换：

```html
<div class="col span5 offset-by-one">14</div>
```

**备注：** 注意你需要别让横跨多列的列太多，给偏移留点空间！

试着载入，刷新来查看区别，或者查看我们的fluid-grid-offset.html示例（另见实时示例）。完成的示例应该看起来像这样：

![](/zh-CN/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods/offset-grid-finished.png)

**备注：** 作为额外练习，你能实现一个 `offset-by-two` 类吗？

### 浮动网格的限制

当你想用这个网格系统时，你得仔细看看你的总长是否正确，并且每行中的元素所横跨的列数不超过这一行可容纳的列数。由于浮动布局实现的方式，如果网格列的数目对与网格来说太大，在最后边的元素会跑到下一行去，搞坏了布局。

还要记住，如果元素内容比行宽，它会溢出，看起来一团糟。

这个系统的最大限制是，它本质上是一维的。我们在处理列、让元素跨越列，但是处理不了行。如果不设置一个确定的高度，用老方法很难控制元素高。这个方法很不灵活 —它只有在你确定你的内容有个明确的高的情况下有用。

弹性盒网格？
------

如果你读了之前关于flexbox的文章，你大概会想，弹性布局是个写网格布局的好办法。现在有很多基于弹性布局的网格布局，并且弹性布局可以解决很多上面讲的问题。

但是，弹性布局不是为网格布局而设的，把它当网格布局来用也有新的挑战。举个简单的例子，我们可以使用我们在上面使用过的同样的示例标记，用下面的 CSS 样式化`wrapper`、`row`和`col`类：

```css
body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}

.row {
  display: flex;
}

.col {
  margin-left: 2.08333333%;
  margin-bottom: 1em;
  width: 6.25%;
  flex: 1 1 auto;
  background: rgb(255, 150, 150);
}
```

你可以试着在你自己的示例里做这些替换，或者看下我们的flexbox-grid.html示例代码（另见实时版）。

这里，我们会把每行变成一个弹性容器。有了弹性盒为基础的网格，我们仍然需要行，以让我们的元素加起来能不超过 100%。我们将容器设为`display: flex`。

在`.col`上。我们设定`flex`属性的第一个值（`flex-grow`）为 1，这样我们的物件可以变大；第二个值（`flex-shrink`）为 1，这样我们的物件可以缩小；第三个值（`flex-basis`）为`auto`。因为我们的元素的`width`被设定了， `auto`将会使用和`flex-basis` 值一样的宽度。

在顶行，我们有十二个整齐的盒子，它们在视口宽度改变时同等地放大缩小。可是在下一行，我们只有四个物件，它们也从六十像素的基础上变大变小。因为它们只有四个，它们可以长得比上一行的物件更快，结果是它们都占据了第二行的相同宽度。

![](/zh-CN/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods/flexbox-grid-incomplete.png)

为了解决这个问题，我们仍然需要包含`span`类，以提供一个用于那个元素的，可以替换掉为 `flex-basis`所使用的值的宽度。

它们也不遵从上面的物件使用的网格，因为它们对它一无所知。

弹性盒设计上是**一维**。它处理单个维度，行的或者列的。我们不能创建一个对行列严格要求的网格，意即如果我们要在我们的网格上使用弹性盒的话，我们仍然需要计算浮动布局的百分比。

在你的工程中，由于弹性盒相比浮动能提供附加的对齐和空间分布能力，你可能仍然选择使用弹性盒“网格”。但是你应该清楚，你仍然是在使用一个被设计用来做其他事情的工具。所以你可能觉得，这就像是在你抵达你想要的结果之前，让你跳过额外的坑。

第三方网格系统
-------

既然我们理解了我们的网格计算背后的数学了，我们现在该看看一些常用的第三方网格系统了。如果你在互联网上搜索“CSS 网格框架”的话，你会发现一个包含了可选项的庞大列表。流行的框架，例如Bootstrap和Foundation，就包含了网格系统。此外还有独立的网格系统，不是用 CSS 开发的就是用预处理器开发的。

让我们看下这些独立系统其中的一个，它阐释了利用网格框架工作的常见技术。我们将要使用的网格是 Skeleton 的一部分，它是一种简单的 CSS 框架。

访问Skeleton 网站以开始，选择“Download”下载 ZIP 文件。解压文件，把 skeleton.css 和 normalize.css 复制到一个新路径下。

制作一个html-skeleton.html文件的副本，在同 skeleton 和 normalize CSS 相同的路径下保存副本。

在 HTML 页面包含 skeleton 和 normalize CSS，通过把以下内容加到文件头部的方式：

```html
<link href="normalize.css" rel="stylesheet" />
<link href="skeleton.css" rel="stylesheet" />
```

Skeleton 不仅包含了网格系统，它还包含了用于排版和其他能作为起始点的页面元素上的 CSS。我们现在把这些部分留作默认值，我们在这里真正感兴趣的是网格。

**备注：** Normalize 是由 Nicolas Gallagher 编写的一个很有用的小 CSS 库，它自动做了一些有用的基础布局修正，让元素默认的样式化在不同浏览器中更加协调。

我们将会使用和在前面的示例中相似的 HTML。将下面的内容加到你的 HTML body 中：

```html
<div class="container">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col">13</div>
    <div class="col">14</div>
    <div class="col">15</div>
    <div class="col">16</div>
  </div>
</div>
```

要开始使用 Skeleton，我们需要给包装的`<div>`一个`container`类——这已经包含在了我们的 HTML 里面。这让最大宽度为 960 像素的内容居中了。你可以看看盒子现在是怎么不会宽于 960 像素的。

你可以看看 skeleton.css 文件里，CSS 在我们应用这个类的时候是如何使用的。`<div>`用值为`auto`的左右外边距居中，左边和右边还应用了 20 像素的内边距。同我们前面做过的那样，Skeleton 同时把 Skeleton`box-sizing`属性设为`border-box`值，所以这个元素的内边距和边框将会被包含在整个 width 里面。

```css
.container {
  position: relative;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}
```

如果它们在行里面的话，元素只会是网格的一部分，所以对于前面的示例，我们需要一个额外的`<div>`或者其他带有`row`类的、内嵌到`content` `<div>`和我们实际的内容容器的`<div>`之间的元素。我们也已经做了这件事。

现在让我们铺开容器盒子。Skeleton 基于一个 12 列的网格。顶端一行的盒子都需要加上`one column`的类，以让它们横跨一列。

现在加上这些，像是在下面的节录里面显示的那样：

```html
<div class="container">
  <div class="row">
    <div class="one column">1</div>
    <div class="one column">2</div>
    <div class="one column">3</div>
    /* and so on */
  </div>
</div>
```

然后，给第二行的容器加上解释它们应该横跨几个列的类，像这样：

```html
<div class="row">
  <div class="one column">13</div>
  <div class="six columns">14</div>
  <div class="three columns">15</div>
  <div class="two columns">16</div>
</div>
```

试着保存你的 HTML，在你的浏览器里面载入，看下效果。

**备注：** 如果你在实现这个示例的时候遇到麻烦，试着拿它和我们的html-skeleton-finished.html文件进行比较（另见实时运行版）。

如果你看下 skeleton.css 文件的内容，你能理解这是如何实现的。例如，Skeleton 有下面的定义内容，用于样式化加入了“three colomns”类的元素。

```css
.three.columns {
  width: 22%;
}
```

Skeleton（或者其他任何网格框架）正在做的所有事情是，设定一个预定义的类，你可以通过把它们加到你的标记文件里面的方式使用这些框架，和你自己做计算这些百分数的工作完全是一样的。

正如你所看到的这样，我们使用 Skeleton 的时候，几乎不需要写多少 CSS。它在我们向标记文本里面加类的时候，替我们处理了所有的浮动。正是把布局的责任甩给其他人的可能性，使得使用用于网格系统的框架成为了一个强制性的选择！但是现在来看，有了 CSS 网格布局，许多开发者正在停止使用这些框架，转而使用 CSS 提供的内建的原生网格。

小结
--

你现在理解了多种网格系统是如何建立的。这将会在处理老网站的时候，以及理解 CSS 网格布局的原生网格和那些老系统的不同的时候帮到你。

* 上一页
* Overview: CSS layout
* 下一页


支持旧浏览器 - 学习 Web 开发

支持旧浏览器
======

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2020.

* Learn more
* See full compatibility
* Report feedback
* 上一页
* Overview: CSS layout
* 下一页

本文中，我们推荐使用弹性盒和网格作为你的设计的主要布局方式。但是，你的网站的访客会有人使用旧浏览器，或者是不支持你已经使用的方式。这总是 Web 上存在的情况，因为新的特性被开发出来，不同的浏览器会优先支持不同的特性。本文解释了如何使用现代的 Web 技术，而无需让采用旧技术的用户被拒之门外。

|  |  |
| --- | --- |
| 学习前提： | HTML 基础（学习HTML 介绍），了解 CSS 工作的原理（学习CSS 介绍和样式化盒子） |
| 目标： | 理解怎么让你的布局为旧有的、不支持你想要使用的特性的浏览器提供支持。 |

对于你的站点来说，浏览器生态是怎样的？
-------------------

每个网站论目标用户都是不同的，在决定采用哪种方式之前，你需要查明使用旧浏览器浏览你的站点的用户的数量。如果你有一个已有的网站，你想要润色或者替换它，这是一件直截了当的事情，因为你很可能有了可用的、可以告诉你人们使用的技术的分析。如果你没有分析，或者这是一个全新的网站，那么例如Statcounter之类的站点可以提供以位置分类的数据。

你应该同样考虑设备的类型和人们使用你的网站的方式。譬如，你可能更想有超出平均数量的移动设备的访问。无障碍和使用辅助性技术的人总应当被考虑，对于一些站点，这问题可能更加重要。以我的经验，开发者经常担心 1% 的使用旧版 IE 的用户的体验，而不去照顾多得多的有无障碍需求的用户。

对你想使用的特性的支持如何？
--------------

一旦你知道了会访问你的网站的浏览器，你就可以在任何你想使用的技术，与该技术受到的支持以及为不能使用该技术的访客提供一个替代方案的难易度之间做权衡。在 MDN 上，我们在尽力让这个流程变得容易——在每个详述 CSS 属性的页面上提供浏览器的兼容性信息。例如，看一下`grid-template-columns`页面。在这个页面底部有一个表格，列出了主流的浏览器，还有它们开始支持这个属性的版本。

![](/zh-CN/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers/browser-table.png)

另一个流行的查明一个特性有多受到支持的方式是Can I Use网站。这个网站列出了主流的 Web 平台特性，附有它们的浏览器支持信息。你可以按位置查看使用数据——在你致力于一个用户大多数是世界上的某个区域的网站的时候会有用。你甚至可以链接你的 Google Analytics 账户，得到基于你的用户的信息。

理解你的用户拥有的技术，以及支持你可能想要使用的东西，这样你就可以很好地作出你的所有决定，知道最多能多好地支持所有用户。

支持不意味着“看起来一样”
-------------

一个网站可能不会在所有浏览器上看起来都一样，因为你的一些用户将会是在一部手机上，而另一些用户是在桌面大屏幕上浏览你的网站；类似地，你的一些用户会是使用旧版浏览器的，而另外一些是最新版；你的一些用户可能是在聆听由屏幕阅读器朗读的你的内容，或者已经放大了页面来读到内容。支持所有人意味着提供保守设计版本的你的内容，这样在现代浏览器上，它会看起来很棒，而对于旧浏览器用户而言仍然以最低限度可用。

基本级别的支持来源于良好地组织你的内容，从而让你的页面的正常布局流有意义。一个使用非常局限的功能手机的用户可能不怎么会体验到你的 CSS，但是内容会以让阅读容易的方式流过。因而你应该总是从一个良好组织的 HTML 文档开始。*如果你移除掉你的样式表，你的内容还能被理解吗？*

一个选项是，把站点的这种原始视图作为回滚，用在使用很老旧或者受局限的浏览器的用户身上。如果你没有多少来到站点的人是使用这些浏览器的，将时间花在尝试给予他们和使用现代浏览器类似的体验上可能没有商业价值。将时间花在让站点具备更多无障碍的事情上，以服务远远更多的用户，可能会是更好的做法。这样的做法在纯 HTML 页面和花里胡哨之间是有余地的，CSS 事实上让建立这些回滚变得简明。

在 CSS 中构建回滚
-----------

CSS 规范包含了在一个物件上同时应用两种布局的时候，解释浏览器如何反应的信息。例如，规范定义如果一个浮动元素同时又是用 CSS 网格布局实现的网格元素（Grid Item）的时候会发生什么。结合浏览器会忽略掉它不会理解的 CSS 的特点组合起来，你就得到了使用我们已经提到的传统技术构建简单布局的方法，在可以理解你的网格布局的现代浏览器中，传统布局方法会被网格布局覆写掉。

在下面的示例中，我们已经浮动了三个`<div>`，所以它们显示在了一行中。任何使用不支持CSS 网格布局的浏览器的人将会看到以浮动布局实现的一列盒子。一个成为了网格物件的浮动物件失去了浮动的动作，就是说通过把 wrapper 变成网格容器，浮动物件变成了网格物件。如果浏览器器支持网格布局，它会显示网格视图，否则它会忽略`display: grid`相关的属性，使用浮动布局。

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

.wrapper {
  background-color: rgb(79, 185, 227);
  padding: 10px;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.item {
  float: left;
  border-radius: 5px;
  background-color: rgb(207, 232, 220);
  padding: 1em;
}
```

```html
<div class="wrapper">
  <div class="item">Item One</div>
  <div class="item">Item Two</div>
  <div class="item">Item Three</div>
</div>
```

**备注：**`clear`属性也在被清除的物件变为网格物件的时候失效，所以你应该在一个会变成网格布局的布局上，加上被清除的尾部。

### 回滚方式

有很多布局方式，它们同这个浮动示例使用的方式是类似的。你可以选择能够与你需要建立的布局范式符合得最好的那个。

浮动和**清除**

如上所示，如果浮动和清除的物件变成了弹性或网格物件，浮动和清除属性不再影响布局。

display: inline-block

这种方式能被用来建立列布局，如果一个物件被设为`display: inline-block`，但是之后变成了弹性或者网格物件，inline-block 行为将会被忽略。

display: table

这种在这几节课的介绍中描述的建立 CSS 表格的方式可以被用作回滚。被设为 CSS 表格布局的物件将会在它们变为弹性或者网格物件的时候不再表现出这种行为。重要的是，被建立起来用于修复表格结构的匿名盒子没有被建立起来。

Multiple-column Layout

对于某些布局，你能用multi-col作为回滚。如果你的容器有任何一个`column-*`属性被定义，之后变成了网格容器，那么多列行为不会实现。

作为网格的回滚的弹性盒

弹性盒由于受到了 IE10 和 IE11 的支持，比网格有着更好的浏览器支持。不过，在这节课的稍后部分，你一定要看下旧浏览器对弹性盒相当参差不齐而且令人困惑的支持的相关信息。如果你把弹性容器做成了网格容器，任何应用到子元素的`flex`属性都会被忽略。

对于许多在旧浏览器中微调的布局来说，你可能会发现你能用这种方式使用 CSS，给访客亲切的体验。我们加入了一个更简单的基于旧的和受到良好支持技术的布局，然后用新一些的 CSS 建立一个可以由 90% 的用户看到的布局。但是存在这样的情况：回滚代码会需要包含新浏览器也能理解的东西；举例来说就是，我们要往我们的浮动物件中加入百分数宽度，从而让列看起来更像网格，拉伸以充满容器。

在浮动布局中，百分数是依照容器计算的——33.333% 是容器宽度的三分之一。但在网格中，这 33.333% 是根据物件所在的网格区域计算的，所以只要网格布局引入进来，物件的大小实际上变成了我们想要的大小的三分之一。

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

.wrapper {
  background-color: rgb(79, 185, 227);
  padding: 10px;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.item {
  float: left;
  border-radius: 5px;
  background-color: rgb(207, 232, 220);
  padding: 1em;
  width: 33.333%;
}
```

```html
<div class="wrapper">
  <div class="item">Item One</div>
  <div class="item">Item Two</div>
  <div class="item">Item Three</div>
</div>
```

为了处理这种问题，我们需要有能够探测网格是否受到支持的方法，也就是探测它是否会覆写宽度的方法。CSS 在这里为我们提供了一个解决方法。

特性查询
----

特性查询允许你测试一个浏览器是否支持任何特定的一个 CSS 特性。这就是说，你可以写一些面向不支持某项特性的浏览器的 CSS，然后检查以了解浏览器是否支持，如果支持的话就可以加进你的复杂布局了。

如果我们向上面的示例中加入了一条特征查询，要是我们知道网格受到支持的话，我们可以用它把我们的物件宽度设定回`auto`。

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

.wrapper {
  background-color: rgb(79, 185, 227);
  padding: 10px;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

.item {
  float: left;
  border-radius: 5px;
  background-color: rgb(207, 232, 220);
  padding: 1em;
  width: 33.333%;
}

@supports (display: grid) {
  .item {
    width: auto;
  }
}
```

```html
<div class="wrapper">
  <div class="item">Item One</div>
  <div class="item">Item Two</div>
  <div class="item">Item Three</div>
</div>
```

对特性查询的支持，在各现代浏览器中都是很好的。但是你应该注意那些既不支持 CSS 网格，也不支持特征查询的浏览器。这就是说在上面详细介绍的一种方式，对这些浏览器而言，会表现得很好。我们要做的是，先在任何特性查询以外，编写我们的旧 CSS。不支持网格也不支持特性查询的浏览器会使用这部分它们可以理解的布局信息，将其他东西全都忽略掉。支持特性查询和 CSS 网格等的浏览器会运行网格代码和特性查询之内的代码。

对于特性查询的规范，也包含了测试浏览器是否支持某个特性的能力——这只在浏览器支持特性查询的时候有用。未来，一种检测支持的缺失的方式将会实现，同时不支持特性查询的浏览器也将退出历史舞台。但是现在，你要使用这种编写旧 CSS 的方式，然后覆写它，以求最广泛的支持。

弹性盒的旧版本
-------

在旧版浏览器中，你可以找到弹性盒规范的旧有修订版本。在编写这篇文章的时候，这大多数是 IE10 的问题，它在弹性盒上使用了`-ms-`前缀。这也意味着现在还存在着一些过时的文章和教程，这篇有用的指导帮助你分辨你看到的信息；如果你需要在很旧的浏览器中需要 flex 支持的话，它也会帮到你。

IE10 和 IE11 的带前缀版的网格
--------------------

CSS 网格规范最初成形于 IE10，也就是说尽管 IE10 和 IE11 不支持*现代的*网格，虽然这种网格和本站记载的现代布局不同，它们还是有一个很堪用的网格布局版本。IE10 和 IE11 的实现是以`-ms-`为前缀的，也就是说你可以给这两个浏览器用，而在非微软浏览器上，这种属性会被忽略。不过 Edge 仍然能理解旧语法，所以小心点，让每个东西都安全地在你的现代网格 CSS 中覆写。

网格布局渐进增强指导能帮你理解 IE 版的网格，我们在这节课的末尾加入了一些额外的有用的链接。不过，除非你有很多使用旧版 IE 的访客，你可能发现专注于建立一个由所有不支持的浏览器共用的回滚版本是一个更好的选择。

测试旧浏览器
------

由于大多数浏览器都支持弹性盒和网格，测试旧浏览器想想就很难。一种方式是使用在线的测试工具，例如 Sauce Labs，在跨浏览器测试模块里有详细说明。

你也可以下载安装虚拟机，在你的电脑的容器环境中运行旧版浏览器。能够使用旧版 IE 是很有用的，为此，微软已经制作了一些可以免费下载的虚拟机。这些对 Mac、Windows 和 Linux 操作系统都是可以用的，所以即使你没有 Windows 电脑，这也是一个测试旧的和现代 Windows 浏览器的绝佳办法。

小结
--

你现在有了自信地使用例如网格和弹性盒技术、建立面向旧浏览器的回滚以及利用任何可能会在未来出现的 新技术所需的知识。

参见
--

* 在 CSS 中使用媒体查询
* CSS 网格布局和渐进优化
* 使用 CSS 网格：支持没有网格的浏览器
* 使用 IE10 和 IE11 版网格的教程
* 我应该尽力使用 IE10 的网格布局实现吗？
* 有特性查询的层叠式 Web 设计
* 使用特性查询（视频）
* 上一页
* Overview: CSS layout
* 下一页