网络版报告三之CSS基础

CSS 基础学习。

什么是 CSS?

CSS (Cascading Style Sheets,层叠样式表)是用来控制网页在浏览器中的显示外观的声明式语言。

浏览器默认样式

正常情况下你看到的这些样式就是浏览器的默认样式——非常基础的样式,用于确保即使页面作者没有指定样式,网页也能保持可读性。这些样式由浏览器内置的默认 CSS 样式表定义,与 HTML 本身无关。

如果所有网站都长得一样,web 将变得非常无趣。这就是你需要学习 CSS 的原因。

CSS的作用是什么?

通过 CSS,你可以精确控制 HTML 元素在浏览器中的外观,以你喜欢的设计和布局呈现文档。

CSS 可用于网页外观相关的多种用途,例如:

  • 文本样式,包括更改标题和链接的颜色和大小
  • 创建布局,如网格布局或多列布局
  • 特效,如动画

CSS 语言按模块组织,每个模块包含相关功能。例如,你可以查阅背景与边框模块的 MDN 参考页面,了解其用途及包含的属性和功能。在我们的模块页面中,你还可以找到定义这些技术的规范链接。

CSS语法基础

CSS 是一种基于规则的语言——通过定义一组样式规则来指定网页上哪些元素应该应用哪些样式。

例如,你可能希望将页面主标题设置为红色的大号文字。以下代码展示了一个非常简单的 CSS 规则:

1
2
3
4
h1 {
color: red;
font-size: 2.5em;
}
  • 在上面的示例中,CSS 规则以一个选择器开头,用于选择我们要设置样式的 HTML 元素。在这个例子中,我们为一级标题(h1)添加样式。
  • 接着我们使用一对大括号 { }
  • 大括号中包含一个或多个声明,每个声明由属性和值组成。我们在冒号前指定属性(例如上述示例中的 color),并在冒号后指定该属性的值(我们为 color 属性设置取值 red)。
  • 此示例包含两个声明,一个是 color,另一个是 font-size

不同的 CSS 属性允许不同的取值。在我们的示例中,color 属性可以接受多种颜色值,而 font-size 属性则可以接受多种尺寸单位作为值。

一个 CSS 样式表通常包含许多这样的规则,依次书写。

1
2
3
4
5
6
7
8
9
10
h1 {
color: red;
font-size: 2.5em;
}

p {
color: aqua;
padding: 5px;
background: midnightblue;
}

你会发现有些属性值很快就能掌握,而其他的则需要查阅。MDN 上的各个属性页面可以快速帮助你查找属性及其可用值。

CSS如何应用到HTML?

如浏览器如何加载网站所述,当你访问一个网页时,浏览器首先接收包含网页内容的 HTML 文档,并将其转换为一个 DOM 树

随后,网页中找到的 CSS 规则(无论是直接写在 HTML 中,还是引用的外部 .css文件)会根据选择器指定的元素分类到不同的“桶”中。这些规则随后应用到 DOM 树上,生成一个渲染树,最终绘制到浏览器窗口中。

我们来看一个例子。首先定义一个 HTML 片段,CSS 将应用于其中的元素:

1
2
3
4
5
<h1>CSS 很棒</h1>

<p>你可以为文本添加样式。</p>

<p>你还可以创建布局和特效。</p>

现在,我们重复上一节中的 CSS 代码:

1
2
3
4
5
6
7
8
9
10
h1 {
color: red;
font-size: 2.5em;
}

p {
color: aqua;
padding: 5px;
background: midnightblue;
}

这段 CSS:

  • 选择页面上的所有 <h1> 元素,将其文字设为红色并放大。由于示例 HTML 中只有一个 <h1>,只有它会被应用样式。
  • 选择页面上的所有 <p> 元素,设置文字颜色、背景颜色和内边距。示例中有两个 <p> 元素,它们都会应用这些样式。

当 CSS 应用于 HTML 后,页面的渲染效果如下:

玩转CSS

试着操作上面的示例。点击右上角的“Play”按钮,即可在 MDN Playground 编辑器中加载它。

请尝试以下操作:

  1. 在已有的两个段落下方添加另一个段落,观察第二条 CSS 规则如何自动应用到新段落上。
  2. <h1> 下方某处添加一个 <h2> 子标题,比如放在其中一个段落之后。
  3. 尝试为 <h2> 元素设置不同的颜色:复制 h1 的 CSS 规则,将选择器改为 h2,并将 color 的值从 red 改为 purple(紫色)等。
  4. 如果你想挑战一下自己,可以在 MDN 的 #中查找一些新的 CSS 属性和值,添加到你的样式规则中!

如果你还想进一步练习 CSS 基础,可以参考 Scrimba 提供的课程写下你的第一行 CSS!#。这个课程简要介绍了 CSS 的基本语法,并提供了一个交互式挑战,让你练习编写 CSS 声明。

让我们开始CSS的学习之旅

在这篇文章中,我们将会拿一个简单的 HTML 文档做例子,并且在里面使用 CSS 样式,期待你能在此过程中学会更多有关 CSS 的实战性知识。

先从HTML开始吧

我们先以 HTML 文档展开叙述。如果想在自己电脑试一试,可以复制下面的代码。在你的电脑上,将代码以文件名: index.html 的形式保存下来。

添加CSS试试看?

创建文件 styles.css

在 HTML 文档中,<head>语句模块里面加上下面的代码:

1
<link rel="stylesheet" href="styles.css" />

<link> 语句块里面,我们用属性 rel,让浏览器知道有 CSS 文档存在(所以需要遵守 CSS 样式的规定),并利用属性 href 指定,寻找 CSS 文件的位置。你可以做测试来验证 CSS 是否有效:在 styles.css 里面加上 CSS 样式并观察显示的结果。下面,用你的编辑器打出下面的代码。

1
2
3
h1 {
color: red;
}

保存 HTML 和 CSS 文档,刷新浏览器网页,不出意外你将看到网页顶层的大标题变成红色了。如果看到这个现象,恭喜你:你已经成功将某些 CSS 样式运用到 HTML 上了。当然假设没有达到预想结果,可以仔细检查每句代码是否正确,加油:)

你可以一直在本地编辑器练习 styles.css ,或者采用我们教程下面的交互式智能编辑器。这个编辑器会默认把第一个面板里面的 CSS 代码连接到旁边的 HTML 文档,就好像我们上面得两个文档一样互相连接。

样式化HTML元素

通过上一节将大标题变成红色的例子,我们已经展示了我们可以选中并且样式化 HTML 元素。我们通过触发元素选择器实现这一点——元素选择器,即直接匹配 HTML 元素的选择器。例如,若要样式化一个文档中所有的段落,只需使用选择器 p。若要将所有段落变成绿色,你可以利用如下方式:

1
2
3
p {
color: green;
}

用逗号将不同选择器隔开,即可一次使用多个选择器。譬如,若要将所有段落与列表变成绿色,只需:

1
2
3
4
p,
li {
color: green;
}

改变元素的默认行为

只要一个 HTML 文档标记正确,即使像我们的例子那么简单,浏览器都会尽全力将其渲染至可读状态。标题默认使用大号粗体;列表旁总有项目符号。这是因为浏览器自带一个包含默认样式的样式表,默认对任何页面有效。没有了它们,所有文本会夹杂在一起变得一团糟,我们只得从头开始规定,好糟糕。话说回来,所有现代浏览器的默认样式都没什么差距。

不过你可能对浏览器的默认样式不太满意。没关系,只需选定那个元素,加一条 CSS 规则即可。就拿我们的无序列表 <ul>举个例子吧,它自带项目符号,不过要是你跟它有仇,你就可以这样移除它们:

1
2
3
li {
list-style-type: none;
}

把上述代码加到你的 CSS 里面试一试!

欢迎参阅 MDN 上的 list-style-type属性,看看到底有哪些值被支持。list-style-type页首提供交互性示例,你可以输入不同的值来瞅一瞅它们到底有什么用。关于每个值的详细描述都规规整整地列在下面。

通过参阅上述页面,你会发现你不仅能移除项目符号——你甚至能改变它们。赶快试试 square,它能把默认的小黑球变成方框框。

使用类名

目前为止,我们通过 HTML 元素名规定样式。如果你愿意所有元素都一个样,也不是不可以,但大多数情况下,我估计你都不愿意。我知道你想干啥,你想用这种方式样式化这一片元素,又想用那种方式样式化那一片元素,真贪心。不过没关系,你可以给 HTML 元素加个类名(class),再选中那个类名,这样就可以了,大家基本上都这么用。

举个例子吧,咱们把 class 属性加到表里面第二个对象。你的列表看起来应该是这样的:

1
2
3
4
5
<ul>
<li>项目一</li>
<li class="special">项目二</li>
<li>项目 <em></em></li>
</ul>

在 CSS 中,要选中这个 special 类,只需在选择器的开头加个西文句点(.)。在你的 CSS 文档里加上如下代码:

1
2
3
4
.special {
color: orange;
font-weight: bold;
}

保存再刷新,就可以看到变化。

这个 special 类型可不局限于列表,它可以应用到各种元素上。举个例子,你可能也想让段落里边的 <span> 一起又橙又粗起来。试试把special 类属性加上去,保存,刷新,哇,生活就是这么美好。

有时你会发现选择器中,HTML 元素选择器跟类一起出现:

1
2
3
4
li.special {
color: orange;
font-weight: bold;
}

这个意思是说,“选中每个 special 类的 li 元素”。你真要这样,好了,它对 <span> 还有其他元素不起作用了。你可以把这个元素再添上去就是了:

1
2
3
4
5
li.special,
span.special {
color: orange;
font-weight: bold;
}

你们都是懒人,肯定不想每加一个 special 类的元素就改一遍 CSS 表,你肯定想把一个类的属性应用到多个元素上。所以说,有时还是别管元素,光看类就完事了,除非你意志坚定,坚持对这个类的某一种元素创造规则,还不让其他元素用。

根据元素在文档中的位置确定样式

有时候,你希望某些内容根据它在文档中的位置而有所不同。这里有很多选择器可以为你提供帮助,但现在我们只介绍几个选择器。在我们的文档中有两个 <em>元素——一个在段落内,另一个在列表项内。仅选择嵌套在<li> 元素内的<em>我们可以使用一个称为包含选择符的选择器,它只是单纯地在两个选择器之间加上一个空格。

将以下规则添加到样式表。

1
2
3
li em {
color: rebeccapurple;
}

该选择器将选择<li>内部的任何<em>元素(<li>的后代)。因此在示例文档中,你应该发现第三个列表项内的<em>现在是紫色,但是在段落内的那个没发生变化。

另一些可能想尝试的事情是在 HTML 文档中设置直接出现在标题后面并且与标题具有相同层级的段落样式,为此需在两个选择器之间添加一个 + 号 (成为 相邻选择符)

也将这个规则添加到样式表中(选中“紧跟在 h1后面的第一个 p元素”,且这两个元素必须是同级的兄弟元素(有同一个父元素)):

1
2
3
h1 + p {
font-size: 200%;
}

根据状态确定样式

在这篇教程中我们最后要看的一种修改样式的方法就是根据标签的状态确定样式。一个直观的例子就是当我们修改链接的样式时。当我们修改一个链接的样式时我们需要定位(针对) <a> (锚)标签。取决于是否是未访问的、访问过的、被鼠标悬停的、被键盘定位的,亦或是正在被点击当中的状态,这个标签有着不同的状态。你可以使用 CSS 去定位或者说针对这些不同的状态进行修饰——下面的 CSS 代码使得没有被访问的链接颜色变为粉色、访问过的链接变为绿色。

1
2
3
4
5
6
7
a:link {
color: pink;
}

a:visited {
color: green;
}

你可以改变链接被鼠标悬停的时候的样式,例如移除下划线,下面的代码就实现了这个功能。

1
2
3
a:hover {
text-decoration: none;
}

**备注:**在本教程以及整个 MDN 站点中,你会经常看到“无障碍”这个词。“无障碍”一词的意思是,我们的网页应当让每一个访客都能够理解、使用。

你的访客可能在一台使用鼠标和键盘操作的计算机前,也可能正在使用带有触摸屏的手机,或者正在使用屏幕阅读软件读出文档内容,或者他们需要使用更大的字体,或者仅仅使用键盘浏览站点。

一个朴素的 HTML 文档一般来说对任何人都是可以无障碍访问的,当你开始为它添加样式,记得不要破坏这种无障碍。

同时使用选择器和选择符

你可以同时使用选择器和选择符。来看一些例子:

1
2
3
4
5
/* selects any <span> that is inside a <p>, which is inside an <article>  */
article p span { ... }

/* selects any <p> that comes directly after a <ul>, which comes directly after an <h1> */
h1 + ul + p { ... }

你可以将多种类型组合在一起。试试将下面的代码添加到你的代码里:

1
2
3
4
5
body h1 + p .special {
color: yellow;
background-color: black;
padding: 5px;
}

上面的代码为以下元素建立样式:在 <body> 之内,紧接在 <h1> 后面的 <p> 元素的内部,类名为 special。

在我们提供的原始 HTML 文档中,与之符合的元素只有<span class="special">.

如果你现在觉得这份代码太复杂了,别担心,一旦你开始编写更多的 CSS 代码,你很快就能找到窍门。

CSS 选择器

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

选择器是什么?

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

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

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

选择器列表

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

1
2
3
4
5
6
7
h1 {
color: blue;
}

.special {
color: blue;
}

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

1
2
3
h1, .special {
color: blue;
}

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

1
2
3
4
h1,
.special {
color: blue;
}

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

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

1
2
3
4
5
6
7
h1 {
color: blue;
}

..special {
color: blue;
}

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

1
2
3
4
h1,
..special {
color: blue;
}

选择器的种类

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

类型、类和ID 选择器

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

1
2
h1 {
}

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

1
2
.box {
}

亦或,一个 id 选择器:

1
2
#unique {
}

标签属性选择器

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

1
2
a[title] {
}

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

1
2
a[href="https://example.com"] {
}

伪类与伪元素

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

1
2
a:hover {
}

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

1
2
p::first-line {
}

运算符

最后一组选择器可以将其他选择器组合起来,更复杂的选择元素。下面的示例用运算符(>)选择了<article>元素的初代子元素(例子意思是选中所有直接包含在 article里面的 p标签)。

1
2
article > p {
}

与另一种写法的区别如下

选择器 写法 选中范围
子选择器 article > p 仅直接子元素(第一层)。
后代选择器 article p 所有后代(不管嵌套多深)。

属性连接器

从 HTML 的学习中,你已经知道,元素可以带有属性,它提供了关于如何标记的更详细信息。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

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

  • 使用li[class],我们就能匹配任何有 class 属性的选择器。这匹配了除了第一项以外的所有项。
  • li[class="a"]匹配带有一个a类的选择器,不过不会选中一部分值为a而另一部分是另一个用空格隔开的值的类,它选中了第二项。
  • li[class~="a"]会匹配一个a类,不过也可以匹配一列用空格分开、包含a类的值,它选中了第二和第三项。
1
2
3
4
5
6
7
<h1>Attribute presence and value selectors</h1>
<ul>
<li>Item 1</li>
<li class="a">Item 2</li>
<li class="a b">Item 3</li>
<li class="ab">Item 4</li>
</ul>

子字符串匹配选择器

这些选择器让更高级的属性的值的子字符串的匹配变得可行。例如,如果你有box-warningbox-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子字符串。

大小写敏感

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

1
2
3
li[class^="a" i] {
color: red;
}

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

伪类和伪元素

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

什么是伪类?

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

伪类就是开头为冒号的关键字。例如,:hover 就是一个伪类。

简单伪类示例

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

1
2
3
4
5
6
7
8
9
<article>
<p class="first">
蔬菜对你有好处,所以你需要多吃大头菜、大葱、白萝卜、苋菜、番茄酱、甜瓜、红豆、大蒜。
</p>

<p>
秋葵浓汤、甜菜叶、玉米、鸡冠菜、菊苣、秋葵浓汤、葫芦。欧芹、葱、西葫芦、塌棵菜、豌豆芽、蚕豆、羽衣甘蓝、蒲公英、秋葵、裙带菜、番茄。蒲公英、黄瓜、花生、豌豆、花生、鸡冠菜、西葫芦。
</p>
</article>
1
2
3
4
.first {
font-size: 120%;
font-weight: bold;
}

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

1
2
3
4
5
6
7
8
9
<article>
<p>
蔬菜对你有好处,所以你需要多吃大头菜、大葱、白萝卜、苋菜、番茄酱、甜瓜、红豆、大蒜。
</p>

<p>
秋葵浓汤、甜菜叶、玉米、鸡冠菜、菊苣、秋葵浓汤、葫芦。欧芹、葱、西葫芦、塌棵菜、豌豆芽、蚕豆、羽衣甘蓝、蒲公英、秋葵、裙带菜、番茄。蒲公英、黄瓜、花生、豌豆、花生、鸡冠菜、西葫芦。
</p>
</article>
1
2
3
4
article p:first-child {
font-size: 120%;
font-weight: bold;
}

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

用户行为伪类

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

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

伪元素是啥?

伪元素以类似方式表现。不过表现得是像你往标记文本中加入全新的 HTML 元素一样,而不是向现有的元素上应用类。

伪元素开头为双冒号 ::。比如,::before 就是一个伪元素的示例。

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

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

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

1
2
3
4
article p::first-line {
font-size: 120%;
font-weight: bold;
}

这表现得就像是<span>神奇地包在第一个被格式化的行一样,每当行长改变的时候还会更新,并且每一段的第一行都选中了。

把伪类和伪元素组合起来

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

1
2
3
4
article p:first-child::first-line {
font-size: 120%;
font-weight: bold;
}

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

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

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

1
<p class="box">我的 HTML 页面的盒子中的内容。</p>
1
2
3
.box::before {
content: "这应该显示在其他内容之前。";
}

显示效果为

1
这应该显示在其他内容之前。我的 HTML 页面的盒子中的内容。

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

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

1
<p class="box">我的 HTML 页面的盒子中的内容。</p>
1
2
3
.box::after {
content: " ➥";
}

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

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

1
2
3
4
5
6
7
8
.box::before {
content: "";
display: block;
width: 100px;
height: 100px;
background-color: rebeccapurple;
border: 1px solid black;
}

::before::after 伪元素与 content 属性的共同使用,在 CSS 中被叫做“生成内容”,而且你会见到这种技术被用于完成各种任务,比如生成各种箭头。

关系选择器

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

后代选择器

后代选择器通常用单个空格( )字符来组合两个选择器。当第二个选择器匹配的元素存在一个祖先元素(父元素、祖父元素等)与第一个选择器匹配时,该元素就会被选中。通过这种方式组合出的选择器被称作后代选择器。

1
body article p

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

1
2
3
.box p {
color: red;
}

子代关系选择器

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

1
article > p

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

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

1
2
3
4
5
6
7
8
9
10
<ul>
<li>Unordered item</li>
<li>
Unordered item
<ol>
<li>Item 1</li>
<li>Item 2</li>
</ol>
</li>
</ul>
1
2
3
ul > li {
border-top: 5px solid red;
}

邻接兄弟

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

1
p + img

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

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

1
2
3
4
5
6
h1 + p {
font-weight: bold;
background-color: #333;
color: #fff;
padding: 0.5em;
}

通用兄弟

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

1
p ~ img

使用关系选择器

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

1
2
ul > li[class="a"] {
}

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

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

盒模型

在 CSS 中,所有的元素都被一个个的“盒子”包围着,理解这些“盒子”的基本原理,是我们使用 CSS 实现准确布局、处理元素排列的关键。本文以 CSS 盒模型为主题,你将了解其工作原理和相关术语。

区块盒子与行内盒子

在 CSS 中,我们有几种类型的盒子,一般分为区块盒子(block boxes)和行内盒子(inline boxes)。类型指的是盒子在页面流中的行为方式以及与页面上其他盒子的关系。盒子有内部显示(inner display type)和外部显示(outer display type)两种类型。

一般来说,可以使用 display属性为显示类型设置各种值,该属性可以有多种值。

外部显示类型

一个拥有 block 外部显示类型的盒子会表现出以下行为:

  • 盒子会产生换行。
  • widthheight 属性可以发挥作用。
  • 内边距、外边距和边框会将其他元素从当前盒子周围“推开”。
  • 如果未指定 width,方框将沿行向扩展,以填充其容器中的可用空间。在大多数情况下,盒子会变得与其容器一样宽,占据可用空间的 100%。

某些 HTML 元素,如 <h1> 和 ``,默认使用 block 作为外部显示类型。

一个拥有 inline 外部显示类型的盒子会表现出以下行为:

  • 盒子不会产生换行。
  • widthheight 属性将不起作用。
  • 垂直方向的内边距、外边距以及边框会被应用但是不会把其他处于 inline 状态的盒子推开。
  • 水平方向的内边距、外边距以及边框会被应用且会把其他处于 inline 状态的盒子推开。

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

内部显示类型

盒子还有内部显示类型,它决定了盒子内元素的布局方式。

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

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

当你继续详细学习 CSS 布局时,将会遇到 flex以及盒子可以具有的其他各种内部值,例如 grid

备注:想要了解更多有关显示值以及盒子在区块和行内布局中的工作原理,请参阅常规流中的区块和行内布局

什么是CSS盒模型?

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

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

CSS 中组成一个区块盒子需要:

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

下图显示了这些层次:

CSS 标准盒模型

在标准盒模型中,如果在盒子上设置了 inline-sizeblock-size(或 widthheight)属性值,这些值就定义了内容盒子inline-sizeblock-size(水平语言中为 widthheight)。然后将任何内边距和边框添加到这些尺寸中,以获得盒子所占的总大小(见下图)。

1
2
3
4
5
6
7
.box {
width: 350px;
height: 150px;
margin: 10px;
padding: 25px;
border: 5px solid black;
}

方框实际占用的空间宽为 410px(350 + 25 + 25 + 5 + 5),高为 210px(150 + 25 + 25 + 5 + 5)。

**备注:**外边距不计入盒子的实际大小——当然,它影响盒子在页面上所占的总空间,但只影响盒子外的空间。盒子的面积止于边框,不会延伸到外边距中。

CSS 替代盒模型

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

要为某个元素使用替代模型,可对其设置 box-sizing: border-box

1
2
3
.box {
box-sizing: border-box;
}

假设一个盒子的 CSS 与上例相同:

1
2
3
4
5
6
7
8
9
.box {
width: 350px;
inline-size: 350px;
height: 150px;
block-size: 150px;
margin: 10px;
padding: 25px;
border: 5px solid black;
}

现在,盒子实际占用的空间在行向为 350px,在块向为 150px。

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

1
2
3
4
5
6
7
/* 根元素设置替代盒模型,其他元素继承 */
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit; /* 所有元素继承根元素的 box-sizing */
}

玩转盒模型

在下面的示例中,可以看到两个盒子。两个盒子的类都是 .box,因此具有相同的 widthheightmarginborderpadding。唯一不同的是,第二个方框被设置为使用替代盒模型。

通过改变第二个盒子的大小可以使得两个盒子一样大(计算方式同上)

1
2
<div class="box">I use the standard box model.</div>
<div class="box alternate">I use the alternate box model.</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.box {
border: 5px solid rebeccapurple;
background-color: lightgray;
padding: 40px;
margin: 40px;
width: 300px;
height: 150px;
}

.alternate {
box-sizing: border-box;
width: 390px;
height: 240px;
}

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

浏览器开发者工具可以使你更容易地理解盒模型。如果你在 Firefox 的 DevTools 中查看一个元素,你可以看到元素的大小以及它的外边距、内边距和边框。这是一个很好的检查元素大小的方式,可以便捷的判断你的盒子大小是否符合预期!

外边距、内边距和边框

在上面的示例中,你已经看到了 marginpaddingborder属性的作用。该示例中使用了简写属性,允许我们一次性设置盒子的所有边。这些简写属性也有等效的普通属性,可以单独控制盒子的不同边。

接下来,让我们更详细地探究这些属性。

外边距

外边距是盒子周围一圈看不到的空间。它会把其他元素推离盒子。外边距属性值可以为正也可以为负。在盒子一侧设置负值会导致盒子和页面上的其他内容重叠。无论使用标准模型还是替代模型,外边距总是在计算可见部分后额外添加。

我们可以使用 margin 属性一次性控制一个元素的所有外边距,或者每边单独使用等价的普通属性控制:

  • margin-top
  • margin-right
  • margin-bottom
  • margin-left

外边距折叠

区块的上下外边距有时会合并(折叠)为单个边距,其大小为两个边距中的最大值(或如果它们相等,则仅为其中一个),这种行为称为外边距折叠。

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

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

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

1
2
3
4
<div class="container">
<p class="one">I am paragraph one.</p>
<p class="two">I am paragraph two.</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.container {
border: 5px solid blue;
margin: 40px;
}

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

.two {
margin-top: 30px;
}

你可以通过将第 2 段的 margin-top 设置为 0 来测试它。两个段落之间的可见边距不会改变——它保留了第一个段落 margin-bottom 设置的 50 像素。如果将其设置为 -10px,你会发现总边距变成了 40px(从 50px 中减去该负值)。

外边距何时折叠,何时不折叠,由许多规则决定。有关详细信息,请参阅掌握外边距折叠。需要记住的主要一点是,外边距折叠是指在使用外边距创建空间时,如果没有获得预期的空间,就会发生外边距折叠。

边框

边框是在边距和填充盒子之间绘制的。如果你正在使用标准的盒模型,边框的大小将添加到框的宽度和高度。如果你使用的是替代盒模型,边框越大会使内容框越小,因为它会占用一些可用的宽度和高度。

为边框设置样式时,有大量的属性可以使用——有四个边框,每个边框都有样式、宽度和颜色,我们可能需要对它们进行操作。

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

欲分别设置每边的属性,可以使用:

  • border-top
  • border-right
  • border-bottom
  • border-left

欲设置所有边的宽度、样式或颜色,可以使用:

  • border-width
  • border-style
  • border-color

内边距

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

我们可以使用 padding 简写属性一次性控制元素所有边,或者每边单独使用等价的普通属性:

  • padding-top
  • padding-right
  • padding-bottom
  • padding-left

盒子模型和行内盒子

以上所有的方法都完全适用于块级盒子。某些属性也适用于行内盒子,例如由 <span> 元素创建的盒子。

在下面的示例中,我们在一个段落中使用了 <span>,并对其应用了 widthheightmarginborderpadding。可以看到,宽度和高度都被忽略了。上下外边距、内边距边框都得到了应用,但不会改变其他内容与行内盒子之间的关系。内边距和边框与段落中的其他文字重叠。左右内边距、外边距和边框会将其他内容从方框中推开。

1
2
3
4
<p>
I am a paragraph and this is a <span>span</span> inside that paragraph. A span
is an inline element and so does not respect width and height.
</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
body {
font-family: sans-serif;
}
p {
border: 2px solid rebeccapurple;
width: 200px;
}
span {
margin: 20px;
padding: 20px;
width: 80px;
height: 150px;
background-color: lightblue;
border: 2px solid blue;
}

使用 display: inline-block

display: inline-blockdisplay 的一个特殊值,它提供了介于 inlineblock 之间的中间位置。如果不希望项目换行,但又希望它使用 widthheight 值并避免出现上述重叠现象,请使用它。

略。

层叠、优先级与继承

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

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

冲突规则

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

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

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

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

层叠

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

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

1
<h1>这是我的标题。</h1>
1
2
3
4
5
6
h1 {
color: red;
}
h1 {
color: blue;
}

优先级

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

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

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

1
<h1 class="main-heading">这是我的标题。</h1>
1
2
3
4
5
6
7
.main-heading {
color: red;
}

h1 {
color: blue;
}

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

继承

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

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

1
2
<p>由于主题颜色被设置为蓝色,因此该颜色会被子元素继承。</p>
<p>我们可以通过选择器定位元素来改变颜色,比如这个<span>内容跨越</span>元素。</p>
1
2
3
4
5
6
7
body {
color: blue;
}

span {
color: black;
}

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

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

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

理解继承

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ul class="main">
<li>项目 1</li>
<li>
项目 2
<ul>
<li>2.1</li>
<li>2.2</li>
</ul>
</li>
<li>
项目 3
<ul class="special">
<li>
3.1
<ul>
<li>3.1.1</li>
<li>3.1.2</li>
</ul>
</li>
<li>3.2</li>
</ul>
</li>
</ul>
1
2
3
4
5
6
7
8
9
10
.main {
color: rebeccapurple;
border: 2px solid #ccc;
padding: 1em;
}

.special {
color: black;
font-weight: bold;
}

width(上面提到的)、marginpaddingborder 不会被继承。如果 border 可以被继承,每个列表和列表项都会获得一个边框——可能就不是我们想要的结果!

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

控制继承

pass

重设所有属性值

CSS 的简写属性 all 可以用于同时将这些继承值中的一个应用于(几乎)所有属性。它的值可以是其中任意一个(inheritinitialunsetrevert)。这是一种撤销对样式所做更改的简便方法,以便回到之前已知的起点。

1
2
3
.fix-this {
all: unset;
}

理解层叠

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

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

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

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

资源顺序

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

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

优先级

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

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

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

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

1
2
3
<h2>未设置类的标题</h2>
<h2 class="small">设置了 small 类的标题</h2>
<h2 class="bright">设置了 bright 类的标题</h2>
1
2
3
4
5
6
7
8
9
10
11
12
13
h2 {
font-size: 2em;
color: #000;
font-family: Georgia, "Times New Roman", Times, serif;
}

.small {
font-size: 1em;
}

.bright {
color: rebeccapurple;
}

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

一个选择器的优先级可以说是由三个不同的值(或分量)相加,可以认为是百(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

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

1
2
3
4
5
6
7
8
<div class="container" id="outer">
<div class="container" id="inner">
<ul>
<li class="nav"><a href="#"></a></li>
<li class="nav"><a href="#"></a></li>
</ul>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* 1. 优先级:1-0-1 */
#outer a {
background-color: red;
}

/* 2. 优先级:2-0-1 */
#outer #inner a {
background-color: blue;
}

/* 3. 优先级:1-0-4 */
#outer div ul li a {
color: yellow;
}

/* 4. 优先级:1-1-3 */
#outer div ul .nav a {
color: white;
}

/* 5. 优先级:0-2-4 */
div div li:nth-child(2) a:hover {
border: 10px solid black;
}

/* 6. 优先级:0-2-3 */
div li:nth-child(2) a:hover {
border: 10px dashed black;
}

/* 7. 优先级:0-3-3 */
div div .nav:nth-child(2) a:hover {
border: 10px double black;
}

a {
display: inline-block;
line-height: 40px;
font-size: 20px;
text-decoration: none;
text-align: center;
width: 200px;
margin-bottom: 10px;
}

ul {
padding: 0;
}

li {
list-style-type: none;
}

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

  • 前面两个选择器都是链接背景颜色的样式——第二个赢了使得背景变成蓝色,因为它多了一个 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。

**备注:**每一个选择器类编都有它自己的优先级等级,它们不会被具有较低优先级的选择器覆盖。

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

简单地说:CSS优先级是逐个看的,先看 ID,ID 赢了,后面不用看。

内联样式

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

简单地说,写在 HTML 标签里的 style,优先级高于一切 CSS 选择器。

后面没看了,看不懂。

CSS值和单位

CSS规则包含声明,而声明又由属性和值组成。在 CSS 中使用的每个属性都有一个值类型,用于描述该属性允许拥有何种类型的值。在本课中,我们将了解一些最常用的值类型、它们是什么以及如何起作用。

什么是CSS的值?

在 CSS 规范和 MDN 的属性页上,你将能够发现值类型(value type)的存在,它们被尖括号(<、>)包围,如 <color><length>。当你看到值类型 <color>对特定属性有效时,这意味着你可以使用任何有效的颜色作为该属性的值,如 <color>参考页面所列。

有时值类型和属性可能具有相同或相似的名称——例如 color属性和 <color>数据类型。你可以使用尖括号来区分每种情况下你所研究的具体对象。HTML 元素也使用尖括号,但从上下文应该能清楚你所查看的是哪一个。如果你不确定,可以尝试在 MDN 上搜索它。

**备注:**你还将看到被称为数据类型(data type)的 CSS 值。这些术语基本上是可以互换的——当你在 CSS 中看到被称为数据类型的东西时,它实际上只是另一种表达值类型的方式。术语(value)指的是你所选择的值类型所支持的任何特定表达式。

在下面的例子中,我们使用关键字设置标题的颜色,使用 rgb() 函数设置背景:

1
2
3
4
h1 {
color: black;
background-color: rgb(197 93 161);
}

在 CSS 中,值类型是一种定义可使用的值的集合的方式。这意味着,如果你看到的 <color>是有效的,那么你就不需要纠结要使用哪种颜色值类型——关键字、十六进制值或者 rgb()函数。只要你的浏览器支持,你可以使用任意可用的 <color>值。MDN 上每个值类型的页面都将提供有关浏览器支持的信息。例如,如果你查看 <color>页面,你会看到浏览器兼容性部分列出了不同类型的颜色值以及对它们的支持情况。

让我们来看看你可能经常遇到的一些值和单位类型,并提供一些示例,以便你尝试使用各种值的可能性。

数值、长度和百分比

你可能会发现自己在 CSS 中使用了各种数值数据类型。以下全部归类为数值:

数据类型 描述
<integer> <integer>是一个整数,比如 1024 或 -55。
<number> <number>表示一个十进制数——它可能有小数部分,也可能没有。例如 0.255、128 或 -1.2。
<dimension> <dimension>是一个 <number>。它有一个附加的单位,例如 45deg、5s 或 10px。<dimension>是一个伞形类别,包括 <length><angle><time><resolution>类型。
<percentage> <percentage>表示一些其他值的一部分,例如 50%。百分比值总是相对于另一个量。例如,一个元素的长度相对于其父元素的长度。

长度

最常见的数字类型是 <length>,例如 10px(像素)或 30em。CSS 中有两种类型的长度——相对长度和绝对长度。重要的是要知道它们之间的区别,以便理解它们控制的元素将变得有多大。

绝对长度单位

以下都是绝对长度单位——它们与其他任何东西都没有关系,通常被认为总是相同的大小。

单位 名称 等价换算
cm 厘米 1cm = 37.8px
mm 毫米 1mm = 3.78px
Q 四分之一毫米 1Q = 0.945px = 0.25mm
in 英寸 1in = 96px = 2.54cm
pc 派卡 1pc = 16px ≈ 0.4233cm
pt 1pt = 4/3 px ≈ 0.3527mm
px 像素 1px ≈ 0.2646mm

这些单位大多在用于印刷而非屏幕输出时更有用。例如,我们通常不在屏幕上使用 cm(厘米)。你唯一应该常用的值是 px(像素) 。

相对长度单位

相对长度单位是相对于其他某些东西的。例如:

  • em 相对于本元素的字体大小,或者在用于 font-size 时相对于父元素的字体大小。rem 相对于根元素的字体大小。
  • vhvw 分别相对于视口的高度和宽度。

使用相对单位的好处是,通过一些精心的规划,你可以使文本或其他元素的大小相对于页面上的任何指定的东西进行缩放。要获取可用的相对单位的完整列表,请参阅 <length> 类型的参考页面。

在本节中,我们将探讨一些最常见的相对单位。

探索一个例子

在下面的示例中,你可以看到一些相对长度单位和绝对长度单位的行为。第一个盒子以像素为单位设置 width。作为一个绝对单位,无论其他地方如何变化,这个宽度将保持不变。

第二个盒子的宽度设置为 vw (视口宽度)单位。这个值相对于视口宽度,所以 10vw 是视口宽度的 10%。如果你更改浏览器窗口的宽度,那么框的大小应该会更改。但是,这个示例使用 <iframe>嵌入到页面中,所以这不会起作用。要查看实际情况,你必须在打开示例的浏览器选项卡后尝试该示例。

第三个盒子使用 em 单位。这些是相对于字体大小的。我在包含 <div>的元素上设置了一个 1em 的字体大小,它有一个 .wrapper 类。将这个值更改为 1.5em ,你将看到所有元素的字体大小都增加了,但是只有最后一项会变宽,因为宽度相对于字体大小。

按照上面的说明操作之后,尝试以其他方式处理这些值,看看你将收获什么。

1
2
3
4
5
<div class="wrapper">
<div class="box px">我的宽度为 200px</div>
<div class="box vw">我的宽度为 10vw</div>
<div class="box em">我的宽度为 10em</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.box {
background-color: lightblue;
border: 5px solid darkblue;
padding: 10px;
margin: 1em 0;
}

.wrapper {
font-size: 1em;
}

.px {
width: 200px;
}

.vw {
width: 10vw;
}

.em {
width: 10em;
}

em 和 rem

em (Element Multiplier) 和 rem (Root EM)是调整任何大小(譬如盒子或文本)时最常用的两个相对长度单位。理解它们的工作原理及区别非常重要,尤其是在学习更复杂的主题时,比如样式化文本或 CSS 布局。下面的示例将为你演示这些概念。

下面展示的 HTML 代码是一组嵌套列表。我们总共有两个列表,它们的 HTML 代码相同。唯一的区别在于,第一个列表有一个 ems 类,而第二个列表有一个 rems 类。

首先,我们将 <html>元素的字体大小设置为 16px。

概括地说,em 单位在用于 font-size 时表示“父元素的字体大小”(而在用于其他属性时则表示“自身的字体大小”)。类为 ems 的 <ul>元素内部的 <li>元素的尺寸是从它们的父元素继承的。因此,每一层嵌套都会逐渐变大,因为每个元素的字体大小都被设置为 1.3em —— 即其父元素字体大小的 1.3 倍。

概括地说,rem 单位的意思是“根元素的字体大小”(rem 代表“root em”)。类为 rems 的 <ul>内部的 <li>,其字体大小取决于根元素(<html>)。这意味着每层嵌套不会让字体越变越大。

但是,如果你在 CSS 中更改 <html>字体大小,你将看到所有其他相关内容都发生了更改——用 rem 和 em 设置大小的文本都会变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<ul class="ems">
<li></li>
<li></li>
<li>

<ul>
<li>三 A</li>
<li>
三 B
<ul>
<li>三 B 2</li>
</ul>
</li>
</ul>
</li>
</ul>

<ul class="rems">
<li></li>
<li></li>
<li>

<ul>
<li>三 A</li>
<li>
三 B
<ul>
<li>三 B 2</li>
</ul>
</li>
</ul>
</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
html {
font-size: 16px;
}

.ems li {
font-size: 1.3em;
}

.rems li {
font-size: 1.3rem;
}

行高单位

lh (Line Height,行高) 和 rlh 是类似于 emrem 的相对长度单位。lhrlh 的区别在于,前者是相对于元素自身的行高,而后者是相对于根元素(通常是 <html>)的行高。

使用这些单位,我们可以将盒子的装饰与文本精确对齐。在此示例中,我们通过用 repeating-linear-gradient()的方法,使用 lh 单位创建类似记事本的线条。无论文本的行高是多少,线条始终会从正确的位置开始。

1
2
3
4
5
6
7
8
p {
margin: 0;
background-image: repeating-linear-gradient(
to top,
lightskyblue 0 2px,
transparent 2px 1lh
);
}
1
2
3
4
5
6
7
<p style="line-height: 2em">
蝉鸣拉开盛夏序章,记忆里总藏着几帧特别的画面。若要将今夏浓缩成两个片段,该是山间溯溪的清凉,与老城茶肆的烟火。竹影婆娑的碎石小径上,还留着前夜骤雨打落的合欢花。
</p>

<p style="line-height: 4em">
踩着溪石逆流而上,水花在膝间绽成碎玉。偶遇深潭便纵身跃入,惊起白鹭掠过崖壁青苔。暮色里寻到半山茶铺,粗陶碗中沉浮的野茶,混着柴火灶的焦香,竟比龙井更沁脾腑。藤编矮凳上的裂纹在月光下蜿蜒,竟与白日里溯溪的河道有几分相似。
</p>

没看懂。

百分比

在许多情况下,百分比与长度的处理方法是一样的。百分比的问题在于,它们总是相对于其他值设置的。例如,如果将元素的字体大小设置为百分比,那么它将是元素父元素字体大小的百分比。如果使用百分比作为宽度值,那么它将是父值宽度的百分比。

在下面的示例中,两个用百分比设置尺寸的盒和两个用像素设置尺寸的盒具有相同的类名。它们的宽度分别为 200px 和 40%。

区别在于,第二组的两个盒子位于一个宽度为 400 像素的容器内。第二个宽度为 200px 的盒子与第一个盒子的宽度相同,但第二个宽度为 40% 的盒子现在是 400px 的 40%——比第一个盒子窄了很多!(第一个宽度为40%的盒子的父元素这里没有给出,这里应该是 <body>的宽度,可能就是浏览器窗口的宽度)

尝试更改包装器的宽度或百分比值,看看这是如何工作的:

1
2
3
4
5
6
<div class="box px">我的宽度为 200px</div>
<div class="box percent">我的宽度为 40%</div>
<div class="wrapper">
<div class="box px">我的宽度为 200px</div>
<div class="box percent">我的宽度为 40%</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.box {
background-color: lightblue;
border: 5px solid darkblue;
padding: 10px;
margin: 1em 0;
}
.wrapper {
width: 400px;
border: 5px solid rebeccapurple;
}

.px {
width: 200px;
}

.percent {
width: 40%;
}

下一个示例以百分比设置字体大小。每个 <li>font-size 都设置为 80%,因此嵌套列表项在从父级继承其大小时将逐渐变小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ul>
<li></li>
<li></li>
<li>

<ul>
<li>三 A</li>
<li>
三 B
<ul>
<li>三 B 2</li>
</ul>
</li>
</ul>
</li>
</ul>
1
2
3
li {
font-size: 80%;
}

注意,虽然许多值接受长度或百分比,但也有一些值只接受长度。你可以在 MDN 属性参考页面上看到它能接受哪些值。如果允许的值包括 <length-percentage>,则可以使用长度或百分比。如果允许的值只包含 <length>,则不可能使用百分比。

数字

有些值接受数字,不添加任何单位。接受无单位数字的属性的一个例子是不透明度属性(opacity),它控制元素的不透明度(它有多透明)。此属性接受 0(完全透明)和 1(完全不透明)之间的数字。

在下面的示例中,尝试将 opacity 的值更改为 01 之间的各种小数值,并查看盒子及其内容是如何变得透明或者不透明的:

1
2
3
<div class="wrapper">
<div class="box">我是个具有 opacity 属性的盒子</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.wrapper {
background-image: url(https://mdn.github.io/shared-assets/images/examples/balloons.jpg);
background-repeat: no-repeat;
background-position: bottom left;
padding: 20px;
}

.box {
margin: 40px auto;
width: 230px;
background-color: lightblue;
border: 5px solid darkblue;
padding: 10px;
opacity: 0.6;
}

**备注:**当你在 CSS 中使用数字作为值时,它不应该用引号括起来。

颜色

颜色值可以在 CSS 的许多地方使用,无论是用于指定文本颜色、背景颜色、边框颜色,还是其他更多属性。CSS 提供了多种设置颜色的方式,使你能够控制许多令人兴奋的属性。

现代计算机支持的标准颜色系统是 24 位色,它通过红、绿、蓝三个通道的不同组合来显示约 1670 万种不同的颜色,每个通道有 256 种不同的值(256 x 256 x 256 = 16,777,216)。

在本节中,我们将首先介绍最常见的颜色指定方式:使用关键字、十六进制值和 rgb() 值。我们还将快速了解其他颜色函数,以便你在遇到它们时能够识别,或者尝试不同的颜色应用方式。

你可能会先选择一个调色板,然后在整个项目中使用这些颜色以及你喜欢的颜色指定方式。你可以混合使用不同的颜色模型,但为了保持一致性,通常最好在整个项目中使用相同的颜色声明方法!

颜色关键字

您会在许多 MDN 代码示例中看到颜色关键字(或“命名颜色”)的使用。由于 named-color数据类型包含的颜色值数量非常有限,这些颜色通常不会在生产环境的网站中使用。由于关键字以人类可读的文本值表示颜色,因此在代码示例中使用命名颜色可以清楚地告诉用户预期的颜色是什么,从而使学习者能够专注于所教授的内容。

尝试在下面的实时示例中使用不同的颜色值,以更好地理解它们的工作原理:(class="box one" 表示属于多个类,boxone) 。

1
2
3
4
5
<div class="wrapper">
<div class="box one">古董白(antiquewhite)</div>
<div class="box two">靛色(blueviolet)</div>
<div class="box three">黄绿色(greenyellow)</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box {
padding: 10px;
margin: 0.5em 0;
border-radius: 0.5em;
}
.one {
background-color: antiquewhite;
}

.two {
background-color: blueviolet;
}

.three {
background-color: greenyellow;
}

十六进制RGB值

你可能会遇到的下一类颜色值是十六进制代码。十六进制使用 0-9a-f 共 16 个字符,因此整个范围是 0123456789abcdef。每个十六进制颜色值由一个井号(#)后跟三个或六个十六进制字符组成(例如 #fcc#ffc0cb,三位简写会自动展开为 #ffcccc,每一位重复一次,因此只能表示部分颜色 ),还可以选择性地添加一个或两个十六进制字符来表示前三个或六个字符颜色值的透明度。

当使用十六进制描述 RGB 值时,每一对十六进制字符代表一个通道(红、绿、蓝)的十进制数值,允许我们为每个通道指定 256 个可用值中的任意一个(16 x 16 = 256)。这些值在定义颜色时不如关键字直观,但它们更加通用,因为你可以用它们表示任何 RGB 颜色。

尝试更改以下值,看看颜色如何变化:

1
2
3
4
5
<div class="wrapper">
<div class="box one">#02798b</div>
<div class="box two">#c55da1</div>
<div class="box three">#128a7d</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.box {
padding: 10px;
margin: 0.5em 0;
border-radius: 0.5em;
}

.one {
background-color: #02798b;
}

.two {
background-color: #c55da1;
}

.three {
background-color: #128a7d;
}

RGB值

要直接创建 RGB 值,rgb()函数接受三个参数,分别表示颜色的红、绿和蓝通道值,还可以选择性地添加一个由斜杠 (‘/’) 分隔的第四个参数来表示不透明度,其方式与十六进制值非常相似。RGB 的不同之处在于,每个通道不是由两个十六进制数字表示,而是由一个介于 0 到 255 之间的十进制数字或一个介于 0% 到 100% 之间的百分比表示(但不能混合使用两者)。

让我们使用 RGB 颜色重写上一个示例:

1
2
3
4
5
<div class="wrapper">
<div class="box one">rgb(2 121 139)</div>
<div class="box two">rgb(197 93 161)</div>
<div class="box three">rgb(18 138 125)</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box {
padding: 10px;
margin: 0.5em 0;
border-radius: 0.5em;
}
.one {
background-color: rgb(2 121 139);
}

.two {
background-color: rgb(197 93 161);
}

.three {
background-color: rgb(18 138 125);
}

你可以向 rgb() 传递第四个参数,它代表颜色的 alpha 通道,控制不透明度。如果你把这个值设置为 0,它将使颜色完全透明,而 1 将使它完全不透明。介于两者之间的值会给你带来不同级别的透明度。

1
rgb(255 0 0 / 0.5)

**备注:**在颜色上设置 alpha 通道与使用我们前面看到的 opacity 属性有一个关键区别。当你使用不透明度时,你让元素和它里面的所有东西都不透明,而使用 RGB 与 alpha 参数的颜色只让你指定的颜色不透明。

在下面的示例中,我们为彩色盒子的容器块添加了背景图像。然后,我们将这些盒子设置为不同的不透明度值 —— 请注意,当 alpha 通道值较小时,背景会更多地显示出来。在此示例中,尝试更改 alpha 通道值,看看它如何影响颜色输出。

1
2
3
4
5
<div class="wrapper">
<div class="box one">rgb(2 121 139 / .3)</div>
<div class="box two">rgb(197 93 161 / .7)</div>
<div class="box three">rgb(18 138 125 / .9)</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.wrapper {
background-image: url(https://mdn.github.io/shared-assets/images/examples/balloons.jpg);
padding: 40px 20px;
}

.box {
padding: 10px;
margin: 0.5em 0;
border-radius: 0.5em;
}

.one {
background-color: rgb(2 121 139 / 0.3);
}

.two {
background-color: rgb(197 93 161 / 0.7);
}

.three {
background-color: rgb(18 138 125 / 0.9);
}

图像

<image> 值类型用于图像为有效值的任何地方。它可以是一个通过 url() 函数指向的实际图像文件,也可以是一个渐变。

在下面的例子中,我们演示了一个图像和一个渐变作为 CSS background-image 属性的值。

1
2
<div class="box image"></div>
<div class="box gradient"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.box {
height: 150px;
width: 300px;
margin: 20px auto;
border-radius: 0.5em;
}
.image {
background-image: url(https://mdn.github.io/shared-assets/images/examples/big-star.png);
}

.gradient {
background-image: linear-gradient(
90deg,
rgb(119 0 255 / 39%),
rgb(0 212 255 / 100%)
);
}

最终视觉效果如下,第一个用图片作为背景,默认会 平铺(重复) 填满 300×150 的盒子。第二个使用一个颜色渐变的图像作为背景。

位置

<position> 值类型表示一组二维坐标,用于定位背景图像等元素(通过 background-position)。它可接受诸如 top、left、bottom、right 和 center 等关键字,将元素与二维盒子的特定边界对齐,同时还可以使用长度值来表示从盒子顶部和左侧边缘的偏移量。

一个典型的位置值由两个值组成。第一个值水平地设置位置(top,center, bottom),第二个值垂直地设置位置(left, center, right)。如果只指定一个轴的值,另一个轴将默认为 center。

在以下示例中,我们使用关键字将背景图像定位在容器顶部右侧 40px 处。尝试调整这些值,看看如何移动图像的位置。

个人解释:这里说的不清楚,这两个参数指定的是图片左上角相对于容器左上角的位置,下面例子,第一个right说明水平方向在最右边,第二个40px说明垂直方向从最上面往下移动 40px,也就是垂直方向的偏移量

1
<div class="box"></div>
1
2
3
4
5
6
7
8
9
10
.box {
height: 100px;
width: 400px;
background-image: url(https://mdn.github.io/shared-assets/images/examples/big-star.png);
background-repeat: no-repeat;
background-position: right 40px;
margin: 20px auto;
border-radius: 0.5em;
border: 5px solid rebeccapurple;
}

字符串和标识符

在上面的示例中,我们已经看到了一些使用关键字作为值的地方(例如 <color>关键字,如 red、black、rebeccapurple 和 goldenrod)。这些关键字更准确地描述为标识符,即 CSS 能理解的特殊值。因此,它们不需要加引号——它们不会被当作字符串处理。

在某些情况下,你会在 CSS 中使用字符串。例如,在指定生成的内容时。在这种情况下,值会被加上引号以表明它是一个字符串。在下面的示例中,我们使用了不加引号的颜色关键字以及加了引号的生成内容字符串。

1
<div class="box"></div>
1
2
3
4
5
6
7
8
9
10
11
.box {
width: 400px;
padding: 1em;
border-radius: 0.5em;
border: 5px solid rebeccapurple;
background-color: lightblue;
}

.box::after {
content: "这是个字符串。我知道是这样是因为 CSS 中用引号包裹了它们。";
}

函数

在编程中,函数是一段执行特定任务的代码。函数非常有用,因为你可以编写一次代码,然后多次重复使用它,而不必一遍又一遍地编写相同的逻辑。大多数编程语言不仅支持函数,还提供了方便的常用内置函数,因此你不必从头开始自己编写这些函数。

CSS 也有函数,其工作方式与其他语言中的函数类似。事实上,我们在上面的颜色部分已经看到了 CSS 函数,例如 rgb()hsl() 函数。

除了应用颜色之外,你还可以使用 CSS 函数来完成许多其他任务。例如,变换函数是一种常见的在页面上移动、旋转和缩放元素的方式。你可能会看到 translate() 用于水平或垂直移动某物,rotate() 用于旋转某物,或者 scale() 用于放大或缩小某物。

数学函数

在为项目创建样式时,你可能会从诸如 300px的长度或 200ms的持续时间等数值开始。如果你希望这些值基于其他值发生变化,则需要进行一些数学计算。你可以计算某个值的百分比或将一个数字与另一个数字相加,然后使用结果更新你的 CSS。

CSS 支持数学函数,它允许我们执行计算,而不是依赖于静态值或在 JavaScript 中进行计算。最常见的数学函数之一是 calc(),它允许你执行加法、减法、乘法和除法等操作。

例如,假设我们希望将某个元素的宽度设置为其父容器宽度的 20% 加上 100px。我们无法使用静态值指定此宽度 —— 如果父容器使用百分比宽度(或诸如 em 或 rem 之类的相对单位),则它会根据使用环境以及其他因素(例如用户的设备或浏览器窗口宽度)而变化。但是,我们可以使用 calc()将该元素的宽度设置为其父容器宽度的 20% 加上 100px。20% 基于父容器(.wrapper)的宽度,如果该宽度发生变化,计算结果也会随之变化:

1
2
3
<div class="wrapper">
<div class="box">我的宽度是计算出来的。</div>
</div>
1
2
3
4
5
6
7
8
9
10
.wrapper {
width: 400px;
}
.box {
padding: 1em;
border-radius: 0.5em;
border: 5px solid rebeccapurple;
background-color: lightblue;
width: calc(20% + 100px);
}

这是关于 CSS 中高级数学函数的介绍,以下是这段文字的 OCR 识别结果:

CSS 中还有许多其他数学函数可供使用,例如 min()max()clamp();它们分别允许你从一组值中选择最小、最大或中间值。你还可以使用三角函数,例如 sin()cos()tan(),来计算围绕某点旋转元素的角度,或选择以色相角度(英语)作为参数的颜色。当你需要对某物的移动和外观进行非常精细的控制时,指数函数也可用于动画和过渡。

了解 CSS 函数非常有用,这样当你看到它们时就能识别出来。你应该开始在项目中尝试使用它们——它们将帮助你避免编写自定义或重复的代码来实现你可以通过常规 CSS 获得的结果。

在 CSS 中调整大小

在前面的课程中你已经看到了几种使用 CSS 为页面中元素设定尺寸的方法。在我们设计网页的时候,需要理解这些不同方法之间的差异。在本课程中,我们将总结设定元素尺寸的方法,并定义几个术语,这些内容将会在未来对你有所帮助。

原始尺寸,或固有尺寸

在受 CSS 设置影响之前,HTML 元素有其原始的尺寸。一个直观的例子就是图像。一幅图像的长和宽由这个图像文件自身确定。这个尺寸就是固有尺寸。

如果你把图片放置在网页中的时候没有在 <img> 标签或 CSS 中设置其尺寸,那么将使用其固有尺寸显示。我们给下面的示例图像绘制了一个边框,以便你看出图像文件的长宽。

1
2
3
<img
alt="star"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
1
2
3
img {
border: 5px solid darkblue;
}

一个空的 <div>是没有尺寸的。如果你在你的 HTML 文件中添加一个空 <div>并给予其边框(就像刚才我们为图像做的那样),你会在页面上看到一条线。这是边框被压缩后的效果——它内部没有内容。在我们下面的例子中,边框宽度扩展到整个容器宽度,因为它是块级元素,而块级元素的行为就是这样的。它没有高度,或者说高度为 0,因为内部没有内容。

1
<div class="box"></div>
1
2
3
.box {
border: 5px solid darkblue;
}

在上面的例子中,试着在空元素内部添加些内容。现在边框内包含一些文字了,因为元素的高度由其所含内容高度确定。再强调一次,这就是元素的固有尺寸——由其所包含的内容决定。

设置具体的尺寸

我们当然可以给设计中的元素指定具体大小。当给元素指定尺寸(然后其内容需要适合该尺寸)时,我们将其称为外部尺寸。以上面例子中的 <div>举例——我们可以给它一个具体的 widthheight值,然后不论我们放什么内容进去它都是该尺寸。正如我们在上一课有关溢出的内容中所发现的,如果内容的数量超出了元素可容纳的空间,则设置的高度会导致内容溢出。

1
2
3
4
5
6
7
<div class="wrapper">
<div class="box"></div>
<div class="box">
These boxes both have a height set, this box has content in it which will
need more space than the assigned height, and so we get overflow.
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
body {
font: 1.2em sans-serif;
}
.wrapper {
display: flex;
}

.wrapper > * {
margin: 20px;
}

.box {
border: 5px solid darkblue;
height: 100px;
width: 200px;
}

由于存在溢出问题,在网络上使用长度或百分比固定元素的高度需要非常小心。

使用百分比

许多时候,百分比是长度单位,正如我们在值和单位这节课中讨论的那样,它们常常可与长度互换。当使用百分比时,你需要清楚,它是什么东西的百分比。对于一个处于另外一个容器当中的盒子,如果你给予了子盒子一个百分比作为宽度,那么它指的是父容器宽度的百分比。

1
<div class="box">I have a percentage width.</div>
1
2
3
4
5
6
7
8
body {
font: 1.2em sans-serif;
}

.box {
border: 5px solid darkblue;
width: 50%;
}

这是因为百分比是以包含盒子的块为根据解析的。如果我们的 <div> 没有被指定百分比的值,那么它会占据 100% 的可用空间,因为它是块级别的元素。如果我们给了它一个百分比作为宽度,那么这就是它原来情况下可以占据空间的百分比(因为父元素是 <body> ,默认就是浏览器窗口宽度)。

把百分比作为内外边距

如果你把 margins 和 padding 设置为百分比的话,你会注意到一些奇怪的表现。在下面的例子里,我们有一个盒子,我们给了里面的盒子 10% 的 margin 以及 10% 的 padding。盒子底部和顶部的内外边距,和左右外边距有同样的大小。

1
<div class="box">I have margin and padding set to 10% on all sides.</div>
1
2
3
4
5
6
7
8
9
body {
font: 1.2em sans-serif;
}
.box {
border: 5px solid darkblue;
width: 200px;
margin: 10%;
padding: 10%;
}

或许,你期望元素的上下外边距是其高度的百分比,元素的左右外边距是其宽度的百分比。但情况并非如此!

实际三,所有的外边距或填充都是父元素宽度的 10%。请记住一个事实,当你使用百分比作为元素外边距或填充的单位时,你将得到一个相同尺寸的外边距或填充。

这样设计的原因:页面加载时 宽度通常已知,高度常由内容决定,可能还不存在,因统一用宽度指标。

min- 和 max- 尺寸

除了让万物都有一个确定的大小以外,我们可以让 CSS 给定一个元素的最大或最小尺寸。如果你有一个包含了变化容量的内容的盒子,而且你总是想让它至少有个确定的高度,你应该给它设置一个 min-height属性。盒子就会一直保持大于这个最小高度,但是如果有比这个盒子在最小高度状态下所能容纳的更多内容,那么盒子就会变大。

在以下的示例中,你可以看到两个盒子,两个都有 150 像素的确定高度,左边的盒子有 150 像素高,右边的盒子有需要更多空间才能装下的内容,所以它变得比 150 像素高。

1
2
3
4
5
6
7
8
<div class="wrapper">
<div class="box"></div>
<div class="box">
These boxes both have a min-height set, this box has content in it which
will need more space than the assigned height, and so it grows from the
minimum.
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
body {
font: 1.2em sans-serif;
}
.wrapper {
display: flex;
align-items: flex-start;
}

.wrapper > * {
margin: 20px;
}

.box {
border: 5px solid darkblue;
min-height: 100px;
width: 200px;
}

这在避免溢出的同时并处理变化容量的内容的时候是很有用的。

max-width 的常见用法为,在没有足够空间以原有宽度展示图像时,让图像缩小,同时确保它们不会比这一宽度大。

作为示例,如果你设定一个图像的属性为 width: 100%,而且它的原始宽度小于容器,图像会被强制拉伸以变大,看起来像素更加明显。如果它的原始宽度大于容器,它则会溢出。两种情形都不是你想要看到的。

如果你使用了 max-width: 100%,那么图像可以变得比原始尺寸更小,但是不会大于原始尺寸的 100%。

在下面的示例里,我们使用了两次相同的图片。第一次使用,属性值已设为 width: 100%,位于比图片大的容器里,因此图片拉伸到了与容器相同的宽度;第二次的属性值则设为 max-width: 100%,因此它并没有拉伸到充满容器;第三个盒子再一次包含了相同的图片,同时设定了 max-width: 100% 属性,这时你能看到它是怎样缩小来和盒子大小相适应的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="wrapper">
<div class="box">
<img
alt="star"
class="width"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
</div>
<div class="box">
<img
alt="star"
class="max"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
</div>
<div class="mini-box">
<img
alt="star"
class="max"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.box {
width: 200px;
}
.mini-box {
width: 50px;
}
.width {
width: 100%;
}
.max {
max-width: 100%;
}

这个技术是用来让图片可响应的,所以在更小的设备上浏览的时候,它们会合适地缩放。你无论怎样都不应该用这个技术先载入大原始尺寸的图片,再对它们在浏览器中进行缩放。图像应该合适地调整尺寸,以使它们不会比预计中展示时所需要的最大尺寸大。下载过大的图像会造成你的网站变慢,如果用户使用按量收费的网络连接,会让用户花更多钱。

视口单位

视口,即你在浏览器中看到的部分页面,也是有尺寸的。在 CSS 中,我们有与视口尺寸相关的度量单位,即意为视口宽度的 vw 单位,以及意为视口高度的 vh 单位。使用这些单位,你可以把一些东西做得随用户的视口改变大小。

1vh 等于视口高度的 1%,1vw 则为视口宽度的 1%。你可以用这些单位约束盒子的大小,还有文字的大小。在下面的示例里,我们有一个大小被设为 20vh 和 20vw 的盒子。这个盒子里面有一个字母 A,其 font-size 属性被设成了 10vh。

1
<div class="box">A</div>
1
2
3
4
5
6
7
8
9
10
body {
font-family: sans-serif;
}

.box {
border: 5px solid darkblue;
width: 20vw;
height: 20vh;
font-size: 10vh;
}

如果你改变了 vh 和 vw 的对应值,盒子和字体的大小也会改变;视口大小的变化也会让它们的大小变化,因为它们是依照视口来定大小的。

在你的设计中,根据视口改变物件的大小是很有用的。例如,如果你想要在你其他内容之前,有一个充满整个视口的视觉宣传段落,让你的页面的那个部分有 100vh 高的话,会把剩下的内容推到视口的下面,只有向下滚动文档的时候它们才会出现。

背景与边框

在本课程中,我们将看看可以用 CSS 背景和边框做的一些创造性事情。从添加渐变、背景图像到圆角,背景和边框可以解决 CSS 中的许多样式问题。

CSS的背景样式

CSS background 属性是本课程中我们将遇到的一些普通背景属性的简写表示。如果你在样式表中发现了一个复杂的背景属性,可能会觉得有点难以理解,因为可以同时传入这么多的值:

1
2
3
4
5
6
7
8
9
10
11
.box {
background:
linear-gradient(
105deg,
rgba(255, 255, 255, 0.2) 39%,
rgba(51, 56, 57, 1) 96%
)
center center / 400px 200px no-repeat,
url(image.png) center no-repeat,
rebeccapurple;
}

我们将在本教程的后半部分回到这个简写表示的工作原理,但首先让我们通过查看各个背景属性来了解在 CSS 中可以对背景做哪些不同的事情。

背景颜色

background-color属性定义了 CSS 中任何元素的背景颜色。属性接受任何有效的 <color>值。
background-color可以延伸至元素的内容和内边距盒子的下面(这指的是背景色不仅会填充文字(内容)所在的区域,还会填充元素内部的空白区域(内边距,即 padding),即“内容 + 内边距”范围内的颜色)。

在下面的例子中,我们用各种颜色值为盒子、标题和 <span>元素添加背景色。尝试修改为任何可用的 <color>值。

1
2
3
4
<div class="box">
<h2>背景颜色</h2>
<p>尝试修改背景<span>颜色</span></p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.box {
padding: 0.3em;
background-color: #567895;
}

h2 {
background-color: black;
color: white;
}
span {
background-color: rgb(255 255 255 / 50%);
}

背景图像

background-image属性可以在一个元素的背景中显示一个图像。在下面的例子中,我们有两个盒子,其中一个盒子具有比盒子大的背景图像(balloons.jpg ),另一个盒子具有较小的单个星星的图像(star.png )。

这个示例演示了关于背景图像的两种情形。默认情况下,大图不会缩小以适应盒子,因此我们只能看到它的一个小角(左上角),而小图则是平铺以填充方框

1
2
3
4
<div class="wrapper">
<div class="box a"></div>
<div class="box b"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.wrapper {
display: flex;
}

.box {
width: 200px;
height: 80px;
padding: 0.5em;
border: 1px solid #ccc;
margin: 20px;
}

.a {
background-image: url(https://mdn.github.io/shared-assets/images/examples/balloons.jpg);
}

.b {
background-image: url(https://mdn.github.io/shared-assets/images/examples/star.png);
}

如果除了背景图像外,还指定了背景颜色,则图像将显示在颜色的顶部。尝试向上面的示例添加 background-color 属性,看看效果如何。指定背景颜色为黑色,效果如下(左图无变化,右图有变化)。结论是:背景颜色会铺满整个盒子,背景图片会盖在颜色上面。

控制背景平铺行为

background-repeat属性用于控制图像的平铺行为。可用的值是:

  • no-repeat——阻止背景重复平铺。
  • repeat-x——仅水平方向上重复平铺。
  • repeat-y——仅垂直方向上重复平铺。
  • repeat——默认值,在水平和垂直两个方向重复平铺。

在下面的示例中尝试这些值。我们已经将值设置为 no-repeat,因此你将只能看到一个星星。尝试不同的值(repeat-x和 repeat-y),看看它们的效果如何。

1
<div class="box"></div>
1
2
3
4
.box {
background-image: url(https://mdn.github.io/shared-assets/images/examples/star.png);
background-repeat: no-repeat;
}

调整背景图像的大小

在上面的例子中,我们有一个很大的图像(ballons.jpg),由于它比作为背景的元素大,所以最后被裁剪掉了。在这种情况下,我们可以使用 background-size 属性,它可以设置长度或百分比值,来调整图像的大小以适应背景。

你也可以使用关键字:

  • cover:浏览器将使图像足够大,使它完全覆盖了盒子区域,同时仍然保持其宽高比。在这种情况下,图像的部分区域可能会跳出盒子外。
  • contain:浏览器会将图像调整到适合框内的尺寸。在这种情况下,如果图像的长宽比与盒子的长宽比不同,你可能会在图像的两边或顶部和底部出现空隙。

在下面的示例中,我使用了上面示例中的 ballons.jpg 图片,并使用长度单位来调整方框内的大小。你可以看到这扭曲了图像。

试试这些:

  • 改变用于修改背景大小的长度单位。
  • 去掉长度单位,看看使用 background-size: coverbackground-size: contain 时会发生什么。
  • 如果你的图像小于盒子,可以更改 background-repeat 的值来重复平铺图像。
1
<div class="box"></div>
1
2
3
4
5
.box {
background-image: url(https://mdn.github.io/shared-assets/images/examples/balloons.jpg);
background-repeat: no-repeat;
background-size: 80px 10em;
}

背景图像定位

background-position 属性允许你选择背景图片出现在它所应用的盒子上的位置。这使用了一个坐标系,其中方框的左上角是 (0,0),方框沿水平(x)和垂直(y)轴定位。

备注:默认的 background-position 值是 (0,0)。

最常见的 background-position 值有两个单独的值——一个水平值后面跟着一个垂直值。

你可以使用像 top 和 right 这样的关键字(在 background-image 页面上查找其他的关键字):

1
2
3
4
5
.box {
background-image: url(image.png);
background-repeat: no-repeat;
background-position: top center;
}

或者使用长度和百分比

1
2
3
4
5
.box {
background-image: url(image.png);
background-repeat: no-repeat;
background-position: 20px 10%;
}

你也可以将关键字与长度或百分比混合在一起,在这种情况下,第一个值必须指水平位置或偏移,第二个值指垂直位置。例如:

1
2
3
4
5
.box {
background-image: url(image.png);
background-repeat: no-repeat;
background-position: 20px top;
}

最后,你还可以使用四值语法来指示到盒子的某些边的距离——在本例中,长度单位是与其前面的值的偏移量。所以在下面的 CSS 中,我们将背景定位在距顶部 20px 和右侧 10px 处:

1
2
3
4
5
.box {
background-image: url(image.png);
background-repeat: no-repeat;
background-position: top 20px right 10px;
}

使用下面的示例来处理这些值并在框内移动星星。

1
<div class="box"></div>
1
2
3
4
5
.box {
background-image: url(https://mdn.github.io/shared-assets/images/examples/star.png);
background-repeat: no-repeat;
background-position: 120px 1em;
}

备注:简写属性 background-position用于替代 background-position-xbackground-position-y,它们允许用户分别设置不同坐标轴的值。

渐变背景

当渐变用于背景时,也可以使用像图像一样的 background-image属性设置。

你可以在 MDN 的 <gradient>数据类型页面上,了解更多关于渐变的不同类型,以及使用它们可以做的事情。使用渐变的一个有趣的方法是使用网络上许多 CSS 渐变生成器中的一个,比如这个。你可以创建一个渐变,然后复制和粘贴生成它的源代码。

在下面的示例中尝试一些不同的渐变。在这两个盒子里,我们分别有一个线性渐变,它延伸到整个盒子上,还有一个径向渐变,它有一个固定的大小,因此会重复。

1
2
3
4
<div class="wrapper">
<div class="box a"></div>
<div class="box b"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.wrapper {
display: flex;
}

.box {
width: 400px;
height: 80px;
padding: 0.5em;
border: 1px solid #ccc;
margin: 20px;
}

.a {
background-image: linear-gradient(
105deg,
rgb(0 249 255 / 100%) 39%,
rgb(51 56 57 / 100%) 96%
);
}

.b {
background-image: radial-gradient(
circle,
rgb(0 249 255 / 100%) 39%,
rgb(51 56 57 / 100%) 96%
);
background-size: 100px 50px;
}

删除径向渐变的背景大小,就不会重复了,如下

多个背景图像

也可以有多个背景图像——在单个属性值中指定多个 background-image 值,用逗号分隔每个值

当你这样做时,你可能会出现背景图片相互重叠的情况。背景将分层,最后列出的背景图片位于最下层,而之前的每张图片都堆在代码中紧随其后的那张图片之上。

**备注:**渐变可以与常规的背景图像很好地混合在一起。

其他 background-* 属性也可以像 background-image 一样使用逗号分隔的方式设置:

1
2
3
4
5
6
background-image:
url(image1.png), url(image2.png), url(image3.png), url(image4.png);
background-repeat: no-repeat, repeat-x, repeat;
background-position:
10px 20px,
top right;

不同属性的每个值,将与其他属性中相同位置的值匹配。例如,上面的 image1background-repeat 值将是 no-repeat。但是,当不同的属性具有不同数量的值时,会发生什么情况呢?答案是较小数量的值会循环——在上面的例子中有四个背景图像,但是只有两个背景位置值。前两个位置值将应用于前两个图像,然后它们将再次循环——image3 将被赋予第一个位置值,image4 将被赋予第二个位置值。

我们来试一试。在下面的示例中包含了两个图像。为了演示叠加顺序,请尝试切换哪个背景图像在列表中最先出现。或使用其他属性更改位置、大小或重复值。

1
2
3
<div class="wrapper">
<div class="box"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.wrapper {
display: flex;
}

.box {
width: 500px;
height: 80px;
padding: 0.5em;
border: 1px solid #ccc;
margin: 20px;
}

.box {
background-image:
url(https://mdn.github.io/shared-assets/images/examples/star.png),
url(https://mdn.github.io/shared-assets/images/examples/big-star.png);
}

背景附加

我们为背景提供的另一个选项是指定内容滚动时的滚动方式。这是用 background-attachment 属性控制的,它可以取以下值:

  • scroll(默认):使元素的背景在页面滚动时滚动。如果滚动了元素内容,则背景不会移动。实际上,背景被固定在页面的相同位置,所以它会随着页面的滚动而滚动。
  • fixed:使元素的背景固定在视口上,这样当页面或元素内容滚动时,它就不会滚动。它将始终保持在屏幕上相同的位置。
  • local:将背景固定在它所设置的元素上,所以当你滚动该元素时,背景也随之滚动。

简单的说,如下表

页面滚动时 盒子内部滚动时
scroll(默认) ✅ 背景跟着动 ❌ 背景不动
fixed ❌背景不动 ❌ 背景不动
local ✅ 背景跟着动 ✅ 背景跟着动

background-attachment 属性只有在有内容要滚动时才会有效果,所以我们做了一个示例来演示这三个值之间的区别——看看 background-attachment.html(分别拉动页面的滚动条和元素的滚动条试试效果)。

使用 background 简写属性

正如我在本课开始时提到的,你将经常看到使用 background 属性指定的背景。这种简写形式允许你一次设置所有不同的属性。

如果使用多个背景,则需要为第一个背景指定所有普通属性,然后在逗号后面添加下一个背景。在下面的示例中,我们有一个渐变,它指定大小和位置,然后是指定为 no-repeat 的图像背景,它指定位置,然后是颜色。

这里有一些规则,需要在简写背景图像属性时遵循,例如:

  • background-color 只能在最后一个逗号之后指定。
  • background-size 值只能立即包含在 background-position 之后,用"/"字符分隔,例如:center/80%

查看 background 的 MDN 页面,以查看所有的注意事项。

1
<div class="box"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.box {
width: 500px;
height: 300px;
padding: 0.5em;
background:
linear-gradient(
105deg,
rgb(255 255 255 / 20%) 39%,
rgb(51 56 57 / 100%) 96%
)
center center / 400px 200px no-repeat,
url(https://mdn.github.io/shared-assets/images/examples/big-star.png) center
no-repeat,
rebeccapurple;
}

背景的无障碍考虑

当你把文字放在背景图片或颜色上面时,你应该注意你有足够的对比度让文字对你的访客来说是清晰易读的。如果指定了一个图像,并且文本将被放置在该图像的顶部,你还应该指定一个 background-color,以便在图像未加载时文本也足够清晰。

屏幕阅读器不能解析背景图像,因此背景图片应该只是纯粹的装饰;任何重要的内容都应该是 HTML 页面的一部分,而不是包含在背景中。

边框

在学习盒子模型时,我们发现了边框如何影响盒子的大小。在这节课中,我们将看看如何创造性地使用边框。通常,当我们使用 CSS 向元素添加边框时,我们使用一个简写属性在一行 CSS 中设置边框的颜色、宽度和样式。

我们可以使用 border 为一个框的所有四条边设置边框。

1
2
3
.box {
border: 1px solid black;
}

或者我们可以只设置盒子的一条边,例如:

1
2
3
.box {
border-top: 1px solid black;
}

这些简写等价于:

1
2
3
4
5
.box {
border-width: 1px;
border-style: solid;
border-color: black;
}

也可以使用更加细粒度的属性:

1
2
3
4
5
.box {
border-top-width: 1px;
border-top-style: solid;
border-top-color: black;
}

有各种各样的样式可以用于边框。在下面的例子中,我们为框的四个边使用了不同的边框样式。调整边框样式、宽度和颜色,看看边框是如何工作的。

1
2
3
4
<div class="box">
<h2>边框</h2>
<p>尝试对边框做出调整。</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
* {
padding: 0.2em;
}
.box {
width: 500px;
background-color: #567895;
border: 5px solid #0b385f;
border-bottom-style: dashed;
color: #fff;
}

h2 {
border-top: 2px dotted rebeccapurple;
border-bottom: 1em double rgb(24 163 78);
}

圆角

正如我在本课开始时提到的,你将经常看到使用 border-radius属性和与盒子的每个角相关的普通属性来实现的。两个长度或百分比可以作为一个值,第一个值定义水平半径,第二个值定义垂直半径。在很多情况下,你只会传入一个值,这个值会被用于这两个。

例如,要使一个盒子的四个角都有 10px 的圆角半径:

1
2
3
.box {
border-radius: 10px;
}

或使右上角的水平半径为 1em,垂直半径为 10%(两个值是椭圆,不好看,没必要设置2个):

1
2
3
.box {
border-top-right-radius: 1em 10%;
}

我们在下面的示例中设置了所有四个角,然后更改右上角的值使之不同。你可以使用这些值来更改圆角样式。

1
2
3
4
<div class="box">
<h2>边框</h2>
<p>尝试对边框做出调整。</p>
</div>
1
2
3
4
5
6
7
8
.box {
width: 500px;
height: 110px;
padding: 0.5em;
border: 10px solid rebeccapurple;
border-radius: 1em;
border-top-right-radius: 10% 30%;
}

溢出的内容

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

什么是溢出?

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

CSS 尽力减少“数据损失”

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

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

1
2
3
4
5
6
7
<div class="box">
This box has a height and a width. This means that if there is too much
content to be displayed within the assigned height, there will be an overflow
situation. If overflow is set to hidden then any overflow will not be visible.
</div>

<p>This content is outside of the box.</p>
1
2
3
4
5
.box {
border: 1px solid #333333;
width: 250px;
height: 100px;
}

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

1
<div class="word">Overflow</div>
1
2
3
4
5
.word {
border: 1px solid #333333;
width: 100px;
font-size: 250%;
}

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

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

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

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

overflow 属性

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

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

1
2
3
4
5
6
.box {
border: 1px solid #333333;
width: 250px;
height: 100px;
overflow: hidden;
}

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

如果你移除了下面的盒子里的一些内容,你可以看一下,滚动条是否还会在没有能滚动的东西的时候保留。(试了一下,还会保留,只是不能滚动了,因为不需要滚动了)

1
2
3
4
5
6
.box {
border: 1px solid #333333;
width: 250px;
height: 100px;
overflow: scroll;
}

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

1
2
3
4
5
6
.box {
border: 1px solid #333333;
width: 250px;
height: 100px;
overflow-y: scroll;
}

你也可以用 overflow-x,以在 x 轴方向上滚动,尽管这不是处理长英文词的好办法!如果你真的需要在小盒子里面和长英文词打交道,那么你可能要了解一下 word-break或者 overflow-wrap属性。

1
<div class="word">Overflow</div>
1
2
3
4
5
6
.word {
border: 5px solid #333333;
width: 100px;
font-size: 250%;
overflow-x: scroll;
}

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

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

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

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

1
2
3
4
5
6
.box {
border: 1px solid #333333;
width: 250px;
height: 100px;
overflow: auto;
}

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

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

这段话其实是在为你上一轮的学习做深度补充

你之前学会了用 overflow来防止文字溢出盒子,而这段话解释了背后的原理:因为设置了 overflow值,你实际上创建了一个 BFC(独立房间),正是这个机制把那些调皮的文字乖乖地“收纳”在了盒子内部。

网页设计时不需要的溢出

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

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

图像、媒体和表单元素

在这节课里,我们来看一下,CSS 是如何处理某些特殊元素的。图像、其他媒体和表格元素的表现和普通的盒子有些不同,这取决于你使用 CSS 格式化它们的能力。理解什么可能做到,什么不可能做到能够省些力气,本节课将会聚焦于一些你需要知道的主要的事情上。

可替换元素

图像和视频被描述为可替换元素。这意味着 CSS 不能影响它们的内部布局——而仅影响它们在页面上相对于其他元素的位置。但是,正如我们将看到的,CSS 可以对图像执行多种操作。

某些替换元素(例如图像和视频)也具有宽高比。这意味着它在水平(x)和垂直(y)方向上均具有大小,并且默认情况下将使用文件的固有尺寸进行显示。

调整图像大小

正如你从之前的几节课中所学到的那样,CSS 中万物皆盒。如果你把一张图片放在一个盒子里,而这张图片的原始长和宽比盒子的小或大,那么这张图要么缩在盒子里,要么就从盒子里面溢出。你需要决定如何处理这样的溢出。

下面的示例中有两个盒子,长宽均为 200 像素:

  • 一个包含了一张小于 200 像素的图像,它比图小,并且不会自动拉伸来充满盒子。
  • 另一张图像大于 200 像素,溢出了盒子。
1
2
3
4
5
6
7
8
9
10
11
12
<div class="wrapper">
<div class="box">
<img
alt="star"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
</div>
<div class="box">
<img
alt="balloons"
src="https://mdn.github.io/shared-assets/images/examples/balloons.jpg" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.wrapper {
display: flex;
align-items: flex-start;
}

.wrapper > * {
margin: 20px;
}

.box {
border: 5px solid darkblue;
width: 200px;
}

img {
}

那么该如何处理溢出问题呢?

正如我们在之前的课程所学的那样,一个常用的方法是将一张图片的 max-width 设为 100%。这将会使图片的尺寸小于等于盒子。这个技术也会对其他替换元素(例如 <video>,或者 <iframe> 起作用。

尝试向上面的示例中的 <img> 元素加入 max-width: 100%,你会看到,左边那张小的图像没有变化,而大的图像变小了,恰好装在了盒子里。

你可以选择对容器内的图像作其他方式的处理。例如,你可能想把一张图像调整到能够完全盖住一个盒子的大小。

object-fit 属性可以在这里帮助你。当使用 object-fit 时,替换元素可以以多种方式被调整到合乎盒子的大小。

下面的示例中我们使用了值 cover 来缩小图像,同时维持了图像的原始比例。这样图像就可以充满盒子。但由于比例保持不变,图像多余的一部分将会被盒子裁切掉。

如果我们使用值 contain,图像就会被缩放到足以完整地放到盒子里面的大小。如果它和盒子的比例不同,将会出现“开天窗”的结果。(这两个都会保持图片的原始比例)

你可能也想试试 fill 值,它可以让图像充满盒子,但是不会维持比例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="wrapper">
<div class="box">
<img
alt="balloons"
class="cover"
src="https://mdn.github.io/shared-assets/images/examples/balloons.jpg" />
</div>
<div class="box">
<img
alt="balloons"
class="contain"
src="https://mdn.github.io/shared-assets/images/examples/balloons.jpg" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.wrapper {
display: flex;
align-items: flex-start;
}

.wrapper > * {
margin: 20px;
}

.box {
border: 5px solid darkblue;
width: 200px;
height: 200px;
}

img {
height: 100%;
width: 100%;
}

.cover {
object-fit: cover;
}

.contain {
object-fit: contain;
}

布局中的替换元素

在对替换元素使用各种 CSS 布局时,你可能会发现他们的表现方式与其他元素有一些细节上的差异。例如,flex 或者 grid 布局中,默认情况下元素会被拉伸到充满整块区域。但是图像不会被拉伸,而会对齐到网格区域或者弹性容器的起始处。

你可以在下面的示例中看到这一现象。该示例有一个两列两行的网格容器,里面有四个物件。所有的 <div> 元素有自己的背景色,被拉伸到充满了行和列。但是,图像并没有被拉伸。

1
2
3
4
5
6
7
8
<div class="wrapper">
<img
alt="star"
src="https://mdn.github.io/shared-assets/images/examples/big-star.png" />
<div></div>
<div></div>
<div></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 100px 100px;
gap: 20px;
}

.wrapper > div {
background-color: rebeccapurple;
border-radius: 0.5em;
}

如果你是按序阅读这些课程的,那么你可能还没有看到布局的部分。不过没关系,只要记住替换元素在成为网格或者弹性布局的一部分时,有不同的默认行为就好了。这一默认行为很有必要,因为它避免了替换元素被布局拉伸成奇怪的样子。

为了强制图像拉伸,以充满其所在的网格单元,你必须做类似于下面的事情:

1
2
3
4
img {
width: 100%;
height: 100%;
}

这将会无条件地拉伸图像,所以很可能不会是你想要的。

form 元素

用 CSS 格式化表单元素是一个需要技巧的工作,HTML 表单指南包含了详细的格式化表单元素的指导,我不会在这里复述。本节需要介绍的是一些值得关注的关键基础内容。

很多表单控件是通过 <input> 元素添加到网页上的。该元素定义了简单的表单区域,例如文字输入。更进一步还有 HTML5 新加入的更加复杂的区域,例如颜色和日期撷取器。另外还有一些其他元素,例如用于多行文本输入的 <textarea>,以及那些用来包含和标记表单特定部分的元素,例如 <fieldset><legend>

HTML5 还包含了允许 Web 开发者指定必填区域的特性,甚至还能检验填入内容的类型。如果用户输入了一些不符合要求的内容,或者未填写必填区域,浏览器会显示错误提示。不同的浏览器在给此类元素样式化和自定义方面不尽相同。

样式化文本输入元素

允许文本输入的元素有很多,例如 <input type="text">,及其指定特定类型的元素,如 <input type="email"> 以及 <textarea> 元素,这些都是相当容易样式化的,它们和页面上其他盒子的表现相同。只不过在不同的操作系统和浏览器上访问时这些元素默认的样式化方式可能不同。

在下面的示例中,我们已经将一些文本输入元素用 CSS 样式化了。可以看到,边框、内外边距之类的东西都如期生效了。现在,我们使用属性选择器来指向不同的输入类型,尝试通过改变边框、添加输入区域背景色、改变字体和内边距的方式来改变表单的外观。

1
2
3
4
5
6
<form>
<div><label for="name">Name</label> <input id="name" type="text" /></div>
<div><label for="email">Email</label> <input id="email" type="email" /></div>

<div class="buttons"><input type="submit" value="Submit" /></div>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
input[type="text"],
input[type="email"] {
border: 2px solid #000;
margin: 0 0 1em 0;
padding: 10px;
width: 80%;
}

input[type="submit"] {
border: 3px solid #333;
background-color: #999;
border-radius: 5px;
padding: 10px 2em;
font-weight: bold;
color: #fff;
}

input[type="submit"]:hover,
input[type="submit"]:focus {
background-color: #333;
}

**警告:**你应该谨慎改变表单样式,确保用户仍然能轻松辨认表单元素。原则上,你可以创建一个没有边框和背景的,几乎无法与周围的内容区分开来的输入表单,但这会使辨认和填写变得非常困难。

下面内容先略过。

样式化表格

为 HTML 表格设计样式并不是世界上最光彩的工作,但有时我们不得不这么做。本文将介绍如何让 HTML 表格看起来更美观,并重点介绍一些具体的表格样式设计技巧。

一个典型的HTML表格

让我们从一个典型的 HTML 表格开始——“典型”的意思是说,大多数 HTML 表格都是关于鞋子、天气或员工的。我们决定通过制作英国著名的朋克乐队来让事情变得更有趣。标记如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<table>
<caption>
A summary of the UK's most famous punk bands
</caption>
<thead>
<tr>
<th scope="col">Band</th>
<th scope="col">Year formed</th>
<th scope="col">No. of Albums</th>
<th scope="col">Most famous song</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Buzzcocks</th>
<td>1976</td>
<td>9</td>
<td>Ever fallen in love (with someone you shouldn't've)</td>
</tr>
<tr>
<th scope="row">The Clash</th>
<td>1976</td>
<td>6</td>
<td>London Calling</td>
</tr>

<!-- several other great bands -->

<tr>
<th scope="row">The Stranglers</th>
<td>1974</td>
<td>17</td>
<td>No More Heroes</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row" colspan="2">Total albums</th>
<td colspan="2">77</td>
</tr>
</tfoot>
</table>

由于使用了 scopecaptiontheadtbody等特性,该表格标记得很好,易于风格化和访问。遗憾的是,在屏幕上呈现出来的效果并不好。

仅使用默认的浏览器样式,它看起来很拥挤,难以阅读,而且很无聊。我们需要使用 CSS 来解决这个问题。

为表格添加样式

让我们一起来为我们的表格示例添加样式。

  1. 下载 html 和 2个图片文件,然后将三个文件放在本地计算机的某个工作目录中。

  2. 接下来,创建一个名为 style.css 的新文件,并将其保存在与其他文件相同的目录中。

  3. 将 CSS 链接到 HTML 中,将下面的 HTML 代码放到 HTML 的 <head> 中:

    1
    <link href="style.css" rel="stylesheet" />

间距和布局

我们需要做的第一件事是整理出空间/布局——默认的表格样式是如此的拥挤!要做到这一点,请将以下 CSS 添加到你的 style.css 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 间距 */

table {
table-layout: fixed;
width: 100%;
border-collapse: collapse;
border: 3px solid purple;
}

thead th:nth-child(1) {
width: 30%;
}

thead th:nth-child(2) {
width: 20%;
}

thead th:nth-child(3) {
width: 15%;
}

thead th:nth-child(4) {
width: 35%;
}

th,
td {
padding: 20px;
}

最重要的部分如下:

  • 在你的表格上,给 table-layout 属性设置 fixed 值通常是一个好主意,因为它使表格的行为在默认情况下更可预测。通常情况下,表格列的尺寸会根据所包含的内容大小而变化,这会产生一些奇怪的结果。通过 table-layout: fixed,你可以根据列标题的宽度来规定列的宽度,然后适当地处理它们的内容。因此,我们使用 thead th:nth-child(n)nth-child)选择器,选择了 元素中 的第 n 个子元素)选择了四个不同的标题,并为它们设置了百分比宽度。整个列宽度与列标题的宽度是一样的,这是一种很好的设定表格列尺寸的方式。Chris Coyier 在https://css-tricks.com/fluid-width-variable-height-tables/中更详细地讨论了这一技术。

我们将这些样式与 100% 的 width 结合在一起,这意味着表格将填满它所放置的任何容器,并能很好地响应(尽管还需要再做一些工作,才能在窄屏幕宽度上看起来不错)。

  • border-collapse 属性设置 collapse 值对于任何表格样式的工作来说都是一个标准的最佳实践。默认情况下,当你在表格元素上设置边框时,它们之间将会有间隔,如下图所示:

    这看起来不太好(虽然可能是你想要的样子,谁知道呢?)。使用 border-collapse: collapse; ,让边框合为一条,现在看起来好多了:

  • 我们在整个表格周围设置了 border,这是必要的,因为我们将在表格页眉和页脚后面设置一些边框——当你在表格外面没有一个边界而且以空隙结尾的时候,它看起来很奇怪,而且是不连贯的。

  • 我们在 <th><td> 元素上设置了一些 padding——这些元素使数据项有了一些空间,使表格看起来更加清晰。

此刻,我们的表格看起来好多了:

一些简单的排版

现在我们把文字整理一下。

首先,我们在 Google 字体上找到了一种适合于朋克乐队的字体。如果你愿意,你可以去那里找一个不同的;你只需将我们提供的 <link> 元素和自定义 font-family 声明替换为 Google 字体提供的声明即可。

首先,将下面的 <link> 元素添加到你的 HTML 头部,就在你现有的 <link> 元素之上:

1
2
3
4
<link
href="https://fonts.googleapis.com/css?family=Rock+Salt"
rel="stylesheet"
type="text/css" />

现在将下面的 CSS 添加到你的 style.css 文件,在之前内容后面添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* 排版 */

html {
font-family: "helvetica neue", helvetica, arial, sans-serif;
}

thead th,
tfoot th {
font-family: "Rock Salt", cursive;
}

th {
letter-spacing: 2px;
}

td {
letter-spacing: 1px;
}

tbody td {
text-align: center;
}

tfoot th {
text-align: right;
}

这里没有什么特别的东西。我们通常会对字体样式进行调整,使其更易于阅读:

  • 我们已经设置了一个全局无衬线字体;这纯粹是一种风格上的选择。我们还在 <thead><tfoot> 元素的标题上设置了自定义字体,这是一种很不错的、很有朋克风格的外观。
  • 我们在标题和单元格上设置了一些 letter-spacing,因为我们觉得它有助于提高可读性。再次强调,这主要是一种风格上的选择。
  • 我们在 <tbody> 中的表格单元格中对文本进行了居中对齐,使它们与标题对齐。默认情况下,单元格的 text-align 设置为 left 值,并且标题设置为 center 值,但是通常情况下,让两者对齐看起来更好。标题字体的默认粗体值足以区分它们的外观。
  • 我们在 <tfoot> 中对标题进行了右对齐,以便与它的数据点更好地关联。

结果看起来更整洁一些:

图形和颜色

现在轮到图形和颜色了!因为表格上充满“朋克“和“个性”,我们需要给它再搭配一些鲜艳的造型。别担心,你不必让你的表格“燥起来”,你可以选择一些更巧妙、更有品位的东西。

首先将下面的 CSS 添加到 style.css 文件中,在底部添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 图形和颜色 */

thead,
tfoot {
background: url(leopardskin.jpg);
color: white;
text-shadow: 1px 1px 1px black;
}

thead th,
tfoot th,
tfoot td {
background: linear-gradient(to bottom, rgb(0 0 0 / 10%), rgb(0 0 0 / 50%));
border: 3px solid purple;
}

同样,对于表格这里没有什么特别的,但有几件事值得注意。

我们已经将 background-image 声明添加到 <thead><tfoot>,并将页眉和页脚的所有文本颜色 color 更改为白色(并给它一个 text-shadow),这样它的可读性就更好了。你应该确保你的文字与你的背景形成鲜明的对比,使得它是可读的。

我们还为 <th><td> 添加了一个线性渐变(从顶部到底部,颜色从10%透明度的黑色过渡到50%透明度的黑色),在页眉和页脚中添加了一个漂亮的纹理,同时也为这些元素提供了一个明亮的紫色边框。有多个嵌套的元素是很有用的,这样你就可以将样式层叠在一起。是的,我们可以通过设置多组背景图像属性值来在 <thead><tfoot> 元素上同时使用背景图像和线性渐变,但是我们决定分开使用,因为考虑到不支持多个背景图像或线性渐变的老浏览器。

斑马条纹

我们想用一个单独的部分来展示如何实现斑马条纹——通过改变不同数据行的颜色,使表格中交替行不同的数据行可以更容易地进行解析和读取。将下面的 CSS 添加到你的 style.css 文件底部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 斑马条纹 */

tbody tr:nth-child(odd) {
background-color: #ff33cc;
}

tbody tr:nth-child(even) {
background-color: #e495e4;
}

tbody tr {
background-image: url(noise.png);
}

table {
background-color: #ff33cc;
}
  • 你在前面看到了 :nth-child 选择器用于选择特定的子元素。它也可以用一个公式作为参数,来选择一个元素序列。公式 2n-1 会选择所有奇数的子元素(第一个、第三个、第五个等),而公式 2n 会选择所有偶数的子元素(第二个、第四个、第六个等)。我们在代码中使用了 oddeven 关键字,这与前面提到的公式作用完全相同。在这里,我们给奇数行和偶数行不同的(醒目的)颜色。
  • 我们还为所有的行添加了一个重复的噪点背景色块(一个半透明的 .png,有一点视觉上的扭曲)来提供一些纹理。
  • 最后,我们给整个表格提供了一个纯的背景颜色,这样不支持 :nth-child 选择器的浏览器仍然有它们的正文行的背景(老浏览器兼容性)。

这种颜色爆炸的结果如下:

现在,这可能有点过头不符合你的品味,但我们在这里想要指出的一点是,表格并非只能是枯燥无味的,学术性的。

样式化标题

对我们的表格还有最后一点处理——样式化标题。要做到这一点,请将以下内容添加到你的 style.css 文件底部:

1
2
3
4
5
6
7
8
9
10
11
/* 标题 */

caption {
font-family: "Rock Salt", cursive;
padding: 20px;
font-style: italic;
caption-side: bottom;
color: #666;
text-align: right;
letter-spacing: 1px;
}

这里没有什么值得注意的地方,除了 caption-side属性,它被设置为 bottom值。这就导致标题被放置在表格的底部,与其他声明一起提供了最后的外观。

调试 CSS

你有时写 CSS 会碰到这样的问题:结果看起来和你想的不太一样。你可能会认为 selector(选择器)匹配到了元素但是什么都没发生,还可能会觉得盒子的大小与你想的有出入。这篇文章会教你着手调试 CSS,向你展示现代浏览器中的 DevTools 是怎样让你更方便地获悉发生了什么。

如何使用浏览器开发者工具

What are browser developer tools 解释了如何在不同的浏览器和平台上打开这些工具。你可能会选择大部分时间在某个浏览器上开发去熟悉里面的工具,不过你还是有必要了解如何在其他浏览器中打开同样的工具。要是你看到多个浏览器间不同的渲染结果,这就会很方便了。

我们这节课会重点看用于处理 CSS 的开发者工具中的一些有用特性。为此,我会用一个示例。想跟着学习的话,就在新标签页加载该网页吧,并打开开发者工具(上述文章对该工具有描述)。

比较DOM和 View Source

刚接触开发者工具的人可能会在这个地方产生失误:网页源码(或服务器端的 HTML 文件)显示的和开发者工具的 HTML Pane 显示的相比起来不太一样。通过 View Source,内容看起来差不多,然而一些差异还是存在的。

浏览器在渲染的 DOM 中已为你纠正了一些错误的 HTML 部分。如果你错误地闭合了元素(比如开始标签是 <h2>,结束标签是 </h3>。),浏览器会尽力弄清你的意图,之后 DOM 中的 HTML 就以<h2>起始,以 </h2> 结束了。浏览器还会处理好 HTML 文档,JavaScript 做出的更改都会由 DOM 表现出来。

相比之下,View Source 就是服务器端的 HTML 源码。DevTools 内的 HTML tree 展示了浏览器任意时间的渲染结果,让你深入理解正在发生什么。

审查CSS

从页面上选择一个元素,可以通过以下方法:右键该元素,选择审查元素(Inspect);从 DevTools 左侧 HTML tree 中选择该元素。试试选择 class 为 box1 的元素吧,它是页面上的第一个元素,周围画有边框。

下面看不懂了。

CSS文本样式

基本文本和字体样式

在这篇文章中,我们将带你开始掌握 CSS 的文字样式的旅程。这里我们将详细介绍文本/字体样式的所有基本原理,包括设置文字的粗细,字体和样式,文字的属性简写,文字的对齐,和其他效果,以及行和字母间距。

CSS中的文字样式涉及什么?

正如你已经在你使用 HTML 和 CSS 完成工作时所经历的一样,元素中的文本是布置在元素的内容框中。以内容区域的左上角作为起点 (或者是右上角,是在 RTL 语言的情况下),一直延续到行的结束部分。一旦达到行的尽头,它就会进到下一行,然后继续,再接着下一行,直到所有内容都放入了盒子中。文本内容表现地像一些内联元素,被布置到相邻的行上,除非到达了行的尽头,否则不会换行,或者你想强制地,手动地造成换行的话,你可以使用 <br> 元素。

备注: 如果上面的段落让你感到困惑,没关系,在继续之前,可以重新看看我们的 Box model 文件,复习盒模型的理论。

用于样式文本的 CSS 属性通常可以分为两类,我们将在本文中分别观察。

  • 字体样式: 作用于字体的属性,会直接应用到文本中,比如使用哪种字体,字体的大小是怎样的,字体是粗体还是斜体,等等。
  • 文本布局风格: 作用于文本的间距以及其他布局功能的属性,比如,允许操纵行与字之间的空间,以及在内容框中,文本如何对齐。

备注: 请记住,包含在元素中的文本是作为一个单一的实体。你不能将文字其中一部分选中或添加样式,如果你要这么做,那么你必须要用适合的元素来包装它们,比如 (<span> 或者 <strong>), 或者使用伪元素,像 ::first-letter (选中元素文本的第一个字母), ::first-line (选中元素文本的第一行),或者 ::selection (当前光标双击选中的文本)

字体

我们直接来看看样式字体的属性。在这个例子中,我们会在一个相同的 HTML 示例中应用一些不同的 CSS 属性,就像这样:

1
2
3
4
5
6
7
8
9
10
11
<h1>Tommy the cat</h1>

<p>I remember as if it were a meal ago...</p>

<p>
Said Tommy the Cat as he reeled back to clear whatever foreign matter may have
nestled its way into his mighty throat. Many a fat alley rat had met its
demise while staring point blank down the cavernous barrel of this awesome
prowling machine. Truly a wonder of nature this urban predator — Tommy the cat
had many a story to tell. But it was a rare occasion such as this that he did.
</p>

颜色

color属性设置选中元素的前景内容的颜色 (通常指文本,不过也包含一些其他东西,或者是使用 text-decoration属性放置在文本下方或上方的线 (underline overline)。

color也可以接受任何合法的 CSS 颜色单位,比如:

1
2
3
p {
color: red;
}

这将导致段落变为红色,而不是标准的浏览器默认的黑色,如下所示:

字体种类

要在你的文本上设置一个不同的字体,你可以使用 font-family属性,这个允许你为浏览器指定一个字体 (或者一个字体的列表),然后浏览器可以将这种字体应用到选中的元素上。浏览器只会把在当前机器上可用的字体应用到当前正在访问的网站上;如果字体不可用,那么就会用浏览器默认的字体代替 default font. 下面是一个简单的例子:

1
2
3
p {
font-family: arial;
}

这段语句使所有在页面上的段落都采用 arial 字体,这个字体可在任何电脑上找到。

网络安全字体

说到字体可用性,只有某几个字体通常可以应用到所有系统,因此可以毫无顾忌地使用。这些都是所谓的 网页安全字体

大多数时候,作为网页开发者,我们希望对用于显示我们的文本内容的字体有更具体的控制。问题在于,需要一个方法来知道当前正在浏览我们的网站网页的电脑,它有哪些可用字体。我们并不是总能在每种情况下都知道这一点,但是网络安全字体在几乎所有最常用的操作系统(Windows,Mac,最常见的 Linux 发行版,Android 和 iOS 版本)中都可用。

实际的 Web 安全字体列表将随着操作系统的发展而改变,但是可以认为下面的字体是网页安全的,至少对于现在来说 (它们中的许多都非常流行,这要感谢微软在 90 年代末和 21 世纪初期的倡议 Core fonts for the Web):

字体名称 泛型 注意
Arial sans-serif 通常认为最佳做法还是添加 Helvetica 作为 Arial 的首选替代品,尽管它们的字体面几乎相同,但 Helvetica 被认为具有更好的形状,即使 Arial 更广泛地可用。
Courier New monospace 某些操作系统有一个 Courier New 字体的替代(可能较旧的)版本叫 Courier。使用 Courier New 作为 Courier 的首选替代方案,被认为是最佳做法。
Georgia serif
Times New Roman serif 某些操作系统有一个 Times New Roman 字体的替代(可能较旧的)版本叫 Times。使用 Times 作为 Times New Roman 的首选替代方案,被认为是最佳做法。
Trebuchet MS sans-serif 你应该小心使用这种字体——它在移动操作系统上并不广泛。
Verdana sans-serif

默认字体

CSS 定义了 5 个常用的字体名称(实际不是名称,网址说错了,这应该是通用字体类型):serif, sans-serif, monospace, cursive, 和 fantasy. 这些都是非常通用的,当使用这些通用名称时,使用的字体完全取决于每个浏览器,而且它们所运行的每个操作系统也会有所不同。这是一种糟糕的情况,浏览器会尽力提供一个看上去合适的字体。 serif, sans-serifmonospace 是比较好预测的,默认的情况应该比较合理,另一方面,cursivefantasy 是不太好预测的,我们建议使用它们的时候应该稍微注意一些,多多测试。

字体栈

由于你无法保证你想在你的网页上使用的字体的可用性 (甚至一个网络字体可能由于某些原因而出错), 你可以提供一个字体栈 (font stack),这样的话,浏览器就有多种字体可以选择了。只需包含一个 font-family 属性,其值由几个用逗号分离的字体名称组成。比如

1
2
3
p {
font-family: "Trebuchet MS", Verdana, sans-serif;
}

在这种情况下,浏览器从列表的第一个开始,然后查看在当前机器中,这个字体是否可用。如果可用,就把这个字体应用到选中的元素中。如果不可用,它就移到列表中的下一个字体,然后再检查。

在字体栈的最后提供一个合适的通用的字体名称是个不错的办法,这样的话,即使列出的字体都无法使用,浏览器至少可以提供一个还算合适的选择。为了强调这一点,如果没有其他选项可用,那么段落将被赋予浏览器的默认衬线字体 - 通常是 Time New Roman - 这对于 sans-serif 字体是不利的!

**备注:**有一些字体名称不止一个单词,比如Trebuchet MS ,那么就需要用引号包裹。

让我们把它添加到之前的例子上,给段落一个 sans-serif 的字体。

1
2
3
4
p {
color: red;
font-family: Helvetica, Arial, sans-serif;
}

这给我们以下结果

字体大小

字体大小(通过 font-size 属性设置)最常用的单位是:

  • px (像素): 将像素的值赋予给你的文本。这是一个绝对单位,它导致了在任何情况下,页面上的文本所计算出来的像素值都是一样的。
  • em 是一个相对单位,1em 等于当前元素的父元素所设置的字体大小。在多层嵌套且各级字体大小不一的结构中,em 的计算会变得复杂,但这完全可行。之所以使用这种看似麻烦的单位,是因为它非常灵活:一旦你习惯了这种思维方式,就可以用 em 统一缩放页面中的所有元素(不仅限于文本)。构建一个全站统一使用 em 的网站,后期维护反而会更加简单。
  • rem: 这个单位的效果和 em 差不多,除了 1 rem 等于 HTML 中的根元素的字体大小,(i.e. <html>),而不是父元素。这可以让你更容易计算字体大小,但是遗憾的是, rem 不支持 Internet Explorer 8 及以下的版本。如果你的项目需要支持较老的浏览器,你可以坚持使用 empx,或者是 polyfill 就像 rem-unit-polyfill. (这个单位在“CSS 的值和单位”一节也有讲解)

元素的 font-size 属性是从该元素的父元素继承的。所以这一切都是从整个文档的根元素——<html> 开始,浏览器的 font-size 标准设置的值为 16px。在根元素中的任何段落 (或者那些浏览器没有设置默认大小的元素),会有一个最终的大小值:16px。其他元素也许有默认的大小,比如 <h1> 元素有一个 2em 的默认值,所以它的最终大小值为 32px。当你开始更改嵌套元素的字体大小时,事情会变得棘手。比如,如果你有一个 <article> 元素在你的页面上,然后设置它的 font-size 为 1.5em (通过计算,可以得到大小为 24px),然后想让 <article> 元素中的段落获得一个计算值为 20px 的大小,那么你应该使用多少 em。

1
2
3
4
5
6
<!-- document base font-size is 16px -->
<article>
<!-- If my font-size is 1.5em -->
<p>My paragraph</p>
<!-- How do I compute to 20px font-size? -->
</article>

你需要将 em 的值设置为 20/24, 或者 0.83333333em. 这个计算可能比较复杂,所以当你设置的时候,你需要仔细一些。如果可以使用 rem 的话,那实现起来就变得简单不少,避免在可能的情况下设置容器元素的字体大小。

一个简单的 size 示例

当调整你的文本大小时,将文档 (document) 的基础 font-size 设置为 10px 往往是个不错的主意,这样之后的计算会变得简单,所需要的 ®em 值就是想得到的像素的值除以 10,而不是 16。做完这个之后,你可以简单地调整在你的 HTML 中你想调整的不同类型文本的字体大小。在样式表的指定区域列出所有font-size的规则集是一个好主意,这样它们就可以很容易被找到。

我们的新结果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
html {
font-size: 10px;
}

h1 {
font-size: 2.6rem;
}

p {
font-size: 1.4rem;
color: red;
font-family: Helvetica, Arial, sans-serif;
}

字体样式、字体粗细、文本转换和文本装饰

CSS 提供了 4 种常用的属性来改变文本的样子:

以下是图片中的文字内容提取:

  • font-style: 用来打开和关闭文本 italic (斜体)。可能的值如下 (你很少会用到这个属性,除非你因为一些理由想将斜体文字关闭斜体状态):

    • normal : 将文本设置为普通字体 (将存在的斜体关闭)
    • italic : 如果当前字体的斜体版本可用,那么文本设置为斜体版本;如果不可用,那么会利用 oblique 状态来模拟 italics。
    • oblique : 将文本设置为斜体字体的模拟版本,也就是将普通文本倾斜的样式应用到文本中。
  • font-weight: 设置文字的粗体大小。这里有很多值可选 (比如 -light, -normal, -bold, -extrabold, -black, 等等), 不过事实上你很少会用到 normalbold 以外的值:

    • normal, bold: 普通或者加粗的字体粗细
    • lighter, bolder: 将当前元素的粗体设置为比其父元素粗体更细或更粗一步。 100 – 900: 数值粗体值,如果需要,可提供比上述关键字更精细的粒度控制。
  • text-transform: 允许你设置要转换的字体。值包括:

    • none: 防止任何转型。
    • uppercase: 将所有文本转为大写。
    • lowercase: 将所有文本转为小写。
    • capitalize: 转换所有单词让其首字母大写。
    • full-width: 将所有字形转换成全角,即固定宽度的正方形,类似于等宽字体,允许拉丁字符和亚洲语言字形(如中文,日文,韩文)对齐。

你应该注意到 text-decoration可以一次接受多个值,如果你想要同时添加多个装饰值,比如 text-decoration: underline overline;。同时注意 text-decoration是一个缩写形式,它由 text-decoration-linetext-decoration-styletext-decoration-color构成。你可以使用这些属性值的组合来创建有趣的效果,比如 text-decoration: line-through red wavy.

我们来看一下这几个属性添加到我们的例子中:

我们的新结果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
html {
font-size: 10px;
}

h1 {
font-size: 2.6rem;
text-transform: capitalize;
}

h1 + p {
font-weight: bold;
}

p {
font-size: 1.4rem;
color: red;
font-family: Helvetica, Arial, sans-serif;
}

文字阴影

你可以为你的文本应用阴影,使用text-shadow属性。这最多需要 4 个值,如下例所示:

1
text-shadow: 4px 4px 5px red;

4 个属性如下:

  1. 阴影与原始文本的水平偏移(向右4像素),可以使用大多数的 CSS 单位 length and size units,但是 px 是比较合适的。这个值必须指定。
  2. 阴影与原始文本的垂直偏移(向下4像素);效果基本上就像水平偏移,除了它向上/向下移动阴影,而不是左/右。这个值必须指定。
  3. 模糊半径 - 更高的值意味着阴影分散得更广泛。如果不包含此值,则默认为 0,这意味着没有模糊(它控制阴影是“硬边”还是“软边”,以及阴影扩散得有多大)。可以使用大多数的 CSS 单位 length and size units.
  4. 阴影的基础颜色,可以使用大多数的 CSS 颜色单位 CSS color unit. 如果没有指定,默认为 black.

**备注:**正偏移值可以向右移动阴影,但也可以使用负偏移值来左右移动阴影,例如 -1px -1px.

多种阴影

你可以通过包含以逗号分隔的多个阴影值,将多个阴影应用于同一文本,例如:

1
2
3
4
5
text-shadow:
-1px -1px 1px #aaa,
0px 4px 1px rgba(0, 0, 0, 0.5),
4px 4px 5px rgba(0, 0, 0, 0.7),
0px 0px 7px rgba(0, 0, 0, 0.4);

如果我们把这个样式应用到我们 “Tommy the cat” 示例中的 <h1> 元素,就像这样:

文本布局

有了基本的字体属性,我们来看看我们可以用来影响文本布局的属性。

文本对齐

text-align 属性用来控制文本如何和它所在的内容盒子对齐。可用值如下,并且在与常规文字处理器应用程序中的工作方式几乎相同:

  • left: 左对齐文本。
  • right: 右对齐文本。
  • center: 居中文字
  • justify: 使文本展开,改变单词之间的差距,使所有文本行的宽度相同。你需要仔细使用它,它可以看起来很可怕。特别是当应用于其中有很多长单词的段落时。如果你要使用这个,你也应该考虑一起使用别的东西,比如 hyphens,打破一些更长的词语。

如果我们应用 text-align: center; 到我们例子中的 <h1> 元素中,结果如下

行高

line-height 属性设置文本每行之间的高,可以接受大多数单位 length and size units,不过也可以设置一个无单位的值,作为乘数,通常这种是比较好的做法。无单位的值乘以 font-size 来获得 line-height。当行与行之间拉开空间,正文文本通常看起来更好更容易阅读。推荐的行高大约是 1.5-2 (双倍间距。) 所以要把我们的文本行高设置为字体高度的 1.5 倍,你可以使用这个:

1
line-height: 1.5;

把这个样式应用到我们示例中的 <p> 元素,结果如下:

字母和单词间距

letter-spacing 和 word-spacing 属性允许你设置你的文本中的字母与字母之间的间距、或是单词与单词之间的间距。你不会经常使用它们,但是可能可以通过它们,来获得一个特定的外观,或者让较为密集的文字更加可读。它们可以接受大多数单位 length and size units.

所以作为例子,如果我们把这个样式应用到我们的示例中的 <p> 段落的第一行:

1
2
3
4
p::first-line {
letter-spacing: 2px;
word-spacing: 4px;
}

Font 简写

许多字体的属性也可以通过 font 的简写方式来设置。这些是按照以下顺序来写的:font-style, font-variant, font-weight, font-stretch, font-size, line-height, and font-family.

如果你想要使用 font 的简写形式,在所有这些属性中,只有 font-size 和 font-family 是一定要指定的。

font-size 和 line-height 属性之间必须放一个正斜杠。

一个完整的例子如下所示:

1
2
3
4
font:
italic normal bold normal 3em/1.5 Helvetica,
Arial,
sans-serif;

为列表添加样式

一个简单的列表示例

首先,让我们看一个简单的列表示例。文章中我们将看到无序、有序和描述列表——它们都具有相似的样式特性,而某些特性却又各不相同。

示例列表的 HTML 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<h2>Shopping (unordered) list</h2>

<p>
Paragraph for reference, paragraph for reference, paragraph for reference,
paragraph for reference, paragraph for reference, paragraph for reference.
</p>

<ul>
<li>Hummus</li>
<li>Pita</li>
<li>Green salad</li>
<li>Halloumi</li>
</ul>

<h2>Recipe (ordered) list</h2>

<p>
Paragraph for reference, paragraph for reference, paragraph for reference,
paragraph for reference, paragraph for reference, paragraph for reference.
</p>

<ol>
<li>Toast pita, leave to cool, then slice down the edge.</li>
<li>
Fry the halloumi in a shallow, non-stick pan, until browned on both sides.
</li>
<li>Wash and chop the salad.</li>
<li>Fill pita with salad, hummus, and fried halloumi.</li>
</ol>

<h2>Ingredient description list</h2>

<p>
Paragraph for reference, paragraph for reference, paragraph for reference,
paragraph for reference, paragraph for reference, paragraph for reference.
</p>

<dl>
<dt>Hummus</dt>
<dd>
A thick dip/sauce generally made from chick peas blended with tahini, lemon
juice, salt, garlic, and other ingredients.
</dd>
<dt>Pita</dt>
<dd>A soft, slightly leavened flatbread.</dd>
<dt>Halloumi</dt>
<dd>
A semi-hard, unripened, brined cheese with a higher-than-usual melting
point, usually made from goat/sheep milk.
</dd>
<dt>Green salad</dt>
<dd>That green healthy stuff that many of us just use to garnish kebabs.</dd>
</dl>

现在,如果你去到示例的展示页面,并使用浏览器开发者工具查看那些列表元素,你会注意到若干个默认的样式预设值:

  • <ul><ol> 元素含有 16px (1em)的顶部和底部 margin 和 40px (2.5em)的 padding-left
  • 列表项(<li> 元素)默认是没有设置间距的。
  • <dl> 元素设置含有 16px (1em)的顶部和底部 margin,但不含内边距。
  • <dd> 元素含有 40px (2.5em)的 margin-left
  • 在参考中提到的 `` 元素设置含有 16px (1em)的顶部和底部 margin ——与其他的列表类型相同。

处理列表间距

当为列表添加样式时,你需要调整样式,使其保持与周围元素相同的垂直间距(例如段落和图片,有时称为垂直节奏)和相互间的水平间距。

用于文本样式和间距的 CSS 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* 通用样式 */

html {
font-family: Helvetica, Arial, sans-serif;
font-size: 10px;
}

h2 {
font-size: 2rem;
}

ul,
ol,
dl,
p {
font-size: 1.5rem;
}

li,
p {
line-height: 1.5;
}

/* 描述列表样式 */

dd,
dt {
line-height: 1.5;
}

dt {
font-weight: bold;
}
  • 第一条规则集设置一个网站字体,基准字体大小为 10px。页面上的所有内容都将继承该规则集。
  • 规则集 2 和 3 为标题、不同的列表类型和段落以及设置了相对字体大小(这些列表的子元素将会继承该规则集),这就意味着每个段落和列表都将拥有相同的字体大小和上下间距,有助于保持垂直间距一致。
  • 规则集 4 在段落和列表项目上设置相同的 line-height,因此段落和每个单独的列表项目将在行之间具有相同的间距。这也将有助于保持垂直间距一致。
  • 规则集 5 和 6 适用于描述列表。我们在描述列表的术语和其描述上设置与段落和列表项相同的 line-height。再次强调一遍,这里很好地实现了一致性!我们还使描述术语具有粗体字体,因此它们在视觉上脱颖而出。

列表特定样式

现在我们来了解一下列表的一般间距,我们来研究一些列表具有的特定属性。我们从三个属性开始了解,这三个属性可以在 <ul><ol>元素上设置:

  • list-style-type:设置用于列表的项目符号的类型,例如无序列表的方形或圆形项目符号,或有序列表的数字、字母或罗马数字。
  • list-style-position:设置在每个项目开始之前,项目符号是出现在列表项内,还是出现在其外(防止盒子里面还是外面,默认在外面)。
  • list-style-image:允许为项目符号使用自定义图片,而不是简单的方形或圆形。

(对应的英文属性名为:list-style-type, list-style-position, list-style-image)

项目符号样式

像上面所提及的,list-style-type 属性允许你设置项目符号的类型,在我们的示例中,我们在有序列表上设置了大写罗马数字:

1
2
3
ol {
list-style-type: upper-roman;
}

效果显示如下:

项目符号位置

list-style-position 设置在每个项目开始之前,项目符号是出现在列表项内,还是出现在其外。如上所示,默认值为 outside,这使项目符号位于列表项之外。

如果值设置为 inside,项目符号则位于行内。

1
2
3
4
ol {
list-style-type: upper-roman;
list-style-position: inside;
}

使用自定义的项目符号图片

list-style-image 允许使用自定义图片作为项目符号,如下

1
2
3
ul {
list-style-image: url(star.svg);
}

然而,这个属性在控制项目符号的位置,大小等方面是有限的。最好使用 background系列属性。在这里我们仅做一点尝试!

在我们的示例中,我们的无序列表最终样式像这样(在之前所见的顶部):

1
2
3
4
5
6
7
8
9
10
11
12
ul {
padding-left: 2rem;
list-style-type: none;
}

ul li {
padding-left: 2rem;
background-image: url(star.svg);
background-position: 0 0;
background-size: 1.6rem 1.6rem;
background-repeat: no-repeat;
}

我们做了这些事情:

  • <ul>padding-left 从默认的 40px 下调为 20px,然后在列表项上设置相同的数值。这就是说,整个列表项仍然排列在列表中,但是列表项产生了一些用于背景图像的填充。如果我们没有设置填充,背景图像将与列表项文本重叠,这看起来会很乱。
  • list-style-type 设置为 none,以便默认情况下不会显示项目符号。我们将使用 background 属性来代替项目符号。
  • 为每个无序列表项插入项目符号,相关的属性如下:
    • background-image:充当项目符号的图片文件的参照路径。
    • background-position:这定义了所选元素背景中的图像将出现在哪里——在我们的示例中设置为 0 0,这意味着项目符号将出现在每个列表项的最左上侧。
    • background-size:设置背景图片的大小。理想条件下,我们想要项目符号与列表项的大小相同(比列表项稍大或稍小亦可)。我们使用的尺寸为 1.6rem (16px),它非常吻合我们为项目符号设置的 20px 的填充,16px 加上 4px 的空格间距,可以使项目符号和列表项文本效果更好。
    • background-repeat:默认条件下,背景图片不断复制直到填满整个背景空间,在我们的示例中,背景图片只需复制一次,所以我们设置值为 no-repeat。

效果显示如下:

list-style 简写

上述提到的三种属性可以用一个单独的简写属性 list-style 来设置。例如,以下 CSS:

1
2
3
4
5
ul {
list-style-type: square;
list-style-image: url(example.png);
list-style-position: inside;
}

可以被如下方式代替:

1
2
3
ul {
list-style: square url(example.png) inside;
}

属性值可以任意顺序排列,你可以设置一个,两个或者所有三个值(不包括的属性使用的默认值是 discnoneoutside),如果指定了 typeimage,如果由于某种原因导致图像无法加载,则 type 将用作回退(如果图片加载失败/不存在,则自动使用disc (实心圆))。

管理列表计数

有时,你可能想在有序列表上进行不同的计数方式。例如:从 1 以外的数字开始,或从后面倒数,或者按大于 1 的步长计数。HTML 和 CSS 有一些工具可以帮助你:

start

start 属性允许你从1以外的数字开始计数。以下示例:

1
2
3
4
5
6
7
8
<ol start="4">
<li>Toast pita, leave to cool, then slice down the edge.</li>
<li>
Fry the halloumi in a shallow, non-stick pan, until browned on both sides.
</li>
<li>Wash and chop the salad.</li>
<li>Fill pita with salad, hummus, and fried halloumi.</li>
</ol>

输出的结果如下:

reversed

reversed 属性将列表反向计数。以下示例:

1
2
3
4
5
6
7
8
<ol start="4" reversed>
<li>Toast pita, leave to cool, then slice down the edge.</li>
<li>
Fry the halloumi in a shallow, non-stick pan, until browned on both sides.
</li>
<li>Wash and chop the salad.</li>
<li>Fill pita with salad, hummus, and fried halloumi.</li>
</ol>

输出的结果如下

**备注:**如果反向计数的列表项数比 start 属性的值还要多,计数将继续到零并向负数方向增加。

value

value 属性允许设置列表项指定数值,以下示例:

1
2
3
4
5
6
7
8
<ol>
<li value="2">Toast pita, leave to cool, then slice down the edge.</li>
<li value="4">
Fry the halloumi in a shallow, non-stick pan, until browned on both sides.
</li>
<li value="6">Wash and chop the salad.</li>
<li value="8">Fill pita with salad, hummus, and fried halloumi.</li>
</ol>

**备注:**即使使用非数字的list-style-type,仍需要在 value 属性中使用等效的数值。即使你把编号显示成 A、B、CⅠ、Ⅱ、Ⅲ,value里依然必须写阿拉伯数字(1、2、3),浏览器会自动转为相应的编号。

样式化链接

当为链接添加样式时,理解利用伪类有效地建立链接状态是很重要的,以及如何为链接添加样式来实现常用的功能(如导航菜单和选项卡)。我们将在本文中关注所有这些主题。

让我们来看一些链接

在创建超链接一文中,我们了解了如何根据最佳实践在 HTML 中实现链接。在本文中,我们将以这些知识为基础,向你展示为超链接设计样式的最佳做法。

链接状态

第一件需要理解的事情是链接状态的概念,链接存在时处于不同的状态,每一个状态都可以用对应的伪类来应用样式:

  • Link:有目的地的链接(即不只是一个具名锚点),使用 :link 伪类来应用样式。
  • Visited:已访问过(存在于浏览器历史记录中)的链接,使用 :visited 伪类来应用样式。
  • Hover:被用户鼠标指针悬停的链接,使用 :hover 伪类来应用样式。
  • Focus:被选中的链接(比如通过键盘的 Tab 移动到这个链接,或者使用像 HTMLElement.focus() 这样的方法编程地聚焦链接),使用 :focus 伪类来应用样式。
  • Active:激活(点击的瞬间)的链接,使用 :active 伪类来应用样式。

默认样式

下面的示例说明了默认情况下链接的外观和行为;不过 CSS 会放大文本并将其居中,使其更加突出。你可以将示例中默认样式的外观和行为与本页中应用了更多 CSS 样式的其他链接的外观和行为进行比较。默认链接具有以下属性:

  • 链接以下划线表示。
  • 未访问链接为蓝色。
  • 已访问链接为紫色。
  • 悬停链接时,鼠标指针会变成一个小手图标。
  • 聚焦链接的周围有一个轮廓——按下键盘上的制表符键,就能聚焦本页面上的链接。(在 Mac 上,需要使用 option + tab,或通过按下 Ctrl + F7 启用全键盘控制 选项。
  • 活动链接为红色。尝试在点击链接时按住鼠标键(不然太快了,看不到)。
1
<p><a href="#">一个简单的链接</a></p>
1
2
3
4
p {
font-size: 2rem;
text-align: center;
}

有趣的是,这些默认的样式与 20 世纪 90 年代中期浏览器早期的风格几乎相同。这是因为用户知道以及期待链接就是这样变化的,如果链接的样式不同,就会让一些人感到奇怪。不过这不意味着你不应该为链接添加任何样式,只是你的样式不应该与用户预期的相差太大,你应该至少:

  • 为链接使用下划线,但是不要在其他内容上也用下划线,以作区分。如果你不想要带有下划线的链接,那你至少要用其他方法来高亮突出链接。
  • 当用户悬停或选择的时候,使链接有相应的变化,并且在链接被激活的时候,变化会有一些不同。

可以使用以下 CSS 属性关闭/更改默认样式:

  • color 以改变文字的颜色。
  • cursor 以改变鼠标光标的样式,除非有非常充分的理由,否则不应关闭此功能。
  • outline 以改变文字的轮廓。轮廓有点像边框,唯一的区别是边框占用了盒模型的空间,而轮廓没有;它只是设置在背景图片的顶部。轮廓是一种有用的无障碍辅助工具,因此如果不增加另一种表示重点链接的方法,就不应删除轮廓。

备注: 链接样式并不局限于上述属性,你可以自由使用任何你喜欢的属性。

将样式应用到一些链接

现在我们已经详细地看了默认的状态,让我们看一下典型的链接样式的设置。

开始之前,我们先写出我们的空规则集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a {
}

a:link {
}

a:visited {
}

a:focus {
}

a:hover {
}

a:active {
}

这几个规则的顺序是有意义的,因为链接的样式是建立在另一个样式之上的,比如,第一个规则的样式也会在后面的规则中生效,一个链接被激活的时候,它也是处于悬停状态的。如果你搞错了顺序,那么就可能不会产生正确的效果。要记住这个顺序,你可以尝试这样帮助记忆:LoVe Fears HAte。

现在让我们再添加一些信息,得到正确的样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
body {
width: 300px;
margin: 0 auto;
font-size: 1.2rem;
font-family: sans-serif;
}

p {
line-height: 1.4;
}

a {
outline-color: transparent;
}

a:link {
color: #6900ff;
}

a:visited {
color: #a5c300;
}

a:focus {
text-decoration: none;
background: #bae498;
}

a:hover {
text-decoration: none;
background: #cdfeaa;
}

a:active {
background: #6900ff;
color: #cdfeaa;
}

这里还提供了一些示例 HTML,供你应用 CSS:

1
2
3
4
5
6
<p>
有很多可用的浏览器,如 <a href="#">Mozilla Firefox</a><a href="#"
>Google Chrome</a
>
<a href="#">Microsoft Edge</a>
</p>

把这两个放在一起,我们得到这样的结果:

那么我们在这里做了什么?这个看起来肯定和默认的样式不同,但仍然提供了一个熟悉的体验,好让用户知道发生了什么:

  • 前两条规则对本讨论并不重要。
  • 第三条规则使用 a 选择器来去掉焦点轮廓(反正不同浏览器的焦点轮廓也不一样)。
  • 接下来,我们使用 a:linka:visited 选择器为未访问链接和已访问链接设置一些颜色变化,使它们截然不同。
  • 接下来的两条规则使用 a:focusa:hover 为聚焦和悬停链接设置无下划线和不同的背景颜色。
  • 最后,a:active 用于在链接被激活时为其提供一种反色方案,以便让人清楚地看到有重要的事情正在发生!

在链接中包含图标

常见的做法是在链接中包含图标,使链接提供更多关于链接指向的内容的信息。让我们来看一个简单的例子,例子中为一个外部链接(链接指向的不是本站,而是外部站点)。这样的图标通常看起来像一个指向盒子的小箭头,比如,我们会使用 icons8.com 上的这个优秀的范例。

让我们来看一些能给我们这个效果的 HTML 和 CSS。先是一些简单的等待你样式化的 HTML:

1
2
3
4
5
6
7
<p>
要获取关于天气的更多信息,请访问我们的<a href="weather.html">天气页面</a
>,查看<a href="https://zh.wikipedia.org/wiki/天气">维基百科上的天气信息</a
>,或检查
<a href="http://www.extremescience.com/weather.htm">Extreme Science</a
>上的天气信息。
</p>

接着是 CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
body {
width: 300px;
margin: 0 auto;
font-family: sans-serif;
}

p {
line-height: 1.4;
}

a {
outline-color: transparent;
text-decoration: none;
padding: 2px 1px 0;
}

a:link {
color: blue;
}

a:visited {
color: purple;
}

a:focus,
a:hover {
border-bottom: 1px solid;
}

a:active {
color: red;
}

a[href^="http"] {
background: url("external-link-52.png") no-repeat 100% 0;
background-size: 16px 16px;
padding-right: 19px;
}

那么这里发生了什么?我们将跳过大部分的 CSS,因为那些只是你之前看过的相同信息。最后一条规则很有趣,这里,我们在外部链接上插入了一个自定义背景图片,这和上篇自定义列表项目符号文章的做法很像。这次,我们使用了 background 简写,而不是分别使用多个属性。我们设置了我们想要插入的图片的路径,指定了 no-repeat ,这样我们只插入了一次图片,然后指定位置为 100% ,使其出现在内容的右边,距离上方是 0 像素。

我们也使用 background-size 来指定要显示的背景图像的大小,为了满足响应式网站设计的需要,在图标更大,需要再重新调整它的大小的时候,这样做是很有帮助的。但是,这仅适用于 IE 9 及更高版本。所以你如果需要支持那些老的浏览器,只能调整图像的原始大小,然后插入。

最后,我们在链接上设置 padding-right ,为背景图片留出空间,这样就不会让它和文本重叠了。

最后的问题,我们是如何只选中了外部链接的?如果你正确编写你的 HTML 链接,你应该只会在外部链接上使用绝对 URL,如果链接是链接你的站点的其他部分,那么使用相对链接是更加高效的。因此“http”文本应该只出现在外部链接上,为此我们可以使用一个属性选择器 —— a[href^=“http”] —— 选中 <a>元素,但是这样只会选中那些拥有 href 属性,且属性的值以“http”开头的 <a>元素。

就是这样。重温上文的动手练习部分,试试这种新技巧吧!

样式化链接为按钮

本文所介绍的工具还可以用于其他方面。例如,像悬停这样的状态可以用来样式化许多不同的元素,而不仅仅是链接——你可能想样式化段落、列表项或其他东西的悬停状态。

此外,在某些情况下,链接的样式通常看起来像按钮。网站导航菜单可以标记为一组链接,而这组链接的样式可以看起来像一组控制按钮或标签,让用户可以访问网站的其他部分。让我们一起来探索一下。

首先,一些 HTML:

1
2
3
4
5
6
7
<nav class="container">
<a href="#">主页</a>
<a href="#">披萨</a>
<a href="#">音乐</a>
<a href="#">袋熊</a>
<a href="#">芬兰</a>
</nav>

接着,是我们的 CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
body,
html {
margin: 0;
font-family: sans-serif;
}

.container {
display: flex;
gap: 0.625%;
}

a {
flex: 1;
text-decoration: none;
outline-color: transparent;
text-align: center;
line-height: 3;
color: black;
}

a:link,
a:visited,
a:focus {
background: palegoldenrod;
color: black;
}

a:hover {
background: orange;
}

a:active {
background: darkred;
color: white;
}

这给我们以下结果:

HTML 定义了一个 <nav> 元素,该元素具有 “container” 类。其中的 <nav> 包含我们的链接。

  • 第二条规则的含义:
    • 容器为弹性盒。其中包含的项目(本例中为链接)将是弹性项。
    • 柔性项之间的间隙为容器宽度的 0.625%。
  • 第三条规则为链接设置样式:
    • 第一个声明 flex: 1 表示将调整项的宽度,以便它们使用容器中的所有可用空间。
    • 接下来,我们关闭默认的 text-decorationoutline —— 我们不想让它们破坏我们的外观。
    • 最后三项声明是将每个链接内的文字居中,将 line-height 设置为 3 以增加按钮的高度(这样做的好处是可以将文字垂直居中),并将文字颜色设置为黑色。

Web 字体

在模块的第一篇文章中,我们探讨了用于样式化字体和文本的基本 CSS 特性。在这篇文章中,我们将更进一步,详细地探索 web 字体——它们允许你下载自定义字体和你的 web 页面,以允许更多不同的、自定义的文本样式。

字体种类回顾

正如我们在基本文本和字体样式中所看到的那样,应用到你的 HTML 的字体可以使用 font-family属性来控制。你需要提供一个或多个字体种类名称,浏览器会在列表中搜寻,直到找到它所运行的系统上可用的字体。

1
2
3
p {
font-family: Helvetica, "Trebuchet MS", Verdana, sans-serif;
}

这个系统运行良好,但是对于传统的 web 开发人员来说,字体选择是有限的。只有少数几种字体可以保证兼容所有流行的操作系统——这就是所谓的 Web 安全字体。你可以使用字体堆栈来指定可选择的字体,后面是 Web 安全的替代选项,然后是默认的系统字体,但是为了确保你的设计在每种字体中都显示正常,这样增加了测试的开销。

下载Web 字体

但是还有一种选择,它非常有效,回到 IE 版本 6。Web 字体是一种 CSS 特性,允许你指定在访问时随你的网站一起下载的字体文件,这意味着任何支持 Web 字体的浏览器都可以使用你指定的字体。太酷啦!所需的语法如下所示:

首先,在 CSS 的开始处有一个 @font-face 块,它指定要下载的字体文件(url() 中是下载的字体文件的相对路径):

1
2
3
4
@font-face {
font-family: "myFont";
src: url("myFont.ttf");
}

在这个下面,你可以使用 @font-face 中指定的字体种类名称来将你的定制字体应用到你喜欢的任何东西上,比如说:

1
2
3
html {
font-family: "myFont", "Bitstream Vera Serif", serif;
}

语法确实比这更复杂,下面我们将详细介绍。

关于网页字体有两件重要的事情要记住:

  1. 浏览器支持不同的字体格式,因此你需要多种字体格式以获得良好的跨浏览器支持。例如,大多数现代浏览器都支持 WOFF / WOFF2(Web Open Font Format versions 1 and 2,Web 开放字体格式版本 1 和 2),它是最有效的格式,但是旧版本 IE 只支持 EOT (Embedded Open Type,嵌入式开放类型) 的字体,你可能需要包括一个 SVG 版本的字体支持旧版本的 iPhone 和 Android 浏览器。我们将向你展示如何生成所需的代码。
  2. 字体一般都不能自由使用。你必须为他们付费,或者遵循其他许可条件,比如在代码中 (或者在你的站点上) 提供字体创建者。你不应该在没有适当的授权的情况下偷窃字体。

这么做划不来,原因有2点:

  1. 大部分字体下载使用需要授权,只能用部分免费商用字体
  2. 中文字体7MB+ 没法随手扔给浏览器下载,加载太慢
  3. 英文字体更没必要,因为英文有 web 安全字体

因此,大多数中文网站就不折腾 Web 字体,而是走这条路:

1
2
3
4
5
6
7
body {
font-family:
"PingFang SC", /* macOS/iOS 自带 */
"Microsoft YaHei", /* Windows 自带 */
"Hiragino Sans GB", /* 老 macOS */
sans-serif; /* 兜底 */
}

不下载任何字体文件,直接用访客手机/电脑里已经装好的字体来渲染。每个系统显示的不完全一样(Win 上看是微软雅黑,Mac 上看是苹方),但:

  • 零下载、零流量、瞬间显示
  • ✅ 可读性完全没问题

使用在线字体服务

如谷歌在线字体

1
<link href='https://fonts.googleapis.com/css?family=Lobster|Raleway' rel='stylesheet' type='text/css'>

CSS布局

此刻,我们已经看过 CSS 的基础知识,如何设置文本的样式,以及如何设置和操作内容所在的框。现在是时候看看如何把你的盒子放在与视口相关的正确位置上。我们已经涵盖了必要的先决条件,所以我们现在可以深入到 CSS 布局,查看不同的显示设置,涉及浮动和定位的传统布局方法,以及像 flexbox 这样的现代布局工具。

介绍 CSS 布局

本文将回顾我们以前模块中已经介绍过的一些 CSS 布局特性——例如不同的display值——并介绍我们将在本模块中使用的一些概念。

CSS 页面布局技术允许我们拾取网页中的元素,并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。在这个模块中将涉及更多关于页面布局技术的细节:

  • 正常布局流
  • display 属性
  • 弹性盒子
  • 网格
  • 浮动
  • 定位
  • CSS 表格布局
  • 多列布局

每种技术都有它们的用途,各有优缺点,相互辅助。通过理解各个布局方法的设计理念,你能够找到构建你想要的网页需要的布局方案。

正常布局流

正常布局流(normal flow)是指在不对页面进行任何布局控制时,浏览器默认的 HTML 布局方式。让我们快速地看一个 HTML 的例子:

1
2
3
4
5
6
7
8
9
<p>I love my cat.</p>

<ul>
<li>Buy cat food</li>
<li>Exercise</li>
<li>Cheer up friend</li>
</ul>

<p>The end!</p>

默认情况下,浏览器的显示如下:

注意,HTML 元素完全按照源码中出现的先后次序显示——第一个段落、无序列表、第二个段落。

出现在另一个元素下面的元素被描述为元素,与出现在另一个元素旁边的内联元素不同,内联元素就像段落中的单个单词一样。

当你使用 css 创建一个布局时,你正在离开正常布局流,但是对于页面上的多数元素,正常布局流将完全可以创建你所需要的布局。从一个结构良好的 Html 文档开始是非常重要,因为你可以按照默认的方式来搭建页面,而不是自造车轮。

下列布局技术会覆盖默认的布局行为:

  • display 属性 — 标准的 value,比如 block,inline 或者 inline-block 元素在正常布局流中的表现形式 (见 Types of CSS boxes). 接着是全新的布局方式,通过设置 display 的值,比如 CSS Grid 和 Flexbox.
  • 浮动——应用 float 值,诸如 left 能够让块级元素互相并排成一 行,而不是一个堆叠在另一个上面。
  • position 属性 — 允许你精准设置盒子中的盒子的位置,正常布局流中,默认为 static ,使用其他值会引起元素不同的布局方式,例如将元素固定到浏览器视口的左上角。
  • 表格布局—表格的布局方式可以用在非表格内容上,可以使用 display: table 和相关属性在非表元素上使用。
  • 多列布局—这个 Multi-column layout 属性可以让块按列布局,比如报纸的内容就是一列一列排布的。

display 属性

在 css 中实现页面布局的主要方法是设定 display 属性的值。此属性允许我们更改默认的显示方式。正常流中的所有内容都有一个 display 的值,用作元素的默认行为方式。例如,英文段落显示在一个段落的下面,这是因为它们的样式是 display:block。如果在段落中的某个文本周围创建链接,则该链接将与文本的其余部分保持内联,并且不会打断到新行。这是因为 <a> 元素默认为 display:inline

你可以更改此默认显示行为。例如,<li> 元素默认为 display:block,这意味着在我们的英文文档中,列表项显示为一个在另一个之下。如果我们将显示值更改为 inline,它们现在将显示在彼此旁边,就像单词在句子中所做的那样。事实上,你可以更改任何元素的 display 值,这意味着你可以根据它们的语义选择 html 元素,而不必关心它们的外观。他们的样子是你可以改变的。

除了可以通过将一些内容从 block 转换为 inline (反之亦然)来更改默认表示形式之外,还有一些更大的布局方法以 display 值开始。但是,在使用这些属性时,通常需要调用其他属性。在讨论布局时,对我们来说最重要的两个值是 display: flexdisplay: grid

弹性盒子

Flexbox 是 CSS 弹性盒子布局模块(Flexible Box Layout Module)的缩写,它被专门设计出来用于创建横向或是纵向的一维页面布局。要使用 flexbox,你只需要在想要进行 flex 布局的父元素上应用 display: flex,所有直接子元素都将会按照 flex 进行布局。我们来看一个例子。

设置 display:flex

下面这些 HTML 标记描述了一个 class 为 wrapper 的容器元素,它的内部有三个 <div> 元素。它们在我们的英文文档当中,会默认地作为块元素从上到下进行显示。

现在,当我们把 display: flex 添加到它的父元素时,这三个元素就自动按列进行排列。这是由于它们变成了 flex 项 (flex items),按照 flex 容器(也就是它们的父元素)的一些 flex 相关的初值进行 flex 布局:它们整整齐齐排成一行,是因为父元素上 flex-direction 的初值是 row。它们全都被拉伸至和最高的元素高度相同,是因为父元素上 align-items 属性的初值是 stretch。这就意味着所有的子元素都会被拉伸到它们的 flex 容器的高度,在这个案例里就是所有 flex 项中最高的那一项。所有项目都从容器的开始位置进行排列,排列成一行后,在尾部留下一片空白。

1
2
3
4
5
6
7
8
9
10
11
12
* {
box-sizing: border-box;
}

.wrapper > div {
border-radius: 5px;
background-color: rgb(207, 232, 220);
padding: 1em;
}
.wrapper {
display: flex;
}
1
2
3
4
5
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>

设置 flex 属性

除了上述可以被应用到 flex 容器的属性以外,还有很多属性可以被应用到 flex 项 (flex items) 上面。这些属性可以改变 flex 项在 flex 布局中占用宽/高的方式,允许它们通过伸缩来适应可用空间。

作为一个简单的例子,我们可以在我们的所有子元素上添加 flex 属性,并赋值为 1,这会使得所有的子元素都伸展并填充容器,而不是在尾部留下空白,如果有更多空间,那么子元素们就会变得更宽,反之,他们就会变得更窄。除此之外,如果你在 HTML 标记中添加了一个新元素,那么它们也会变得更小,来为新元素创造空间——不管怎样,最终它们会调整自己直到占用相同宽度的空间。

1
2
3
4
5
6
7
.wrapper {
display: flex;
}

.wrapper > div {
flex: 1;
}

Grid 布局

Flexbox 用于设计横向或纵向的布局,而 Grid 布局则被设计用于同时在两个维度上把元素按行和列排列整齐。

设置 display: grid

同 flex 一样,你可以通过指定 display 的值来转到 grid 布局:display: grid。下面的例子使用了与 flex 例子类似的 HTML 标记,描述了一个容器和若干子元素。除了使用 display:grid,我们还分别使用 grid-template-rowsgrid-template-columns两个属性定义了一些行和列的轨道。定义了三个 1fr的列,还有两个 100px的行之后,无需再在子元素上指定任何规则,它们自动地排列到了我们创建的格子当中。

1
2
3
4
5
6
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 100px 100px;
grid-gap: 10px;
}
1
2
3
4
5
6
7
8
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
<div class="box5">Five</div>
<div class="box6">Six</div>
</div>

在网格内放置元素

一旦你有了一个 grid,你也可以显式地将元素摆放在里面,而不是依赖于浏览器进行自动排列。在下面的第二个例子里,我们定义了一个和上面一样的 grid,但是这一次我们只有三个子元素。我们利用 grid-columngrid-row两个属性来指定每一个子元素应该从哪一行/列开始,并在哪一行/列结束(左闭右开)。这就能够让子元素在多个行/列上展开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 100px 100px;
grid-gap: 10px;
}

.box1 {
grid-column: 2 / 4;
grid-row: 1;
}

.box2 {
grid-column: 1;
grid-row: 1 / 3;
}

.box3 {
grid-row: 2;
grid-column: 3;
}
1
2
3
4
5
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>

这篇指南的其余部分介绍了其他的布局方式,它们与你的页面的主要布局结构关系不大,但是却能够帮助你实现特殊的操作。同时,只要你理解了每一个布局任务的初衷,你就能够马上意识到哪一种布局更适合你的组件。

浮动

把一个元素“浮动”(float)起来,会改变该元素本身和在正常布局流(normal flow)中跟随它的其他元素的行为。这一元素会浮动到左侧或右侧,并且从正常布局流 (normal flow) 中移除,这时候其他的周围内容就会在这个被设置浮动(float)的元素周围环绕。

float 属性有四个可能的值:

  • left — 将元素浮动到左侧。
  • right — 将元素浮动到右侧。
  • none — 默认值,不浮动。
  • inherit — 继承父元素的浮动属性。

在下面这个例子当中,我们把一个 <div> 元素浮动到左侧,并且给了他一个右侧的 margin,把文字推开。这给了我们文字环绕着这个 <div> 元素的效果,在现代网页设计当中,这是你唯一需要学会的事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h1>Simple float example</h1>

<div class="box">Float</div>

<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>
1
2
3
4
5
6
.box {
float: left;
width: 150px;
height: 150px;
margin-right: 30px;
}

定位技术

定位 (positioning) 能够让我们把一个元素从它原本在正常布局流 (normal flow) 中应该在的位置移动到另一个位置。定位 (positioning) 并不是一种用来给你做主要页面布局的方式,它更像是让你去管理和微调页面中的一个特殊项的位置。

有一些非常有用的技术在特定的布局下依赖于 position 属性。同时,理解定位 (positioning) 也能够帮助你理解正常布局流 (normal flow),理解把一个元素移出正常布局流 (normal flow) 是怎么一回事。

有五种主要的定位类型需要我们了解:

  • 静态定位 (Static positioning) 是每个元素默认的属性——它表示“将元素放在文档布局流的默认位置——没有什么特殊的地方”。
  • 相对定位 (Relative positioning) 允许我们相对于元素在正常的文档流中的位置移动它——包括将两个元素叠放在页面上。这对于微调和精准设计 (design pinpointing) 非常有用。
  • 绝对定位 (Absolute positioning) 将元素完全从页面的正常布局流 (normal layout flow) 中移出,类似将它单独放在一个图层中。我们可以将元素相对于页面的 <html> 元素边缘固定,或者相对于该元素的最近被定位祖先元素 (nearest positioned ancestor element)。绝对定位在创建复杂布局效果时非常有用,例如通过标签显示和隐藏的内容面板或者通过按钮控制滑动到屏幕中的信息面板。
  • 固定定位 (Fixed positioning) 与绝对定位非常类似,但是它是将一个元素相对浏览器视口固定,而不是相对另外一个元素。这在创建类似在整个页面滚动过程中总是处于屏幕的某个位置的导航菜单时非常有用。
  • 粘性定位 (Sticky positioning) 是一种新的定位方式,它会让元素先保持和 position: static 一样的定位,当它的相对视口位置 (offset from the viewport) 达到某一个预设值时,它就会像 position: fixed 一样定位。

简单定位示例

我们将展示一些示例代码来熟悉这些布局技术。这些示例代码都作用在下面这一个相同的 HTML 上:

1
2
3
4
5
<h1>Positioning</h1>

<p>I am a basic block level element.</p>
<p class="positioned">I am a basic block level element.</p>
<p>I am a basic block level element.</p>

该 HTML 将使用以下 CSS 默认样式:

1
2
3
4
5
6
7
8
9
10
11
12
body {
width: 500px;
margin: 0 auto;
}

p {
background-color: rgb(207, 232, 220);
border: 2px solid rgb(79, 185, 227);
padding: 10px;
margin: 10px;
border-radius: 5px;
}

相对定位

相对定位 (relative positioning) 让你能够把一个正常布局流 (normal flow) 中的元素从它的默认位置按坐标进行相对移动。比如将一个图标往下调一点,以便放置文字。我们可以通过下面的规则添加相对定位来实现效果:

1
2
3
4
5
.positioned {
position: relative;
top: 30px;
left: 30px;
}

这里我们给中间段落的 position一个 relative 值——这属性本身不做任何事情,所以我们还添加了 topleft属性。这些可以将受影响的元素向下向右移——这可能看起来和你所期待的相反,但你需要把它看成是左边和顶部的元素被“推开”一定距离,这就导致了它的向下向右移动(但是上游和下游的文字和元素的位置都不会发生改变,所以不能这么理解吧)。

添加此代码将给出以下结果:

1
2
3
4
5
6
7
.positioned {
position: relative;
background: rgba(255, 84, 104, 0.3);
border: 2px solid rgb(255, 84, 104);
top: 30px;
left: 30px;
}

绝对定位

绝对定位用于将元素移出正常布局流 (normal flow),以坐标的形式相对于它的容器定位到 web 页面的任何位置,以创建复杂的布局。有趣的是,它经常被用于与相对定位和浮动的协同工作。

回到我们最初的非定位示例,我们可以添加以下的 CSS 规则来实现绝对定位:

1
2
3
4
5
.positioned {
position: absolute;
top: 30px;
left: 30px;
}

这里我们给我们的中间段一个 positionabsolute值,并且和前面一样加上 topleft属性。但是,添加此代码将给出以下结果:

1
2
3
4
5
6
7
.positioned {
position: absolute;
background: rgba(255, 84, 104, 0.3);
border: 2px solid rgb(255, 84, 104);
top: 30px;
left: 30px;
}

这和之前截然不同!定位元素现在已经与页面布局的其余部分完全分离,并位于页面的顶部。其他两段现在靠在一起,好像之前那个中间段落不存在一样。topleft属性对绝对位置元素的影响不同于相对位置元素。在这一案例当中,他们没有指定元素相对于原始位置的移动程度。相反,在这一案例当中,它们指定元素应该从页面边界的顶部和左边的距离 (确切地说,是 <html>元素的距离)。我们也可以修改作为容器的那个元素 (在这里是 <html>元素),要了解这方面的知识,参见关于定位 (positioning) 的课程。

我们现在暂时不讨论固定定位 (fixed positioning) —— 它基本上以相同的方式工作,除了它仍然固定在浏览器窗口的边缘,而不是它定位的父节点的边缘。

固定定位

固定定位 (fixed positioning) 同绝对定位 (absolute positioning) 一样,将元素从文档流 (document flow) 当中移出了。但是,定位的坐标不会应用于"容器"边框来计算元素的位置,而是会应用于视口 (viewport) 边框。利用这一特性,我们可以轻松搞出一个固定位置的菜单,而不受底下的页面滚动的影响。

在这个例子里面,我们在 HTML 加了三段很长的文本来使得页面可滚动,又加了一个带有position: fixed的盒子。

1
2
3
4
5
6
7
<h1>Fixed positioning</h1>

<div class="positioned">Fixed</div>

<p>Paragraph 1.</p>
<p>Paragraph 2.</p>
<p>Paragraph 3.</p>
1
2
3
4
5
.positioned {
position: fixed;
top: 30px;
left: 30px;
}

移动滚动条,这个盒子也不会移动,始终在视口的固定位置

粘性定位

粘性定位 (sticky positioning) 是最后一种我们能够使用的定位方式。它将默认的静态定位 (static positioning) 和固定定位 (fixed positioning) 相混合。

当一个元素被指定了position: sticky时,它会在正常布局流中滚动,直到它出现在了我们给它设定的相对于容器的位置,这时候它就会停止随滚动移动,就像它被应用了position: fixed一样。(就是没出现的时候是正常的,出现了就会停止滚动,始终在固定位置)

1
2
3
4
5
.positioned {
position: sticky;
top: 30px;
left: 30px;
}

表格布局

HTML 表格对于显示表格数据是很好的,但是很多年前——在浏览器中支持基本的 CSS 之前——web 开发人员过去也常常使用表格来完成整个网页布局——将它们的页眉、页脚、不同的列等等放在不同的表行和列中。这在当时是有效的,但它有很多问题——表布局是不灵活的,繁重的标记,难以调试和语义上的错误(比如,屏幕阅读器用户在导航表布局方面有问题)。

一个 <table>标签之所以能够像表格那样展示,是由于 css 默认给<table>标签设置了一组 table 布局属性。当这些属性被应用于排列非 <table> 元素时,这种用法被称为“使用 CSS 表格”。

下面这个例子展示了一个这样的用法。使用 CSS 表格来进行布局,在现在这个时间点应该被认为是一种传统方法,它通常会被用于兼容一些不支持 Flexbox 和 Grid 的浏览器。

让我们来看一个例子。首先,创建 HTML 表单的一些简单标记。每个输入元素都有一个标签,我们还在一个段落中包含了一个标题。为了进行布局,每个标签/输入对都封装在 <div>中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<form>
<p>First of all, tell us your name and age.</p>
<div>
<label for="fname">First name:</label>
<input type="text" id="fname" />
</div>
<div>
<label for="lname">Last name:</label>
<input type="text" id="lname" />
</div>
<div>
<label for="age">Age:</label>
<input type="text" id="age" />
</div>
</form>

现在,我们例子中的 CSS。除了使用 display属性外,大多数 CSS 都是相当普通的。 <form><div><label><input>被告知要分别显示表、表行和表单元——基本上,它们会像 HTML 表格标记一样,导致标签和输入在默认情况下排列整齐。我们所要做的就是添加一些大小、边缘等等,让一切看起来都好一点,我们就完成了。

你会注意到标题段落已经给出了 display: table-caption;——这使得它看起来就像一个表格 <caption>——同时出于设计需要,我们通过 caption-side: bottom;告诉标题应该展示在表格的底部,即使这个 <p> 标记在源码中是在 <input>之前。这就能让你有一点灵活的弹性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
html {
font-family: sans-serif;
}

form {
display: table;
margin: 0 auto;
}

form div {
display: table-row;
}

form label,
form input {
display: table-cell;
margin-bottom: 10px;
}

form label {
width: 200px;
padding-right: 5%;
text-align: right;
}

form input {
width: 300px;
}

form p {
display: table-caption;
caption-side: bottom;
width: 300px;
color: #999;
font-style: italic;
}

结果如下:

看不懂,这是古早的做法,现在都使用 Flexbox(弹性盒子)Grid(网格布局)

多列布局

多列布局模组给了我们一种把内容按列排序的方式,就像文本在报纸上排列那样。由于在 web 内容里让你的用户在一个列上通过上下滚动来阅读两篇相关的文本是一种非常低效的方式,那么把内容排列成多列可能是一种有用的技术。

要把一个块转变成多列容器 (multicol container),我们可以使用 column-count属性来告诉浏览器我们需要多少列,也可以使用 column-width来告诉浏览器以至少某个宽度的尽可能多的列来填充容器。

在下面这个例子中,我们从一个 class 为 container 的 <div>容器元素里边的一块 HTML 开始。

1
2
3
4
5
6
<div class="container">
<h1>Multi-column layout</h1>

<p>Paragraph 1.</p>
<p>Paragraph 2.</p>
</div>

我们指定了该容器的column-width为 200 像素,这让浏览器创建了尽可能多的 200 像素的列来填充这一容器。接着他们共同使用剩余的空间来伸展自己的宽度。

1
2
3
.container {
column-width: 200px;
}

下面对这一每部分的具体解释的部分就不看了,先看个基础。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2026 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信