精通-CSS-全-

精通 CSS(全)

原文:zh.annas-archive.org/md5/6E7477B42C94A8805922EA40B81890C7

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

HTML、CSS 和 JavaScript 是网络的三种核心语言。你对它们三者了解得越多,你就会越好。在这三者中,CSS 的作用是作为网络的展示语言。它描述了诸如颜色、字体和页面布局等内容。

本书有一些基本先决条件。我希望你了解如何编写 HTML,并了解基本的 CSS,包括样式化字体、添加边距、填充和背景颜色等,以及十六进制颜色代码是什么。在接下来的章节中,我将介绍一些基本概念,如盒模型、显示属性和样式表类型。我还会涉及少量 JavaScript 和 jQuery。你不需要任何关于这些的先前知识,但你将在本书中有所涉猎。

现在,让我们来看一下我们将要构建的最终网站。为了学习 CSS,我们将完成构建以下关于鲨鱼的 HTML5 网站。我说完成构建这个网站,是因为基本的 HTML 和 CSS 已经就位,你可以从本书的下载包中下载它们。我们将添加我将向你展示的所有东西,以及更多。这个网站采用了模块化和可重用的 CSS,你将在本书中学到。该网站首先将使用浮动进行布局,然后我们将使用 flexbox 重写布局。我们还会为文本使用 Web 字体:

导航功能包括使用 CSS 动画的下拉菜单:

该网站还具有一个带有 CSS 渐变的行动号召按钮:

该网站是完全响应式的。当我们调整浏览器大小时,可以看到我们的两列布局转变为单列布局:

此外,我们的菜单会变成专为移动设备设计的菜单:

如果我们向下滚动一点,我们会看到使用 CSS 过渡的幽灵按钮。它已经准备好适用于苹果的视网膜显示屏等高分辨率设备:

网站上的大部分图像都使用 SVG:

在页面的最底部,我们使用了一个图标字体:

因此,你会学到一些非常酷的东西。为了充分利用它,我建议你跟着我一起编码。

本书涵盖的内容

第一章,CSS 基础,介绍了掌握 CSS 所必需的基本概念。

第二章,加速,讨论了 Sublime 文本编辑器;CSS 重置,用于重置浏览器中的默认样式;以及后代选择器。

第三章,使用浮动创建页面布局,深入探讨了浮动。我们将介绍浮动的基本用法,然后使用浮动创建布局,并了解浮动可能引起的常见问题以及如何解决。

第四章,使用模块化、可重用的 CSS 类和 CSS3 创建按钮,涵盖了模块化 CSS 和多个类,并使用 CSS3 为我们的按钮添加过渡、悬停状态、变换和渐变。

第五章,创建主导航和下拉菜单,解释了我们主要导航的功能和展示。

第六章,变得响应式,介绍了响应式网页设计的基础知识,并解释了如何将其实现,将我们的静态网站转变为移动网站。

第七章,Web 字体,讨论了@font-face规则的基本语法、字体服务、使用场景以及 Web 字体和图标字体的提供者。

第八章,HiDPI 设备的工作流程,涵盖了为准备图像以适应 Retina 而使用 SVG 和srcset属性等技术的技术。

第九章,Flexbox第一部分,介绍了 Flexbox 模块,涵盖了基本实现和属性。

第十章,Flexbox第二部分,更深入地介绍了 Flexbox,构建了一个新的产品列表和更高级的属性。

第十一章,总结,总结了本书中涵盖的 CSS 概念,并提供了一些关于其他可以探索的 CSS 功能的信息。

您需要为这本书做好准备

在整本书中,我一直使用 Chrome 作为我的浏览器,因为它的 DevTools 等原因,但其他浏览器也有类似的工具。我们将使用 DevTools 直接在浏览器中探索代码。

我也一直在使用 macOS。如果您是 Windows 用户,而我在书中提到命令(cmd)键,您应该假装我是在提到Ctrl键。除此之外,我认为这不会成为问题。

我使用的文本编辑器是Sublime Text 3。我应该说 Sublime 并不是唯一一个好的文本编辑器。还有其他像 Atom 和 Visual Studio Code 这样的编辑器,它们可以做很多相同的事情。

尽管这本书是关于掌握 CSS,但没有 HTML,我们无法做太多事情。因此,我们将在 HTML 中进行相当多的工作。我们的目标是使用非常干净、语义化的 HTML;这是我们的目标。

这本书是为谁准备的

这本书是为希望在其网站项目中掌握 CSS 最佳实践的网页设计师和开发人员而写的。您应该已经知道如何处理网页,并准备使用 CSS 来掌握网站呈现。

约定

在这本书中,您会发现一些区分不同信息类型的文本样式。以下是这些样式的一些示例及其含义的解释。

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:"要更改文本大小,请使用font-size属性。"

代码块设置如下:

h2 {
  font-size: 26px;
  font-style: italic;
  color: #eb2428;
  margin-bottom: 10px;
} 

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:

h2 {
 font-size: 26px;
 font-style: italic;
 color: #eb2428;
 margin-bottom: 10px;
} 

任何命令行输入或输出都以以下形式书写:

# cp /usr/src/asterisk-addons/configs/cdr_mysql.conf.sample/etc/asterisk/cdr_mysql.conf

新术语和重要单词以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会以这样的形式出现在文本中:"单击“下一步”按钮将您移至下一个屏幕。"

警告或重要说明会以这样的形式出现在方框中。

提示和技巧会出现在这样的形式中。

第一章:CSS 基础

在这第一章中,CSS 基础,我们将看一下掌握 CSS 所必需的基本概念。你将学习到在网页开发中的最佳实践。

在网页开发的世界中,事物经常变化。例如,在过去,表格是布局网页的首选技术。但今天,使用表格进行布局绝对不是你想要做的事情。浮动一直是创建布局的最常见方式,也是我们首先要学习的内容。在过去的一年左右,flexbox 开始取代浮动进行布局,我们将在本书的后期学习 flexbox。CSS 正在进步,其他新的布局模块被设计来取代浮动进行页面布局。网格布局和 CSS 区域可能是未来的发展方向。由于前端网页开发领域的事物迅速发展,我们的关键是我们不能停止学习 CSS。一般来说,一旦停止学习,你的知识将很快过时。我的目的是教授能够长期受益的概念和技术。

在本章的两个部分中,我们将回顾对网页设计和 CSS 至关重要的核心概念。我们将首先回顾如何创建 CSS 中最基本的东西-规则集-并讨论我们可以写这些规则集的不同位置。

规则集的解剖和三种类型的样式表

我们现在对这本书的内容和我们将要构建的网站有了更多的了解。在我们开始深入研究更高级的主题之前,让我们回顾一下一些 CSS 基础知识。在本书中,我会使用诸如选择器、属性和值等术语,你需要确切理解这些术语的含义,以便跟上进度。我们将首先回顾一个规则集,然后再看看我们可以写这些规则集的三个不同位置。所以让我们开始吧。

解剖规则集

让我们跳到一个 CSS 文件中,看看下面代码块中的一个规则集。它是针对h2-一个二级标题。它设置了font-size26pxfont-styleitaliccolor为红色,margin-bottom10px

h2 { 
  font-size: 26px; 
  font-style: italic; 
  color: #eb2428; 
  margin-bottom: 10px; 
} 

所以这里没有什么可怕的!不过让我们来解剖一下:

selector { 
  property: value; 
  property: value;
  property: value;
} 

在上面的代码中,h2选择器。我们选择页面上的一个元素来定位我们的样式规则。h2选择器可以是plidiva或者我们想要定位的任何 HTML 元素。它也可以是一个类、一个 ID 或一个元素属性,我稍后会谈到。接下来,我们在花括号内有属性和值。从开花括号到闭花括号是声明块。你可以在花括号内有尽可能多的属性。font-sizecolorfont-stylemargin只是你可以使用的许多不同属性中的一部分。每个属性都有一个对应的值。在每个属性和值之间,你必须有一个冒号。值之后是一个分号,这也是必需的。每个属性和值被称为一个声明。所以声明块是花括号内的所有内容,声明是包括属性和值的单行。但实际上,在规则集的解剖中有三个重要的事情需要记住:选择器、属性和值。现在让我们看看我们可以在哪里写这些规则集。

外部样式表

目前,我们将规则集写在外部样式表中。你可以看到它实际上是一个独立的文件:

在屏幕左侧的文件夹结构中,你可以看到它在一个名为css的文件夹中:

除了嵌入样式表之外。外部样式表是编写样式的最佳位置;它是一个单独的文件,链接到每个 HTML 页面。外部样式表可以控制整个网站,这是首选样式表的主要原因。在index.html文件的<head></head>标签之间的任何位置;这是您可以链接到外部样式表的地方:

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

href属性指向文件的位置。这里它指向css文件夹,然后是一个名为style.css的文件。还有一个rel属性,基本上表示这是一个stylesheet。在过去,您可能已经看到text/css作为type属性的值,如下面的代码块所示,但在 HTML5 中这不再是必需的:

<head>
  <link rel="stylesheet" href="css/style.css" type="text/css"> 
</head>

您可能还看到了自关闭标签上的结束斜杠,比如link元素,但在 HTML5 中,这个斜杠不再是必需的。因此,包括它或排除它对您的网站没有任何影响。

嵌入样式表

除了使用最佳类型的样式表,外部样式表,我们还可以在 HTML 文档的头部编写我们的规则集。这被称为嵌入样式表。有很多原因不这样做。主要的两个原因是它阻碍了工作流程,而且它只控制站点的单个页面。我们要做的就是在head标签中创建这些开放和关闭的<style>标签:

<head>
  <style> 

  </style> 
</head>

在这个开放的<style>标签内的任何位置,我们可以开始添加我们的规则集,这将只影响这一页:

<head>
  <style> 
    h2 { 
      font-size: 50px; 
   } 
  </style> 
</head>

再次强调,这不是编写样式的最佳位置。将它们保留在外部样式表中,99%的时间都是最好的选择,但您可以选择将样式嵌入到文档的head标签中。

内联样式表

最后,第三种样式表是内联样式表。它实际上不是样式表-更像是内联样式。我们可以在 HTML 元素内部实际上写一个style属性:

<h2 style=""> 

内联样式与使用传统规则集的外部和嵌入样式表有些不同;这里没有选择器,也没有完整的规则集,因为您是在 HTML 标记内部编写它。我们可以输入font-size10px。我们以与规则集相同的方式编写属性和值,并且应该用分号结束:

<h2 style="font-size: 10px;"> 

我们还可以更改颜色并用分号结束:

<h2 style="font-size: 10px; color: deeppink;"> 

保存这个,刷新网站,你就可以看到结果:

这绝对是编写样式的最低效方式。然而,在 HTML 元素中直接编写 CSS 会赋予它最大的权重,并且会覆盖所有针对相同元素的嵌入样式和所有外部样式,除非使用!important关键字。在第四章,使用模块化、可重用的 CSS 类和 CSS3 创建按钮中的特异性规则部分,我深入探讨了级联和其他因素,这些因素使某些规则的权重更大,并覆盖其他规则。

好的,现在我们已经创建了一个规则集,并学会了规则集的每个部分的名称,特别是选择器、属性和值。这些信息对您来说将是有帮助的,因为我经常会使用这些术语。我们还回顾了可以创建样式表的三种不同位置:外部、嵌入在<head>标签中,以及内联,直接在元素内部。再次强调,外部样式表是最有效的,因为它可以控制整个网站。这是我写 CSS 的唯一位置。接下来,我们将回顾另外两个核心概念:盒模型和display属性。

盒模型和块与内联元素

在这一部分,我们将回顾 CSS 的另外两个基础:盒模型和块级与内联元素。充分掌握这两个概念是以后掌握 CSS 的关键。首先,我们将回顾盒模型,然后我们将看看它与块级元素的关系。接着我们将讨论内联元素的特点。

盒模型

盒模型定义了页面上元素的宽度和高度。要确定一个元素占据的水*空间,你需要将content + padding-left + padding-right + border-left + border-right + margin-left + margin-right相加:

所以让我们通过查看我们网站上的h1来实际看一下。这是蓝色文字,上面写着“Old Chompy”。

这是使这个标题看起来像这样的规则集:

h1 { 
  font-size: 40px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae 
} 

让我们添加以下属性,给它一个widthpaddingbordermargin。以及一个显眼的background-color

h1 { 
  font-size: 40px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae 
 background-color: black; 
 width: 300px; 
 padding: 50px; border: 10px solid blue; margin: 50px; 
}

现在我们的标题看起来是这样的。一个大盒子:

所以这个元素的盒模型现在有这 5 个属性;从前面的屏幕截图中可以看出,这个h1看起来真的像一个盒子。我们可以看到10px的边框,marginborder外面是50px,填充在边框和文本之间是50px。然后填充内部的宽度是300px。所以这个元素的宽度实际上是300 + 20 + 100 + 100,总共是520px。所以即使我们在 CSS 文件中定义了width属性为300px,这个元素实际占据的空间是520px

现在,这是传统的盒模型。我可以使用box-sizing属性和border-box值修改这个传统的盒模型。所以让我们使用box-sizing属性,看看它如何影响网站。将属性和值添加到h1声明块的底部,如下所示:

h1 { 
  font-size: 40px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae 
  background-color: black; 
  width: 300px; 
  padding: 50px; 
  margin: 50px; 
  border: 10px solid blue;
  box-sizing: border-box;
}

如下截图所示,border-box将从widthheight计算中减去paddingborder。如果我将300px作为我的width,那么我指定的300px将减去20px的边框和100px的填充。这是一个更直观的盒模型,它与 Internet Explorer 8 及更高版本兼容,以及所有其他主要浏览器。这个元素现在占据的最终水*空间从520px变成了400px

块级元素与内联元素

让我们稍微谈谈块级元素。标题 1(h1)、标题 2(h2)、段落(p)、列表项(li)和div都是自然块级元素的例子。块级元素有两个定义特征:它们扩展了整个可用宽度,并且它们强制后面的元素出现在下一行,这意味着它们堆叠在一起。所以让我们从我们的声明块中删除box-sizing属性以及width属性,以演示如果没有指定宽度,它们将占用整个可用宽度:

h1 { 
  font-size: 40px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae 
  background-color: black; 
  padding: 50px; 
  margin: 50px; 
  border: 10px solid blue;
}

保存并刷新网站。你可以在下面的截图中看到,当你将浏览器窗口放大时,它占据了整个可用宽度,除了我们设置的margin是四周的50px

现在让我们进入 HTML 文件,在 HTML 中再添加两个这样的h1标签,并保存:

<section> 
  <h1>Old Chompy</h1> 
  <h1>Old Chompy</h1> 
  <h1&gt;Old Chompy</h1> 

这就是它的样子:

现在你可以看到这些块级元素如何堆叠在一起:好老的块级元素。

另一方面,内联元素的行为不同。它们水*相邻,并且不占用整个可用宽度。它们只占用它们需要的宽度。一些天然的内联元素是锚点(<a>)、<span><i><b><strong><em>标签。

好了,让我进入 HTML 并向页面添加三个span标签:

<section> 
  <h1>Old Chompy</h1> 
  <h1>Old Chompy</h1> 
  <h1>Old Chompy</h1> 
  <span>Inline</span> 
  <span>Inline</span> 
  <span>Inline</span> 

我还会通常在规则集中针对那些span元素并给它们一个绿色的背景,只是为了看到它们的区别:

span { 
  background-color: green; 
} 

这是它的外观:

您可以注意到绿色的内联元素是水*排列而不是垂直堆叠。没有什么特别的,但我们可以看到它们不会占用整个可用宽度,它们只会占用它们需要的宽度。

有一些内联元素不会做的事情。它们不会响应widthmargin-topmargin-bottom。因此,如果一个元素自然是内联的,并且您给它一个width和一个margin-topmargin-bottom,就像下面的代码所示,它将绝对不会做任何事情:

span { 
  background-color: green;
 width: 1000px;
  margin-top: 1000px; 
} 

没有任何变化:

内联元素只是不遵守这些属性,这些属性对它们没有影响,所以我们将删除它们。

还有一件有趣的事情可以做。有一个display属性,允许您将自然的块级元素更改为内联元素,反之亦然。所以让我们在我们的span选择器中添加一个display属性,值为block,并在浏览器中查看。所以,我可以说display: block,还可以添加一些margin-top

span { 
  background-color: green; 
 display: block; 
  margin-top: 10px; 
}

我们可以看到这些元素现在堆叠在彼此上面,并且现在遵守margin-topmargin-bottom的值:

display属性设置为block的元素会遵守我给它的任何width值,但它也会占用整个可用宽度。您可以看到它延伸到屏幕的边缘。我们也可以在我们的h1选择器上轻松使用display: inline属性,将显示的性质从块状更改为内联。最后,我们可以使用display: none,这会完全隐藏页面上的元素,并且通常出于各种原因而使用。所以让我们去我们的h1声明并说display: none

h1 { 
  font-size: 40px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae; 
  background-color: black; 
  padding: 50px; 
  margin: 50px; 
  border: 10px solid blue; 
 display: none; 
} 

现在,如果我们查看我们的网站,那个h1是不可见的。它不再是浏览器要向我们展示的东西:

总之,所有元素都符合框模型。框模型会根据box-sizing属性的使用方式而略有变化,如果有的话。此外,框模型会根据元素是块级还是内联而变化,这是两种最常见的显示属性。

总结

在本章中,我们取得了很多成就。我们已经讨论了 CSS 是 Web 的呈现语言,真正使您的网站看起来像一个网站。我们现在熟悉了我们将要构建的网站和我们将在接下来的章节中使用的工具。我们已经涵盖了诸如规则集、链接到外部样式表和框模型和显示属性等核心概念,这些都是掌握 CSS 所必不可少的。

在下一章中,我们将介绍一些编写 CSS 所必需的工具,例如良好的文本编辑器、CSS 重置和 Chrome 的开发者工具。

第二章:加速

为了成为一个优秀的编码人员,你需要加速并学习一些能帮助你成为更好的开发人员的东西。在本章中,我们将看看可以加快工作流程的文本编辑器。然后,我们将看看CSS 重置,它重置默认浏览器,使其样式减少到最低,并内置浏览器开发者工具,帮助我们排除代码故障。然后,我们将看看如何使用类和 ID 重命名元素,并使用后代选择器限定我们的选择器。

文本编辑器

HTML、CSS 和 JavaScript 可以在任何文本编辑应用程序中编写。这是这三种核心网络语言的伟大之处之一。问题在于,编写 HTML、CSS 和 JavaScript 极易出错。对于 CSS,逗号、分号和大括号需要在正确的位置输入。在大多数情况下,需要完美地遵守特定的语法,否则你的页面将无法按预期渲染。以下是 Mac 上的 TextEdit 的示例。它与 Windows 上的记事本类似,因为它没有许多使编写代码变得容易的功能:

无论如何,让我们在 TextEdit 中写一些代码。我们从 HTML 文档类型开始。之后,我们添加一个 HTML 开放和闭合标签,然后是head标签,里面是title标签。你很快就会意识到,这是一个相当乏味的过程,也就是在 TextEdit 中编写代码。我们可以在这里写代码,但我们真的得不到任何东西,没有语法高亮,也没有其他任何帮助:

幸运的是,一个好的文本编辑器可以真正为你做一些艰苦的工作。在本章中,我们将看看这样一个文本编辑器,即 Sublime Text 3,以及它具有的一些很好的功能,可以帮助你更好地编写 HTML 和 CSS。首先,我们将看看片段,然后我们将看看语法高亮,接着是代码建议和多个光标。Sublime Text 3 是我选择的文本编辑器,因为它快速且易于使用。我喜欢的一件事是它如何轻松自然地让我编写代码。

片段

在 Sublime Text 3 中,你只需在 HTML 文件中输入html:5,然后按下Tab键,就可以获得 HTML 的基本样板。所以,我们在 TextEdit 中必须输入的所有代码都可以很快地为我们写好:

<!DOCTYPE html> 
<html> 
<head> 
        <title></title> 
</head> 
<body> 

</body> 
</html> 

另一件事是,当你输入div并按下Tab键时,可以自动创建div的闭合标签,并将光标放在开放和闭合div标签之间:

我们可以对任何 HTML 元素做到这一点;只需输入像p这样的东西,然后按下Tab键,将光标放在中间:

太棒了!拥有如此简单的东西真的很好。

我们可以再进一步安装 Emmet 包。我强烈鼓励你这样做。这将为您提供更好的代码片段。实际上,之前生成基本 HTML 样板的html:5代码片段实际上是一个 Emmet 片段;它不是 Sublime 的标准功能:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="UTF-8">     
    <title>Document</title> 
</head> 
<body> 

</body> 
</html> 

在 Sublime 中安装包(基本上是插件)的能力,是它如此强大的另一个原因。对于 Sublime 没有默认提供的所有内容,都有一个可用的包。所以,假设你需要 ColdFusion 代码的语法高亮;有一个可用的包可以为你做到这一点。我在我的网站上有一篇文章,介绍了包安装,这非常简单。只需在richfinelli.com/installing-sublime-package-manager/上查看它:

到目前为止,这是最好的包,你应该安装的第一件事就是 Emmet。有了 Emmet,比如你去到你的 HTML 并输入像这样的东西:

div>ul>li*5>a{link$} 

这将扩展为以下内容:

<div>
  <ul>
    <li><a href="">link1</a></li>
    <li><a href="">link2</a></li>
    <li><a href="">link3</a></li>
    <li><a href="">link4</a></li>
    <li><a href="">link5</a></li>
  </ul>
</div>

请注意,$在第一个a中扩展为 1,第二个为 2,依此类推,这可能非常有用。使用类似 CSS 选择器的语法快速编写 HTML 只是 Emmet 允许你做的好事之一。

多重光标

使用 Emmet 扩展的div标签,让我们来看看 Sublime 的多重光标功能。由于我们有五个列表,我们可能需要在每个列表中输入相同的内容。如果按住cmd键并单击代码的不同行,您实际上可以创建多个光标。如图所示,您现在可以在五个不同的位置输入相同的内容:

现在,假设您想在多个光标处输入一些占位文本。首先,输入“lorem5”,或者输入“lorem”后跟任何其他数字,您将获得相应数量的占位“lorem ipsum”文本:

然后,只需按下Tab,它将自动扩展到我们的情况下,即 5 个字的 lorem ipsum 文本,如图所示:

语法高亮

让我们暂时切换到我们的 CSS。另一个将使我们的工作更加轻松的功能是语法高亮。请注意,所有规则集都遵循一种颜色方案。选择器是红色的,属性是蓝色的,值是紫色的。它们将开始嵌入到您的潜意识中:

Sublime Text 为您做的是,它微妙地指出了您的错误。我经常在需要冒号的地方输入了分号。这将导致您的 CSS 无法工作。尽管如此,语法高亮告诉我有些地方不对,因为如下截图所示,颜色方案发生了变化:

很容易发现颜色差异,但如果您不寻找它,很难看出冒号和分号之间的区别:

代码建议

有一些很酷的功能可用,比如代码完成和代码建议。因此,如果您开始输入类似border-的内容,您将获得所有以border开头的不同属性:

在这种情况下,我正在寻找border-radius,所以我可以直接转到该建议并按下Tab,它会自动为我完成任务:

我喜欢这个文本编辑器的原因还有很多,我就不一一列举了。它的价格是 70 美元,但有一个无限免费试用版,您可以用来确定您是否喜欢它——相信我,您会喜欢的。现在我并不是说 Sublime 是您应该使用或尝试的唯一文本编辑器。还有其他好的编辑器,比如 Visual Studio Code,Atom,Adobe Brackets 等。如果您使用的是其他大部分我所说的功能,并且对您有用的东西,那就坚持使用它。只是确保您不要使用记事本或文本编辑器来编写代码,因为那将非常痛苦和低效。

良好的文本编辑器对于编写良好的 HTML 和 CSS 至关重要,并将使我们的生活更加轻松。接下来,您将了解 CSS 重置以及它们如何帮助我们为编写 CSS 创建一个非常好的起点。

CSS 重置

在上一节中,您了解了良好文本编辑器的强大功能。在本节中,我们将使用该文本编辑器来探索一种称为CSS 重置的东西。开始网站需要放置并且通常是您网站样板的一部分的许多部分。我称这些部分为您的“基础层”。这个基础层的一个重要部分是 CSS 重置。重置允许您消除浏览器在默认浏览器样式方面的不一致,并一般上消除所有默认浏览器样式。它允许更轻松地使用 CSS 提供手工制作的样式。在本节中,我们将首先加载 CSS 重置,然后检查该重置并看看它在做什么。最后,我们将添加和自定义重置以满足我们的需求。

加载 Eric Meyer 的 CSS 重置

有几种不同的重置可供选择,但我已经迷上了 CSS 大师 Eric Meyer 的重置。让我们从meyerweb.com/eric/tools/css/reset/获取它:

所以,向下滚动一点,找到重置的顶部,然后只需突出显示所有代码,直到你到达闭合大括号:

切换到 Sublime,打开你的样式表,然后粘贴进去:

在保存之前,让我们打开我们网站的 index.html 文件。你可以用 Sublime 做的一件事是:如果你右键点击你的 HTML 文件,你可以选择在浏览器中打开,它会打开你的默认浏览器:

在我的情况下,是 Chrome。所以这就是没有重置的网站会是什么样子:

正如你在下一个截图中看到的,我们添加的所有 CSS 实际上移除了我们的一点点样式。这就是为什么我们称它为重置。所有文本看起来都一样——没有边距,没有填充,什么都没有。

检查 CSS 重置

在我们的样式表的顶部,有一个 CSS 注释,归功于 Eric Meyer 的重置。我们会留下这个注释:

接下来,我们有大部分的重置。这一大块代码模糊地提醒了你在 第一章 中学到的规则集,CSS 基础。它实际上只是一个带有非常长选择器的规则集。选择器中用逗号分隔了几乎每个 HTML 元素:

这意味着所有这些元素都将从声明块中接收相同的样式:

... {
  margin: 0; 
  padding: 0; 
  border: 0; 
  font-size: 100%; 
  font: inherit; 
  vertical-align: baseline; 
}

正如你在这个声明块的前三个声明中看到的,marginpaddingborder 都被设置为 0。使用值 0 和使用 0px 是一样的,只是少了两个字符。如果值是零,你就不需要指定像素。这将从所有元素中移除默认的 margin、padding 和 border。在这些声明的下面,我们有 font-size 属性,它是 100%。这一行是一个指令,使所有字体都是浏览器默认的,基本上就是 16px,因为大多数桌面浏览器的默认字体大小是 16px

在这个声明块下面,我们有新的 HTML5 元素,我们将它们的显示设置为块级。这允许一些不认识这些新元素的旧浏览器现在将它们视为块级元素。这使得 HTML5 可以在一些旧浏览器中工作:

/* HTML5 display-role reset for older browsers */ 
article, aside, details, figcaption, figure,  
footer, header, hgroup, menu, nav, section { 
    display: block; 
} 

接下来,我们有一个新的选择器和声明,将 line-height 设置为 1

body { 
  line-height: 1; 
} 

line-height 属性向下级联,这意味着如果我们在一个元素上设置它,例如 body,它将被继承到它包含的所有其他元素。值 1 是一个无单位的值,所以 1 将等于字体的大小。值 1.2 将是字体大小的 1.2 倍。所以,如果 font-size16pxline-height1,那么 line-height 将等于 16px。如果 line-height 设置为 2,你的字体大小是 16px,那么 line-height 将等于 32px

接下来在样式表中是有序和无序列表,我们从 ulol 中移除了项目符号和编号,通过级联的方式也会应用到 li 中:

ol, ul { 
    list-style: none; 
} 

在此之下,你会看到重置为 blockquoteq 元素设置了一些默认值。我很少使用块引用,而且这个重置有点长,所以通常我会删除这部分重置。但如果你经常使用这些元素,那就保留它:

blockquote, q { 
    quotes: none; 
} 
blockquote:before, blockquote:after, 
q:before, q:after { 
    content: ''; 
    content: none; 
} 

接下来,我们重置了 2 个 table 属性:border-collapseborder-spacing:我从未深入研究过,但最终处理了一些微妙的表格不一致,你在任何现代桌面浏览器中都看不到。

table { 
    border-collapse: collapse; 
    border-spacing: 0; 
} 

这基本上就是 CSS 重置的解剖。这个重置应该是你的 CSS 基础层的一部分,让你开始。我们现在将看看如何添加和自定义它。

自定义 CSS 重置

让我们更新body元素上的line-heightfont-family属性,这将建立所谓的“垂直韵律”,并使Arial成为所有元素的默认font-family

body { 
    line-height: 1.4; 
    font-family: Arial, Helvetica, sans-serif; 
} 

然后你会看到它如何影响文本,主要是在文本的行之间添加一些垂直空间:

为所有这些默认值尽早建立是很好的;这样,你就不必一遍又一遍地为 CSS 中的每个元素描述line-heightfont-family。请注意,并非所有属性都像font-familyline-height那样被子元素继承;只有某些属性具有这种效果,主要是文本级别的属性才会表现出这种行为。在这种情况下,我们在body元素上设置了这些属性,但它们被级联到了h1h2和我们的p,使它们都具有Arialfontline-height1.4

我想在我们的重置中再添加几个规则集。让我们在重置的底部留出一些空间。我想要添加的第一个是clearfix,如下一段代码所示。我现在不打算详细介绍clearfix。我将在第三章中详细解释它,使用浮动创建页面布局。这个默认值对于清除浮动非常有帮助;我们将需要它:

/* micro clear fix */ 
.grouping:before, 
.grouping:after { 
    content: " "; 
    display: table;  
} 
.grouping:after { 
    clear: both;  
} 

接下来我们要做的是为媒体元素设置max-width,以确保响应式媒体。我将在第六章中更详细地讨论这个问题,成为响应式

img, iframe, video, object { 
  max-width: 100%;  
} 

最后,我想取消对strongb元素的重置,并确保它们确实具有boldfont-weight

strong, b { 
    font-weight: bold;  
} 

所以关于重置就是这样。现在,转到我们的 HTML,我想详细说明两个不在重置中的基础层的部分:

<!doctype html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 

<!-- description --> 
    <title>Section 2-Ramping Up - Mastering CSS</title> 

<!-- stylesheets --> 
    <link rel="stylesheet" href="css/style.css"> 

<!-- stylesheets for older browsers -->       
    <!-- ie6/7 micro clearfix --> 
    <!--[if lte IE 7]> 
        <style> 
        .grouping { 
            *zoom: 1; 
        } 
        </style> 
    <![endif]--> 
    <!--[if IE]> 
        <script   src="img/html5.js"></script> 
    <![endif]--> 
</head> 

首先,我们有一个处理IE 7clearfix,如下面的代码所示。你不需要知道这到底是在做什么,但你可能想知道它使clearfix在 IE7 中工作。如果你不支持较旧版本的 IE,你可以省略这部分。同样,我们将在第三章中详细讨论clearfix使用浮动创建页面布局

<!-- stylesheets for older browsers -->       
    <!-- ie6/7 micro clearfix --> 
    <!--[if lte IE 7]> 
        <style> 
        .grouping { 
            *zoom: 1; 
        } 
        </style> 
    <![endif]--> 
    <!--[if IE]> 
        <script 
         src="img/
         html5.js"></script> 
    <![endif]--> 

如果我们放大这段代码,它恰好是一个嵌入样式表。你可以看到有一个开头和结尾的style标签,中间有一个规则集:

<style> 
  .grouping { 
    *zoom: 1; 
  } 
</style> 

在嵌入样式表之外,紧接着开头的style标签的那一行是所谓的IE条件注释,它说:“如果低于或等于IE 7,请看下面的规则。”

<!--[if lte IE 7]> 

在规则集下面,我们有一个指向 HTML5 Shiv 库的script,它使旧版本的 IE 能够理解更新的 HTML5 元素:

<!--[if IE]> 
    <script 
     src="img/
     html5.js"></script> 
<![endif]--> 

这也是在 IE 条件注释中,但它是针对所有版本的 IE。实际上,IE 10 及更高版本不再支持 IE 条件注释,因此这个脚本只支持 IE9 及更低版本;然而,它确保我们的 HTML5 元素在较旧的浏览器中得到支持。同样,如果你不支持这些较旧的浏览器,也可以省略这部分。

在本节中,我们剖析了我们的 CSS 重置以及如何准备好基础层来编写代码。现在,让我们来看看Chrome DevTools部分。

Chrome DevTools

到目前为止,我们所做的大部分 CSS 都相当简单。我们尝试的时候,所有的东西都能一次成功,但这并不总是发生。通常,CSS 不起作用,我总是在想我错过了什么。我的编辑器中的语法高亮虽然有帮助,但并不能阻止我忽略错误。通常,是一个小错误导致某些东西不起作用,很难找到错误并修复它。在本节中,我们将简单地看一下如何打开 DevTools。然后,我们将在检查器中修改一些 CSS,最后查看控制台以找到错误。

如何打开开发者工具

要打开 Chrome 的 DevTools,您只需右键单击或 Ctrl + 单击页面的任何部分。您将获得一个上下文菜单,如下截图所示。当您选择“检查元素”选项时,您将进入一个全新的技术世界:

通常情况下,DevTools 会占据屏幕的下半部分。如下截图所示,左侧是浏览器渲染的 HTML,技术上称为 DOM。右侧是所有样式:

如果您在左侧悬停在某个元素上,它会在顶部突出显示。因此,如果您悬停在 h2 上或单击它,它会突出显示,如下截图所示:

如果您悬停在 <section> 上或单击它,它会在顶部突出显示:

更改检查器内的 CSS

在检查器的右侧,您将看到您在 DOM 中突出显示的任何元素的所有样式。您甚至可以单击其中任何属性或值并更改它们。因此,如果您单击 font-size 旁边的 26px,您可以将其增加到您想要的任何值。这将立即在浏览器中更新,非常酷:

您甚至可以取消选中某些属性并立即看到更改。因此,如下截图所示,如果您在 DOM 中单击 h2 元素,然后在右侧取消颜色和下边距,这对 h2 元素的更改会立即生效。只需重新选中它们即可添加它们回来:

如果您单击最后一个元素-在这种情况下是 margin-bottom 并按 Tab,它将允许您输入新的属性和值。因此,添加 margin-left-40px,看看效果;这将将此 h2 向左移动 40px

现在这些不是永久更改。一旦刷新浏览器,这些样式就会消失;但是,如果您想保留我们正在尝试的这些更改,可以复制此规则集并将其粘贴到您的代码中。它甚至告诉我们当前样式表中的此规则集位于样式表的第 86 行。如果您将鼠标悬停在那里,它将准确告诉您该文件在您网站文件夹中的位置:

使用控制台查找错误

到目前为止,我们只是在探索 Chrome DevTools 的冰山一角。例如,有时添加图像可能会有些棘手。因此,让我们通过在 HTML 中输入以下图像标签来将其添加到页面上,放在 h2 上方:

<img src="img/sharkey.png" alt="sharky"> 

如果我们保存并刷新网站,我们会发现图像根本没有显示出来,所以肯定有问题。刷新页面后,DevTools 中会出现一个带有数字一的红色错误图标。如下截图所示,这里有一个错误:

要查看错误是什么,请单击“控制台”选项卡。您会看到 sharkey.png 文件找不到:

这是很有用的信息。它告诉您这不是与权限相关的问题。这不是 403;它只是找不到它正在寻找的文件。因此,我会打开我的 images 文件夹,并确保图像在文件夹中,在这种情况下,我们假设它在那里。但是,唯一的问题是,它正在寻找的文件拼写不同:

它正在寻找 sharkey,S-H-A-R-K-E-Y,而实际文件只是 S-H-A-R-K-Y,所以很容易修复。现在您知道问题出在哪里,只需在您的 HTML 中更改名称即可:

<img src="img/sharky.png" alt="sharky"> 

如果您保存后刷新浏览器,此图像应该会显示出来:

所以这两个东西,元素检查器和控制台,在实验和故障排除代码方面都非常有用。我的最大建议是,如果你的 HTML、CSS 和 JavaScript 不像你期望的那样工作,只需打开 DevTools,看看底层。很可能你会整天都打开 DevTools。我还要补充一点,Firefox、Safari 和 IE 都有类似任务的 DevTools,对于这些浏览器的故障排除同样有用。我们只是触及了开发者工具可以做的一小部分。查看我的博客文章,了解如何使用 Chrome DevTools 进行 HTML 和 CSS 故障排除的更多信息;网址是www.richfinelli.com/troubleshooting-html-and-css

它解释了如何创建新的选择器,以及如何访问计算值而不是声明的值,这在调试 CSS 规则和确定哪些规则优先级时非常有用。它还介绍了设备仿真模式等内容:

现在你知道如何使用 Chrome DevTools,它是你故障排除 HTML 和 CSS 的王牌。接下来,你将学习如何重命名元素,这是 CSS 真正发挥作用的地方。

重命名元素——类和 ID

重命名元素是 CSS 的一个强大功能。让我为你设置一下。到目前为止,CSS 一直很好,因为我们能够保持一致。例如,所有的标题 1 都是蓝色,字体大小为 20 像素,但是如果你想让你的h1看起来不同呢?这就是重命名和分类元素真正有用的地方。在这一部分,你将学习如何根据类和 ID 重命名和样式化元素。我们将看看这将如何在我们的鲨鱼网站上得到回报,首先是类,然后是 ID。

看一下index.html文件。你会看到页面中有几个 HTML5 的<section>标签:一个在初始部分,一个在次要部分,一个在替代部分,总共有三个。其中一个如下所示:

在第二个<section>中,有三个div标签,每个标签中都有一个imgh2p和一个a标签。所以这个 HTML 并不复杂。最后一个部分看起来很像第一个部分;它只有h1h2元素以及几个段落。然而,这里有个难题:我们希望页面底部的h1与网站的主h1元素不同。解决方法是添加一个类和基于这个类的样式。所以,在替代部分的h1元素内,我们将添加类属性。我们将输入class="",并输入任何我们认为合适的名称或缩写:

<h1 class="">Feeding Frenzy</h1> 

编程和计算机科学中最困难的工作是命名事物。这个名字应该有足够的意义,这样如果另一个人遇到你的代码并且想要接着你的工作,他们不会完全迷失。所以,在我们的例子中,我们将使用alt-headline。类是区分大小写的,所以我建议你使用小写,并用破折号分隔单词,这是 CSS 中常见的命名约定。如果你使用空格,它会被视为两个类,这并不是我们想要做的事情:

<h1 class="alt-headline">Feeding Frenzy</h1> 

所以我们将保存我们的 HTML 并跳到我们的 CSS。

h1下面,我们将添加我们的类名,前面加上一个句点作为选择器。输入.alt-headline并添加一个字体大小为 40px:

h1 { 
  font-size: 70px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae; 
} 
.alt-headline { 
  font-size: 40px; 
} 

在保存之前,我们将把 CSS 窗口缩小,这样我们就可以在代码旁边看到我们的网站。滚动到你的网站上的h1,你会在左侧的预览中看到它当前是70px

当你保存 CSS 时,h1变成了40px

我把这个新的规则集放在原始的h1规则集下面,你可能会认为因为它是第二个,它会覆盖上面的那个。实际上并不是这里发生的事情。即使我把这个规则集移到h1上面,它仍然是40px。这是因为当作为选择器使用时,类比元素具有更大的权重:

.alt-headline { 
  font-size: 40px; 
} 
h1 { 
  font-size: 70px; 
  line-height:1.4; 
  font-weight: bold; 
  color: #0072ae; 
} 

以下是前面代码的输出:

为了保险起见,让我们保留原始h1选择器下面的alt-headline规则集。

分类多个元素

类也用于对多个元素进行分类。如果你想要改变中间部分的h2标签,使其与页面其他地方的h2标签相似但不同,使用类将是完美的选择。让我们进入我们的 HTML,在secondary-section中的所有div标签中添加一个类,并称之为column-title。转到The OctopusThe CrabThe Whale标题,并使用 Sublime 的多光标浏览器功能为每个标题添加class="column-title"。例如,The Octopus标题应该是这样的:

<h2 class="column-title">The Octopus</h2> 

然后,我们去到我们的 CSS,在h2下面添加.column-title。然后添加一些属性和值。添加font-stylenormal;你想要去掉italic。我们的颜色是蓝色,#0072ae,我们将使font-weight为粗体:

.column-title { 
  font-style: normal; 
  color: #0072ae; 
  font-weight: bold; 
} 

保存这个,转到浏览器,你会看到现在每个图像下面的h2标签与你在网站其他地方看到的h2标签不同:

底部和顶部的h2标签仍然是红色的,而且是斜体的:

类可以非常有用,用于命名和分类你想要看起来相同的相同元素组。接下来,让我们使用 ID 重命名一个元素。

ID

滚动到我们网站的顶部,在我们的 HTML 中,转到h1

让我们给第一个h1标签一个特殊的 ID,叫做main-site-title

<h1 id="main-site-title">Old Chompy</h1> 

有了一个 ID,你也可以在引号内使用任何你想要的名称,只要它有意义。切换到 CSS,滚动到我们的alt-headline类的下面。这就是我们将添加main-site-title的地方。编写类和 ID 的主要区别在于,我们用句点开头的类和用数字符号或井号或井号(你想叫它什么都可以)开头的 ID:

#main-site-title 

在这种情况下,我们可以说颜色是不同的:深粉色。保存并刷新网站以查看效果:

#main-site-title{ 
  color: deeppink; 
} 

以下是前面代码的输出:

你可以看到这只改变了 Old Chompy 的h1,即具有 ID 的那个。

我们应该使用类还是 ID?

现在,你可能会想,类和 ID 之间有什么区别?嗯,首先要说的是,ID 的权重比类更大,确切地说是 10 倍。保持你的选择器轻量级是可扩展、可重用的 CSS 的关键之一。权重更大到底意味着什么?这意味着它更具体,ID 将覆盖任何类。我们将深入研究第四章中的特异性规则和权重,使用模块化、可重用的 CSS 类和 CSS3 创建按钮。现在,只需知道当目标相同元素时,ID 将覆盖类。第二点是,ID 是唯一的,因此,它只能在页面上使用一次。鉴于这两点,主要是第一点,作为编码标准,我很少使用 ID 进行样式设置,因为类几乎总是足够的。

使用简单类重命名元素是如此强大,可能是 CSS 中最有用的东西。虽然命名类有时可能有点棘手,但重要的是要使名称语义化或有意义。例如,如果您正在命名您的博客文章容器,将其命名为"blog-post-container"是可以的,因为这完美地描述了它是什么。ID 虽然有其时机和地点,但并不像类那样有用。在大多数情况下最好只使用类来保持您的特异性低。在下一节中,您将学习如何使用后代选择器根据其上下文来定位元素。

后代选择器

如你在上一节中学到的,使用类重命名元素是 CSS 中非常强大的功能。然而,这并不是定位特定类型元素的唯一方法。后代选择器允许您基于其祖先元素来定位页面上的元素。这通常是必要的,因为您只想根据元素的上下文应用边距或新字体。您可以使用后代选择器来获取上下文,而无需每次都在每个元素上放置一个类。我将首先解释父元素、兄弟元素和子元素是什么,以及祖先和后代元素是什么。如果我们想要使用后代选择器,我们需要对这些清楚明了。接下来,我们将使用后代选择器的一个实际示例,并通过计算后代选择器的权重来结束。

父元素、子元素和兄弟元素

让我们去我们的 HTML,看看secondary-section中这个嵌套良好的 HTML 代码。所以基本上,我们这里有一个section标签和三个在该部分内部的div标签:

<section>
  <div>
    <figure>
      <img src="img/octopus-icon.png" alt="Octopus">
    </figure>
    <h2 class="column-title">The Octopus</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Tenticals</a>
  </div>
  <div>
    <figure>
      <img src="img/crab-icon.png" alt="Crab">
    </figure>
    <h2 class="column-title">The Crab</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Crabby</a>
  </div>
  <div>
    <figure>
      <img src="img/whale-icon.png" alt="Whale">
    </figure>
    <h2 class="column-title">The Whale</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Stuart</a>
  </div>
</section>

所以<div><section>的子元素,而<section>是父元素。换句话说,<div><section>的后代,<section><div>的祖先。<figure>也是<section>的后代,<img><section>的后代。请注意,<figure><h2><p>在 HTML 中处于同一级别,因此它们是兄弟元素,它们也都是<section>的后代。这就是它的复杂程度;没有叔叔、没有阿姨,也没有远房表兄弟。

创建后代选择器

在上一节中,重命名元素-类和 ID,我们给所有<h2>添加了一个类,因为我们知道 HTML 中secondary-section<h2>标签与所有其他<h2>标签不同。所以我们可能也想要将这个区域中的其他元素也设置为不同。这是我们可以做到最好的方式。不要在<h2>标签上放置类,而是在section标签上放置它,并从那里使用后代选择器。让我们去掉所有<h2>标签中的class="column-title"。在section元素上,让我们添加一个新的类,即secondary-section

<section class="secondary-section">
  <div>
    <figure>
      <img src="img/octopus-icon.png" alt="Octopus">
    </figure>
    <h2>The Octopus</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Tenticals</a>
  </div>
  <div>
    <figure>
      <img src="img/crab-icon.png" alt="Crab">
    </figure>
    <h2>The Crab</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Crabby</a>
  </div>
  <div>
    <figure>
      <img src="img/whale-icon.png" alt="Whale">
    </figure>
    <h2>The Whale</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Stuart</a>
  </div>
</section>

保存这个,您会看到<h2>标签失去了它们的蓝色粗体颜色,因为在 CSS 中,我们仍然在定位已经不存在的.column-title类:

现在我要做的是进入 CSS,找到.column-title类,更新它:

.secondary-section h2 {
  font-style: normal;
  color: #eb2428;
  margin-bottom: 10px;
}

这就是我们的后代选择器。如果我们保存并刷新,我们会看到它将那些<h2>标签改回我们想要的蓝色、粗体和非斜体的font-style

所以下面 CSS 中显示的.secondary-section选择器是一个后代选择器。它定位了所有在secondary-section内部的h2

.secondary-section h2 { 
  font-style: normal; 
  color: #0072ae; 
  font-weight: bold; 
} 

如果我们回头看一下 HTML,您会看到h2确实在secondary-section中:

<section class="secondary-section"> 
    <div> 
        <figure> 
            <img src="img/octopus-icon.png" alt="Octopus"> 
        </figure> 
        <h2>The Octopus</h2> 

现在我们可以更进一步。进入 CSS,在我们现有的.secondary-section h2规则集下面,键入.secondary-section p。这将定位我们secondary-section内部的段萌。添加一个深粉色的颜色,保存并刷新,您会看到现在所有的段落都是粉色的:

.secondary-section p { 
  color: deeppink; 
} 

它看起来是这样的:

我们也可以对我们的image标签进行同样的操作。如果您回顾一下 HTML,我们的image标签位于div标签内,而div标签位于figure标签内。

<section class="secondary-section"> 
    <div> 
        <figure> 
            <img src="img/octopus-icon.png" alt="Octopus"> 
        </figure> 

切换回我们的 CSS,我们可以输入选择器.secondary-section div figure img,然后我们添加一个10px的实线边框,颜色为灰色:

.secondary-section div figure img { 
  border: 10px solid #333; 
} 

以下是前面代码的输出:

虽然我们可以看到它起作用了,并且我们在网站上的图像周围有了灰色边框,但我们的选择器比我们需要的更具体。我们可以只输入img而不是divfigure,边框仍然会存在:

.secondary-section img { 
   border: 10px solid #333; 
} 

使用这样一个非常长的选择器还有另一个问题。以下选择器的权重更大,可能会覆盖您不希望覆盖的其他样式:

.secondary-section div figure img { 
   border: 10px solid #333; 
} 

这违反了保持代码轻量级的原则。特异性是我真的想要强调的东西;不要用非常长的选择器过度使用它。事实上,作为一个经验法则,尽量不要超过三级深;当然也有例外,但在编写 CSS 时要记住这一点。原因是计算 CSS 选择器的权重是一门确切的科学,我将在后面的章节中详细介绍。我至少想现在介绍一下,这样我们就可以开始熟悉它。

计算选择器的权重

一个类值为 10 分,所以.secondary-section值为 10 分。像pdiv这样的普通元素值为 1 分。因此,.secondary-section p选择器值为 11 分。.secondary-section div figure img选择器值为 13 分。让我们在值为 13 分的选择器下面创建另一个选择器,我们有.secondary-section img。然后,让我们将border-color改为blue

.secondary-section div figure img { 
   border: 10px solid #333; 
} 
.secondary-section img { 
 border: 10px solid blue; 
}

当我们保存时,我们的边框将保持灰色,因为我们最后一个选择器的点数仅为 11;它被前一个选择器的 13 点的点数击败了。这就是这些较长的后代选择器的问题,它们的权重更重:

以下是前面代码的输出:

ID 的点值为 100 分,这就是为什么我建议不要使用它们。它们有太多不必要的权重,使特异性水*飙升。分配点值听起来有点像在视频游戏中记分,但不同之处在于您希望尽量保持在这个游戏中的点值低。如果您这样做,您将能够编写更简单的 CSS。

BEM

保持特异性低的一种技巧是完全避免使用后代选择器,而是使用BEM。BEM 代表块元素修饰符,是 CSS 的命名约定。其思想是使用特定的命名约定为您最终要样式化的每个元素添加一个类。这样,每个元素的特异性得分为 10,因此每个元素的特异性相同。除此之外还有很多内容,我建议您在getbem.com/上了解更多。我倾向于使用 BEM 方法,但这并不意味着后代选择器完全需要避免。我认为它们有时机和地方。我的建议是保持您的后代选择器合理,并避免超过 3 级的较长后代选择器。

总结

在本章中,您了解了良好文本编辑器的特性,讨论了 CSS 重置,探索了 Chrome 的 DevTools 的故障排除功能,并学习了如何使用类重命名元素。在本章的最后一节中,您了解了后代选择器。

下一章是关于使用浮动创建多列层并了解浮动引起的问题的解决方案。

第三章:使用浮动创建页面布局

为了在所有浏览器中创建支持的多列布局,我们将使用浮动。浮动乍看起来非常简单,但对它们有一些不直观的怪癖,如果不完全理解可能会引起一些挫折。这可能是因为浮动的真正起源不是用于布局,而是为了实现文本围绕图像轻松流动的常见杂志技术。因此,在本章中,我们将深入研究浮动。我们将介绍浮动的基本用法,然后通过使用浮动创建布局(并在后续部分中解决浮动引起的头痛)

浮动介绍-围绕图像流动的文本

让我们从介绍浮动开始这一章。我们将讨论浮动的最初目的,然后是它们引起的基本问题以及如何清除浮动后面的元素。在本节中,我们还将开始制作一个关于鲨鱼电影的新 HTML 页面,您可以在书籍下载包中找到。

鲨鱼电影页面

这里有一个关于鲨鱼电影的新 HTML 页面。如果您查看此页面,您会看到一个图像位于标题的顶部,文本的顶部,并且链接的顶部;当您向下滚动时,您会看到每个电影都有三个类似的部分:

HTML 非常简单。有三个部分,每个部分都有一个wrapper类的div标记,用于居中内容。在wrapper类中,有一个包含图像的锚点标记。在锚点标记下面是一个包含标题和一些段落文本的h1标记。然后是一个锚点标记,这是一个链接以了解更多信息。以下是第一部分的屏幕截图:

浮动的最初目的

让我们看一下最终项目,如下图所示。我们希望将图像浮动到左侧,并使标题和文本围绕它流动:

让我们在 CSS 中定位该图像。我们不是在选择器中定位图像,而是实际上定位图像的容器,即具有figure类的锚点标记:

<a href="#" class="figure"> 

我不想只将.figure类作为我的选择器目标,因为我可能会在其他图像容器上使用这个类,并且可能不希望它们全部浮动。因此,让我们基于其父级使用后代选择器。其父级位于部分顶部,具有多个类:content-blockstyle-1wave-border

<section id="jaws" class="content-block style-1 wave-border"> 

这是一种模块化的方法,我们将在下一节中更详细地介绍。我们正在寻找的主要类是content-blockstyle-1style-2类只控制两种不同的颜色方案,wave-border添加了波浪的重复背景图像到第一部分的顶部。最后,在我们的 CSS 中,我们的选择器将是.content-block .figure,因此我们正在针对任何具有content-block类的元素中具有figure类的元素进行定位:

.content-block .figure { 
  margin: 30px; 
} 

因此,在这个规则集下,我们将在margin属性下输入float: left

.content-block .figure { 
  margin: 30px; 
 float: left 
} 

当我们刷新页面时,我们看到一切都按计划进行。这几乎太简单了。我们在所有三个部分几乎完全实现了我们的目标:

让我们在我们的 CSS 中为h1p添加背景颜色,只是为了看看这里发生了什么。我们将为h1赋予deeppink的背景颜色,并通过content-blockp赋予green的背景颜色:

.content-block h1 { 
  color: #fff; 
  background-color: deeppink; 
} 
.content-block p { 
  font-family: Georgia, 'Times New Roman' sans-serif; 
  background-color: green; 
} 

以下是前面代码块的输出:

注意背景是如何在图像后面的。文本向右流动,但元素本身不再将浮动元素,即图像,视为正常流的一部分。当它们的显示属性受到影响时,浮动元素本身会发生变化。例如,浮动的锚点标签,或者真的是一个 class 为figure的锚点,开始像块级元素一样行事。它现在会响应宽度和上下边距;正如我们所见,它已经响应了下边距。然而,它不一定会强制换行。让我们将它向右浮动,它应该有一个非常相似的效果:

.content-block .figure { 
  margin: 30px; 
  float: right; 
} 

以下是上述代码块的输出:

使用 clear 属性解决浮动的基本问题

我们可以使用clear属性来阻止浮动元素下面的元素表现异常。例如,让我们给段落添加clear属性。我们将添加clear: both,它清除左右两侧的浮动元素:

.content-block p { 
  font-family: Georgia, 'Times New Roman' sans-serif; 
  background-color: green; 
  clear: both; 
} 

现在,当您刷新时,您会看到段落文本坐在浮动元素下面:

我们也可以对h1做同样的事情,它会位于下面:

.content-block .figure { 
  margin: 30px; 
  float: right; 
} 
.content-block h1 { 
  color: #fff; 
  background-color: deeppink; 
 clear: right; 
} 

我们也可以直接说clear: right,因为在它上面的浮动是向右浮动的。

保存 CSS 并查看网站后,您会看到它起作用了。h1标签也位于.figure下面:

然而,如果您在h1的规则集中输入clear: left,它不一定会起作用,因为这里没有左浮动的元素:

.content-block h1 { 
  color: #fff; 
  background-color: deeppink; 
  clear: left; 
} 

以下是上述代码块的输出:

在这里,Nonefloatclear的默认值。因此,我们可以在这两个上说clear: none,它将恢复到添加clear属性之前的状态:

.content-block h1 { 
  color: #fff; 
  background-color: deeppink; 
  clear: none; 
} 
.content-block p { 
  font-family: Georgia, 'Times New Roman' sans-serif; 
  background-color: green; 
  clear: none; 
} 

以下是上述代码块的输出:

然而,由于clear: none是默认值,您可以从这两个选择器中删除整个属性;这将对网站产生相同的影响。我几乎从不使用 clear left 和 clear right;both值似乎在大多数情况下都足够了。

在本节中,我们看到了浮动元素的传统用法,以及浮动元素下面的元素如何围绕浮动元素流动。这可以使用clear属性来停止。这种技术很有用,但老实说,浮动对于构建多列布局更有用。现在让我们来看看。

创建多列布局

浮动设计用于围绕图像流动文本。然而,浮动也是构建多列布局的最常见方式。在本节中,我们将看看如何将元素浮动到彼此旁边以创建页面布局。

所以,我们目前在 HTML 中次要部分有三个 class 为columndiv标签:

以下截图展示了最终的网站。这是我们的目标。我们希望有三个相等的列,之间有一个小的间距或边距:

在我们当前的网站中,列是堆叠在彼此上面的。现在,我们有简单的行,所以我们想使用浮动来解决这个问题。在我们最终的网站中,我们希望将所有内容都居中在页面的中间,但现在,我们所有的内容都从浏览器窗口的一边到几乎是浏览器窗口的另一边:

让我们通过居中我们的div标签来修复这个问题。

居中一个元素

我们真正需要做的是将整个内容包裹在一个div标签中;所以让我们这样做。进入 HTML 文件。在开放的section标签下一行,添加<div class="wrapper">。并在关闭的section标签之前,用</div>关闭它:

<section class="secondary-section"> 
    <div class="wrapper">
        <div>...</div>
        <div>...</div>
        <div>...</div>
    </div> 
</section>

现在,切换到 CSS 文件。.wrapper标签将是一个更可重用的类。为了居中任何元素,我们会给它一个 margin,并且我们会使用两值语法:上和下将是零,左和右将是自动。我们还必须给它一个宽度为960px。没有宽度,你真的无法使用这种 margin 技术来居中它:

.wrapper { 
  margin: 0 auto; 
  width: 960px; 
} 

好了,我们完成了;所有的内容现在应该都居中在这个包裹器内:

就像我说的,wrapper类很好而且可重用。我会在网站的任何地方使用wrapper类来居中一组元素。

浮动列

所以,回到我们的业务顺序:在我们的主页上浮动这三列。为了做到这一点,我想给每个div标签一个column类,这样我们就可以对其进行样式设置。所以,在 HTML 中,让我们去到次要部分的每个div标签,并使用 Sublime Text 的多重光标功能一次性给它们都添加class="column"

<section class="secondary-section"> 
    <div class="wrapper">
        <div class="column">...</div>
        <div class="column">...</div>
        <div class="column">...</div>
    </div> 
</section>

在我的 CSS 中,我已经用一个大的注释标注了这部分 CSS,我鼓励你也这样做。

在这个注释下面,我们将针对.column并应用float: left。宽度将是320px

/**************** 
3 columns 
****************/ 
.column { 
  float: left; 
  width: 320px; 
} 

理想情况下,每当你浮动元素时,尝试添加一个宽度。如果所有三列都是320px,那将恰好加起来是 960 像素,正好适应那个包裹器的宽度。如果我们使用的数字加起来超过 960 像素,那么不是所有的三个div标签都会适应那个空间。其中一个会换行到底部,所以它们不会都在一行上。重要的是,所有浮动的div标签的宽度永远不要超过父div标签的宽度。所以保存这个并刷新网站:

看起来所有三列都浮动在一起。这个效果还不错,只是列之间没有任何间距。所以让我们回到我们的代码,给它一个margin-left属性,值为30px。保存并刷新浏览器:

.column { 
  float: left; 
  width: 320px; 
  margin-left: 30px; 
} 

以下是前面代码块的输出:

我们得到了30px的边距,但我们的第三列也因为无法适应允许的宽度而漂移到了底部。

让我们通过将列的宽度减小到每个300px来修复这个问题:

.column { 
  float: left; 
  width: 300px; 
  margin-left: 30px; 
} 

现在如果你看浏览器,你也会看到我们不需要在第一列上添加margin-left。我们不需要在空白处旁边添加左边距:

让我们去掉第一列的左边距。我们可以通过使用一个叫做first child的伪类来定位那个单独的.column属性。

使用伪类定位.column

添加.column:first-child选择器将定位到列元素的第一次出现。我们将把margin-left添加为零。当我们保存这个时,我们得到了三个相等的列,每个都有一个margin-left,除了第一个:

.column:first-child { 
  margin-left: 0; 
} 

以下是前面代码块的输出:

这种技术对于两列、四列或任意数量的列都同样适用。

折叠的容器

所以,列的一切都很好,除了如果你尝试向下滚动页面,你会发现我们离底部非常紧。

让我们看看当我们为包裹所有内容的容器secondary-section添加margin-bottom属性时会发生什么:假设margin-bottom: 40px

/**************** 
3 columns 
****************/ 
.secondary-section { 
  margin-bottom: 40px; 
} 

如果我们保存这个,它在浏览器中实际上什么也没做。内容仍然紧贴着底部。让我进一步说明这个问题。如果我有一个绿色的背景颜色,那么你会期望整个背景都是绿色的:

.secondary-section { 
  margin-bottom: 40px; 
  background-color: green; 
} 

然而,如果我们添加前面的代码并保存,背景颜色并没有变成绿色。所以,让我们实际检查一下这个元素。使用 Chrome 的 DevTools 在浏览器中检查secondary-section。我们会看到margin-bottombackground-color都在被应用的过程中。但我们在页面上看不到任何绿色的东西:

当您将鼠标悬停在secondary-section元素上时,您将看到它在屏幕上以桃红色突出显示的空间(如果您正在查看打印副本,则将以不同的灰色阴影显示):

容器实际上已经坍塌了。当父元素内的所有元素都被浮动时,就会发生这种情况:容器坍塌,浮动被带出正常流,容器没有高度。

让我们看看我们能做些什么来解决这个问题。

解决浮动的问题

好的,让我们看看我们的问题。您已经学会了如何将堆叠的行转换为水*列,以实现多列布局,但是我们围绕浮动元素的包含元素已经完全坍塌并且失去了高度,因为它内部的所有元素都被浮动了。作为一个坍塌的元素,它看起来不像是响应margin-bottom属性或我们分配给它的background-color。因此,在本节中,我们将看看四种不同的方法来解决这个坍塌,并尝试理解处理它的最佳方法。首先,我们将使用clear方法,然后是overflow: hidden方法,然后是float方法,最后是最优选的方法:clearfix hack。

使用清除方法

让我们使用clear属性来解决这个问题。在secondary-section的末尾,我们将向一个新的div添加一个clear类,使用以下代码:

<div class="clear"></div> 

接下来,我们将进入我们的 CSS,在全局样式保留区域,在针对wrapper类的规则集下面;这是我们将创建clear选择器并添加clear: both的地方:

/***************
Global
***************/
::-moz-selection {
  background-color: #eb2428; 
}
::selection {
  background-color: #eb2428; 
}
.wrapper {
  margin: 0 auto;
  width: 960px;
}
.clear {
 clear: both;
}

因此,如果我们保存并返回到浏览器,我们的背景颜色将是绿色,底部间距为50px。一切都运行得很好:

然而,我们在页面上添加了额外的非语义标记。我们甚至可能因此而受到 SEO 的扣分。让我们探索其他方法来做到这一点,而不添加额外的标记。去掉我们刚刚添加到 HTML 中的额外标记:

<div class="clear"></div> <!-- delete this -->

我们的坍塌将返回。现在我们将无法再看到绿色的背景;这就是我们知道坍塌存在的方式:

使用 overflow 属性和 hidden 值

我们将看看的下一种方法是overflow: hidden。转到您的 CSS 并找到.secondary-section类。我们可以做的是添加值为hiddenoverflow属性:

.secondary-section { 
  margin-bottom: 50px; 
  background-color: #7EEEAF; 
  overflow: hidden; 
} 

overflow: hidden是一个真正的 hack。它从来不是用来修复坍塌的容器的;它是用来隐藏任何溢出其容器的内容图像或文本的。然而,神奇的是,overflow: hidden也清除了坍塌。如果我们保存我们的 CSS 并转到我们的网站,我们将看到这一点,因为背景现在是绿色的:

overflow: hidden的一个小问题是,您可能希望内容溢出容器,例如下拉菜单或工具提示。overflow: hidden hack 将隐藏溢出内容 - 没有什么意外!这是一个解决方案,但并不总是理想的。例如,在我们确切的情况下,我们可能希望这只章鱼有点从容器中爬出来。让我们进入 Chrome DevTools 并给它margin-top: -50px。如您所见,现在图像的顶部不再显示,溢出被隐藏了:

所以这对我们来说不是一个好的解决方案。让我们从我们的 CSS 文件中删除overflow: hidden声明,并看看下一种方法:float方法。

浮动方法

我们可以通过将容器浮动到左侧或右侧来防止元素坍塌。让我们这样做;让我们向我们的secondary-section添加float: leftfloat: right。任何一个都可以:

.secondary-section { 
  margin-bottom: 50px; 
  background-color: #7EEEAF; 
  float: left; 
} 

一旦我们保存了这个,我们会看到我们有绿色的背景,所以坍塌不再发生,但明显的问题是我们已经向左浮动了。我们希望这个 div 居中:

这种方法显然有一个明显的缺点。有些情况和一些情况下它可能是一个完美的解决方案,但在这种情况下,有一个明显的问题:我们不再居中。从你的 CSS 中删除float: left,并探索我的最喜欢,我认为最好的解决方案:clearfix hack

clearfix hack

如果我们看一下下面的 CSS,在我们的重置之后,这些规则集构成了我们的clearfix

/* clearfix */ 
.grouping:before, 
.grouping:after { 
  content: " "; 
  display: table;  
} 
.grouping:after { 
  clear: both; 
} 

这段代码实际上是我们 CSS 的基础层。基本上,这样做是在任何具有grouping类的元素之前和之后创建一个伪元素。这个伪元素有一个空白内容和显示设置为表。然后我们在代码块下面有after伪元素,它有clear属性设置,并清除它之前的任何浮动。

有时你可能会看到clearfix作为类名,而不是grouping。我倾向于使用grouping,因为我认为这更有意义;你在某种程度上在分组元素,这更有语义。不过这并不重要;clearfixgrouping都能做同样的事情。

好了,这已经在 CSS 中了,所以我们除了去 HTML 中的secondary-section,只需添加这个grouping类。所以我们给它添加了第二个类:

<section class="secondary-section grouping"> 

当我们保存并刷新时,我们有了我们的容器;坍塌已经解决了。在下一张截图中,我们看到了背景颜色和底部边距。我们在这里的情况非常好:

clearfix 在 IE8 中可以工作,但在 IE7 中不行,除非你添加一个 IE 特定的样式表。这实际上是放在索引中的。所以在index.html的头部,我有这个样式表,如下一张截图所示,专门为 IE7。它的作用是给 grouping 一个1的缩放。这会触发旧版本的 IE 中的hasLayout,从而清除坍塌:

如果这对你来说没有太多意义,不要担心;它不一定要有。不过,知道这可以让 clearfix hack 在旧版本的 IE 中工作。总的来说,这是非常深的浏览器支持,而且非常容易使用,这是许多前端开发人员首选的清除坍塌浮动的方法。这绝对是我解决问题的最喜欢的方法。

在这一节中,你学会了使用以下方法来修复由浮动引起的父元素坍塌:

  1. 一个空的清除 div。

  2. overflow: hidden

  3. 通过浮动父元素。这三种方法都有效,但都有轻微到严重的缺陷。

  4. 具有非常深的支持、易于使用和语义化风格的 clearfix hack 往往是最佳方法。我在每个项目中都使用它。它可以轻松解决浮动的最大问题之一:坍塌。我喜欢 clearfix hack 的另一点是它是一种非常模块化的 CSS 方法。只需在你的标记中的任何地方添加clearfix类,你就可以摆脱坍塌的容器。

总结

在本章中,我们讨论了传统的浮动元素的使用:围绕图像流动文本。然后我们看了看使用浮动来构建多列布局。最后,我们学会了如何解决使用浮动时出现的问题。我们发现 clearfix hack 是修复浮动坍塌的最佳方法。在下一章中,我们将扩展模块化 CSS,使用模块化方法创建现代按钮。

第四章:使用模块化、可重用的 CSS 类和 CSS3 创建按钮

拥有模块化和可重用的 CSS 使其组织有序且简洁,从而避免出现让你抓狂的情况。如果能够在标记中的任何位置使用其类并且不需要这些类被父元素限定为后代选择器,那么 CSS 就是“可重用”的。术语“模块化”指的是通过向其添加另一个类来为按钮添加变化的能力,以便一个元素可以有两个类,这两个类可以组合在一起形成非常不同的东西。

一个很好的例子是如何编写模块化和可重用的 CSS:创建按钮。然而,这个概念应该应用到网站的所有组件中。在本章中,我们有很多内容要讨论。在前两节中,我们将介绍模块化 CSS 和多个类,然后我们将转变话题,讨论选择器如何在特异性规则部分互相覆盖。然后,我们将深入研究 CSS3 的过渡、变换和渐变,并逐步介绍创建和样式化一个大的呼吁行动按钮的每个步骤。

使用模块化 CSS 创建按钮

在这一部分,我们将创建具有模块化 CSS 类的按钮。我们将找出模块化 CSS 究竟是什么,以及为什么它很有用。首先,让我们看一下我们将要创建的最终网站,并探索我们将使用的不同按钮类型。

不同的按钮类型

在顶部,我们有一个巨大的“立即订阅”呼吁行动按钮:

在首页向下滚动一点,我们会发现这些带有漂亮悬停状态的“幽灵”按钮:

在电影页面上,我们有相同的标准按钮。它只是颜色不同,并且位置有点不同。这出现在所有三个电影部分中:

因此,在这一部分,我们将在所有三列的底部构建这些标准按钮:

构建标准按钮

我们的起点让我们走了很长一段路,但应该相当容易:

让我们跳转到我们次要部分的 HTML 中:

我将在每个列底部的三个锚元素中添加button类。

<a href="#" class="button">Tenticals &raquo;</a>

现在,跳到我们 CSS 的底部,让我们为我们的新部分添加一个巨大的注释,并命名为“按钮”。

/****************
Buttons
****************/

这是我们所有按钮样式的地方。

我们要做的是创建.button选择器。所有共享的样式属性都将放在这里。我们不会在按钮选择器中放置任何定位属性,因为按钮可以放置在任何位置:

/****************
Buttons
****************/
.button {
}

让我们从添加边框开始。我们将选择两像素实线和深灰色。我们将把相同的颜色应用于文本:

.button {
   border: 2px solid #333;
   color: #333;
}

保存并刷新浏览器后,它开始略微类似按钮:

现在我们需要添加一些填充。让我们回到我们的 CSS,并使用两个值的填充快捷方式:10px用于顶部和底部,0px用于左右。这是因为我们最终会将文本居中。让我们还将显示属性更改为block,因为这些是内联元素,我们希望它们的行为像块级元素一样:

.button{
   border: 2px solid #333;
   color: #333;
   padding: 10px 0;
 display: block;
}

保存这个,刷新浏览器,看看效果:

正如你所看到的,我们现在必须添加一些文本级属性。首先,让我们添加一个字体系列。我们将选择典型的sans-serif堆栈:Arial, Helvetica, sans-serif。然后,使用text-align属性将文本对齐到元素的中心。我们还将把font-weight设置为bold,然后使用另一个叫做letter-spacing的属性,并添加一个值1.5px。如果你不熟悉letter-spacing属性,它基本上就是你所想的——它在每个字母之间创建了水*空间:

/****************
Buttons
****************/
.button{
  border: 2px solid #333;
  color: #333;
  padding: 10px 0;
  display: block;
  font-family: Arial, Helvetica, sans-serif;
  text-align: center;
 font-weight: bold;
 letter-spacing: 1.5px;
}

保存并刷新网站后,我们将拥有我们的按钮元素。目前还没有悬停状态;我们将在另一节中介绍:

如果您现在转到电影页面,您将看到那里的“了解更多”链接,也需要成为按钮:

让我们跳到shark-movies.html文件中的标记,并做同样的事情。在每个电影部分底部的每个锚点标签中添加button类:

<a href="" class="button">Learn More</a>

保存并刷新,您将立即得到一个按钮:

它有点起作用;我们有一个按钮,但不完全。它们看起来像按钮,但颜色不对,太宽,而且没有定位到右侧。此外,文本与背景的对比度不佳,特别是在较暗的部分。因此,我们需要做一些修复,因为这些按钮与主页上的按钮不同,主页上的按钮是全宽的。

让我们现在修复这些按钮,看看如何更加模块化,并添加多个类以变化按钮。

多个类

总结一下,到目前为止,您已经学会了如何创建一个可以在网页的任何地方重复使用的类,以创建一个按钮。然而,按钮在网站上往往会有所不同。例如,您可能会有像“确定”、“关闭”、“取消”、“提交”和“添加到购物车”等按钮。所有这些按钮都有不同的含义,因此它们的颜色或样式略有不同。在某些情况下,例如我们的电影和索引页面,按钮最终会因页面布局的差异而有所不同。在本节中,我们将变得更加模块化,并学习如何使用多个类来改变我们按钮的外观。我们将看一些多个类如何为我们提供一些便利,以便在整个网站上样式化我们的按钮。

以下截图展示了最终的网站。我们希望按钮看起来像“了解更多”按钮。它们向右浮动,是白色的,有白色边框,宽度更窄:

目前我们的网站情况如下。我们的按钮是深灰色的,并且宽度全屏,但不符合我们的要求:

更改按钮的宽度

首先,让我们通过创建一个名为button-narrow的新类来解决宽度问题。在我们的 CSS 中,在上一节创建的.button规则集下面,创建一个名为.button-narrow的新类。非常简单,宽度将是25%

/****************
Buttons
****************/
.button {
  border: 2px solid #333;
  color: #333;
  padding: 10px 0;
  display: block;
  font-family: Arial, Helvetica, sans-serif;
  text-align: center;
  font-weight: bold;
  letter-spacing: 1.5px;
}
.button-narrow {
 width: 25%;
}

保存这个。接下来,转到shark-movies.html文件。转到每个带有button类的三个锚点标签。我只会展示“了解更多”按钮的代码,但对于所有按钮,代码更改都是相同的:

<a href="" class="button ">Learn More</a>

让我们将新的button-narrow类添加到这些元素中:

<a href="" class="button button-narrow">Learn More</a>

保存后,转到浏览器,您会看到所有三个部分的按钮现在都变得更小了:

让我们再进一步,创建另一个名为button-alt的类,用于控制边框和字体颜色。

更改按钮的边框和字体颜色

让我们也将button-alt类添加到这 3 个“了解更多”按钮中。

<a href="" class="button button-narrow button-alt">Learn More</a>

现在转到 CSS,并在我们的.button-narrow选择器下方输入.button-alt作为我们的新选择器。我选择button-alt作为类,因为这是另一种按钮颜色。然后,指定color为白色,border-color为白色:

.button-alt {
  color: #fff;
  border-color: #fff;
}

保存后,转到网站,您会看到我们几乎到达目标了:

定位按钮

最后一件事是按钮的位置。它目前位于左侧,需要位于右侧。当然,我们可以创建一个名为button-right的类,将按钮浮动到右侧。然而,将元素浮动到左侧或右侧是非常常见的,甚至在按钮之外也是如此。最好将类名保持更通用,例如float rightfloat left。这样,我们可以将任何东西浮动到右侧或左侧。在我的情况下,在 CSS 的Buttons部分之前,我有我的全局样式:

在全局列表下面,我将复制我的标准模块化样式库:

这是我多年来建立的基本样板的一部分,其中包括float-leftfloat-rightclearboldhidden和一些其他常见的模块化类。您可以在下载包中看到完整的列表。这些可以在整个网站中重复使用。现在,在shark-movies.html文件中,让我们简单地将float-right类添加到我们的三个锚标签中:

<a href="" class="button button-narrow button-alt float-right">Learn More</a>

保存并刷新鲨鱼电影网站。您现在将看到按钮向右浮动:

我还应该指出,我们每个部分周围的容器不会坍塌。让我们进入 DevTools 看看原因。以下截图中突出显示的具有content-block类的部分之所以没有坍塌,是因为我向其中添加了 clearfix grouping类:

如果我将这个删除并从那一行中删除grouping,您将看到坍塌会发生。因为我们有这个grouping类,我们确保这个部分不会坍塌:

因此,总之,我们遵循了一种非常模块化和可重用的方法来构建我们的按钮,并创建了一些可以用来改变按钮样式的模块化按钮相关类。我们还有其他方法可以做到这一点。我可以使用后代选择器来根据它们的父级样式化按钮。这样,content-block 内的所有按钮将始终向右浮动,并且是白色而不是深灰色。如果除了内容块之外的其他区域也提供了相同的替代按钮样式,这将是一个不错的选择。接下来,让我们谈谈为什么需要一种模块化、可重用和轻量级的 CSS 方法。我们将通过讨论特异性规则来做到这一点。

特异性规则

我们开始了解到,CSS 的模块化方法使我们能够将类作为 CSS 的小块,可以在网页的任何地方使用来为任何元素设置样式。这使得编写 CSS 非常方便。然而,这仅在 CSS 保持轻量级时才有效。正如您将在本节中了解到的,每个 CSS 选择器都可以在一个规模上进行衡量,最重的选择器将赢得两个竞争选择器之间的样式战。因此,我将首先解释不同选择器的权重以及它们如何相互推翻。然后,我们将稍微讨论一下通用选择器和!important声明如何适用于选择器的权重。

不同选择器的权重

所有选择器都被分配了一个权重,最重的选择器在存在冲突的 CSS 规则时优先。在构建网站时,通常会出现一种情况,即通用样式会在不同情况下被更具体的样式覆盖。在样式表顶部的全局区域中,为所有段落元素设置了一个非常广泛的样式:

p {
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 20px;
}

字体大小为16px。有一个line-height属性为1.6margin-bottom20px。自然地,我可能想要在不同情况下覆盖line-heightmargin-bottom。让我们尝试使用选择器.content-block p来覆盖它:

p {
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 20px;
}
.content-block p {

}

这是一个后代选择器。现在让我们添加line-height1.8margin-bottom40px

.content-block p {
 line-height: 1.8;
 margin-bottom: 40px
}

切换到网站上查看原始设置。这个后代选择器应该针对主文本区域中的任何内容或段落文本:

当我们保存我们的 CSS 并刷新网站时,我们会得到更多的行高和底部边距,就像你在下面的截图中看到的那样:

那么每个选择器的权重是多少呢?嗯,你可以认为内联样式值为 1000 分,ID 值为 100 分,类值为 10 分,元素值为 1 分。在我们一直在看的例子中,单个p元素选择器的值只有 1 分,而.content-block p,它是一个类和一个元素,值为 11 分:

这个分数系统决定了哪个选择器会获胜。在这种情况下,这两个选择器都是针对段落元素的;然而,因为.content-block p值为 11 分,它将击败它上面的规则集,因为作为元素选择器,它只值 1 分:

p {
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 20px;
}
.content-block p {
  line-height: 1.8;
  margin-bottom: 40px
}

ID 的权重是 100 分,是类的 10 倍。在我们的shark-movies.html文件中,你可以看到《大白鲨》的第一部分有jaws ID:

<section id="jaws" class="content-block style-1 wave-border grouping">

现在让我们切换回我们的样式表,并创建一个新的规则集,如下所示:

#jaws p {
  line-height: 3;
}

当我们刷新浏览器时,你会看到line-height3生效了:

我们使用的 ID 值为 101 分的选择器将覆盖具有类和元素值仅为 11 分的选择器,以及仅具有 1 分元素值的选择器。ID 的权重,在我的情况下意味着我倾向于在可以的情况下远离它们进行样式设置。ID 也比类更不灵活;它们在页面上只能使用一次。我真的尽量避免使用它们,因为它们不太可重用。

另一件要避免的事情是内联样式,我们可以认为它值 1000 分。内联样式将击败一切,包括具有 ID 的选择器。让我们再次定位段落来演示这一点。我们将直接进入shark-movies.html文件,并实际添加一个内联样式。在jaws部分的h1选择器下面,我们有我们的段落,所以让我们给它添加我们的内联样式。我们将输入style="line-height: 1"

<p style="line-height: 1">

当我们保存这个时,我们会返回到我们的网站并刷新浏览器。一旦我们这样做,我们会看到line-height使用了内联样式,因为它的权重更大。它比我们样式表中的所有其他选择器都要重:

那么什么能击败内联样式呢?你还有一个王牌:!important声明。

!important 声明

让我们看看!important声明是如何工作的。我们回到 CSS 中的这个元素选择器,它只是一个段落:

p {
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 20px;
}

我们可以进入line-height值本身,然后在该行的末尾添加!important。行高将增加到1.6

p {
  font-size: 16px;
  line-height: 1.6 !important;
  margin-bottom: 20px;
}

以下是上述代码的输出:

让我们检查一下这个段落,确保它实际上使用了!important声明。正如你在 Chrome 的 DevTools 中看到的那样,值为 1 的内联样式被划掉了;我们可以看到值为 101 分的 ID 与一个元素也被划掉了:

如果我们再往下滚动一点,我们会看到我们的类加上被划掉的元素:

再往下滚动一点,你会看到它确实是使用了带有!important声明的元素选择器的line-height

添加!important声明实际上可以被认为值为 10,000 分,击败了所有类、ID 和内联样式的那个属性。就像你想远离内联样式和 ID 一样,你也想远离使用!important声明,除非你有一个非常好的理由。还有另一个选择器的权重小于 1 分:通用选择器。

通用选择器

通用选择器只是一个星号。它值为零,因此只有在没有其他选择器竞争时才起作用。在我们的 CSS 中去掉!important声明。在我们的其他规则集之上,让我们添加一个*作为选择器,并添加font-size9pxline-height.5

* {
  font-size: 9px;
  line-height: .5;
}

从技术上讲,这个星号应该应用于每个元素,除非定义了更具体的内容。任何东西都能打败*选择器。现在当您转到网站时,您会发现一旦去掉!important声明,您就会回到内联样式的line-height属性:

在 DevTools 中,我们可以看到通用选择器最终被划掉了。它没有应用于这段文字或实际上任何东西。它在页面上没有被应用太多:

由于它的权重很少,很多时候你会看到通用选择器被用作原始重置。您可以将margin: 0padding: 0的属性和值添加到通用选择器中,并将其放在样式表的顶部。这将真正将每个元素的边距和填充重置为零:

* {
  margin: 0;
  padding: 0;
}

让我们重新审视一下显示不同选择器权重的图表。您已经学会了将!important视为价值 10000 分,通用选择器视为价值零分:

点系统的现实

但实际上,我描述的点系统并不完全真实。假设您有这样一个选择器,包括 11 个元素:div div p p p p p p p p p { ... }。使用我已经描述的系统,这值得 11 分。我已经描述了类选择器的值为 10 分。然而,长元素选择器永远不会打败一个单一的类:.i-beat-any-number-of-elements。因此从技术上讲,元素的值为 0,0,0,1,类的值为 0,0,1,0,ID 的值为 0,1,0,0,内联样式的值为 1,0,0,0。但是!停顿以强调。如果您创建了一个由超过 10 个元素组成的选择器,那么您将会有非常糟糕的体验。那将是一个非常糟糕的主意,我建议您尽量不要超过 3 或 4 个。因此,与其认为元素的值为 0,0,0,1,类的值为 0,0,1,0,我们可以根据我之前描述的术语来思考,其中类的值为 10 分,元素的值为 1 分,依此类推。

另外,重要的是要记住,以任何合理的规模编写 CSS 都更容易,因为您可以轻松地创建模块化的可重用类,形式为按钮。创建现代网站的一个重要部分是在必要时覆盖样式;您不希望这变得困难。我强烈建议您坚持使用类和元素选择器,并且在使用!important声明时要非常保守;完全避免内联样式和 ID。

过渡

了解 CSS 的特异性以及选择器如何相互覆盖可以在使用 CSS 时减轻很多挫折感。现在我们对此有了更好的理解,让我们回到我们的项目,完成我们一直在工作的按钮的样式。按钮如果没有流畅的悬停状态和*滑的过渡就是不完整的。我们将通过使用伪选择器:hover来开始本节。然后,我们将通过过渡使其*滑,最后讨论供应商前缀何时是必要的。

创建悬停状态

目前,我们网站上的按钮是幽灵按钮。它们没有背景颜色,有深灰色边框或深灰色文本,如下面的屏幕截图所示:

我们想要创建一个按钮,当悬停时,它将具有深灰色背景颜色,并显示白色文本。因此,让我们使用:hover伪类来实现这一点。在第一个现有的.button规则集下,添加一个名为.button:hover的新选择器。添加background-color: #333,并将文本颜色设置为白色:

/****************
Buttons
****************/
.button {
 border: 2px solid #333;
 color: #333;
 padding: 10px 0;
 display: block;
 font-family: Arial, Helvetica, sans-serif;
 text-align: center;
 font-weight: bold;
 letter-spacing: 1.5px;
}
.button:hover {
 background-color: #333;
 color: #fff;
}

注意我没有使用十六进制代码的全部六个字符。如果所有六个字符都相同,只使用三个字符也是可以的。现在如果我们保存并刷新,当我们将鼠标悬停在按钮上时,我们将有悬停状态:

然而,悬停状态的过渡非常突然;它立即发生。因此,下一步是使用 CSS3 过渡属性来*滑地从无悬停到悬停的状态变化。

使用过渡属性

我们可以选择要过渡的属性、过渡的持续时间和过渡的时间函数。所有三个属性都可以分别列出为transition-propertytransition-durationtransition-timing-function;然而,使用简写似乎是最简单的方法。因此,我们将在.button规则集中输入transition作为一个新属性,并使用.25s,或四分之一秒。我们将指定要过渡的所有属性。我们将使用linear时间函数:

.button {
  border: 2px solid #333;
  color: #333;
  padding: 10px 0;
  display: block;
  text-align: center;
  font-weight: bold;
  letter-spacing: 1.5px;
  font-family: Arial, Helvetica, sans-serif;
  transition: .25s all linear;
}

现在当我们在浏览器中查看时,当你将鼠标移到每个按钮上时,变化会更加渐进:

从深灰色过渡到白色文本以及背景颜色和边框需要 0.25 秒。四分之一秒似乎刚刚好,但你可以尝试更快或更慢的过渡。你可以将其更改为十分之一秒,那也很好,非常快,几乎立即。你可以将其更改为一秒,那将慢十倍,可能太慢了。我发现 0.2 到 0.3 秒似乎是过渡的“金发女孩区”。

0.25s之后我们添加的下一个值是all

transition: .25s all linear;

这可以设置为你想要过渡的某个属性,或者所有属性。因此,如果你愿意,你可以将其设置为color

transition: .25s color linear;

只有文本颜色会过渡。如果你尝试这样做,你会看到按钮的深灰色背景立即过渡,但文本颜色会在 0.25 秒内过渡:

如果我们愿意,我们可以添加一个逗号分隔的属性列表进行过渡。在这种情况下,我正在过渡colorbackground-color。这样做的一个原因是,如果你需要过渡多个属性,但不是每个属性。

 transition: .25s color linear, .25s background-color linear;

因此,背景颜色和文本颜色将以相同的速度过渡。我们使用all关键字更有效地实现了这一点,以过渡文本颜色和背景颜色。然而,在某些情况下,保持属性的过渡速度与其他属性不同可能是有用的。让我们将background-color的过渡时间函数更改为 1.25 秒:

transition: .25s color linear, 1.25s background-color linear;

现在,文本颜色的过渡速度将比背景颜色的过渡速度更快。在我们目前的情况下,这并不是特别有用,所以让我们改回到之前的方式:

 transition: .25s all linear;

在我们的情况下,时间函数设置为linear。我们还可以使用easeease-inease-outease-in-out

transition: .25s all ease-in-out;

对于我们正在使用的短过渡,线性方法或默认方法都可以正常工作;任何一种都可以正常工作。在这种非常快的过渡中,实际上很难区分easeease-inease-in-outlinear之间的区别。我建议尝试每一种方法,以确定哪一种最适合你的需求。你可能需要改变过渡的持续时间才能清楚地看到效果。

好的,所以当悬停时,过渡为我们的按钮添加了一个很好的体验层。我们还可以过渡活动和焦点状态。焦点是当用户使用Tab键而不是将鼠标指针悬停在按钮上时,按钮的状态。我喜欢使所有悬停状态与焦点状态相同。这很容易通过添加逗号来添加选择器来实现。所以就像我们有.button:hover一样,我们可以做.button:focus

.button:focus,
.button:hover {
  background-color: #333;
  color: #fff;
}

如果您添加这个,焦点状态也会被触发。当您按Tab键和Shift + Tab键从一个按钮移动到另一个按钮时,它们的悬停状态也将是它们的焦点状态。出于可访问性原因,这是很好的。

供应商前缀

如前所述,过渡是 CSS3 的一个属性。所有现代(主要)浏览器都支持它们:Chrome、Firefox、Safari、Internet Explorer 和 Edge。旧版浏览器,如 IE9 及以下版本,不支持它们。它们仍然会得到悬停状态,但没有任何过渡,会显得突兀。这并不是一个问题,因为过渡通常不是您网站的核心功能,而更多的是一个附加的体验层。不过,它们是 CSS3,我们可以通过包括供应商前缀版本来更多地利用它们。传统上,-webkit-前缀用于 Safari 和 Chrome;-moz-用于 Firefox 和-o-用于 Opera。然而,Firefox 和 Opera 现在也使用-webkit-,所以从技术上讲,您不需要-moz--o-,就像您以前需要它们一样;然而,对于这些浏览器的旧版本,您仍然可以包括它们:

-webkit-transition: .25s all ease-in-out;
-moz-transition: .25s all ease-in-out;
-o-transition: .25s all ease-in-out;
transition: .25s all ease-in-out;

或者您可以通过一半的 CSS,仍然让 99%的用户看到您的过渡效果,只需使用-webkit-供应商前缀:

-webkit-transition: .25s all ease-in-out;
transition: .25s all ease-in-out;

过渡是 CSS3 的一个很棒的特性,它为用户体验增加了一个额外的层次。到目前为止,我们已经为我们的按钮创建了一个悬停状态,并使用过渡效果来*滑状态变化。然后,我们添加了供应商前缀以支持旧版浏览器。接下来,我们将看看 CSS3 的另一个特性:变换。

变换

与过渡一样,变换是 CSS3 的一个特性。不过它们得到了更多的支持,因为所有主要的浏览器,包括 IE9 及以上版本,都提供了支持。变换允许您做一些事情,包括旋转、缩放和*移。在本节中,我们将看一些实际的例子。首先,我们将为我们的按钮应用一个比例,然后我们将进行*移,然后是对旋转值的独特使用。

将比例应用到我们的按钮

让我们从我们在 CSS 中留下的按钮继续下去。在过渡下面,让我们添加一个 transform。我们将添加transform: scale(.9, .9),就像这样:

-o-transition: .25s all ease-in-out;
transition: .25s all ease-in-out;
transform: scale(.9,.9);

请注意,通过使用.9作为宽度和高度的值,我们实际上使我们的按钮变小了,原始尺寸的九分之一:

让我们再次将scale属性添加到按钮的悬停/焦点状态,以获得更整洁的交互:

.button:focus,
.button:hover {
  background-color: #333;
  color: #fff;
  transform: scale(1.1, 1.1);
}

比例值是一个 css 函数,分别采用宽度和高度。1.1 表示原始尺寸的 1.1 倍。

当您保存并刷新时,您会看到当您悬停在按钮上时,按钮实际上会变得更大。这是一个很好的*滑过渡,因为我们已经应用了过渡属性:

使用 translate 函数

让我们再进一步,也使用translate函数。这将添加到与我们刚刚编写的transform: scale代码相同的行或声明中。translate函数可以将元素移动到左侧、右侧、顶部或底部。正如您在下一行代码中所看到的,第一个值是左右移动的值。但我们不打算将其向左或向右移动,所以我们将使用0。第二个值是上下移动的值。我实际上会将其向上推-5px。如果我使用正值,那将会将其向下推:

transform: scale(1.1,1.1) translate(0, -5px);

现在当我们刷新并悬停在按钮上时,我们会看到它确实稍微向上移动了,确切地说是五个像素:

请注意,我用一个空格分隔了两个函数。这里的语法非常重要:

transform: scale(1.1,1.1) translate(0, -5px);

你可能会自然地在这里添加一个逗号,但如果我实际上在两个函数scaletranslate之间添加逗号,我们将得不到任何关于transform的交互,因为这个语法是不正确的:

transform: scale(1.1,1.1), translate(0, -5px); /* don't use a comma to separate transforms :-( */

使用旋转值

还有另一个变换函数,我想介绍一下,但如果我们给这些按钮添加更多的装饰,它们将会分散注意力。相反,让我们在电影页面上的电影图像上添加一个非常有趣的悬停效果。每个电影标题旁边的图像实际上是指向电影的外部链接:

然而,我希望在悬停时发生视觉交互,这真的表明这是一个超链接,或者至少让用户知道有一些可以执行的操作。让我们使用transform: rotate()来实现这一点。

这是我们在最终网站中的目标。一个白色框架,里面有一张图像,悬停效果是在这个白色框架内旋转:

正如你在下图中所看到的,当你悬停在图像上时,图像会旋转并略微放大——即使图像放大了,它也不会溢出其父容器:

我们需要有一个元素来包裹我们的图像,以实现这一点。我们确实有这个——一个带有figure类的锚标签,它是每个图像的父元素。这就是我们将添加这个厚厚的白色边框的地方。我们需要在a标签中添加overflow: hidden,因为当我们对图像进行缩放和旋转时,溢出隐藏可以防止它从容器中弹出。

让我们开始工作。.content-block .figure选择器已经存在,所以让我们首先给它添加白色边框。我会稍后再添加overflow: hidden。首先,让我们将border属性设置为10pxsolidwhite

.content-block .figure {
  float: left;
  margin: 30px;
  border: 10px solid #fff;
}

在我们刷新当前网站之前,它看起来是这样的:

当我们刷新浏览器时,我们得到了白色边框:

正如你所看到的,我们得到了图像底部和边框之间的间隙。我们可以通过两种方式来纠正这个问题。我们可以设置容器的高度与图像的高度完全一致;我们可以使用height属性来实现这一点,但这并不是最好的解决方案。或者,我们可以将图像浮动到左侧。为此,我们可以使用float属性,因为这是足够简单和更强大的解决方案。但是,我们要针对的是.content-block .figure内部的图像本身。所以让我们这样做,并将其浮动到左侧。

.content-block .figure img {
  float: left;
}

现在刷新浏览器,我们将看到这样可以消除图像和边框之间的间隙:

我们还将在图像上添加rotatescalerotate函数与scaletransition有些不同,因为它在函数内部不需要两个参数。它只需要一个:你想要旋转的角度。在我们的例子中,这是15deg。所以我们将创建一个新的选择器,用于悬停在图像上:

.content-block .figure img {
  float: left;
}
.content-block .figure img:hover {
 transform: rotate(15deg);
}

接下来,在水*方向和垂直方向分别添加缩放:1.25,记住不要在两个函数之间添加逗号。这是这个代码:

.content-block .figure img {
  float: left;
}
.content-block .figure img:hover {
  transform: rotate(15deg) scale(1.25, 1.25);
}

保存所有这些,转到网站,现在当你悬停时,图像会立即从容器中弹出:

让我们在parent .figure选择器中添加overflow:hidden。这正是overflow:hidden的用途:

.content-block .figure {
  float: left;
  margin: 30px;
  border: 10px solid #fff;
  overflow: hidden;
}
.content-block .figure img {
  float: left;
}
.content-block .figure img:hover {
  transform: rotate(15deg) scale(1.25, 1.25);
}

当我们现在去网站,我们看到它工作正常。我们得到了旋转,我们得到了稍微放大并且更加包含在其容器内而没有溢出的缩放:

然而,从默认状态到悬停状态的变化仍然太突然了。让我们添加一个transition属性,以使其更加*滑。我们希望将过渡效果添加到图像的非悬停状态。让我们添加一个四分之一秒的过渡效果:

.content-block .figure img {
  float: left;
  transition: .25s all ease-in-out;
}

现在我们从默认状态*稳过渡到悬停状态:

添加供应商前缀和:focus 状态

我们要做的最后一件事是为我们的transformtransition属性添加供应商前缀。就像过渡一样,我将添加声明的-webkit-前缀版本,以支持较旧版本的 Chrome、Safari、Firefox 和 Opera。而且我还将添加-ms-前缀版本,以支持 Internet Explorer 9。

.content-block .figure img {
  float: left;
  -webkit-transition: .25s all ease-in-out;
  transition: .25s all ease-in-out;
}
.content-block .figure img:hover {
 -webkit-transform: rotate(15deg) scale(1.25, 1.25);
 -ms-transform: rotate(15deg) scale(1.25, 1.25);
  transform: rotate(15deg) scale(1.25, 1.25);
}

也许值得强调的是,使用transform属性时,我添加了-ms-供应商前缀。碰巧 IE9 将支持变换,如果您为其提供-ms-前缀:

-ms-transform: rotate(15deg) scale(1.25, 1.25);

但是,我没有使用过渡来做这个,因为添加-ms-供应商前缀不会有任何区别,因为 IE9 根本就不支持过渡。

让我们也添加:focus状态,以使其更具网络可访问性:

.content-block .figure img {
  float: left;
  -webkit-transition: .25s all ease-in-out;
  transition: .25s all ease-in-out;
}
.content-block .figure img:hover,
.content-block .figure img:focus {
  -webkit-transform: rotate(15deg) scale(1.25, 1.25);
  -ms-transform: rotate(15deg) scale(1.25, 1.25);
  transform: rotate(15deg) scale(1.25, 1.25);
}

好的,这就结束了我们对过渡和变换的简要介绍。通过添加不同类型的变换以及过渡来*滑转换,我们将我们的体验层提升到了另一个水*。还有其他可用的变换,我们没有介绍,比如skewtranslate xtranslate yscale xscale y等。还有真正将其提升到另一个水*的 3D 变换,这绝对值得探索,因为浏览器支持已经变得更好了。接下来,我们将继续通过为站点上的主要呼吁行动按钮进行样式设置来继续我们的样式培训。

设计呼吁行动的按钮

在这一章中,我们在样式化按钮方面取得了长足的进步。现在是时候再添加一个了。在最终站点中,我们还有一个需要构建的主页呼吁行动按钮。在本节中,让我们逐步介绍样式化呼吁行动按钮的每个步骤。首先,我们将添加 HTML,然后正确定位它并添加适当的 CSS;最后,我们将为其添加一个漂亮的悬停效果。

这是我们当前的站点:

以下是我们的最终目标站点,我们将创建 Go Premium 呼吁行动按钮:

添加 HTML

让我们将标记添加到我们的index.html文件中。在Intro Section中使用一个按钮的锚标记,文本为Go Premium

<!-- 
===============
Intro Section
===============
-->
<section>
  <div class="wrapper">
    <h1>Old Chompy</h1>
    <h2>Dedicated to sharks and other aquatic species</h2>
    <p>Lorem ipsum dolor ...</p>
    <a href="#">Go Premium</a>
  </div><!-- end wrapper -->
</section><!-- end section -->

在此下方,添加一个p标记,其中列出了您需要尽快单击的原因,即将成为巨大的呼吁行动按钮。这个段落标记也将有一个锚点,以了解有关我们虚构的高级产品的更多信息:

<!-- 
===============
Intro Section
===============
-->
<section>
  <div class="wrapper">
    <h1>Old Chompy</h1>
    <h2>Dedicated to sharks and other aquatic species</h2>
    <p>Lorem ipsum dolor ...</p>
    <a href="#">Go Premium</a>    <p>So many awesome features when you go premium. <a href="#">Learn more &raquo;</a></p>
  </div><!-- end wrapper -->
</section><!-- end section -->

现在我们真的在这个顶部部分创建了一个两列布局。我们需要将内容的左侧块浮动到左侧,将内容的高级部分浮动到右侧。这样做的最佳方法是将两者都包装在一个具有唯一类名的div标记中,为每个添加宽度,并将它们都浮动。所以首先添加标记:

<section>
    <div class="wrapper">
 <div class="intro-content">
            <h1>Old Chompy</h1>
            <h2>Dedicated to sharks and other aquatic species</h2>
            <p>Lorem ipsum dolor ...</p>
 </div><!-- end of intro-content -->
 <div class="go-premium">
            <a href="#">Go Premium</a>
            <p>So many awesome features when you go premium. <a href="#">Learn more &raquo;</a></p>
 </div><!-- end of go-premium -->
    </div><!-- end wrapper -->
</section><!-- end section -->

当我们应用这个并查看我们的网站时,我们看到呼吁行动的按钮位于我们期望的位置,直接在介绍内容下面,因为我们还没有添加特定于布局的 CSS:

让我们深入研究 CSS 并进行更改。

使用 CSS 定位

在这里对于我们来说,定位应该不是什么新鲜事。只需在我们的 CSS 中创建一个Go Premium部分,其中包含以下规则集:

/****************
Go Premium
****************/
.intro-content {
  width: 360px;
  margin-right: 60px;
  float: left;
}
.go-premium {
  width: 300px;
  float: left;
}

我们的.intro-content.go-premium区域都有定义的固定宽度。我们还应该在介绍内容上添加margin-right,以在两者之间添加一些空间。它们都向左浮动。所以这段代码真正实现的是这样的:

我们在左侧获取我们的介绍性内容,右侧是我们的Go Premium内容。然而,我们在这里有一些问题。高级内容在页面上太高了,然后在下面,我们的内容侵入并流向介绍内容的右侧。这就是我们面临的浮动不清除的问题。

顶部边距应该解决我们的第一个问题,所以在.go-premium选择器中添加margin-top125px

.go-premium {
  width: 360px;
  float: left;
  margin-top: 125px;
}

以下是上述代码的输出:

我们的第二个问题是内容实际上围绕浮动元素流动,并且在我们的 Go Premium 按钮上方有点侵入。我们可以在包裹整个顶部部分的容器上使用清除浮动 hack 类来解决这个问题。查看我们index.html文件中的介绍部分。整个顶部部分,包括介绍内容和 go premium,都包裹在一个包装器内:

<section>
    <div class="wrapper">
        <div class="intro-content">
            <h1>Old Chompy</h1>
            <h2>Dedicated to sharks and other aquatic species</h2>
            <p>Lorem ipsum dolor ...</p>
        </div><!-- intro-content -->
        <div class="go-premium">
            <a href="#">Go Premium</a>
            <p>So many awesome features when you go premium. <a     
            href="#">Learn more   
            &raquo;</a></p>
        </div><!-- end of go-premium -->
    </div><!-- end wrapper -->
</section><!-- end section -->

让我们在这个包装器中添加清除浮动的 hack,使用我们的grouping类,这将解决我们网站上的问题:

<div class="wrapper grouping">

以下是上述代码的输出:

按钮样式

让我们继续样式化按钮。为了样式化目的,让我们给我们的 go premium 锚点添加一个call-to-action类:

<a class="call-to-action" href="#">Go Premium</a>

快速查看最终网站,这就是我们在 Go Premium 按钮上的目标。有一个白色边框,白色文本,蓝色渐变,并且周围有足够的填充:

悬停状态去除了渐变,并将文本颜色和边框颜色更改为蓝色:

请注意,我们将无法使用上面图片中的确切网络字体。我们暂时将使用纯蓝色背景代替渐变,因为我们将在下一节回到它,并在本书的后面再次添加渐变和字体。

在 CSS 中,在.go-premium规则集下面,添加一个.call-to-action选择器和一个 2 像素的白色实线边框。我们还将使文本颜色为白色,背景颜色为蓝色。在顶部和底部添加25px的填充,左右位置为零,因为我们最终会将文本居中:

/****************
Go Premium
****************/
.intro-content {
  width: 360px;
  margin-right: 60px;
  float: left;
}
.go-premium {
  width: 300px;
  float: left;
}
.call-to-action {
 border: 2px solid #fff;
 color: #fff;
 background-color: #0072ae;
 padding: 25px 0;
}

现在我们的按钮看起来有点奇怪,因为锚点是内联元素,它的填充没有向下推挤下面的文本。这就是内联元素的工作方式:

最简单的解决方法是将显示更改为block

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  background-color: #0072ae;
  padding: 25px 0;
  display: block;
}

以下是上述代码的输出:

我们需要将文本对齐到中心并添加圆角。像这样添加:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  background-color: #0072ae;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
}

我们不再需要为边框半径添加供应商前缀,因为这个 CSS3 属性规范比变换和过渡属性更成熟。刷新浏览器,你会看到我们的按钮开始变得非常漂亮:

现在我们可以增加字体大小和字重:

font-size: 22px;
font-weight: bold;

以下是上述代码的输出:

我们的按钮看起来很棒。让我们添加悬停样式。在 CSS 中添加:hover:focus选择器。我们需要将边框和文本的颜色从白色更改为蓝色;border-color会处理这个问题。使用关键字nonebackground属性将去除背景颜色:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  background-color: #0072ae;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
}
.call-to-action:hover,
.call-to-action:focus {
 border-color: #0072ae; 
 color: #0072ae; 
 background: none;
}

如果我们现在转到我们的网站,并悬停或聚焦在我们的按钮上,我们将看到呼吁行动按钮的不同处理方式:

最后,让我们添加一个过渡效果,使状态变化更加微妙。在我们的 CSS 中,为按钮的非悬停状态添加transition: all .25s ease-in-out和供应商前缀:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  background-color: #0072ae;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
  font-size: 22px;
  font-weight: bold;
  -webkit-transition: all .25s ease-in-out;
 transition: all .25s ease-in-out;
}

添加了过渡效果后,我们有了一个完全样式的呼吁行动按钮(减去正确的网络字体和渐变)。

我们现在已经定位了我们的呼吁行动区域,并将按钮本身样式化得非常棒。接下来,让我们完成呼吁行动按钮,并学习更多关于 CSS 渐变的知识。

渐变

我们的大呼吁行动按钮几乎完成了。我们只需要添加一个渐变,就像变换、过渡和边框半径一样,这是 CSS3 中的一个特性。

使用终极 CSS 渐变生成器

由于渐变规范和语法有些冗长,并且在各个浏览器之间不一致,使用它的最简单方法是通过一个可以为我们创建 CSS 输出的应用程序。通常,我会避开诸如此类的东西,因为我更喜欢编写自己的代码,但是我会为渐变做个例外。最终的 CSS 渐变生成器似乎对我非常有效。该网站是www.colorzilla.com/gradient-editor/。我们要实现的渐变相当简单。它从顶部的浅蓝色到底部的深蓝色:

让我们去www.colorzilla.com/gradient-editor/。该工具默认为以下内容。右上角甚至有一个预览:

默认情况下有四个颜色停止点,我们只需要两个。因此,点击渐变条中间的两个停止点并删除它们。单击颜色停止点会显示一组新的控件,包括删除按钮:

我们的渐变条应该如下所示:

现在双击第一个停止点。您的屏幕应该是这样的:

现在我们输入要使用的颜色,即33D3FF,然后点击确定。整体上是一个很好的类似 Photoshop 的界面:

现在,双击第二个颜色停止点,并添加00718e颜色:

这种颜色和渐变看起来像我们一直在追求的。但是我们可以稍微移动颜色停止点,改变渐变。我将它拖到大约三分之一的位置:

我们还可以通过更改大小为 370 x 100 来调整预览显示的高度,使其更像我们实际的 CTA 按钮的高度:

CSS 输出就在预览栏下面。我们只需点击复制即可复制它。切换到我们的 CSS 文件,并将其粘贴到我们的 CTA 选择器内:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  background-color: #0072ae;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
  font-size: 22px;
  font-weight: bold;
  -webkit-transition: all .25s ease-in-out;
  transition: all .25s ease-in-out;
  /* Permalink - use to edit and share this gradient: 
  http://colorzilla.com/gradient-editor/#33d3ff+0,00718e+73 */
 background: #33d3ff; /* Old browsers */
 background: -moz-linear-gradient(top, #33d3ff 0%, #00718e 73%); 
  /* FF3.6-15 */
 background: -webkit-gradient(top, #33d3ff 0%,#00718e 73%); 
  /* Chrome10-25,Safari5.1-6 */
 background: -webkit-linear-gradient(to bottom, #33d3ff 0%,#00718e 73%);      
  /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
 background: -o-linear-gradient(top, #33d3ff 0%, #0071ae 72%); 
  /* Opera 11.10+ */
 background: -ms-linear-gradient(top, #33d3ff 0%, #0071ae 72%); 
  /* IE10+ */
 background: linear-gradient(to bottom, #33d3ff 0%, #0071ae 72%); 
  /* W3C */
 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#33d3ff', endColorstr='#00718e',GradientType=0 ); 
/* IE6-9 */
}

最终渐变生成器的 CSS 输出

最终的渐变生成器创建了八个不同的属性。哇!第一个只是旧版浏览器的背景颜色,不支持渐变的语法:

background: #33d3ff; /* Old browsers */

实际上,我们想将其更改为#0072AE,因为这是我们网站的官方品牌颜色。因此添加并删除前面在声明中提到的background-color: #0072AE属性:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
  font-size: 22px;
  font-weight: bold;
  -webkit-transition: all .25s ease-in-out;
  transition: all .25s ease-in-out;
  /* Permalink - use to edit and share this gradient: 
  http://colorzilla.com/gradient-editor/#33d3ff+0,00718e+73 */
 background: #0072ae; /* Old browsers */
  background: -moz-linear-gradient(top, #33d3ff 0%, #00718e 73%); 
  /* FF3.6-15 */
  background: -webkit-gradient(top, #33d3ff 0%,#00718e 73%); 
  /* Chrome10-25,Safari5.1-6 */
  background: -webkit-linear-gradient(to bottom, #33d3ff 0%,#00718e 73%);      
  /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
  background: -o-linear-gradient(top, #33d3ff 0%, #0071ae 72%); 
  /* Opera 11.10+ */
  background: -ms-linear-gradient(top, #33d3ff 0%, #0071ae 72%); 
  /* IE10+ */
  background: linear-gradient(to bottom, #33d3ff 0%, #0071ae 72%); 
  /* W3C */
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#33d3ff', endColorstr='#00718e',GradientType=0 ); 
/* IE6-9 */
}

这是大量生成的 CSS。如果我们仔细看一些这些,我想知道有多少人在使用 Firefox 3-15,考虑到当前版本是 55?同样,对于 Chrome 10-25,当前版本是 60?

background: -moz-linear-gradient(top, #33d3ff 0%, #00718e 73%); 
 /* FF3.6-15 */
background: -webkit-gradient(top, #33d3ff 0%,#00718e 73%); 
 /* Chrome10-25,Safari5.1-6 */

此外,Chrome 和 Firefox 都是无限期更新的浏览器,这意味着它们会在不提示用户的情况下自动更新自己。

因此,我需要对所有这些前缀版本进行第二意见。让我们看看“Autoprefixer CSS Online”对此的看法,autoprefixer.github.io/。Autoprefixer 自称为管理供应商前缀的工具。它根据当前浏览器的流行度和对供应商前缀的支持来添加缺少的前缀并删除过时的前缀。

我将在 Autoprefixer 工具的左侧输入非前缀声明,它将根据我提供的浏览器流行度标准输出需要的供应商前缀。我希望我的渐变在市场份额大于 0.1%的所有浏览器中显示。

现在剩下的只有-webkit-供应商前缀和非前缀或 W3C 标准版本:

background: -webkit-linear-gradient(top, #33d3ff 0%,#0071ae 76%,#0071ae 76%);     
background: linear-gradient(to bottom, #33d3ff 0%,#0071ae 76%,#0071ae 76%); }

因此,让我们更新我们的规则集:

.call-to-action {
  border: 2px solid #fff;
  color: #fff;
  padding: 25px 0;
  display: block;
  text-align: center;
  border-radius: 10px;
  font-size: 22px;
  font-weight: bold;
  -webkit-transition: all .25s ease-in-out;
  transition: all .25s ease-in-out;
 background: -webkit-linear-gradient(top, #33d3ff 0%,#0071ae   
  76%,#0071ae 76%);     
  background: linear-gradient(to bottom, #33d3ff 0%,#0071ae 76%,#0071ae 
  76%); }
}

我不知道你,但我对我们刚刚做的事情感到非常满意!

我们将保存这个并转到我们的按钮。在浏览器刷新之前,您可以看到它是纯色的:

当我们刷新时,我们得到了我们的渐变,如下图所示。这非常好。它将在拥有超过 0.1%市场份额的所有浏览器中运行。

并且要非常清楚,我并没有说 1%的市场份额。我说的是 0.1%的市场份额。

在本节中,我们成功地为呼吁行动的按钮添加了样式,并使用了一个处理繁重工作的程序来应用渐变,这让我们能够更快地工作。

总结

在本章中,您学会了如何使用模块化 CSS 创建按钮,并使用多个类来改变按钮的外观。您还了解了 CSS 的特异性如何工作,以及选择器如何相互覆盖。您现在知道如何保持 CSS 的轻量和可管理性。最后,您学会了如何使用过渡、悬停状态、变换和渐变来为我们的按钮添加样式。

在下一章中,我们将继续创建我们的主要导航工具。通过这样做,您将学习有关 CSS 定位、CSS3 伪类、CSS3 动画以及如何纯粹使用 CSS 创建下拉菜单。这非常有趣!

第五章:创建主导航和下拉菜单

在本章中,我们将为网站的主要导航构建所有功能和展示。这一章非常深入,因为构建我们的主导航涉及伪类;静态、绝对、相对和固定定位;以及 CSS 动画。

开始导航

在本节中,我们将首先创建尽可能干净的 HTML,然后插入基本的 CSS 以启动它。以下是我们最终网站应该看起来的样子;这是我们的目标:

我们有一个典型的水*导航栏。其中一些项目有下拉菜单。我们还在导航栏的左侧有一个鲨鱼标志,它很好地悬挂在那里。

构建菜单的语义化 HTML

让我们立即输入我们需要的 HTML。我们将从这个漂亮的大 HTML 注释开始。您可能已经注意到,我喜欢这些大家伙。这是因为更容易快速定位到我需要的代码部分:

<!-- 
===============
Nav
===============
-->

我们将把所有内容包装在 HTML5 的nav元素中,并应用grouping类,因为我们将在其中浮动所有内容。最终将需要一个清除浮动,以防容器坍塌:

<!-- 
===============
Nav
===============
-->
<nav class="grouping">

</nav>

现在让我们添加一个figure元素,它将包裹我们的鲨鱼图像:

<nav class="grouping">
 <figure>
 <img src="img/sharky.png" alt="sharky">
 </figure>
</nav>

接下来,我们将开始一个带有primary-nav类的无序列表。很久以前,人们确定使用列表进行导航非常语义化,因为它本质上是一个链接列表:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
 <ul class="primary-nav">

 </ul>
</nav>

我们将从四个列表项开始。我们将在每个列表项中放置一个锚点标记:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
    <ul class="primary-nav">
      <li><a href="#"></a></li> 
 <li><a href="#"></a></li> 
 <li><a href="#"></a></li> 
 <li><a href="#"></a></li> 
    </ul>
</nav>

当我们将其应用到我们的网站时,我们将得到一个鲨鱼图像和四个链接,全部垂直堆叠:

我们需要将这四个链接水*排列,就像块一样。我们将使用浮动和其他一些属性来实现这一点。

使用 CSS 来设计导航

在我们的 CSS 中,首先我们将找到导航的大块注释:

/****************
nav
****************/

然后我们将定位.primary-nav类。让我们使用一种特殊类型的后代选择器,它只定位列表项的第一级:

.primary-nav > li

这很重要。我们这样做是因为稍后我们将在这些列表项中嵌套另一个无序列表以获得一个下拉菜单。假设我们创建相同的选择器,但没有大于号符号:

.primary-nav li

这将定位primary-nav内的任何和所有li标签--子代、孙代、曾孙代等。如果您只想定位直接子代,请使用此选择器;它被称为子组合器:

.primary-nav > li

元素之间的大于号确保我们只定位直接子代。让我们也将这些列表项浮动到左侧,然后刷新浏览器:

.primary-nav > li {
 float: left;
}

以下是前面代码的输出:

这是一个开始;还有很多工作要做。

让我们使用相同类型的子组合器来仅定位.primary-nav的直接子项中的直接子项锚点:

.primary-nav > li {
  float: left;
}
.primary-nav > li > a {

}

所以我们将在顶部添加25px的填充,左右各0。我们还会添加一个宽度;每个宽度将为150px,并且我们会给每个添加一个 1 像素实线的border-left并将它们颜色设置为灰色:

.primary-nav > li > a {
 padding: 25px 0;
 width: 150px;
 border-left: 1px solid #ada791;
}

我们看到它现在开始松散地类似于我们最终的导航:

我们现在将把整个规则集放在我们的 CSS 中,放在主要导航选择器下面:

.primary-nav > li > a {
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}
nav li a{
 font-family: Arial, Helvetica, sans-serif;
 color: #766e65;
 text-transform: uppercase;
 font-size: 15px;
 text-align: center;
 -webkit-transition: 0.15s background-color linear;
 transition: 0.15s background-color linear;
}

这是一个更熟悉的后代选择器,适用于我们将应用于主导航项目以及下拉导航项目的一些样式。这是一种很好的DRY不要重复自己)方法,这样我们就不必以后为下拉菜单重新编写这段代码。让我们更仔细地检查这个规则集。基本上,我们将font-family设置为Arial

font-family: Arial, Helvetica, sans-serif;

我们有这个文本颜色:

color: #766e65;

我们使用了text-transform: uppercase。这将确保我们可以在 HTML 中为导航项输入小写字母,并将每个字母转换为大写字符。这样,如果我们以后决定普通情况比全部大写更好,那么我们只需要在一个地方进行更改,而不是更新整个 HTML:

text-transform: uppercase;

接下来,我们有一个字体大小:

font-size: 15px;

我们还将文本对齐到中心:

text-align: center;

我们也添加了一个过渡,就像在上一章讨论的那样。这是为了过渡背景颜色:

-webkit-transition: 0.15s background-color linear;
transition: 0.15s background-color linear;

这是我们保存更改并刷新浏览器时得到的结果:

我们有一些问题。一个问题是我们的锚点标签是内联元素,所以问题是它们实际上并不像块级元素一样行为。因此,我们可以做的一件事是将它们也浮动到左侧。为此,在.primary-nav > li > a规则集中添加float: left属性:

.primary-nav > li > a {
  float: left;
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}

以下是上述代码的输出:

这看起来好多了。

现在让我们来定位焦点和悬停状态。在我们的最后一个规则集下面,我们将添加另一个规则集:

nav li a:focus,
nav li a:hover,
nav li a.active {
  background-color: #eb2428;
  color: #fff;
}

这将不仅针对焦点和悬停状态,还将针对active类。这不是一个“状态”,就像焦点或悬停状态一样。这是一个我们将应用于元素的类,表示您正在访问该页面。它将与悬停状态相同。接下来,我们将背景颜色设置为红色,文本颜色设置为白色。现在,当我们刷新时,我们得到了悬停和焦点状态,这很好:

我们现在唯一需要做的就是弄清楚导航栏的位置,并将整个东西推到右边,因为现在它正好位于我们的图像下方。所以让我们将整个导航栏浮动到右侧。让我们这样做:

.primary-nav {
  float: right;
}

以下是上述代码的输出:

正如您所看到的,这效果相当不错。整个导航都位于鲨鱼下方。我们可以通过将鲨鱼浮动到左侧来解决这个问题,但是如果我们使用绝对定位,我们还可以实现一些不错的功能,这是我们稍后在本章中将要介绍的。

最后,让我们通过添加白色背景并将我们的图像限制为160px的宽度来稍微整理一下这个导航。

/****************
nav
****************/
nav {
  background-color: #fff;
}
nav img {
  width: 160px;
}

这是没有白色背景的网站,我们的鲨鱼相当大:

当我们刷新网站时,我们将得到我们想要的白色背景和一个较小的鲨鱼:

好了,我们已经为导航的第一层构建了 HTML 和大部分 CSS。接下来,您将学习如何使用伪类来解决导航中的某些问题。

使用伪类

您已经学会了如何向元素添加类以应用特殊样式。您总是需要进入 HTML 添加类。有时这可能会成为一个问题。例如,当内容通过内容管理系统动态生成时,您可能无法编辑任何元素,因为它可能不存在于静态 HTML 文件中。这时就需要使用伪类。伪类允许您根据元素在 HTML 中的位置和其他特性来定位元素。在本节中,我们将看一下first-child伪类,它可以帮助我们样式化我们的导航。然后我们将看一下其他几个伪类,例如last-childnth-child

最后,我不希望主菜单,也就是第一个菜单,有border-left,因为它是第一个元素。所以,我想摆脱它:

第一个子元素

为了在我们的 CSS 中定位第一个元素,我们将在锚点元素后添加first-child。所以我们将复制这个选择器,并将其粘贴在自身下面:

.primary-nav > li > a {
  float: left;
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}

然后我们将:first-child添加到选择器中,删除属性,并添加border-left,值设置为none

.primary-nav > li > a:first-child {
  border-left: none;
}

保存这个,转到网站,然后刷新页面:

结果并不是我们可能期望的。实际上,我们从导航中删除了每个项目的左边框。这是因为首先,所有锚点都是它们直接父元素li的子元素。所以我们实际上应该以不同的方式处理这个问题。

快速查看我们 HTML 中的nav。锚点是li内的第一个元素;没有第二个元素。所以,如果我们想要定位ul内的第一个元素,它不会是锚点,而是列表项,即<li>

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
    <ul class="primary-nav">
        <li><a href="#">Home</a></li>
        <li><a href="#">Movies</a></li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

在我们的 CSS 中,我们实际上要将伪类从这里移动:

.primary-nav > li > a:first-child {

我们将它从a中移除,并将它附加到li,如下所示:

.primary-nav > li:first-child > a {

现在我们所有的导航元素上都有border-left属性,除了第一个:

关于first-child的一件事是,它必须是出现在父元素内的第一个元素。所以即使我们特别将li作为主导航的第一个子元素,如果我们在第一个li标签之前在ul内有其他东西,那么我们的选择器就不起作用了。让我们看看这个。让我们在ul元素的子元素中添加一个h2

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
    <ul class="primary-nav">
        <h2>not valid html</h2>
        <li><a href="#">Home</a></li>
        <li><a href="#">Movies</a></li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

这不是有效的 HTML,但是为了好玩,注意我们在第一个li标签上重新获得了左侧边框:

这是因为它不再是第一个孩子。h2现在是第一个孩子。这是在使用first-child伪类时的一个常见错误。

最后一个孩子

现在让我们看看last-child伪类。让我们创建一个新的选择器:

.primary-nav > li:last-child > a {

}

我们将通过将背景颜色设置为亮粉色,文本颜色设置为白色,使示例更加明显:

.primary-nav > li:last-child > a {
  background-color: deeppink
  color: #fff;
}

现在我们的最后一个孩子也应用了这些属性:

我更喜欢first-child,因为它在 IE7 及更早版本中有更深的支持,而last-child的支持从 IE9 开始。

nth-child 伪类

nth-child类允许我们选择其父元素内的任何元素。让我们进入 CSS 并将last-child更改为nth-child(2)

.primary-nav > li:nth-child(2) > a {
  background-color: deeppink;
  color: #fff;
}

保存代码并刷新网站:

所以,在我们的网站上,粉色实际上应该应用于h2和 HOME,因为h2ul内的第一个元素,而 HOME 是第二个。

如果你是一个 JavaScript 人,nth-child不是从零开始的,所以第一个不是零:第一个是一。

考虑到这一点,让我们将nth-child设置为1:这本质上与使用first-child相同:

.primary-nav > li:nth-child(1) > a {
  background-color: deeppink;
  color: #fff;
}

让我们快速从我们的 HTML 中去掉这个h2标签:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
    <ul class="primary-nav">
        <!--<h2>not valid html</h2>-->
        <li><a href="#">Home</a></li>
        <li><a href="#">Movies</a></li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

我们现在看到粉色保留在第一个导航项上:

您还可以使用关键字oddeven。所以如果我在那里加入evenodd,你会得到应用这些属性的数字二和四:

.primary-nav > li:nth-child(even) > a {
  background-color: deeppink;
  color: #fff;
}

刷新网站,你会得到以下结果:

这是一个很好的技术,可以为表格或列表添加斑马条纹,以增加可读性。

nth-of-type 伪类

还有nth-of-type。在我们的 CSS 中,将nth-of-type(2)添加到primary-nav选择器中:

.primary-nav > li:nth-of-type(2) > a {
  background-color: deeppink;
  color: #fff;
}

nth-of-typenth-child之间的区别在于,nth-of-type预先限定只查找它附加的元素。例如,在我们的例子中,我们已经将nth-of-type附加到了一个li,所以它只匹配li标签:

.primary-nav > li:nth-of-type(2) > a {
  background-color: deeppink;
  color: #fff;
}

让我们看看这个实例。让我们重新添加我们的h2标签:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="sharky">
    </figure>
    <ul class="primary-nav">
        <h2>not valid html</h2>
        <li><a href="#">Home</a></li>
        <li><a href="#">Movies</a></li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

它不会只是指ul内的任何孩子。我们现在看到第二个li标签应用了这些属性:

所以nth-of-typenth-child更精确。nth-childnth-of-type的浏览器支持从 IE9 和更高版本开始,以及其他主要浏览器。

在这一部分,我们学习了一些伪类,可以根据它们在 HTML 中的顺序来定位元素。然而,这些并不是我们迄今为止使用的第一个伪类。我主要指的是基于状态的伪类,比如hoverfocus,这些我们迄今为止已经使用了很多。在下一节中,我们将转变方向,讨论 CSS 定位以进一步提升我们的导航。

绝对定位

在这一部分,我们将开始研究不同的 CSS 定位属性,以及它们的补充偏移属性。首先,我们将绝对定位鲨鱼标志,然后使用固定定位来处理整个导航栏。

绝对定位鲨鱼

我们已经把菜单放好了,但是鲨鱼明显是在导航栏的上面。我们需要它水*对齐,或多或少。我们需要修复鲨鱼,使其悬挂在导航栏上方。我们还希望整个导航栏保持固定在浏览器窗口的顶部:

所以让我们去我们的 CSS 中,给nav figure选择器添加position: absolute。在nav规则集下面创建一个新的选择器。我们将其称为nav figure,并给它一个position属性,值为absolute

/****************
nav
****************/
nav {
  background-color: #fff;
}
nav figure{
  position: absolute;
}
nav img {
  width: 160px;
}

立刻看起来好多了:

让我们谈谈我们刚刚做的事情。所有元素,默认情况下都是static定位。静态元素遵循正常流,这意味着块级元素只是简单地堆叠在一起,只要它们没有浮动。将position改为absolute会将其从正常流中移出。它的块级特性消失了,其他元素对它没有任何影响。它可以被视为存在于另一个*面或层次上。一旦绝对定位,你就可以开始使用偏移属性,比如toprightbottomleft

让我们这样做。给nav figure元素添加两个属性,即topleft

nav figure{
  position: absolute;
  top: -50px;
  left: 50px;
}

这些将会像margin-topmargin-left一样起作用。如果你查看结果,你应该会看到鲨鱼距离左边有50px,距离顶部有-50px

那么当我们将top属性与bottom交换,将left属性与right交换时会发生什么:

nav figure{
  position: absolute;
  bottom: : -50px;
  right: 50px;
}

它实际上将鲨鱼移动到了页面的底部和右侧!

这张图片更清楚地展示了偏移属性与绝对定位的结合工作原理。偏移属性现在是基于浏览器视口的,但通常我们不想这样做;相反,我们希望通过将父元素设置为position: relative来基于父元素定位。

图片的父元素是nav选择器,所以让我们将其设置为相对定位:

nav {
  background-color: #fff;
  position: relative;
}
nav figure{
  position: absolute;
  bottom: -50px;
  right: 50px;
}

你可以看到,即使现在我们距离右边有50px,因为导航栏一直延伸到右边缘,而我们距离导航栏底部有-50px,因为鲨鱼在那里延伸到导航栏下方:

position: relative声明为子元素建立了一个坐标系,具有position: absolute的子元素。

让我们把鲨鱼移回它应该在的位置:

nav figure{
  position: absolute;
 top: -20px;
 left: 50px;
}

鲨鱼很好地重叠在我们的导航栏上。它现在坐在我们的标题上面,有点好笑,但我们马上就会回到这个问题:

首先,通过添加position: fixed来使整个导航栏固定在顶部。

使用固定定位来处理导航栏

让我们将导航栏的position属性从absolute改为fixed,看看结果如何:

nav {
  background-color: #fff;
  position: fixed;
}

以下是上述代码的输出:

fixed值,例如relative,仍然像坐标系一样,用于任何绝对定位的子元素或后代元素,但它也有一些超能力。现在,这些超能力完全破坏了我们的导航。问题在于:position: relative仍然保留其块元素的特性,而position: fixed在从正常流中移除时失去了许多这些块特性,导航现在看起来有点滑稽:它没有延伸到浏览器窗口的全宽。让我们通过一些偏移属性来修复这个问题。

我们实际上可以通过设置 left: 0right: 0 来拉伸导航。让我们还添加 top: 0 来确保它被定位在顶部:

nav {
  background-color: #fff;
  position: fixed;
 left: 0;
 right: 0;
 top: 0;
}

看起来更好。而且,因为导航的位置设置为固定,当我们滚动页面时,导航内的所有内容都固定在顶部,其他所有内容都在其下面移动:

但是,如果您滚动到顶部,您会发现网站标题现在位于导航栏后面。这是因为导航不再是正常流的一部分:

让我们通过向intro-content和我们的go-premium按钮添加margin-top来修复这个问题。我们将转到我们的go-premium规则集,并将margin-top的值添加为150px

.go-premium {
  width: 300px;
  float: left;
  margin-top: 150px;
}

我们还将转到我们的intro-content规则集,并添加margin-top125px

.intro-content {
  width: 600px;
  margin-right: 60px;
  float: left;
  margin-top: 125px;
}

现在看起来非常好:

因此,您已经了解了相对、绝对和固定定位。您还了解到每个元素的默认位置是静态的。relative位置为子元素创建了一个坐标系。absolute位置允许您将元素移动到自己的宇宙中,并根据最*的相对定位的父元素进行积极定位。fixed位置将使元素基于浏览器的视口而粘性,而不是基于任何相对定位的元素。absolutefixed元素都将作为坐标系,相对于其他子元素。在下一节中,我们将看看如何构建下拉菜单,其中我们将再次使用绝对定位。

构建下拉菜单

让我们创建一个纯 CSS 下拉菜单!我们将首先添加标记,然后添加 CSS。

创建基本的 HTML 列表

通常,在构建诸如通常隐藏在视图中的下拉菜单之类的组件时,我会将其构建得好像它没有被隐藏一样。然后,一旦完成并完全样式化,我会创建下拉行为。这也是我们在这里要做的。所以让我们在我们现有的index.html文档中创建 HTML。我们将转到我们的导航栏的无序列表,如下所示:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="Shark">
    </figure>
    <ul class="primary-nav grouping">
        <li><a href="#">Home</a></li> 
        <li><a href="shark-movies.html">Movies</a></li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

最佳实践是在无序列表内构建菜单,其中每个菜单项都是列表项内的锚点。对于下拉菜单,我们需要在具有下拉菜单的li内部嵌套另一个ul标签。我们将在这里嵌套它:

<li><a href="shark-movies.html">Movies</a></li>

但首先,我们将为任何将有下拉菜单的导航项添加一个特殊的类has-submenu

<li class="has-submenu"><a href="shark-movies.html">Movies</a></li>

这样,通过has-submenu类,我们可以在 CSS 中专门针对这些li标签及其后代。在这个电影li标签内部,我们将创建一个新的带有li标签的ul,并在这些li标签内部放入一个锚标签。以下是下拉菜单的标记:

<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="Shark">
    </figure>
    <ul class="primary-nav grouping">
        <li><a href="#">Home</a></li> 
       <li class="has-submenu"><a href="shark-movies.html">Movies</a>
 <ul>
 <li><a href="#">Jaws</a></li>
 <li><a href="#">Sharknado</a></li>
 <li><a href="#">Open Water</a></li>
 </ul>
        </li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

下拉菜单中有三个子菜单:

我们只需要对其进行样式化,使其看起来像我们最终的菜单。

样式化下拉菜单

我们需要适当地样式化下拉菜单,以适应我们现有的菜单。这是我们要达到的效果:

我想要将下拉菜单的样式与主导航的样式分开。我们将通过在主导航下面创建这个大的下拉菜单注释来实现这一点:

/****************
Drop Down Menu
****************/

下拉菜单可以在这里有自己的小节。所以让我们首先只针对has-sub menu内部的ul。为了使子菜单放置在白色导航栏之外,让我们将其绝对定位并且距离top70px

/****************
Drop Down Menu
****************/
.has-submenu ul{
 position: absolute;
 top: 70px;
}

这给我们带来了以下效果:

现在我们只需要样式化下拉菜单,使其看起来像它应该的样子。请注意,在我们的网站上,没有一个li标签像我们的主导航一样向左浮动。这是因为,正如你记得的那样,我们使用了一种后代选择器,只针对primary-nav的直接子li。我们不需要取消之前的样式。让我们回过头来看看,如果我们不这样做会发生什么。

这是子组合选择器的位置:

.primary-nav > li {
  float: left;
}
.primary-nav > li > a {
  float: left;
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}

为了进行快速测试,让我们从两个选择器中删除大于号符号:

.primary-nav li {
  float: left;
}
.primary-nav li a {
  float: left;
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}

这是它的样子:

请注意,我们在顶部菜单中的所有样式都在子菜单中重复。这是我们想要避免的,因为我们不希望编写额外的 CSS 来取消整个菜单向左浮动并在不需要的地方添加边框。因此,让我们将那些大于号符号添加回我们的.primary-nav选择器中:

.primary-nav > li {
  float: left;
}
.primary-nav > li > a {
  float: left;
  padding: 25px 0;
  width: 150px;
  border-left: 1px solid #ada791;
}

好吧,让我们给.has-submenu添加白色背景和边框。在底部、左侧和右侧添加边框。我们不希望顶部有边框,所以我们将使用border-bottomborder-leftborder-right,而不是使用border的简写:

/****************
Drop Down Menu
****************/
.has-submenu ul {
  position: absolute;
  top: 70px;
  background-color: #fff;
 border-bottom: 1px solid #ada791;
 border-left: 1px solid #ada791;
 border-right: 1px solid #ada791;
}

现在开始类似下拉菜单:

一个明显的问题是宽度。我们需要给它一个width150px,以匹配其父元素的宽度。另外,让我们给bottom-leftbottom-right角添加border-radius

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
 border-radius: 0 0 15px 15px
}

请注意border-radius的简写。它与边距和填充的简写非常相似。第一个值是左上角,然后顺时针方向。因此,第二个值是右上角,第三个是右下角,第四个是左下角。

现在我们有了所需的widthborder-radius

有一件奇怪的事情是,我们的导航项的文本看起来并不是居中对齐的。锚元素的文本是居中对齐的。如果你右键单击a标签的文本,然后选择“检查”,你就可以看到这一点:

问题在于li标签占据了整个宽度,而a标签是内联元素,只占据它们需要的宽度。让我们添加一个新的选择器:.has-submenu a,使用display: blockpadding,上下各为20px

.has-submenu a{
  display: block;
  padding: 20px 0;
}

下拉菜单看起来好多了:

我们的悬停状态从主导航中继承过来,这很好。唯一的问题是我们最后的悬停状态——Open Water——隐藏了圆角:

修复悬停状态

有两种方法可以解决悬停在Open Water子菜单项上时丢失圆角的问题。第一种方法是使用last-child伪类,你在前面的几节中学到了,来定位a选择器和子菜单的最后一个li选择器。这应该可以正常工作,但如果我们想要更深层次的浏览器支持,我们需要使用另一种技术,在ul元素——父元素上使用overflow: hidden。我倾向于在这里使用overflow: hidden的方法,因为它很简洁,并且具有更深层次的浏览器支持。

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  overflow: hidden;
}

如果我们现在查看浏览器,问题已经解决了:

现在我们只剩下静态菜单。它总是打开的。我们需要创建一个下拉行为,当你将鼠标悬停在 MOVIES 导航项上时,它会出现。一种方法是默认隐藏下拉菜单,然后使用hoverfocus伪类来显示它。

默认情况下使用display: none隐藏下拉菜单。让我们首先使用display: none隐藏整个ul标签:

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  overflow: hidden;
  display: none;
}

我们可以通过创建一个新的选择器.has-submenu:hover ul,只在has-submenu悬停时才针对ul进行定位:

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  display: none;
}
.has-submenu a {
  display: block;
  padding: 20px 0;
}
.has-submenu:hover ul {
 display: block;
}

根据这个规则集,当你悬停在电影菜单上时,里面的ul元素将被显示出来。然后,因为我们在之前的选择器中添加了display: none——非悬停状态,默认情况下,ul标签,也就是下拉菜单,不会被显示出来。现在默认情况下没有子菜单可用:

现在当我们悬停在电影菜单上时,子菜单就会出现:

我还要提到的一件事是,display: none存在无法让屏幕阅读器宣布内容的问题。

还有另一种选择:使用屏幕外隐藏技术,这需要额外的工作,但是非常值得。

使用屏幕外隐藏技术隐藏下拉菜单

基本思想是绝对定位一个元素,远离可见屏幕,这样它就不可见了,但屏幕阅读器仍然可以宣布它。关于无障碍还有很多要学习的地方。我建议你首先查看这篇文章css-tricks.com/places-its-tempting-to-use-display-none-but-dont/,至少了解一下如何使用屏幕外隐藏技术,然后再从无障碍方面继续学习:

Web 无障碍是一个非常深入的话题,它值得有一本专门的书来讲述。所以我在这里无法全面地讲述它。不过,我们可以用类似下面的方法来替代使用display: none来隐藏我们的下拉菜单,以使其更具有可访问性:

.accessibly-hidden {
  position: absolute;
  top: -9999px;
  left: -9999px;
}

这将隐藏内容,对于有视力的用户来说,但对于屏幕阅读器用户来说,仍然会宣布内容。

我们已经很顺利地完成了下拉菜单的设置。这次效果非常完美。你第一次尝试创建下拉菜单可能不会这么顺利,但使用这些技巧,你可以避免一些可能遇到的问题。

我最大的两个建议是:

  • 首先构建下拉菜单,就好像它将一直可见,然后在样式设置好并且看起来不错之后隐藏它。

  • 由于推荐的方法是在无序列表中使用无序列表,因此值得仔细设置你的规则集以避免混淆。例如,一个适用于父ul和子ul(即ul li)的样式规则集;另一个只适用于父ulli选择器,使用子组合器(即ul > li);最后,一个只适用于子ul的规则集(即.has-submenu ul)。这样,你就不必为子ul创建一堆可能令人困惑的覆盖样式。

导航的下一个部分需要我们实际创建下拉效果;我们将使用 CSS 动画来实现这一点。

CSS 动画(第一部分)

我们的主导航现在正在成形,我们的下拉功能几乎完成了。下拉菜单的最后一点润色是 CSS 动画,以使下拉菜单向下*滑动画。动画非常有趣,现代浏览器,包括 Chrome,Firefox,Opera 和从 IE10 开始的浏览器都支持它们。IE9 仍然会显示下拉菜单,但它只会简单地出现/消失。动画与过渡非常相似,但我们可以对静态元素进行动画处理,并使用不同的动画属性和关键帧来控制动画。我们稍后会更深入地讨论这个问题。所以在这一部分,我们要做的是:我们将在要进行动画处理的元素的选择器中定义animation-nameanimation-durationanimation-timing-function。之后,我们将继续定义我们要进行动画处理的关键帧。

定义动画名称、持续时间和时间函数

让我们回顾一下我们的下拉菜单的 CSS:

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  overflow: hidden;
  display: none;
}
.has-submenu a {
  display: block;
  padding: 20px 0;
}
.has-submenu:hover ul {
  display: block;
}

让我们以如下方式定位has-submenuhover状态:

.has-submenu:hover ul {
  display: block;
}

现在,我们将使用非前缀的/W3C 标准属性名称,并在最后添加所需的前缀。因此,要进行动画,我们使用animation-name,并将slideDown作为动画名称:

.has-submenu:hover ul {
  display: block;
 animation-name: slideDown;
}

我可以随意命名这个动画,只要不使用任何空格。就像类名一样,我也不能以数字开头。此外,关键字none不能用作动画名称,因为它是保留的用于移除动画的特殊关键字。接下来,我们将以秒为单位指定动画持续时间和动画的时间函数:

.has-submenu:hover ul {
  display: block;
  animation-name:slideDown;
 animation-duration: .25s;
 animation-timing-function: ease;
}

对于timing-function,我使用了ease,但您还可以指定linearease-inease-outease-in-out函数,这些函数与我们用于transitions的相同时间函数。这段代码本身不会做任何事情。我们必须指定在使用@keyframes进行动画时会发生什么。因此,在最后一个规则集下面,我们将添加一个带有我们之前想出的动画名称slideDown@keyframes at-rule

@keyframes slideDown {
}

在花括号内,我们将指定fromto的时间偏移量:

@keyframes slideDown {
  from {}
 to {}
}

花括号内的任何内容都将是动画的起点,花括号内的任何内容都将是动画的终点。我们可以在动画中放置几个属性;让我们从translateY变换函数开始,值为负 100%:

@keyframes slideDown {
  from {transform: translateY(-100%);}
  to {}
}

这将使无序列表向上移动负 100%,使其成为起点。百分比是元素的高度。50%会使其向下移动一半的元素高度,而100%会使其向下移动整个元素的高度。因此,-100%将使其垂直向上移动整个元素的高度。在这里,translateY函数对我们来说是新的。它很像translate,只是它只用于垂直*移。translateX函数可以进行水**移。在to的花括号内,我们将把translateY设置为0%

@keyframes slideDown {
  from {transform: translateY(-100%);}
  to {transform: translateY(0%);}
}

我们现在可以看到菜单向下动画:

设置额外的关键帧

到目前为止,我们的动画完全可以用transition来实现,因为没有真正引入任何新内容。但是动画的强大之处在于我们可以设置额外的关键帧。让我们将 CSS 中的fromto更改为分别为0%100%,如下所示:

@keyframes slideDown {
  0% {transform: translateY(-100%);}
  100% {transform: translateY(0%);}
}

我们不仅可以添加开始和结束,还可以在这两个点之间添加任意数量的停止。让我们添加一个新的关键帧,比如90%,其translateY10%

@keyframes slideDown {
  0% {transform: translateY(-100%);}
  90% {transform: translateY(10%);}
  100% {transform: translateY(0%);}
}

我们正在将下拉菜单的位置从-100%10%进行翻译,在 0.25 秒的前 90%。然后,在 0.25 秒的最后 10%,垂直移动从10%0%。这使动画在最后有一点跳动或弹跳:

我们不仅可以添加多个关键帧,还可以在每个关键帧中添加多个属性。所以让我们将opacity添加到我们的动画中。假设我们从不可见的关键帧开始,最终不透明度为1,即完全可见。我们不会在 90%关键帧上动画不透明度:

@keyframes slideDown {
  0% {transform: translateY(-100%);opacity: 0;}
  90% {transform: translateY(10%);}
  100% {transform: translateY(0%);opacity: 1;}
}

菜单现在向下动画并淡入:

供应商前缀

为了完成我们的下拉动画,让我们通过添加必要的供应商前缀来获得最大的浏览器支持:

/****************
Drop Down Menu
****************/
@-webkit-keyframes slideDown {
 0% {-webkit-transform: translateY(-100%); opacity: 0; }
 90% {-webkit-transform: translateY(10%);}
 100% {-webkit-transform: translateY(0%); opacity: 1; }
}
@keyframes slideDown {
  0% {transform: translateY(-100%); opacity: 0; }
  90% {transform: translateY(10%);}
  100% {transform: translateY(0%); opacity: 1; }
}
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  overflow: hidden;
  display: none;
}
.has-submenu a {
  display: block;
  padding: 20px 0;
}
.has-submenu:hover ul {
  display: block;
 -webkit-animation-name: slideDown;
  animation-name: slideDown;
 -webkit-animation-duration: 2.5s;
  animation-duration: 2.5s;
 -webkit-animation-timing-function: ease;
  animation-timing-function: ease;
}

@keyframes动画都需要-webkit-供应商前缀,以及transformanimation-nameanimation-durationanimation-timing-function属性。

当我们来到这一部分的结尾时,我们的下拉菜单动画已经就位。CSS 动画在 IE10 及更高版本中受支持,因此较旧版本的 IE 和其他较旧的浏览器不会显示动画,但它们仍然可以访问菜单和其所有内容。在我们的情况下,由于这只是整体体验的额外触摸,如果较旧的浏览器错过了这一点,这并不是一个严重的问题;它们仍然可以获得他们需要的所有核心内容。在下一节中,我们将继续使用 CSS 动画,通过尝试我们的鲨鱼标志来创建一个更加强大的动画。

CSS 动画(第二部分)

我们的主导航下拉菜单的滑动动作已经完成。现在让我们通过尝试我们的鲨鱼标志和探索其他动画属性,如delayiteration-countfill-mode以及animation(这是简写)来深入研究 CSS 动画。

动画延迟,迭代次数和填充模式

让我们为鲨鱼图像添加一个动画,以便从不同的角度看动画可以做什么,并且每次页面加载时都会发生。我们将其命名为crazyShark

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
 animation-name: crazyShark;
 animation-duration: .25s;
 animation-timing-function: ease;
}
@-webkit-keyframes crazyShark {

}
nav img {
  width: 160px;
}

让我们添加一堆同时*移和旋转鲨鱼图像的@keyframes动画:

@keyframes crazyShark {
  0% {transform: translate(90%, 70%);}
 33% {transform: translate(40%, 20%) rotate(90deg);}
 66% {transform: translate(10%);}
 100% {transform: translate(0%) rotate(0deg);}
}

现在,让我们去我们的动画属性,并将持续时间从0.25秒更改为1秒:

animation-duration: 1s;

鲨鱼真的在四处移动,因此我们的动画被命名为crazyShark

请注意,我使用的translate语法与我们之前使用的略有不同。由逗号分隔的两个值分别用于xy坐标,而当xy坐标相同时,可以使用一个单一值:

/*translate shorthands for translateX and translateY*/

transform: translate(40%, 20%); 
/*2 values (x first, y second) when both values are different*/

transform: translate(10%); 
/*1 value is used when x and y coordinates are the same*/

还有其他几个动画属性,其中两个是animation-delayanimation-iteration-count。我发现这两个都很有用:

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
  animation-name: crazyShark;
  animation-duration: 1s;
  animation-timing-function: ease;
 animation-delay: 2s;
 animation-iteration-count: 2;
}

现在,动画开始前将有 2 秒的延迟,我不会尝试在书本格式中说明这一点。然后它应该完全动画两次:

如果我们愿意,我们也可以无限重复动画;我们可以只添加infinite关键字而不是一个数字,鲨鱼将永远继续前进。我绝对不会尝试在书本格式中说明这一点! 让我们摆脱animation-delayanimation-iteration-count

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
  animation-name: crazyShark;
  animation-duration: 1s;
  animation-timing-function: ease;
}

动画填充模式

animation-fill-mode属性告诉被动画化的元素在动画开始前和动画完成后该做什么。使用animation-fill-mode填充动画之前和/或之后的空间。我们现在不需要animation-fill-mode属性。因为鲨鱼动画在页面加载时开始,然后将鲨鱼降落到其静态位置-我们说不要*移和旋转:

 100% {transform: translate(0%) rotate(0deg);}

但是,如果我们以x为 10%,y为 70%,旋转为 10 度结束动画会怎么样呢?

 100% {transform: translate(10%, 70%) rotate(10deg);}

如果您应用这个并转到网站,您会注意到鲨鱼似乎在第一个标题附*结束动画,然后跳回到其原始位置。这由以下两个截图说明:

动画的最后,鲨鱼:

鲨鱼在动画结束后会瞬间传送到其静态位置:

我们可以使用animation-fill-mode: forwards来修复这个问题:

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
  animation-name: crazyShark;
  animation-duration: 1s;
  animation-timing-function: ease;
  animation-delay: 2s;
  animation-iteration-count: 2;
 animation-fill-mode: forwards;
}

现在,在动画结束后,鲨鱼将保持在那个位置而不会跳回到其原始位置:

太棒了!

animation-fill-mode属性的值为backwards将确保被动画化的元素在动画开始之前就填充到其起始位置。both关键字是填充起始和结束位置的一种方式。

让我们将我们的规则集减少到这三个动画属性:

animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;

让我们也调整和减少整个动画。这样,我们的疯狂鲨鱼将变得稍微不那么疯狂,但仍然足够疯狂

@keyframes crazyShark {
  0% {transform: translate(90%, 70%);opacity: 0;} 
  33% {transform: translate(40%, 20%) rotate(90deg);}
  66% {transform: translate(10%, 50%);}
  100% {transform: translate(0%) rotate(0deg);opacity: 1;} 
}

我们将为每个动画属性添加供应商前缀。但在这之前,让我们使用动画属性的简写来使我们的编码生活变得更容易,将所有动画属性合并成一行。

使用动画简写

在我们的nav figure规则集中加入这些声明:

animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;

animation-name和底部两个声明中删除-name,这样我们就剩下了这个:

 animation: crazyShark;

现在,我们将添加1sease

 animation: crazyShark 1s ease;

现在我们应该得到的结果如下:

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
 animation: crazyShark 1s ease;
}

此外,您可以将所有不同的动画属性放入一个简写中。无论您以什么顺序放置它们,只要animation-durationanimation-delay之前。以下是一种可能的方式,可以在一个方便的简写中使用我们讨论过的所有动画属性:

animation: [name] [duration] [timing-function] [delay] [fill-mode] [iteration-count];

现在我们已经有了简写,这将使添加供应商前缀版本变得更容易一些。

供应商前缀

让我们添加-webkit-前缀版本的animation属性:

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
 -webkit-animation: crazyShark 1s ease;
  animation: crazyShark 1s ease;
}

我们将对@keyframes做同样的处理:

@-webkit-keyframes crazyShark {
 0% {-webkit-transform: translate(90%, 70%);opacity: 0;}
 33% {-webkit-transform: translate(40%, 20%);}
 66% {-webkit-transform: translate(10%, 50%);}
 100% {-webkit-transform: translate(0%);opacity: 1;}
}
@keyframes crazyShark {
  0% {transform: translate(90%, 70%);opacity: 0;}
  33% {transform: translate(40%, 20%);}
  66% {transform: translate(10%, 50%);}
  100% {transform: translate(0%);opacity: 1;}
}

请注意,我在@keyframes前面加了@-webkit-keyframes,以及在transform前面加了-webkit-transform

有关动画的其他信息

有关 CSS 动画的更多信息,我建议查看我的文章,“CSS 动画并不那么难。”,网址为richfinelli.com/css-animations-arent-that-tough

总之,我们已经探讨了其他动画属性,比如animation-delayanimation-iteration-countanimation-fill-mode,在创建一个花哨的、过度的动画的过程中。我们还将所有这些属性简化为一个方便的简写。我们还为每个属性添加了-webkit-前缀版本,以获得更好的浏览器支持。在本章的下一节和最后一节中,我们将为整个导航栏添加box-shadow,并修复下拉菜单的一个错误,即z-index

完成导航

我们几乎完成了我们的主导航,但还有一些小事情要做。首先,我们将解决一个z-index问题,我稍后会详细说明。然后,我们需要在我们的导航栏底部添加box-shadow以完成设计。

修复 Z 索引问题

首先,我们将使用z-index属性来修复一个错误。当您悬停在 MOVIES 导航项上时,会出现一个下拉菜单。您会注意到一些事情:

首先,下拉菜单中的一个导航项被突出显示了——而实际上不应该。其次,导航实际上是在 MOVIES 导航项的顶部进行动画。

我们可以将动画速度减慢到2.5 秒,以便更容易查看这个问题:

.has-submenu:hover ul {
  display: block;
  -webkit-animation: slideDown 2.5s ease; 
  animation: slideDown 2.5s ease; 
}

这样可以更容易地看到下拉菜单是在 MOVIES 菜单项的顶部下拉的。

这就是我们的问题,这就是为什么我们最终会出现下拉菜单项中的一个被突出显示的原因。

在我们的 CSS 文件中:

/****************
Drop Down Menu
****************/
.has-submenu ul{
  position: absolute;
  top: 70px;
  background-color: #fff;
  border-bottom: 1px solid #ada791;
  border-left: 1px solid #ada791;
  border-right: 1px solid #ada791;
  width: 150px;
  border-radius: 0 0 15px 15px;
  overflow: hidden;
  display: none;
}
.has-submenu a {
  display: block;
  padding: 20px 0;
  position: relative;
}
.has-submenu:hover ul {
  display: block;
  -webkit-animation: slideDown 2.5s ease; 
  animation: slideDown 2.5s ease; 
}

这个错误,可以说,可以通过一个叫做z-index的新属性来修复。z-index属性设置了重叠元素的堆叠顺序。我们的下拉菜单出现在顶部,因为它在主导航项电影的锚标签之后。自然地,绝对定位的元素会出现在没有设置position属性的元素的上面。这就是为什么下拉菜单出现在主导航栏的顶部。z-index的值是一个数字。它可以应用于设置为relativeabsolutefixed位置的元素,以及透明度小于 1 或应用了transform的元素,以及其他一些情况。只要我们的下拉菜单——也就是z-index小于其容器——我们就可以继续。转到.has-submenu a选择器,让我们应用position:relative声明。这样,元素将接受z-index。我们将添加一个z-index10

.has-submenu a {
  display: block;
  padding: 20px 0;
 position: relative;
 z-index: 10;
}

.has-submenu ul上,我们不需要应用position:relative,因为它已经设置为position: absolute;它将接受z-index5,小于 10。所以理论上,我们应该已经解决了我们的 bug:

.has-submenu ul{
  position: absolute;
  z-index: 5;
  top: 70px;

保存并查看我们的网站。在全速运行时,当你在导航项目上悬停时,没有一个菜单项会被突出显示,下拉菜单会出现在主导航栏后面。现在只是为了确保,再次减慢动画速度。你应该看到它出现在 MOVIES 菜单的后面,这很好:

让我们也把animation-duration改回.5s

.has-submenu:hover ul {
  display: block;
  -webkit-animation: slideDown .5s ease; 
  animation: slideDown .5s ease; 
}

添加 box-shadow

让我们谈谈box-shadow属性。在我们的最终网站上,你可以看到我们的主导航下面有这个阴影:

让我们回到我们的 CSS,找到我们的nav选择器。box-shadow是一个 CSS3 属性:

nav {
  background-color: #fff;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index:1;
  box-shadow: 0 8px;
}

我们针对nav元素并使用非前缀版本,这在所有主要浏览器中都受支持,从 IE9 及以上版本开始。我们不必回头去添加任何供应商前缀,因为规范已经足够成熟,所有浏览器现在都支持非前缀版本。我们添加的前两个值是xy。我们将x值设置为0,将y值设置为8px;这将使box-shadow属性向下发散:

box-shadow: 0 8px;

如果我使用负值,那么子菜单将从导航栏顶部发散。我们希望它从导航栏底部发散。

接下来是模糊值。我们将把它设置为15px

box-shadow: 0 8px 15px;

如果我把模糊值保持为0,我们将得到一个硬的 8 像素边框。模糊是使它看起来更像阴影而不是边框的原因。

我们要使用的最终值是颜色。我们将使用一个名为rgba的新颜色值,这是 CSS3 颜色值。然后我们添加0, 0, 0。这意味着红色、绿色和蓝色都将为零,这意味着它们的输出将是黑色。变量a指的是 alpha 通道,我们将把它设置为.1

 box-shadow: 0 8px 15px rgba(0, 0, 0, .1);

所以如果你去网站上检查并取消检查开发者工具中的 box-shadow,你会看到box-shadow属性的效果。这是没有这个属性时的样子:

这张图显示了我们的网站应用了 box-shadow:

有时候,对于这些 CSS 属性,查看开发者工具是很好的。让我们看看如果我们改变它们的值会是什么样子。我们可以看看增加或减少模糊后 box-shadow 的效果。在下面的截图中,我们看到将值从15px增加到26px后的效果——你可以看到模糊消失了:

如果减少模糊,比如0px,它会变成硬化的阴影:

我认为大约15px是恰到好处的。你也可以看到它在更高的不透明度下是什么样子——更高的 alpha 通道。如果我们把 alpha 通道从.1改为.5,阴影会变得更暗:

我认为.1是合适的。这个效果很好。

总结

这是一个广泛的章节;我们涵盖了很多内容。我们构建并设计了我们的菜单。你学会了伪类,以及我们如何使用它们来针对 HTML 中元素的位置。我们熟悉了定位属性,使用absolute定位我们的鲨鱼图标。我们为我们的菜单建立了一个下拉菜单,并为它添加了动画。我们探索了动画属性,比如animation delayiteration countfill-mode,并将它们应用到我们的鲨鱼图标上。最后,我们通过修复z-index问题和使用box-shadow属性完成了导航的最终设计。在下一章中,我们将看看我最喜欢的主题之一,响应式网页设计,因为我们要为不同的设备尺寸准备这个网站。

第六章:变得响应式

到目前为止,我们几乎所有的东西都是固定尺寸。我们的布局有固定宽度,我们的图片有固定宽度,我们的菜单也有固定宽度。但是当使用手机、*板电脑和其他各种设备尺寸时,这样做并不会带来良好的体验。幸运的是,响应式网页设计可以将我们的静态网站转变为流体、设备友好的网站。

开创一切的书籍- 响应式网页设计,作者Ethan Marcotte2011年。他概述了响应式网页设计的三个主要技术支柱:

  • 流体网格,

  • 灵活的图片,以及

  • 媒体查询。

我们将讨论响应式网页设计的三个基本 CSS 基础,然后讨论如何在较小的屏幕尺寸下构建主导航的适配,最后是viewport元标签。

流体网格

在这一部分,我们将讨论响应式网页设计的三个主要组成部分之一,即流体网格或基于百分比的布局。我们将看看如何将固定宽度布局转换为流体网格,为此,您需要学习将像素转换为百分比的公式。

将像素转换为百分比

现在,我们有一个固定宽度布局,如下面的屏幕截图所示:

如果您缩小浏览器,您会看到它会分解成更小的尺寸,如下面的屏幕截图所示:

创建流体网格是解决这个问题的第一步。目标是将所有基于像素的宽度以及左右边距和左右填充转换为百分比。我们现在先忽略主导航,但稍后我们会回到它。我们将从div标签开始,这是我用来包装大部分内容的wrapper类。让我们将属性width更改为max-width。这表示该元素可以比960px宽,但不能超过这个宽度。让我们还将宽度设置为90%

.wrapper {
 width:960px;
 width: 90%;
  margin: 0 auto;
}

因此,根据这段代码,我们将宽度设置为其父元素的 90%,而父元素没有宽度。因此,它将是浏览器窗口的 90%。这将使其在960px以下的宽度上两侧有 5%的间距。让我们在浏览器中查看网站。您可以刷新浏览器并再次缩小它。下面的屏幕截图显示了它没有显著的影响,看起来相当糟糕:

我们还想在wrapper内部创建这些元素的百分比。由于我们从固定像素宽度开始,我们需要将所有像素转换为百分比。

计算百分比宽度

根据Ethan Marcotte响应式网页设计,有一个将基于像素的布局转换为基于百分比的布局的公式:目标/上下文=结果目标是元素的期望宽度。上下文通常是其父元素的宽度。结果是我们可以插入到我们的 CSS 中的百分比。

如果我们看看intro-content部分内的 HTML,我们可以看到wrapper类和其中的两个div标签,即intro-contentgo-premium,如下面的代码片段所示:

<section class="grouping">
    <div class="wrapper">
        <div class="intro-content">
            <h1>Old Chompy</h1>
            <h2>Dedicated to sharks and other aquatic species</h2>
            <p>Lorem ipsum dolor sit amet...</p>
        </div><!-- end intro-content -->
        <div class="go-premium">
            <a href="#" class="call-to-action">Go Premium</a>
            <p class="reasons">So many awesome features...
                <a href="#">Learn more &raquo;</a>
            </p>
        </div><!-- end go-premium -->
    </div><!-- end wrapper -->
</section><!-- end section -->

回到我们的 CSS,我们的第一个元素是intro-content,它是出现在包装器内部的部分,如下面的代码片段所示:

.intro-content {
  width: 600px;
  margin-right: 60px;
  float: left;
}

这里的目标是 600 像素,上下文是 960 像素。因此我们的计算是 600 除以 960,等于 0.625。我们将这个值作为我们的宽度插入,并添加一个百分比作为我们的度量单位,并将小数点移动两位,使其变为 62.5%:

.intro-content {
  width: 62.5%; /* 600/960 */
  margin-right: 60px;
  float: left;
}

正如您所看到的,声明结束时的我的注释告诉我,元素最初宽度为 600 像素,父元素最初宽度为 960 像素。

margin-right属性也需要是百分比。公式仍然是一样的——目标除以上下文等于结果。我们的目标是 60 像素,我们的上下文仍然是 960 像素——父元素,即wrapper类。60 除以 960 得到 0.0625。我们将这个转换为百分比,将小数点移动两位,得到6.25%

.intro-content {
  width: 62.5%; /* 600/960 */  margin-right: 6.25%; /* 60/960px */
  float: left;
}

接下来是我们的呼吁行动按钮的容器,go-premium

.go-premium {
  width: 300px;
  float: left;
  margin-top: 150px;
}

由于宽度是300px,它也需要转换为百分比。所以让我们做同样的事情,在这种情况下,300 除以 960——我们仍然有同样的父元素。这是 0.3125。将小数点移动两位,加上百分比,然后将其放在 CSS 注释中,以备将来使用:

.go-premium {
  width: 31.25%; /* 300/960 */
  float: left;
  margin-top: 150px;
}

现在我认为我们准备好在浏览器中查看这个了。如果我稍微缩小浏览器窗口,布局不会立即破裂:

但是如果我再稍微缩小浏览器窗口,那么最终它开始看起来非常糟糕:

但是,我们确实取得了一些进展,因为我们的布局开始变得流体。介绍内容和呼吁行动按钮随着浏览器窗口的变小而变窄。最终,它们将开始重叠,但没关系;至少我们为这个顶部部分建立了一个流体基础。

现在让我们看看它下面的三列;它们在窗口变小时有点破碎:

所以让我们看看secondary-section类中的 HTML。这三列在一个div标签中,类名为wrapper,最初也是 960 像素宽(但现在是最大宽度为 960px,宽度为 90%):

<section class="secondary-section grouping">
  <div class="wrapper">
    <div class="column">
      <figure>
        <img src="img/octopus-icon.png" alt="Octopus">
      </figure>
      <h2>The Octopus</h2>
      <p>...</p>
      <a href="#" class="button">Tenticals &raquo;</a>
    </div>
    <div class="column">
      <figure><img src="img/crab-icon.png" alt="Crab"></figure>
      <h2>The Crab</h2>
      <p>...</p>
      <a href="#" class="button">Crabby &raquo;</a>
    </div>
    <div class="column">
      <figure><img src="img/whale-icon.png" alt="Whale"></figure>
      <h2>The Whale</h2>
      <p>...</p>
      <a href="#" class="button">Stuart &raquo;</a>
    </div>
  </div><!-- end wrapper -->
</section>

我们将继续使用它作为我们的上下文,同时将我们的.column宽度从像素转换为百分比。一直到我们的 CSS 底部,我们看到每列宽度为300px

/****************
3 columns
****************/
.column {
  float: left;
  width: 300px; 
  margin-left: 30px; 
}
.column:first-child {
  margin-left: 0;
}

让我们在这里应用我们的公式。我们已经知道 300 除以 960 等于 31.25%,因为这是我们刚刚使用的确切计算:

.column {
  float: left;
  width: 31.25%; /* 300/960 */
  margin-left: 30px; 
}

margin-left属性是30px,所以我们实际上要复制并粘贴 31.25%到这里,但是我们会移动小数点一位,并添加一个注释说明 30 除以 960:

.column {
  float: left;
  width: 31.25%; /* 300/960 */
 margin-left: 3.125%; /* 30/960 */
}

我们在第一列上有一个margin-left属性的值为0。我们不必将 0 更改为百分比,因为 0、0 像素和 0%都是完全相同的东西——什么都没有:

.column:first-child {
  margin-left: 0;
}

顺便说一句,我从来没有改变过任何高度、顶部和底部边距或填充,因为这些对我们来说并不重要。所以现在,如果我们刷新这个部分并使其变小,我们会看到我们的三列会与浏览器窗口一起按比例缩小:

现在我们主页的一切都是流体的,除了我们的导航,我现在打算保持原样。我想要完全不同的处理方式,所以我会将其保持为固定宽度。

将填充更改为百分比

我们从来不必将填充左右更改为百分比,因为我们没有任何填充,但是这样做的过程非常相似。您仍然使用相同的公式——目标除以上下文等于结果。但是上下文现在有点不同;它是元素本身的宽度,而不是父元素的宽度,就像宽度和边距一样。唯一的例外是,如果元素本身没有定义宽度,您可以使用其父元素的宽度或通过确定父元素的宽度来确定元素本身的宽度:

如果您使用box-sizing属性和 border-box 值,填充将不再计入元素的框模型宽度。因此,您可以将其保留为像素长度,只需将宽度和边距转换为百分比,因此box-sizing: border-box肯定会很有帮助。

鲨鱼电影页面上的流体网格

让我们搜索一些其他非百分比宽度/边距/填充。所以我们不用担心任何与垂直距离相关的东西,比如heightmargin-topmargin-bottompadding-toppadding-bottom。我们也不用担心任何值为0的东西。

我们将在wrapper规则集中遇到auto的左右边距:

.wrapper {
  max-width: 960px;
  width: 90%;
  margin: 0 auto;
}

这不需要转换成百分比,因为auto会根据可用空间自动计算宽度,所以它和百分比一样好。

我们担心以下声明块中的margin属性:

.content-block .figure {
  float: left;
  margin: 30px;
  border: 15px solid #fff;
  overflow: hidden;
}

这个规则集有一个margin30px;它使用了单值语法。这意味着上下左右的边距都是30px。我们只想改变左右边距。所以我们可以使用双值语法。第一个值是指上下边距,第二个值是指左右边距。

margin: 30px 30px;

记住content-block .figure是围绕我们的图片的元素,如下图所示。所以我们实际上是在尝试将margin-rightmargin-left转换为百分比:

如果我们在shark-movies.html中查看,我们会发现图片在wrapper中:

<section id="jaws" class="content-block style-1 wave-border grouping">
  <div class="wrapper">
    <a href="#" class="figure">
      <img src="img/jaws.jpg" alt="Jaws movie">
    </a>
    <h1>Jaws</h1>
    <p>...</p>
    <a href="" class="button button-narrow button-alt float-right">Learn More</a>
  </div><!-- end of wrapper -->
</section>

所以,我们知道wrapper是 960 像素。到目前为止,我们的上下文很容易确定,因为我们的上下文一直是wrapper的宽度。

30 除以 960 得到 03.125,即 3.125%,所以我们会保存这个:

.content-block .figure {
  float: left;  margin: 30px 3.125%; /* 30/960 */
  border: 15px solid #fff;
  overflow: hidden;
}

在我们的网站上,除了导航之外,所有硬像素长度都是百分比!并不是所有东西都必须是百分比才能实现响应式网页设计。我们做出了判断,决定我们会处理导航而不使用百分比宽度。这对于流体网格和响应式网页设计的许多其他决定都是正确的;真的没有一种大小适合所有的解决方案。您网站的每个组件都需要从桌面到移动端进行彻底思考,甚至更好的是,从移动端到桌面。因此,第一步,创建流体网格,目前已经完成。这是一个重要的步骤,因为它确保我们的设计将开始在所有屏幕尺寸上很好地适应。在下一节中,我们将看一下灵活的图片。

灵活的图片

我们已经创建了一个流体网格,这是响应式网页设计的第一个基础。基础二是响应式图片或灵活图片。我们希望我们的图片,或者至少某些图片,能像我们的 divs 和 sections 一样行为。我们希望它们是流体的或灵活的。

从我们的网站来看,我们可以注意到章鱼螃蟹鲸鱼的三张图片随着它们所在的列变小。另一方面,顶部的鲨鱼无论浏览器宽度如何,大小似乎都保持不变:

我们导航中的图片不是灵活的。我们列中的三张图片是灵活的。我们将看一下导航中的图片,看看原因。但首先,让我们来看看保证响应式图片的三个因素:

  • img标签放在一个容器中。最语义化的容器通常是figure标签,但它当然可以是任何元素。

  • 使容器变得流体;给它一个百分比宽度。

  • 给所有img标签或至少要成为流体或灵活的img标签分配max-width属性为100%

章鱼、螃蟹和鲸鱼的图片

现在让我们来看看我们 HTML 文件中的一张图片。我们可以看到章鱼图片在一个容器中。容器是figure元素:

<figure>
    <img src="img/octopus-icon.png" alt="Octopus">
</figure>

figure元素没有定义宽度,但它是一个占据整个容器宽度的块级元素。所以我们可以将figure宽度看作100%。它在column div 中:

<div class="column">
    <figure>
        <img src="img/octopus-icon.png" alt="Octopus">
    </figure>
    <h2>The Octopus</h2>
    <p>...</p>
    <a href="#" class="button">Tenticals &raquo;</a>
</div>

如果我们在我们的 CSS 列中查看,我们会看到列宽为31.25%

/****************
3 columns
****************/
.column {
  float: left;
  width: 31.25%; /* 300/960 */  
  margin-left: 3.125%; /* 30/960 */
}

所以我们完成了第一步——我们的图像在容器内。然后我们有第二步——父元素是流动的。第三步是为所有图像分配最大宽度。

让我们滚动到我们的 CSS 文件的顶部。实际上,我在重置中有这个选择器,如下一个屏幕截图所示。它针对imgiframevideoobject,几乎所有类型的媒体。我已经为这个选择器分配了最大宽度为 100%:

img, iframe, video, object {
  max-width: 100%; 
}

我将这个选择器作为我在每个项目中使用的重置或基本样式层的一部分。所以,玩得开心,让我们删除那个属性:

img, iframe, video, object {
 /* max-width: 100%;*/
}

如果我们保存并查看我们的网站,当我们缩小浏览器窗口时,图像不会变小;它们将保持相同的大小并挤在一起:

当我们重新添加max-width: 100%声明时,这些图像再次变得灵活。这表明任何图像的最大宽度只能是其容器的 100%。因此,随着容器变小,图像的宽度也会变小:

img, iframe, video, object {
  max-width: 100%; 
}

鲨鱼图片

鲨鱼图像不会变小有两个原因。让我们来检查一下。我们可以看到鲨鱼图像确实有一个直接的容器元素——一个figure标签。但是该容器是故意不是流动的:

如果您点击图像容器,即nav标签,您会看到它会扩展到浏览器的全宽,说明容器不是流动的:

如果我们检查图像本身,我们会看到它被分配了160px的宽度,这肯定会阻止它成为流动的:

就我个人而言,我不喜欢在图片上设置宽度。即使我不希望这张图片是流动的,我也不希望它有宽度。在这种情况下,让我们做一些清理工作,并更改图片,使得图像元素的宽度为160px,这是图像的容器,而不是图像本身。

nav img {
  width: 160px;
}

这更多地是我的个人偏好:

nav figure {
  position: absolute;
  top: -20px;
  left: 50px;
  width: 160px;
  -webkit-animation: crazyShark 1s ease; 
  animation: crazyShark 1s ease;
}
/* nav img {
 width: 160px;
} */

我们故意将鲨鱼图像保留为固定宽度,因为它不一定需要缩小或增大以使设计具有响应性。我们将在本章后面单独处理页眉部分。

在鲨鱼电影页面上缩小图像

让我们看一下电影页面上的图像。当我们调整浏览器大小时,它们不会缩小。它们有固定的宽度:

我认为它们应该缩小;它们在较小的浏览器尺寸下有点太大了。我们电影页面上的所有三个图像之所以不会缩小,是因为它们的父元素没有定义宽度。让我们使用 Ethan Marcotte 的公式-目标除以上下文等于结果。我们知道图像、标题 1、段落和了解更多按钮所占区域的上下文仍然是 960 像素宽,因为它在wrapper内部:

那么围绕图像的锚点标签的宽度是多少?如果我们查看我们的 CSS,我们有.content-block .figure,那里没有定义宽度:

.content-block .figure {
  float: left;
  margin: 30px 3.125%; /* 30/960 */
  border: 15px solid #fff;
  overflow: hidden;
}

如果我们看一下.figure内部的图像,那里也没有定义宽度:

.content-block .figure img {
  float: left;
  -webkit-transition: transform .25s ease-in-out;
  transition: transform .25s ease-in-out;
}

因此,我们必须利用 Chrome DevTools 的功能来确定围绕img元素的a元素的宽度是多少。如果我们悬停在图像本身上,我们会看到图像是 200 像素乘以 200 像素:

如果我们实际上突出显示锚点,如下面的屏幕截图所示,DevTools 告诉我们宽度为 230 像素。您可以看到它在图像正上方的弹出气泡中。我们的宽度是 230 - 图像的 200 像素加上 15 像素的左边框和 15 像素的右边框。这是有道理的。

现在我们要做的是,当我们将像素值转换为百分比时,我们要使用 230 作为我们的目标。我们还将不得不使用box-sizing: border-box。记住,正如你在第一章中学到的CSS 基础中的盒模型和块与内联元素部分中所学到的,如果你将一个元素设置为box-sizing: border-box,那么borderpadding会被计算到你定义的width中:

.content-block .figure {
  float: left;
  margin: 30px 3.125%; /* 30/960 */
  border: 15px solid #fff;
  overflow: hidden;
  box-sizing: border-box;
}

230 除以 960 等于 0.23958333333333,所以我们将其转换为百分比,得到23.98333333333%

.content-block .figure {
  float: left;
  margin: 30px 3.125%; /* 30/960 */
  border: 15px solid #fff;
  overflow: hidden;
  box-sizing: border-box;
  width: 23.958333333333%; /*230/960*/
}

现在,如果我们刷新浏览器并将其缩小,我们会看到我们的图片变小了。现在可能看起来有点奇怪,但信不信由你,这正是我们想要的,所以很棒!

现在让我们回到 CCS 代码。我们正在使用的这种技术非常有帮助,但我们可能会在整个代码中重复使用box-sizing: border-box。让我们从.content-block .figure规则集中完全删除它,并以稍微不同的方式将其添加到我们的重置中,应该在哪里。让我们将这个添加到我们样式表的重置部分:

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

作为我们的重置的一部分,每个元素都将获得box-sizing: border-box。我们可以看到我们已经将它添加到我们的 HTML 元素中,并使用了通用(星号)选择器,正如你从我们的特异性规则部分中记得的那样,它适用于所有元素。我们只在html元素上应用box-sizing: border-box,但其他所有东西都将获得box-sizing: inherithtml是每个元素的父元素;因此,你正在继承border-box属性到每个元素。好了,我们从灵活的图片中稍微偏离了一下,但我们需要这样做来创造一个积极的前进路径。

因此,总结一下,我们的图片现在是灵活的,但在非常小的浏览器宽度下(比如手机或*板电脑等更小的设备),我们的网站并不完美。在下一节中,我们将找出如何使用媒体查询来处理这个问题。

媒体查询

响应式网页设计的前两个基础只能让你走得更远。最重要的基础是媒体查询。媒体查询基本上是你的 CSS 中的“if”语句或条件逻辑。例如,如果浏览器的宽度小于 500 像素,我们可以根据这些条件应用不同的规则集。媒体查询非常强大,因为在某些点上,我们的网站真的会崩溃并且看起来很糟糕,我们需要用它来修复这个问题。在本节中,我们将找出什么是媒体查询,并使用它来修复网站的剩余问题,特别是在更窄的宽度下。

还要考虑的一件事是,由于我们要缩小浏览器窗口来模拟*板电脑或移动设备,我们将没有太多的空间来查看 DevTools。你可以点击 3 个垂直点的图标,打开下拉菜单将 DevTools 移到右边:

Chrome 会不时地更新 UI,所以图标可能对你来说看起来不一样。

现在我们可以将浏览器窗口缩小到任何更小的宽度,仍然有足够的空间来使用开发者工具:

快速提示

如果你使用 Chrome DevTools 并缩小浏览器窗口,在右上角它会显示浏览器视口的宽度和高度。因此,在下面的图片中,你可以看到它提供了关于宽度的信息,为691px

我们网站目前的问题是,如果达到更窄的宽度,导航栏将会在鲨鱼后面,呼吁行动按钮将被挤压到网站标题中,三列也太窄了:

所以我们将跳过导航栏,最后再回来处理。主要的网站标题和呼吁行动按钮是浮动的。在 1023 像素时,让我们使用媒体查询“取消浮动”这两个部分,并将它们堆叠在一起。

媒体查询的解剖

我将把所有媒体查询放在样式表的底部。您不必这样做,但我会这样做。媒体查询始终以@media开头。然后,它们有两个部分。第一部分是媒体类型。例如:

@media screen

您还可以插入printscreenall和其他一些选项。print仅适用于打印样式表,而screen仅适用于计算机屏幕(但不适用于打印输出)。all将适用于两者。如果我们完全省略媒体类型 - 这是完全可以接受的 - 它将默认为all

媒体查询的第二部分称为媒体特征,并确定何时使用媒体查询。我们需要将其与@media screen分开,使用“and”一词。例如:

@media screen and (max-width: 1023px) { }

同样,这是类似于 JavaScript 中的 if 语句的条件逻辑。如果媒体类型媒体特征都评估为 true,那么媒体查询内部的内容将被应用(是的,我们很快将在媒体查询的大括号内放置内容)。

(max-width: 1023px)表示如果浏览器窗口为 1,023px 或更低,则此媒体查询将应用(或评估为 true)。一旦屏幕宽度超过 1,023px,那么媒体查询将不再应用(或评估为 false)。您还可以使用min-width;它具有相反的效果,适用于所有大于 1,023px 的内容:

@media screen (min-width: 1023px) { }

实际上,您也可以使用任何长度值以及许多其他值。通常,max-widthmin-width与我们要做的事情非常契合,我们现在将坚持使用max-width。注意末尾的大括号。这几乎就像我们刚刚构建了一个 CSS 规则集:

@media screen (max-width: 1023px) { }

在媒体查询的大括号内,我们可以开始编写纯粹的 CSS,只有在浏览器窗口同时(1)是屏幕和(2)1,023px 或更少时才会应用。让我们将float: nonewidth: auto都添加到intro-contentgo-premium

@media screen and (max-width: 1023px){
 .intro-content {
 float: none;
 width: auto;
 }
 .go-premium{
 float: none;
 width: auto;
 }
}

auto值是width属性的默认值,因此它实际上使这些块元素跨越整个可用宽度,这将是wrapper的 100%。auto关键字意味着它将自动计算值。auto根据其配对的属性具有不同的值。在这种情况下,它基本上与“100%”相同。

现在我们可以看到我们的介绍内容不再浮动,宽度是全宽,而go-premium按钮也是一样的:

但是,go-premium按钮和intro-content之间有一个很大的空间,我们需要摆脱它:

为了解决这个问题,我们将添加:

@media screen and (max-width: 1023px){
  .intro-content {
    float: none;
    width: auto;
 padding-bottom: 0;
 margin-bottom: 30px;
  }
  .go-premium {
    float: none;
    width: auto;
  }
}

go-premium按钮本身上,我们将删除顶部边距:

@media screen and (max-width: 1023px){
  .intro-content {
    float: none;
    width: auto;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    float: none;
    width: auto;
    margin-top: 0;
  }
}

我们已经有了我们的标题,副标题,文本和按钮,一切看起来都很好:

考虑 iPad 和其他*板电脑尺寸

我们选择 1,023 作为断点,因为这只是低于横向放置的 iPad 的宽度的一个像素。

这样,我们的媒体查询将适用于所有小于 1,024p 的宽度和设备。截至 2017 年,我会猜测 iPad 是 - 如果不是最受欢迎的*板电脑 - 其中一个最受欢迎的*板电脑。我可以更肯定地说 iPad 绝对不是唯一受欢迎的*板电脑。事实上,令人惊讶的是,有多少不同的*板设备和宽度,因此您可能不希望分别使用 1,024 和 768 作为媒体查询的基础。找出您的布局通常从何处开始中断或看起来有趣,并确定媒体查询的逻辑放置位置。然后,在 iPad 和任何其他设备或模拟器上测试您的网站,以确保您的网站看起来不错。在我们的情况下,我们将使用 1,023 作为基线,因为在 1,024 时,布局仍然看起来不错。

将我们的三列添加到媒体查询

现在,我们只需要将所有的 CSS 添加到我们的媒体查询中,以使网站的其余部分看起来不错,从三列区域开始。正如您在下面的截图中所看到的,这些区域太紧凑了:

为较小的设备创建内容管道是一种标准做法,摆脱任何多列浮动布局。因此,我们将再次从.column中删除浮动,并通过指定auto关键字使宽度成为父元素的全宽。让我们转到 CSS 的底部并更新媒体查询:

@media screen and (max-width: 1023px){
  .intro-content {
    float: none;
    width: auto;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    float: none;
    width: auto;
    margin-top: 0;
  }
 .column {
 float: none;
 width: auto;
 }
}

现在当我们转到我们的网站时,这三列中的每一列都将占满整个宽度,不会浮动:

问题是,我们可能应该将这段文字和图像本身居中。因此,首先我们将在媒体查询中首先针对.column figure进行定位以居中图像:

@media screen and (max-width: 1023px){
  .intro-content {
    float: none;
    width: auto;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    float: none;
    width: auto;
    margin-top: 0;
  }
  .column {
    float: none;
    width: auto;
  }
 .column figure {
 margin: 0 auto;
 max-width: 250px;
 width: 100%;
 }
}

通过使用auto关键字设置左右边距,并且不仅设置width: 100%而且还设置max-width: 25px,我们能够使图像居中:

添加width: 100%确保如果wrapper容器小于 250px(图像的最大尺寸),则图像宽度将是其容器的100%;在非常窄的宽度下起到一种安全保障作用。

接下来,让我们简单地使用text-align: center来居中标题:

@media screen and (max-width: 1023px){
  .intro-content {
    float: none;
    width: auto;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    float: none;
    width: auto;
    margin-top: 0;
  }
  .column {
    float: none;
    width: auto;
  }
  .column figure {
    margin: 0 auto;
    max-width: 250px;
    width: 100%;
  }  .column h2 {
 text-align: center;
 }
}

看起来不错:

我们的响应式设计还可以采用另一种方法-移动优先方法。

移动优先方法

通常,这是最佳实践。我们希望在同一时间或在桌面体验之前考虑较小的显示屏,正如名称所示。这样,内容和设计可以在设计和构建过程中充分考虑移动设备及其所有约束,因此可以完全实现。Mobile first 不仅涉及我们如何编写 CSS。但这是移动优先的代码部分的一般思路:将所有针对最小设备的 CSS 放在任何媒体查询之外。然后使用媒体查询来针对越来越大的设备。

这意味着使用min-width媒体查询为较大的显示屏添加额外的 CSS。例如,我们的布局默认情况下不会有任何浮动;相反,它将是一个单一的内容隧道,这通常是移动设备的标准。然后我们的媒体查询,使用min-width而不是max-width,将添加浮动,这些浮动将应用于更宽的屏幕宽度以创建多列布局。

有关移动优先方法的更多信息,请查看 Luke Wroblewski 关于该主题的定义书籍-Mobile First-可在abookapart.com网站上找到:

解决导航问题

现在让我们谈谈这个导航。解决导航问题并不像使用媒体查询解决我们刚刚解决的问题那么容易。在某些宽度下,我们的鲨鱼会挡住导航,而且在某个时候,我们还需要对下拉菜单做些处理。此外,如果我们要添加更多的导航项,那么在更宽的宽度下,我们将遇到这个问题。

我们遇到了解决导航的第一个真正挑战。出现了一些特定的响应式设计模式。让我们谈谈 Brad Frost 策划的网站-bradfrost.github.io/this-is-responsive

这基本上是一组用于响应式网页设计的模式。它有很多不同的处理导航的方式供您探索。让我们看看模式下的第一个,称为切换。在较宽的宽度下,菜单看起来有点像我们的;顶部只是一些导航项:

在较窄的浏览器宽度下,导航项被替换为菜单链接:

当您单击链接时,它会展开并隐藏菜单:

这通常是我们想要在我们的网站上做的事情;这将需要更多地使用媒体查询和一些 JavaScript 或 jQuery 来显示和隐藏导航点击。准备好挑战了吗?好的,让我们做到!

我们已经使用媒体查询根据屏幕宽度更改了我们的 CSS。这非常有用,因为它修复了我们在较窄宽度下的大部分问题。我们仍然需要在较小宽度下修复导航。在下一节中,我们将使用媒体查询大幅改变我们的导航,使其隐藏,除了一个可以点击以显示带有 jQuery 的导航的菜单图标。

移动菜单

到目前为止,在本章中,您已经了解了 Ethan Marcotte 关于响应式网页设计的三个基本原则-流体网格,可伸缩的图像和媒体查询。在某种程度上,这在某种程度上是容易的部分。困难的部分是弄清楚多种棘手的设计挑战;例如,在移动设备上该怎么处理我们的菜单,特别是当我们决定添加更多菜单时。幸运的是,这是一个体面的设计模式已经出现的领域。我们将放弃水*菜单,而是显示导航栏上的每个按钮的隐藏菜单,而是通过点击或触摸来激活的隐藏菜单。被点击或触摸后,隐藏菜单将垂直滑下并显示所有菜单选项。我们将通过为移动导航设置样式来实现这一点。然后,我们将隐藏导航并添加触发移动菜单打开和关闭的菜单图标。最后,我们将将我们的 HTML 链接到 jQuery CDN 和我们的脚本文件,并编写一些基本的 jQuery 来实现这一点。

在打开状态下为移动导航设置样式

在样式表的最底部,我将添加一个针对900px或更小的新媒体查询:

@media screen and (max-width: 1023px){
  ...
}/* end of media query */

@media screen and (max-width: 900px) {

}

请注意,我还在第一个媒体查询的结束大括号处添加了一个注释。这很有用,这样我们就不会迷失我们的第一个媒体查询的结束位置。新的移动导航只会在新的断点900px宽度时触发。那时它开始看起来很奇怪并且开始破损。首先,我不想导航固定在顶部,所以让我们摆脱固定定位并将其替换为默认的静态位置:

@media screen and (max-width: 900px) {
 nav {
 position: static;
 }
}

静态,你可能还记得之前的部分,是position属性的默认值,因此它基本上关闭了fixed定位,并将其返回为根据正常流行为的元素。

接下来,让我们告诉primary-nav的所有直接列表项向右浮动,而不是向左。我们还将给它一个完整的宽度,因为这些浮动元素只会占用所需的宽度,类似于内联元素,因此我们将告诉它们通过将宽度设置为100%来占用整个可用宽度:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
 .primary-nav > li {
 float: right;
 width: 100%;
 }
}/* close media query */

我还在这个媒体查询的结束大括号处添加了一个注释,以便不会迷失。

现在让我们专注于锚点。让我们将文本对齐到右侧,标准化填充,移除左侧的边框并在底部添加边框,并将字体大小调整为 13px,并将宽度设置为 100%:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
  .primary-nav > li {
    float: right;
    width: 100%;
  }
 .primary-nav li a {
 text-align: right;
 padding: 15px 25px 15px 0;
 width: 100%;
 border-bottom: 1px solid #e7e7e7;
 border-left: none;
 font-size: 13px;
 }
}/* close media query */

好的,这是在应用此 CSS 之前导航的样子:

这是刷新后的样子。现在它开始看起来像移动导航了:

问题在于 100%的宽度实际上并没有起作用。嗯,实际上它起作用了;锚点是其容器的 100%,但它们的容器也需要是 100%。因此,让我们将primary-nav的宽度设置为 100%:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
  .primary-nav > li {
    float: right;
    width: 100%;
  }
  .primary-nav li a {
    text-align: right;
    padding: 15px 25px 15px 0;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
    border-left: none;
    font-size: 13px;
  }  .primary-nav {
 width: 100%;
 }
}/* close media query */

我们可以在待办清单上划掉这一项:

这确实像是一个菜单。房间里的大象是鲨鱼只是占用了太多的空间。让我们来修复一下。

让我们在针对鲨鱼的媒体查询的底部添加一个新的选择器。我们将使它变得更小,向上移动,并使用顶部和左侧位置的偏移属性将其紧贴到左侧,因为这已经是一个绝对定位的元素:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
  .primary-nav > li {
    float: right;
    width: 100%;
  }
  .primary-nav li a {
    text-align: right;
    padding: 15px 25px 15px 0;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
    border-left: none;
    font-size: 13px;
  }  .primary-nav {
    width: 100%;
  }
  nav figure {
 width: 100px;
 top: 0;
 left: 20px;
 }
}/* close media query */

看起来不错:

接下来要修复的是下拉菜单。这样不行:

我们现在需要做一个设计决定。我们应该隐藏下拉菜单,不让任何移动用户访问它吗?我们可以。但这对移动用户不公*。我们显然可以保持它如上所示,但我认为把它融入到primary-nav中会更好地为移动用户服务。我想让它看起来像其他主要菜单。所以我们要针对.has-submenu ul选择器。我们将在媒体查询的底部添加这个规则集。我们将把position属性从absolute改为static,把display属性改为block,移除borderborder-radius属性,并让width横跨整个宽度:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
  .primary-nav > li {
    float: right;
    width: 100%;
  }
  .primary-nav li a {
    text-align: right;
    padding: 15px 25px 15px 0;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
    border-left: none;
    font-size: 13px;
  }  .primary-nav {
    width: 100%;
  }
  nav figure {
    width: 100px;
    top: 0;
    left: 20px;
  }  .has-submenu ul {
 position: static;
 display: block;
 border: none;
 border-radius: 0;
 width: 100%;
 }
}/* close media query */

现在我们有这个:

哇!看起来很不错。我们也可以取消动画,因为它不再需要。我们将在媒体查询内部添加一个新的选择器,并将-webkit-animationanimation设置为none;这个关键字将取消动画:

@media screen and (max-width: 900px) {
  nav {
    position: static;
  }
  .primary-nav > li {
    float: right;
    width: 100%;
  }
  .primary-nav li a {
    text-align: right;
    padding: 15px 25px 15px 0;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
    border-left: none;
    font-size: 13px;
  }  .primary-nav {
    width: 100%;
  }
  nav figure {
    width: 100px;
    top: 0;
    left: 20px;
  }  .has-submenu ul {
    position: static;
    display: block;
    border: none;
    border-radius: 0;
    width: 100%;
  }  .has-submenu:hover ul {
 -webkit-animation: none;
 animation: none;
 }
}/* close media query */

我们不再有动画效果。"电影"菜单的悬停状态以一种奇怪的方式覆盖了鲨鱼,但很快在我们添加汉堡菜单图标时就会得到修复:

移动导航在打开状态下已经完成;现在我们需要隐藏它,并添加汉堡图标来触发它的打开和关闭。

添加汉堡菜单图标

让我们在index.html文件和shark-movies.html文件中的primary-nav正上方添加一个div标签。我们给它一个类名mobile-menu-icon;这很重要:

<!--
===============
Nav
===============
-->
<nav class="grouping">
    <figure>
        <img src="img/sharky.png" alt="Shark">
    </figure>
    <div class="mobile-menu-icon"></div>
    <ul class="primary-nav grouping">
        <li><a href="#">Home</a></li>
        <li class="has-submenu"><a href="shark-movies.html">Movies</a>
            <ul>
                <li class=""><a href="">Jaws</a></li>
                <li class=""><a href="">Sharknado</a></li>
                <li class=""><a href="">Open Water</a></li>
            </ul>
       </li>
        <li><a href="#">Species</a></li>
        <li><a href="#">Chum</a></li>
    </ul>
</nav>

当我们应用这个时,浏览器中什么都没有显示出来,因为这只是一个空的div标签。让我们使用背景图片添加图标。我们不会把这个放在媒体查询中;实际上,我们会移动到 CSS 中原始的 nav 所在的位置,并添加这个规则集:

/****************
nav
****************/
nav {
  background-color: #fff;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index:1;
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
}
nav figure {
  width: 160px;
  position: absolute;
  top: -20px;
  left: 50px;
  -webkit-animation: crazyShark 1s ease; 
  animation: crazyShark 1s ease;
}
.mobile-menu-icon {
 background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
}

我们已经在我们的图片文件夹中有这张图片。我们使用零和零作为我们的背景位置和 no-repeat,以确保这张图片不会自动重复。除非我们添加宽度和高度,否则在浏览器中什么都不会显示。我们知道这张图片宽 30 像素,高 26 像素,所以我们将使用这些确切的尺寸:

.mobile-menu-icon {
  background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
 width: 30px;
 height: 26px;
}

现在当我们保存并刷新时,我们可以看到三条杠图标位于浏览器窗口的顶部:

我们还想把它移到右边,并给它一些距离,使用一些上、右和下的 margin。我们还可以改变光标,这样它看起来就有了不同的外观。让我们把这些属性添加到mobile-menu-icon中:

.mobile-menu-icon {
  background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
  width: 30px;
  height: 26px;
  float: right;
 margin: 10px 15px 10px 0;
 cursor: pointer;
}

在刷新浏览器之前,我们可以看到我们只是在图标上方普通的光标悬停:

刷新后,它移到右边,现在有一个指针类型的光标,表示它是可点击的:

显然,我们在移动设备上看不到这个,但在桌面设备或桌面浏览器上,这是一个小小的好东西。

隐藏菜单

现在让我们默认隐藏菜单。我们不再使用display: none,因为如我之前提到的,这对无障碍原因来说并不好。让我们探索另一种更有创意地隐藏内容的技术,以便屏幕阅读器仍然可以找到并宣布它。我们将回到媒体查询内部,.primary-nav内部。我们将说这个元素的高度是零:

@media screen and (max-width: 900px) {
  .intro-content {
    margin-top: 50px;
  }
  nav {
    position: static;
  }
  .primary-nav {
    width: 100%;
    max-height: 0;
 overflow: hidden;
    -webkit-transition: all ease-out .35s;
    -moz-transition: all ease-out .35s;
    -o-transition: all ease-out .35s;
    transition: all ease-out .35s;
  }
...

以下是前面代码的输出:

就是这样。现在我们要做的是在点击时激活菜单。

使用 jQuery 在点击时触发菜单

在这个阶段,我们需要链接到 jQuery 和我们自己的 JavaScript 文件。我们将在 HTML 的底部,在闭合的</body></html>标签之前做这个。复制一个链接到托管在谷歌网站上的 jQuery CDN。在此之下,添加一个链接到我们自己的 JS 文件。我们将把这个文件放在js文件夹中,并将文件命名为scripts.js

<script src="img/jquery.min.js"></script>
<script src="img/scripts.js"></script>
</body>
</html>

另外,让我们将这个复制到shark-movies.html的相同位置。我们也要创建一个新的 JavaScript 文件。

在 Sublime Text 中创建新文件的简单方法是使用Cmd + N(在 Mac 上)或Ctrl + N(在 Windows 上)。Cmd + S(在 Mac 上)或Ctrl + S(在 Windows 上)将让您保存名称并保存文件。

我们将其保存在js文件夹中:

我们将文件命名为scripts.js

好的,现在让我们写一些 jQuery。如果这里的一切对你来说都不太有意义,不要担心。我不会详细讨论这个话题,因为这超出了本书的范围,但这对我们的响应式设计是必需的。我们要粘贴到我们的新scripts.js文件中的是一个在 DOM 准备就绪时触发的函数:

$(document).ready(function() {

});//end doc ready

我们想把我们写的代码放在这个函数里。这只是告诉脚本在网页准备就绪之前等待,然后再执行其中的代码。这对于 jQuery 来说是标准的。因此,让我们将其粘贴到我们的函数中:

$(document).ready(function() {
  $(".mobile-menu-icon").on("click", function(){
    $(".primary-nav").toggleClass("active");
    $(this).toggleClass("open");
  });
});//end doc ready

首先,我们在这里有一个 jQuery 函数,它专门针对mobile-menu-icon类,当你点击该元素时:

$(".mobile-menu-icon").on("click", function(){

一旦点击了该元素,就会执行两行代码。我们首先要关注的是primary-nav,并切换一个名为active的类:

$(".primary-nav").toggleClass("active");
$(this).toggleClass("open");

因此,如果您点击汉堡菜单,它将向primary-nav添加一个active类。如果您再次点击它,它将删除它,并为我们保持这样做,这很好。下一行是针对$(this)。在这里,$(this)指的是我们点击的任何东西。在这种情况下,我们点击的是mobile-menu-icon,并在其上切换一个名为open的类。查看 DevTools 中的移动菜单图标和primary-nav

DevTools 中的代码行在以下截图中突出显示:

这两者都应该添加类。当我们点击汉堡菜单图标时,我们会看到mobile-menu-icon获得open类,primary-nav获得active类:

当我们再次点击它时,它们两者都消失了。所以现在我们可以继续以我们需要的方式定位这些类。让我们回到我们的 CSS。我们想要定位mobile-menu-icon在打开状态下。因此,我们将该选择器添加到 CSS 的 nav 部分中:

@media screen and (max-width: 900px) {
  ...
  .mobile-menu-icon {
    background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
    width: 30px;
    height: 26px;
    float: right;
    margin: 10px 15px 10px 0;
    cursor: pointer;
  }
  .mobile-menu-icon.open { }
}/* end of media query */

我们要做的就是改变背景图片:

.mobile-menu-icon.open { 
  background-image: url('../images/mobile-menu-close-icon.png');
}

现在当我们点击汉堡图标时,我们得到 x 图标,当我们再次点击它时,我们得到菜单图标。所以很好:

现在我们想要定位primary-nav.active选择器,因此让我们将其添加到我们的 CSS 中,并给它一些高度:

@media screen and (max-width: 900px) {
  .intro-content {
    margin-top: 50px;
  }
  nav {
    position: static;
  }
  .primary-nav {
    width: 100%;
    max-height: 0;
    overflow: hidden;
  }
  .primary-nav.active {
 max-height: 350px;
 }
  ...
}

现在当我们点击图标时,我们得到我们的菜单:

当我们再次点击它时,它消失了:

目前,图像出现并立即消失,所以我们想为其添加一个过渡效果。让我们去.primary-nav并添加一个过渡效果:

@media screen and (max-width: 900px) {
  .intro-content {
    margin-top: 50px;
  }
  nav {
    position: static;
  }
  .primary-nav {
    width: 100%;
    max-height: 0;
    overflow: hidden;
 -webkit-transition: all ease-out .35s;
 transition: all ease-out .35s;
  }
  .primary-nav.active {
    max-height: 350px;
  }
  ...
}

现在我们应该有一个丝般顺滑的过渡效果,当我们点击菜单图标时,隐藏的导航会向下滑动和向上滑动。

我们的移动导航已经完成,有相当多的 CSS 和一点 JavaScript,并且我们的网站现在具有广泛的响应性。我们只需要做一件事 - 我们需要在移动设备上测试我们的网站。我们会注意到,结果与我们将浏览器调整为手机或*板电脑宽度时非常不同。幸运的是,解决方案非常简单 - viewport meta 标签。

视口 meta 标签

我们的响应式网站几乎完成了。除了我们还没有在移动设备上测试过它。在本节中,让我们使用 Chrome 的移动设备模拟器来测试我们的设计,然后看看并尝试理解viewport meta 标签。

在移动设备上测试我们的响应式设计

在手机上测试的一种方法是这样的-让您的网站上线并在实际手机或*板上测试。一个更简单的方法是在手机上进行简单测试(但可能略微不太准确)是使用 Chrome 的设备模拟器。在 DevTools 中有一个设备图标:

一旦你点击了那个,你就可以选择一个手机。我们可以看到我们的网站,但它看起来并不像我们刚刚将浏览器窗口最小化到手机大小时那样:

发生的情况是,大多数移动设备会尝试缩小您的网站以适应手机屏幕,然后如果您的网站不具有响应性,它将看起来像桌面版本,只是小得多。所以一个显而易见的问题是我没有看到移动导航。有一个非常简单的解决方案-viewport元素。我将把它复制粘贴到indexshark-movies页面中:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<!-- description -->
  <title>Section 6-Becoming Responsive - Mastering CSS</title>

<!-- stylesheets -->
  <link rel="stylesheet" href="css/style.css">

<!-- mobile -->
 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">

<!-- stylesheets for older browsers --> 
  <!-- ie6/7 micro clearfix -->
  <!--[if lte IE 7]>
    <style>
    .grouping {
        *zoom: 1;
    }
    </style>
  <![endif]-->
  <!--[if IE]>
    <script src="img/html5.js"></script>
  <![endif]-->
</head>

这只是一个带有viewport名称的meta元素;我们稍后会回到这个问题。现在,让我们看看当我们刷新浏览器时会发生什么:

实际上我们得到了移动版本,所以这看起来好多了,解决了我们遇到的问题。

viewport 元标签的解剖

让我们来看看这个元标签:

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">

在这里,我提供了一个带有值viewport的名称属性。然后,我有一个content属性,里面提供了一些不同的东西。第一件事是width=device-width。这基本上意味着“请不要在移动设备上缩小我的页面,因为我正在使用媒体查询来处理这个问题。谢谢!”第二件事是initial-scale=1.0,基本上是说这个-将其大小调整到设备的宽度,而不多。最后,我们有minimum-scale=1.0。这在您旋转手机时会有所帮助,因此网站在设备的宽度从纵向模式更改为横向模式以及反之后仍然保持在设备的宽度。viewport元标签还有更多内容。我们可以添加user-scalable=no

user-scalable=no这个术语不允许用户在手机上放大或缩小。拥有这个属性的网站可能会非常恼人,这就是为什么我们不会在我们的网站上包含它的原因。

总之,我建议将viewport元标签添加到您的网站的模板中,以便在每个网站上使用。实际设备测试没有替代品,因为实际手机和模拟器永远不会完全相同。

总结

在这一章中,我们涵盖了响应式网页设计的核心概念,这将使您的网站在任何设备上都能看起来很好。您学到了流体网格和灵活图片是使网站适应所有屏幕尺寸的第一步。我们现在了解了媒体查询如何确保网站在较窄的宽度下看起来很好。我们还使用 jQuery 创建了一个移动菜单,以便在点击时触发菜单。最后,我们在 Chrome 的移动设备模拟器上测试了我们的设计,并学会了如何使用viewport元标签来确保我们的网站在移动设备上具有响应性。我强烈建议您在自己使用这些技术时,从设计过程的一开始就考虑移动体验。在下一章中,我们将讨论网络字体。

第七章:网络字体

很长一段时间,我们只能使用一些基本字体,比如 Times New Roman、Georgia 和 Arial。如果你想要更远的偏离,你就会面临字体在别人查看你的网站时不显示的风险,因为他们可能没有安装那个字体。在这段时间里,每当我们想使用一个花哨的字体时,我们都必须将它保存为一个图片,这曾经带来了许多问题。幸运的是,现在网络字体正式出现了,这使我们能够在所有计算机上使用大量出色的字体。在本章中,您将学习有关网络字体的知识。首先,您将学习@font-face规则的基本语法,然后我们将讨论@font-face可能有点棘手,接着我们将介绍提供字体并将其传递到您的网站的服务,比如 Google Web Fonts 和 Typekit。最后我们将介绍图标字体。

@font-face 属性

让我们从学习如何使用@font-face属性向网站添加网络字体开始这一章。首先,我们将在网站的一个文件夹中添加一个 OTF 文件,然后我们将在我们的 CSS 中定义一个新字体,最后,我们将将该 CSS 应用到我们网页上的元素。

直接将字体文件添加到网站

在本节的项目文件中,我们有一个名为fonts的新文件夹。在这个文件夹里,有一个名为LeagueGothic-Regular的 OTF 文件:

现在这个字体就存在于我们网站的文件夹中,访问我们网站的最终用户将下载这个字体到他们的计算机上,就像他们下载 HTML、CSS 文件和图片一样。但首先,我们必须告诉它这样做,并在我们的 CSS 中查找它。

在我们的 CSS 中定义和应用新字体

在 CSS 中,就在我们的重置下面,让我们添加一个名为字体的新部分。添加@font-face;这将允许我们声明一个新字体:

/****************
Fonts
****************/
@font-face {
    font-family: 'League-Gothic';
}

我将首先声明字体名称,可以是任何东西。所以即使字体叫League Gothic-Regular,你也可以把它命名为Bananas Serif。让我们称之为League Gothic,因为这是最有意义的。

我用单引号括起来有两个原因。一是它是一个网络字体,二是它有多个单词,应该总是用引号括起来,就像你会引用'Times New Roman'一样。接下来,我们将声明这个字体存在的位置,使用src属性:

@font-face {
  font-family: 'League Gothic';
 src: url('../fonts/LeagueGothic-Regular.otf');
}

我们要确保拼写与 OTF 文件的名称完全匹配。请注意我使用了../。这是一条指令,要跳出CSS文件夹,然后进入fonts文件夹,查找LeagueGothic-Regular.otf。这是我们项目的文件夹结构:

现在我们可以使用我们习惯的font-family属性将此字体添加到任何规则集中。我们也可以像通常一样指定回退,以防字体未被下载。在样式表的“全局”部分,有一个h1的规则集和另一个h2的规则集:

h1 {
  font-weight: 700;
  font-size: 80px;
  color: #0072ae;
  margin-bottom: 10px; 
}
h2 {
  font-size: 30px;
  margin-bottom: 10px;
  color: #eb2428;
  font-weight: 700; 
}

h2规则集下面,我们将添加另一个,针对h1标签和h2标签添加我们的新网络字体。

h1, h2 {
  font-family: "League Gothic", Arial, Helvetica, sans-serif;
}

以下是前面代码的输出:

以下是我们的字体以前是什么样子的:

当我们刷新时,哇!非常时尚的网络字体被添加到我们的网站上:

我们已经成功地将网络字体添加到我们的网站,但不幸的是,我们所做的实际上并不能在所有浏览器中工作。由于排版可能是网页上最重要的事情,我们必须找到一个更好的解决方案。

@font-face:有点棘手的事情

表面上,网络字体很容易,但实际上,当我们希望它们在所有现代浏览器中工作时,它们变得复杂起来。一些浏览器使用 OTF,其他使用 WOFF,还有一些使用 EOT、RTF 和 SVG。让我们来看看使用@font-face属性使网络字体工作的完整设置。

使其在所有浏览器中工作

CSS Tricks, that describes the ideal @font-face at-rule. (*Using @font-face* by *Chris Coyier* of CSS-tricks.com, August 25, 2016, https://css-tricks.com/snippets/css/using-font-face/.)
@font-face {
  font-family: 'MyWebFont';
  src: url('webfont.eot'); /* IE9 Compat Modes */
  src: url('webfont.eot?#iefix') format('embedded- 
  opentype'), /* IE6-   
  IE8 */
  url('webfont.woff2') format('woff2'), /* Super Modern 
  Browsers */
  url('webfont.woff') format('woff'), /* Pretty Modern   
  Browsers */
  url('webfont.ttf') format('truetype'), /* Safari, 
  Android, iOS */
  url('webfont.svg#svgFontName') format('svg'); /* Legacy 
  iOS */
}

这不仅是寻找字体的七个不同的url,还有五种不同的字体文件:eotwoff2woffttfsvg!正如前面的代码示例中的注释所解释的那样,每种字体文件格式都支持不同的浏览器版本。

根据同一篇CSS Tricks文章,只有woffwoff2文件格式将为您提供相当不错的浏览器支持(Chrome 5+,Safari 5.1+,Firefox 3.6+,IE9+,Edge,Android 4.4+和 iOS 5.1+):

@font-face {
 font-family: 'MyWebFont';
 src: url('myfont.woff2') format('woff2'),
      url('myfont.woff') format('woff');
}

但这仍意味着你需要获取和托管两种文件格式,这当然不像五种文件格式那么具有挑战性,但也不是一件轻而易举的事情。

网络字体比我们希望的要复杂一些。大多数情况下,字体是通过服务提供的,这正是我们将在接下来的两个部分中看到的。Google Web 字体、Typekit 和其他服务使网络字体变得更加容易,并提供多种不同粗细和样式的高质量字体。在下一节中,我们将使用来自 Google 字体的字体。

Google Web 字体

托管自己的网络字体并使用适当的 CSS 来支持所有浏览器稍微有些挑战。有更简单的方法来解决这个问题。我真的很喜欢 Google 字体;它们非常容易使用,而且 100%免费。字体的质量也非常好。在这一部分,我们将用 Google Web 字体替换我们托管的字体。第一步是去 Google 字体并选择我们将使用的两种字体。在两个 HTML 文档的标题中添加 CSS 文件的链接。最后,在我们的 CSS 中添加字体名称。

查找 Google 字体

前往fonts.google.com/,搜索我们的标题字体:Maven。很酷的是,我们可以输入一些文本,比如说我们的网站标题,来看看这种字体中特定单词的样子。大多数字体服务都会输出类似于这样的东西:

所以我们可以只输入 Old Chompy 并了解一下这个字体在我们的h1上会是什么样子。我们甚至可以增加字体大小。让我们搜索并使用Maven Pro;通过点击红色加号图标来实现。在屏幕底部,我们应该选择了一个字体系列:

接下来我们将寻找并获取Droid Serif字体。底部将显示选择的 2 个字体系列:

让我们打开底部的东西,获取更多信息:

我们快要完成了;我们只是在验证和审查。从底部滑出的面板向我们展示了一些有趣的东西:

  • 加载时间

  • 如何在我们的页面上嵌入字体文件

  • 如何在我们的 CSS 中指定这些字体

我可以通过转到自定义选项卡来添加额外的字体粗细和字体样式:

在这里,我可以选择额外的字体粗细和字体样式。如果我选择太多,加载时间指示器就会变慢:

我们只需要 Droid Serif 的普通、斜体和粗体,以及 Maven Pro 的普通和粗体,这从慢到中等:

现在,我们的加载速度设置为中等。我真的很想处于绿色状态,但至少我们没有处于红色状态,所以我们就接受这个吧。

让我们回到嵌入选项卡,并复制这些字体文件的链接:

这段代码实际上只是一个样式表。让我们把它粘贴到index.htmlshark-movies.htmlhead标签中:

我们可以看到这是我们用来指定样式的相同<link/>

<link href="https://fonts.googleapis.com/css?family=Droid+Serif:400,400i,700|Maven+Pro:400,700" rel="stylesheet">

实际上,这是一个指向fonts.googleapis.com的样式表,这就是它获取字体的地方。它实际上显示了两种字体选择,即:Droid Serif 和 Maven Pro。Google 字体托管在 Google 的服务器上,我们只需要进行一次 http 请求,这对性能来说很好。

在 CSS 中应用字体

现在我们想在我们的 CSS 中使用这些字体。正如你所看到的,他们确切地告诉我们如何做到这一点:

首先,在我们的h1h2中用Maven Pro替换League Gothic

h1 {
  font-size: 80px;
  color: #0072ae;
  margin-bottom: 10px; 
  font-family: "Maven Pro", Arial, Helvetica, sans-serif;
  font-weight: 700;
}
h2 {
  font-size: 30px;
  margin-bottom: 10px;
  color: #eb2428;
  font-family: "Maven Pro", Arial, Helvetica, sans-serif;
  font-style: italic;
}

下一步是添加Droid Serif。我实际上想确保我们的所有正文、所有段落、锚点和除了h1标签和h2标签之外的所有内容都使用Droid Serif。我们会多加一些小心,所以我们将添加备用字体。我们将指定备用字体为Georgia,然后是Times New Roman,然后是默认的serif,如下所示:

body {
  background-color: #dcdcdc;
  font-family: "Droid Serif", Georgia, "Times New Roman", sans-serif;
  font-weight: 100;
  font-size: 16px; 
}

保存这些更改。现在当我们转到我们的网站时,在刷新之前,我们可以看到我们的h1h2应用了League Gothic,然后我们的通用Arial用于段落:

刷新后,我们得到了我们的新字体:这非常好。我们的h1h2使用Maven Pro,我们的其他所有文本使用Droid Serif

在本节中,您学会了如何使用 Google 提供的免费字体资源。使用 Google 的 Web 字体是地球上最简单的使用字体的方式,除了根本不指定字体。在下一节中,我们将看看另一个很棒的字体资源——Typekit,这是 Adobe 提供的订阅字体库,提供了大量高质量的字体。

Adobe Typekit

Adobe Typekit 是出色的订阅字体服务之一。但是,为什么要使用 Typekit,当 Google 提供免费字体时呢?我不想说您得到了 Google 的报酬,因为我认为 Google 的字体质量很高,选择很多,但我认为 Typekit 的字体选择和质量也非常出色。然而,我认为最好的功能是,这个字体服务对所有Adobe Creative Cloud订阅者免费。因此,如果您订阅了创意云套件,例如 Photoshop 和 Illustrator 等工具,您也可以访问 Typekit 上的每种字体。如果您不是 Adobe Creative Cloud 的订阅者,您也可以单独订阅 Typekit,这绝对值得。另一个很酷的功能是,您可以很容易地将字体同步到 Photoshop 和 Illustrator,并在这些工具中进行设计,而使用 Google Web 字体则不那么容易。在本节中,我们将从 Typekit 向我们的网站添加另一种字体。

从 Typekit 中选择字体

让我们去typekit.com/

我将使用我的 Adobe ID 和密码登录。如果您没有 Adobe ID,或者既不是 Adobe 的创意云会员,也不是 Typekit 的独立服务会员,您需要注册才能跟着进行。我们可以浏览看起来不错的字体,但让我们实际搜索我们想要的字体expo sans

选择 Expo Sans 后,我们来到一个显示不同粗细和样式的页面。我们有两个基本选项可以使用,即全部同步或添加到套件。同步是为了将此字体同步到我的计算机,以便在 Photoshop、Illustrator 和其他 Adobe 产品中使用。将其添加到套件中允许我在 Web 上使用它。所以让我们这样做,然后点击“添加到套件”按钮:

然后,我们将点击“创建套件”按钮,选择 Expo Sans Pro:

我们将把它命名为Old Chompy,这是我们网站的名称。然后,对于域名,我将使用localhost:8888oldchompy.comlocalhost:8888将用于开发,oldchompy.com将用于网站投入生产后,因为那将是域名。然后我们将点击“继续”:

这是使用 Typekit 的一个小缺点;您必须选择一个域。在整个课程中,我们一直通过文件系统直接向浏览器提供页面。我们还没有需要设置本地开发环境。通常,直到开始使用 AJAX 调用、服务器端代码或内容管理系统(CMS)时,您才需要这样做。为了确保 Typekit 的字体不能随意在任何地方使用,Typekit 将它们交付给特定的域名。

我会使用localhost:8888,这是我的本地服务器通过 MAMP 在我的电脑上运行的地方。建立本地开发环境远远超出了这个项目的范围,所以不要觉得你必须完全跟着这个特定的步骤。我还会输入这个站点理论上将公开的域名,即localhost:8888oldchompy.com

在我们进入这个嵌入代码之前,让我们回到网站,看看 URL 的第一部分:

请注意,我现在通过localhost:8888以不同的方式访问我的网站。这是我的本地服务器正在运行的地方。这与我之前的访问方式不同,之前是直接通过文件系统,进入文件库网页服务器文档,然后进入我的站点文件夹。

我将为整个课程的这一部分做这个。就像我之前说的,如果你无法跟上这部分,不要担心。

将字体添加到网站

让我们回到 Typekit 上的嵌入代码;这个屏幕给了我们 JavaScript 嵌入代码:

我会复制这个,转到 Sublime Text,然后粘贴到我们 HTML 文件的<head></head>标签中。我会在我的shark-movies.html页面中做同样的事情,并保存:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<!-- mobile -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-
  scale=1.0">

<!-- description -->
  <title>Section 7-Web Fonts - Mastering CSS</title>

<!-- stylesheets -->
  <link rel="stylesheet" href="css/style.css">

<!-- fonts -->
  <link href='http://fonts.googleapis.com/css?
  family=Droid+Serif:400,700|Maven+Pro:400,700' rel='stylesheet' type='text/css'>
 <!-- Typekit -->
<script src="img/ycq4ynz.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>

好的,回到 Typekit。我会点击“继续”按钮,进入下一步:

在下一个屏幕上,我们可以做几件事,包括选择我们想要包括的字重和样式。默认情况下,选择了常规、斜体、粗体和粗斜体,总重量为 134K。我可以选择其他字重和样式,它会显示给我看套件大小如何变化。现在,我会保留默认的四种字重和样式。接下来,让我们点击顶部附*的“在 CSS 中使用字体”链接:

这给了我们想要使用的字体的名称,即expo-sans-pro

让我们复制expo-sans-pro,然后回到 CSS 文件。只是为了好玩,把它粘贴在我们的h1选择器中,在Maven Pro之前,然后保存:

h1 {
  font-weight: 700;
  font-size: 100px;
  color: #0072ae;
  margin-bottom: 10px; 
  font-family: 'expo-sans-pro', 'Maven Pro', Arial, sans-serif;
  font-style: normal;
  font-weight: bold;
}

不过,在这个工作之前,我们实际上需要点击“发布”按钮:

现在,它会告诉我们可能需要几分钟才能完全分布到他们的网络中,但通常情况下会比那快得多。如果我们现在去我们的网站并刷新,我们可以看到字体的变化:

这是Expo Sans Pro,一个非常漂亮的字体。我几乎比Maven Pro更喜欢它,这就是使用 Typekit 或付费字体服务的好处之一:它们有如此多令人难以置信的高质量字体。

因此,总的来说,我们使用了 Typekit 的一个漂亮的字体,我很想使用它来代替Maven Pro,但我认为我们会保留 Maven。从 Typekit 这样的服务应用字体涉及一些额外的步骤,但总的来说,它仍然比自己托管字体要容易。在下一节中,我们将看看另一种我们可以使用的字体,叫做图标字体。

图标字体

在这一节中,我们将看看如何将图标字体添加到我们的网站。当您的网站上有实心、彩色的图标时,图标字体可以很好地工作。与将每个图像作为单独的请求不同,所有图标都是整个字体的一部分的请求——这更快。由于我们不使用图像,我们可以使用 CSS 来提供图像的颜色和大小,这意味着我们可以使图标更大而不会失去保真度。我们将在页脚中展示我们的图标字体。因此,首先我们必须为两个页面构建页脚,然后我们将从 ZURB Foundation 下载一个免费的图标字体。接下来,我们将使用 CSS 将图标字体添加到我们的网站。最后,我们将为图标添加一个:hover状态,以尝试如何使用 CSS 来改变它们的外观。

构建页脚

所以这是我们在最终网站的页脚中所追求的目标:

我们希望有三列链接,每个链接都有一个图标。 传统上,您可以使用图像来实现这一点,但是如果您有很多图像,这可能会影响性能。 传统上,许多人将所有这些图标分组到一个称为“图像精灵”的图像文件中,并将其加载为背景图像,仅显示所需图像的背景图像部分,使用background-position属性。 这将确保您只有一个网络请求,而不是 10 个,因为您将使用一个图像文件。 这个过程很棘手,因为您必须使用background-position属性来找到您要查找的图像。 更大的问题是,当涉及更改颜色或添加新图标时,您必须更新精灵,然后更新 CSS。 图像精灵的最大问题是当您必须继续支持 HiDPI 或* Retina *设备时。 图标字体并不完美,但它们解决了这些棘手的问题。

在我们的两个 HTML 文件中,让我们复制页脚的这段代码:

<!-- 
================ 
Footer
================
--> 
<footer>
  <div class="wrapper grouping">
    <ul>
      <li class="list-heading">Social</li>
      <li><a href=""><span></span>Facebook</a></li>
      <li><a href=""><span></span>Twitter</a></li>
      <li><a href=""><span></span>Google+</a></li>
      <li><a href=""><span></span>Dribble</a></li>
    </ul>
    <ul>
      <li class="list-heading">Interwebs</li>
      <li><a href=""><span></span>Github</a></li>
      <li><a href=""><span></span>Stack Overflow</a></li>
      <li><a href=""><span></span>Zurb Foundation</a></li>
    </ul>
    <ul>
      <li class="list-heading">Resources</li>
      <li><a href=""><span></span>Smashing Mag</a></li>
      <li><a href=""><span></span>Treehouse</a></li>
      <li><a href=""><span></span>Designer News</a></li>
    </ul>
    <p class="legal-copy clear">Ol' Chompy - The Shark Site</p>
  </div><!-- end wrapper -->
</footer>

这是没有添加任何 CSS 的样子:

我们需要整理一下。 就在媒体查询开始之前,让我们放一些 CSS 使页脚吸附到位:

/***************
Footer
***************/
footer {
  background: #fff url('../images/seaweed.jpg') repeat-x 0 0;
  padding: 142px 0;
  font-size: 14px;
  line-height: 1.7; 
}
footer ul {
  float: left;
  margin: 0 100px 50px 0; 
}
footer .list-heading {
  text-transform: uppercase;
  color: #333;
  margin-bottom: 30px;
  font-size: 17px; 
  font-family: 'Maven Pro', Arial, Helvetica, sans-serif;
}
footer a {
  color: #333;
}
footer li, 
footer p {
  color: #4D4D4D; 
  line-height: 30px;
}
footer li {
  margin-bottom: 10px;
}
.legal-copy {
  text-align: right;
  font-size: 10px;
}

这样看起来好多了:

从 ZURB Foundation 下载免费图标字体

让我们转到 Zurb 页面,查看 Foundation Icon Fonts 3 zurb.com/playground/foundation-icon-fonts-3

这个图标集中有很多不同的图标字体。 让我们点击“下载字体”按钮。 在 Chrome 中,它将在左下角下载; 我们只需将文件夹放在桌面上,然后双击解压缩它。 然后,我们可以打开Foundation-icons文件夹:

在这个文件夹中有一个 CSS 文件,几个字体文件,一个名为preview.html的文件,然后是一个充满svgs的文件夹。 CSS 文件的样子如下:

@font-face {
  font-family: "foundation-icons";
  src: url("foundation-icons.eot");
  src: url("foundation-icons.eot?#iefix") format("embedded-opentype"),
       url("foundation-icons.woff") format("woff"),
       url("foundation-icons.ttf") format("truetype"),
       url("foundation-icons.svg#fontcustom") format("svg");
  font-weight: normal;
  font-style: normal;
}

将图标字体添加到我们的网站

在我们的 CSS 中,我们可以看到@font-face规则加载不同的字体文件,就像我们在本章的第二部分中查看的 Web 字体一样。 在此之下是每个图标字体的类名,后面是伪元素 before:

我们已经了解了伪类,但还没有了解伪元素。 伪元素:before:after基本上是“虚拟”的元素,它们将出现在您调用的元素之前或之后。 这是使用 CSS 添加内容的一种巧妙方式。 它添加的内容是与类名相关的图标。 因此,如果我们转到选择器的底部,我们可以看到它实际上设置了字体系列,所有不同的字体属性以及其他一些东西:

... {
  font-family: "foundation-icons";
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  -webkit-font-smoothing: antialiased;
  display: inline-block;
  text-decoration: inherit;
}

之后,在下一个选择器中,您可以看到每个图标都添加了内容到其伪元素:

这些内容是与字体系列中的字母对应的代码。 例如:

fi-address-book:before { content: "\f100"; }

这是与地址簿图标对应的内容在字体系列中。 我们在这些代码行中看到的fi-前缀代表foundation icon。 如果您不完全理解所有这些,不要担心; 主要问题是我们需要将此 CSS 复制到我们的 CSS 文件中。 它有 594 行代码,所以我不想将其包含在我们现有的样式表中,因为这将使它比我想要的更加臃肿。 所以我们有两个选择。 我们可以从 CSS 文件中删除并找出我们计划使用的图标,或者我们可以将 CSS 文件链接到单独的文件中。 让我们单独链接到它-这样我们在需要时可以使用整个图标字体库。 理想情况下,稍后,我们将在转到生产之前从未使用的图标字体中删除出来,因为将该文件缩减到只使用的 10 个图标将其从 20kb 减少到 1kb!

让我们将此文件保存在我们项目的css文件夹中,并将其命名为icons.css

现在我们将进入我们的index.html文件,在这个文件的头部添加一个链接到foundation-icons.css,就在style.css的链接下面:

<!-- stylesheets -->
 <link rel="stylesheet" href="css/style.css">
 <link rel="stylesheet" href="css/foundation-icons.css">

保存这个,复制它,并跳到 Shark Movies 粘贴它,然后保存。

接下来,让我们创建一个名为icons的新文件夹。我们将四个不同的字体文件拖到这个新文件夹中:

现在这四个不同的字体文件都在icons文件夹中,回到icons.css文件,我们只需要改变源指向刚刚放置这些字体文件的文件夹。让我们在 url 前面加上../icons/,像这样:

@font-face {
  font-family: "foundation-icons";
  src: url("../icons/foundation-icons.eot");
  src: url("../icons/foundation-icons.eot?#iefix") format("embedded-opentype"),
       url("../icons/foundation-icons.woff") format("woff"),
       url("../icons/foundation-icons.ttf") format("truetype"),
       url("../icons/foundation-icons.svg#fontcustom") format("svg");
  font-weight: normal;
  font-style: normal;
}

所以现在我们的 URL 指向了正确的文件夹。

现在我们需要在我们的 HTML 元素中添加图标类来加载图标。但首先我们需要确定使用哪些类。preview.html文件在这方面非常有帮助,所以让我们从foundation-icons文件夹中打开它:

当我们打开它时,我们可以看到以不同大小显示的图标。搜索 Facebook,这里我们可以看到我们正在寻找的 Facebook 图标以及与之对应的类名fi-social-facebook

复制除了那个类名的句号之外的所有内容,并将其粘贴到index.html中 Facebook 的链接旁边:

<footer>
  <div class="wrapper grouping">
    <ul>
      <li class="list-heading">Social</li>
      <li><a href=""><span class="fi-social-facebook"></span>Facebook</a></li>
      <li><a href=""><span></span>Twitter</a></li>
      <li><a href=""><span></span>Google+</a></li>
      <li><a href=""><span></span>Dribble</a></li>
    </ul>
    <ul>
      <li class="list-heading">Interwebs</li>
      <li><a href=""><span></span>Github</a></li>
      <li><a href=""><span></span>Stack Overflow</a></li>
      <li><a href=""><span></span>Zurb Foundation</a></li>
    </ul>
    <ul>
      <li class="list-heading">Resources</li>
      <li><a href=""><span></span>Smashing Mag</a></li>
      <li><a href=""><span></span>Treehouse</a></li>
      <li><a href=""><span></span>Designer News</a></li>
    </ul>
    <p class="legal-copy clear">Ol' Chompy - The Shark Site</p>

  </div><!-- end wrapper -->
</footer>

保存这个,现在当我们去我们的网站,我们将能够看到 Facebook 图标:

图标字体样式

我们有两个问题:一是太小,二是离单词太*。我们应该对每个图标添加margin-right并使其变大。这意味着 HTML 中的每个span标签都需要一个类。让我们添加class="icon"如下:

<footer>
  <div class="wrapper grouping">
    <ul>
      <li class="list-heading">Social</li>
      <li><a href=""><span class="icon fi-social-facebook"></span>Facebook</a></li>
      <li><a href=""><span class="icon"></span>Twitter</a></li>
      <li><a href=""><span class="icon"></span>Google+</a></li>
      <li><a href=""><span class="icon"></span>Dribble</a></li>
    </ul>
    <ul>
      <li class="list-heading">Interwebs</li>
      <li><a href=""><span class="icon"></span>Github</a></li>
      <li><a href=""><span class="icon"></span>Stack Overflow</a></li>
      <li><a href=""><span class="icon"></span>Zurb Foundation</a></li>
    </ul>
    <ul>
      <li class="list-heading">Resources</li>
      <li><a href=""><span class="icon"></span>Smashing Mag</a></li>
      <li><a href=""><span class="icon"></span>Treehouse</a></li>
      <li><a href=""><span class="icon"></span>Designer News</a></li>
    </ul>
    <p class="legal-copy clear">Ol' Chompy - The Shark Site</p>

  </div><!-- end wrapper -->
</footer>

现在在 CSS 中,在我们的页脚部分,让我们添加一个新的规则集来解决这两个问题:

footer .icon {
  margin-right: 10px;
  font-size: 30px;
}

我们还可以添加一个过渡效果,因为我们将有一个悬停效果,这将有助于缓解状态变化。让我们添加一个过渡效果:

footer .icon {
  margin-right: 10px;
  font-size: 30px;
 -webkit-transition: .25s color ease-in-out;
 transition: .25s color ease-in-out;
}

现在刷新网站,你会看到 Facebook 图标稍微变大了,而且有了更多的空间:

现在我们需要为 Twitter、Google、Dribble 和 HTML 中的其他六个链接添加相应的类:

<footer>
  <div class="wrapper grouping">
    <ul>
      <li class="list-heading">Social</li>
      <li><a href=""><span class="icon fi-social-facebook">
      </span>Facebook</a></li>
      <li><a href=""><span class="icon fi-social-twitter">
      </span>Twitter</a></li>
      <li><a href=""><span class="icon fi-social-google-plus">
      </span>Google+</a></li>
      <li><a href=""><span class="icon fi-social-dribbble">
      </span>Dribbble</a></li>
    </ul>
    <ul>
      <li class="list-heading">Interwebs</li>
      <li><a href=""><span class="icon fi-social-github">
      </span>Github</a></li>
      <li><a href=""><span class="icon fi-social-stack-overflow">
      </span>Stack Overflow</a></li>
      <li><a href=""><span class="icon fi-social-zurb"></span>Zurb 
      Foundation</a></li>
    </ul>
    <ul>
      <li class="list-heading">Resources</li>
      <li><a href=""><span class="icon fi-social-smashing-mag">
      </span>Smashing Mag</a></li>
      <li><a href=""><span class="icon fi-social-treehouse">
      </span>Treehouse</a></li>
      <li><a href=""><span class="icon fi-social-designer-news">
      </span>Designer News</a></li>
    </ul>
    <p class="legal-copy clear">Ol' Chompy - The Shark Site</p>

  </div><!-- end wrapper -->
</footer>

这是它的样子:

好了!现在我们已经将所有与图标相关的类放在了正确的位置,并且我们的页脚上每个链接都有一个图标。图标字体的好处是它们在 HiDPI 设备上会清晰明了。此外,我们可以在悬停状态下更改颜色和其他属性,这是普通光栅图像所做不到的。让我们为所有这些添加一个快速的悬停状态。在我们的 CSS 中,让我们添加一个新的选择器:

footer .icon {
  margin-right: 10px;
  font-size: 30px;
  -webkit-transition: .25s color ease-in-out;
  transition: .25s color ease-in-out;
}
footer a:hover .icon {
 color: #f00;
}

应用这个,你应该看到这个图标很好地过渡到了完全不同的颜色:

图标字体是您网站的一个很好的选择。我建议在您的网站的非关键元素上使用图标字体,因为如果由于某种原因字体在用户的计算机上无法加载,就没有备用方案。备用方案通常默认为一个方块,或者更糟糕的是一个完全无关的字符或字母。在我们的情况下,我认为我们没问题,因为在我们的图标字体无法加载的情况下,我们仍然会有图标旁边的描述。图标字体的好处是,就像任何其他字体一样,它们可以流畅地缩放到视网膜设备。

摘要

我们通过讨论@font-face属性来开始本章关于 Web 字体的内容,使用它来向我们的网站添加字体。我们看了如何使用 Google 字体和 Typekit。最后,您学会了如何使用图标字体,并使用 Zurb 的图标字体构建网站的页脚。在下一章中,我们将讨论视网膜设备,并为 HiDPI 设备的世界准备我们的页面。

第八章:HiDPI 设备的工作流程

视网膜设备现在几乎是苹果电脑、*板电脑和手机的默认设备。此外,“视网膜”一词实际上是苹果公司为计算机设备注册的商标,是他们品牌化描述双倍(或更多)密度屏幕和设备的方式。我将松散地使用“视网膜”一词来描述任何具有高密度显示屏的设备,无论是由苹果制造还是其他制造商。视网膜设备上的所有内容都更清晰,因为与 CSS 设备显示器上的像素相比,现在有*四倍的像素;对于每个“CSS 像素”,现在有四个“设备像素”,从而实现更高质量的显示。不利的一面是,到目前为止我们使用的图像实际上在这样的设备上看起来不会那么好,因为我们没有考虑到更高密度的显示。

在本章中,我们将介绍一些针对视网膜显示器的图像技术。这包括使图像放大两倍。我们还将研究背景图像技术,使用 SVG,并在图像元素上使用srcset属性来进一步考虑视网膜。

2x 图像

2x 图像是宽度和高度的两倍。基本思路是使图像的宽度和高度是我们实际需要的两倍。然后我们将该图像添加到我们的 HTML 中。然后我们将使用 CSS 将图像限制为屏幕上的实际大小。我喜欢处理响应式设计中的灵活图像的方式与此相同:我喜欢确保图像将具有具有设置的包含元素widthheight值。然后,我确保图像本身的max-width设置为 100%。这些要求已经具备。我的所有图像通常都有一个容器,在 CSS 中,我的所有图像都将其max-width设置为 100%。

创建视网膜大小的图像(2x)

所以让我们从鲨鱼电影页面上的光影开始。右键单击“大白鲨”电影图像并检查此元素:

我们可以看到鲨鱼电影页面上的这些图像是 200 x 200 像素。我们的目标是用尺寸为 400 x 400 像素的图像替换它们。如您在下面的屏幕截图中看到的,我已经创建了三个与原始图像相同的图像,只是它们更大,并且带有@2x.jpg后缀,表示这些是视网膜版本:

切换到 HTML,您会看到我已经为所有三个图像的文件名添加了@2x并保存了。例如,这是我们“Open Water”电影的文件名应该是这样的:

<img src="img/open-water@2x.jpg" alt="Open Water movie">

使用 CSS 调整 2x 图像的大小

转到浏览器并刷新。现在,当您查看这张大白鲨的图像时,您实际上不会看到任何明显的区别:

然而,快速检查显示正在提供@2x图像,但它被限制为 200 x 200 的大小,因此您可以看到原始图像是400 x 400,但显示为200 x 200

由于我们对响应式设计的基础,即时包含元素.figure已经设置了23.958333333333百分比的宽度(如下面的代码所示),这相当于网站最宽处的 200 像素:

如果我们从 Chrome DevTools 的样式窗格中删除width,图像将会放大到其实际大小,即400 x 400

因此,是包含元素具有设置的width,以及max-width设置为 100%,使图像受限。如果我们从 Chrome DevTools 的样式窗格中删除这个max-width,图像将不再受限,如下所示:

父元素的溢出选项设置为隐藏,这就是为什么图像的宽度不会超过 23.95%。

在视网膜设备上检查图像质量

现在我们怎么知道图像在视网膜设备上会看起来很好呢?最好的办法是在视网膜设备上测试它,但我们也可以在 Chrome 中作弊一点,将其缩放到 200%。首先,让我们在 DevTools 中直接将宽度设置为 200px:

然后让我们转到 Chrome 工具栏,并将缩放比例调整到 200%:

前面的截图旨在演示在 200%缩放时,图像仍然非常清晰,这有点类似于模拟视网膜设备。

这似乎不是一个准备好视网膜的图像的坏方法。嗯,如果只是这么简单就好了。事实证明,使图像的高度和宽度增加一倍实际上使它们比 1x 版本大三到四倍。因此,如果你看一下images文件夹中的 Jaws 图像,原始大小为 28 KB,而 2x 版本(双倍密度版本)为 105 KB。这是原始大小的四倍!

因此,总之,这只是我们为视网膜网站做准备的开始。我们目前最大的问题是,我们为所有设备提供了一个巨大的视网膜大小的图像,即使那些不是视网膜的设备也是如此。这对于那些不会从中受益的站点来说,是额外的下载和页面负担,这是不负责任的。

在下一节中,我们将介绍类似的背景图像技术。

背景图像

为了处理背景图像,我们可以使用特殊的媒体查询来确定像素比,然后修改background属性以提供视网膜图像。在这一节中,我们将确定如何在视网膜领域处理背景图像。我们首先创建一个专门用于确定像素比的媒体查询。然后,我们将更新正在提供的图像为视网膜版本。页脚中的海藻是一个背景图像,因此将是这项任务的完美图像。

针对页脚中的海藻

这是在电影页面页脚上方的海藻:

如果我们查看 CSS,发生的一切就是页脚有一个重复的背景图像。背景是海藻,我们让它沿着x轴重复:

footer {
  background: #fff url('../images/seaweed.jpg') repeat-x 0 0;
  padding: 142px 0;
  font-size: 14px;
  line-height: 1.7; 
}

因此,我们需要有一个视网膜大小的seaweed.jpg版本。我在我的images文件夹中有这个,我把它命名为seaweed@2x.jpg

在样式表的最底部,在所有我们的媒体查询之后,让我们为视网膜背景图像保留一个位置:

/***************
Retina, Background Images
***************/

这是我们将使用特殊媒体查询来检测视网膜的地方。

设备像素比的媒体查询

我们从第六章中记得这样的媒体查询,变得响应式

@media all and (max-width: 400px) {
  /*rule sets here*/
}

媒体查询有两个部分,媒体类型和媒体特性

@media *media type* and (*media feature*) {
  /*rule sets here*/
}

媒体类型可以是关键字值,如screenprintspeechall。媒体特性也可以是许多东西。在响应式章节中,特性是浏览器的max-width。然而,对于视网膜,我们要查询屏幕的像素比:

@media
screen and (-webkit-min-device-pixel-ratio: 2),
screen and (min-resolution: 192dpi) {
}

在前面的示例中发生了很多事情。有两个不同的查询针对两个不同的媒体特性。分隔这两个查询的逗号类似于说“或”。因此,如果前面的两个查询中的任何一个为真,则媒体查询将生效。但为什么要有两个查询?嗯,第一个查询是针对像 Safari 和旧版 Chrome 这样的 webkit 浏览器,设备的min-device-pixel-ratio2。接下来,我们针对具有 192 像素每英寸或更高像素密度的设备。它不是使用设备像素比,而是使用min-resolution: 192dpi,这考虑到了不同的浏览器,比如 Windows 手机。这两个媒体特性基本上都是针对视网膜。

现在,在媒体查询中,我们将针对页脚并将背景图像更改为我们的视网膜版本。我会在页脚中输入一个开放的大括号,然后是background-image;URL 将是../images/seaweed@2x.jpg

@media
screen and (-webkit-min-device-pixel-ratio: 2),
screen and (min-resolution: 192dpi) {
 footer {
 background-image: url('../images/seaweed@2x.jpg');
 }
}

我们在浏览器中看不到明显的区别。不过,让我们检查一下页脚,以确保它仍然加载常规的seaweed.jpg文件,而不是seaweed@2x.jpg

我们检查这个的原因是因为我不是在视网膜设备上。我们可以使用一些技巧来确保这个工作。让我们去我们的 CSS 并将设备像素比更改为1

@media
screen and (-webkit-min-device-pixel-ratio: 1),
screen and (min-resolution: 192dpi) {
  footer {
    background-image: url('../images/seaweed@2x.jpg');
  }
}

让我们看看在浏览器中是什么样子的:

现在我们得到了 2x 版本,我们可以看到它明显更大。我们有两倍大小的图像;视觉上看起来是两倍大小。它没有被限制在我们预期的显示尺寸上。有一个名为background-size的属性,我们将使用它来解决这个问题。

仅向视网膜设备提供 2x 图像

我们必须使用background-size属性来确保海藻被适当地限制。我们将在页脚部分顶部持有非视网膜版本的规则集中放置background-size属性,而不是在媒体查询中。我们可以很容易地将其放在媒体查询中,这样也可以,但这将适用于非视网膜设备和视网膜设备,因此我们将添加水*200px和垂直100px的背景大小,如下面的代码所示:

footer {
  background: #fff url('../images/seaweed.jpg') repeat-x 0 0;
  background-size: 200px 100px;
  padding: 142px 0;
  font-size: 14px;
  line-height: 1.7; 
}

保存这个并转到浏览器。当我们刷新网站时,海藻应该缩小到 200 x 100,恢复到其常规大小:

如果您查看我们在 DevTools 中的样式,您会看到我们得到了@2x版本。您可以看到浏览器加载 CSS 的方式-它在顶部看到了媒体查询。这是正在使用的。下面是未加载的非媒体查询版本。这正是我们希望它工作的方式,这很好。

我们需要做的最后一件事是将媒体查询恢复为device-pixel-ratio为 2 而不是 1,所以,我们将更改它:

@media
screen and (-webkit-min-device-pixel-ratio: 2),
screen and (min-resolution: 192dpi) {
  footer {
    background-image: url('../images/seaweed@2x.jpg');
  }
}

现在它将加载非视网膜版本,因为我使用的是非视网膜设备:

只有视网膜尺寸的背景图像才会被视网膜设备下载,非视网膜设备会下载常规尺寸的背景图像。一切都很好,但这仍然是相当多的工作。我们可以以更少的工作量处理这些海藻,只需要一张图片-使用 SVG 而不是传统的光栅图形。

可伸缩矢量图形(SVG)

可伸缩矢量图形SVG)-是一种基于 XML 的图形图像格式。它与 JPEG 和 PNG 等光栅格式不同,因为它可以在任何尺寸下缩放而不失去任何分辨率或出现像素化。这意味着我们不需要为视网膜或响应式布局使用多个图像!SVG 的另一个好处是文件大小可以比保存为 JPEG 或 PNG 的相同图像要小得多。所有主要浏览器都支持 SVG 格式,甚至可以追溯到 IE9。SVG 并不是站点上每个图像的替代品-它们特别适用于线条图,通常是通过设计软件(如 Adobe Illustrator)生成的。

在本节中,我们将看看如何将 Adobe Illustrator 文件保存为 SVG,以及我们可以将 SVG 添加到网站的三种不同方式:

  • 将 SVG 添加为background-image

  • 使用<img>标签添加 SVG

  • 使用内联 SVG

我们的网站上有很多图像非常适合使用 SVG,包括网站顶部的鲨鱼:

我们网站中间的所有不同海洋物种也非常适合作为 SVG:

即使我们在上一节中处理的页脚中的海藻也非常适合 SVG:

那么哪些图像不适合作为 SVG?嗯,我们电影页面上的光栅图像绝对不适合:

将 Illustrator 文件保存为 SVG

我在 Illustrator 中打开了一个名为seaweed.ai的 Adobe Illustrator 文件。像 Illustrator 这样的程序是可以创建 SVG 或从头开始绘制 SVG 的地方。在本书中,使用 Illustrator 创建 SVG 远远超出了范围,但我想从这里开始只是为了展示 SVG 可能来自何处。

Illustrator CC 2017中,将 AI 文件保存为 SVG 以供 Web 使用的最佳方法之一是使用文件* > 导出 > *屏幕导出...选项。

这个选项使用artboard名称作为文件名,所以在我们导出为 SVG 之前,让我们通过转到窗口* > * 画板来重命名画板。让我们将 artboard1 重命名为 seaweed,如下面的截图所示:

现在,通过转到文件* > 导出 > *屏幕导出...选项,我们将获得一个 SVG 文件:

这将弹出一个带有几个选项的屏幕。使用“导出到”字段,我们将选择保存此文件的位置,这将不可避免地在我们的images文件夹中。在单击右下角的“导出画板”按钮之前,我们还将更改格式为SVG

保存后,您会看到 SVG 为 1 KB。我们在上一节中使用的@2x版本为 13 KB!

因此,SVG 不仅比@2x版本小 13 倍,而且比常规版本小 6 倍,这真是太神奇了!现在让我们将其整合到我们的 CSS 中。

将 SVG 文件添加为背景图像

在我们的 CSS 中,在针对页脚的规则集内部,我要做的就是将格式从.jpg更改为.svg - 也就是从('.../images/seaweed.jpg')更改为('.../images/seaweed.svg'),如下面的代码所示:

footer {
  background: #fff url('../images/seaweed.svg') repeat-x 0 0;
  background-size: 200px 100px;
  padding: 142px 0;
  font-size: 14px;
  line-height: 1.7; 
}

因为现在我们有一个适用于非视网膜和视网膜设备的 SVG,所以我们将转到底部,并注释掉我们上一节中的这个媒体查询:


/***************
Retina, Background Images
***************/
/*********** @media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-resolution: 192dpi) {
   footer {
    background-image: url('../images/seaweed@2x.jpg');
  } 
}
*************/

这是我们在上一节中用来为视网膜设备提供更大图像的方法,但是如果我们使用 SVG,我们就不需要所有这些额外的代码。所以我把它们都去掉了。

我将刷新浏览器,它看起来完全一样。让我们检查元素,如下面的截图所示。我们可以看到它正在提供seaweed.svg。我们从 2 张图片变成了 1 张。13 KB 变成了 1 KB。我们还去掉了一些复杂的媒体查询中的几行 CSS。您开始明白为什么 SVG 是纯粹的令人敬畏了吗?

将 SVG 添加为常规的

您还可以将 SVG 用作常规<img>。我们碰巧在网站中间有几张图片 - 不同的海洋物种,这些将是使用 SVG 实现的完美候选者:

我已经将章鱼、螃蟹和鲸鱼保存为.svg文件。所以让我们转到 HTML,简单地将章鱼、螃蟹和鲸鱼的图像从.png更改为.svg

<!-- 
===============
Secondary Sections
===============
-->
<section class="secondary-section grouping">
  <div class="wrapper">
    <div class="column">
      <figure>
        <img src="img/octopus-icon.svg" alt="Octopus">
      </figure>
      <h2>The Octopus</h2>
      <p>Lorem ipsum dolor... </p>
      <a href="#" class="button">Tenticals &raquo;</a>
    </div>
    <div class="column">
      <figure>
        <img src="img/crab-icon.svg" alt="Crab">
      </figure>
      <h2>The Crab</h2>
      <p>Lorem ipsum dolor... </p>
      <a href="#" class="button">Crabby &raquo;</a>
    </div>
    <div class="column">
      <figure><img src="img/whale-icon.svg" alt="Whale"></figure>
      <h2>The Whale</h2>
      <p>Lorem ipsum dolor... </p>
      <a href="#" class="button">Stuart &raquo;</a>
    </div>
  </div><!-- end wrapper -->
</section>

images文件夹中的文件名完全相同。唯一的区别是后缀是svg而不是png

保存这个。我们将得到以下输出:

在上图中,我们可以看到文件看起来不错;唯一的问题是它们似乎变大了一点。所以我们将它们缩小到我们想要的尺寸。

你无法阻止 SVG,你只能希望限制它们!

要限制图像的大小,我们需要设置width和/或max-width。我们实际上已经这样做了,但只是在媒体查询中,因此它不会在较大的屏幕上触发:

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
  .column {
    float: none;
    width: auto;
    padding: 0 50px;
  }
 .column figure {
 margin: 0 auto;
 width: 100%; 
 max-width: 250px;
 }
  .column h2 {
    text-align: center;
  }
}/* end of media query */

让我们从媒体查询中删除该规则集,并将其添加到我们最初定义响应式媒体查询之外的 3 列位置:

****************
3 columns
****************/
.column {
  float: left;
  width: 31.25%; /* 300/960 */
  margin-left: 3.125%; /* 30/960 */
}
.column:first-child {
  margin-left: 0;
}
.column figure {
 margin: 0 auto;
 width: 100%;
 max-width: 250px; 
}

我们所做的就是使用自动边距来居中figure元素,确保其宽度是其容器的 100%,只要宽度不超过 250px(max-width)。

既然我们已经将它放在了正确的位置,这就是我们得到的结果:

我们将每个 SVG 图像限制为最大宽度为250px。我们的螃蟹、章鱼和鲸鱼看起来非常好,立即就准备好了。

使用内联 SVG

我们对 SVG 还有另一个选项,称为内联 SVG。由于 SVG 实际上只是文本文件中的 XML,我们实际上可以直接将 SVG 代码嵌入到我们的 HTML 中。这样就不需要额外的 HTTP 请求(对性能有好处)。此外,它允许我们使用 CSS 来改变 SVG,例如提供一个酷炫的悬停状态或动画。这确实给了我们一个巨大的优势;它的重要性无法被过分强调。

所以我们要做的是转到 Sublime Text 中的images文件夹,然后打开crab.svg。但首先,让我们看看当我打开crab.png时会发生什么,Sublime 会显示一个图像:

使用 SVG,它实际上显示了代码!你可以看到它是 XML,与 HTML 类似:

我将复制并粘贴所有的 SVG 代码,并转到我们的index.html文件,然后删除整个img标签:

<div class="column">
  <figure>
 <img src="img/crab-icon.svg" alt="Crab">
  </figure>
  <h2>The Crab</h2>
  <p>Lorem ipsum dolor... </p>
  <a href="#" class="button">Crabby &raquo;</a>
</div>

然后我们将其替换为 SVG 代码:

<div class="column">
  <figure>
    <svg  viewBox="1.5 113.9 256 256">
 <path fill="#9E2610" d="M72.1 296.8s-31.8 11.7-37.9 20.5c0 0 3.5-21.3 
      30.9-32.7l7 12.2zm12.1 10.7s-21.9  
      22.8-23.4 32.7c0 0-5.8-19.3 12.5-40.1l10.9 7.4zm-15.9-28.7s-34 
      2.4-43.3 9.1c0 0 12.3-19.5 42.3-22.8l1 
      13.7zM185.4 295s31.8 11.7 37.9 20.5c0 0-3.5-21.3-30.9-32.7l-7 
      12.2z"/>
 <path fill="#D62D0E" d="M50.5 223.5S13 205.5 41 161c0 0 9-19.5 
      38-16.5L53.5 205l46-32.8s12.5 24.5-11    
      42.2c0 0-13.8 10.2-20.8 9 0 0 4.5 11 12 16.2l3.5 3.2-9.5 
      11c.1.2-20.7-15.3-23.2-30.3z"/>
 <path fill="#9E2610" d="M173.3 305.6s21.9 22.8 23.4 32.7c0 0 
      5.8-19.3-12.5-40.1l-10.9 7.4zm15.9-28.7s34 
      2.4 43.3 9.1c0 0-12.3-19.5-42.3-22.8l-1 13.7z"/>
 <path fill="#D62D0E" d="M207.9 223.5s37.5-18 9.5-62.5c0 
      0-9-19.5-38-16.5l25.5 60.5-46-32.8s-12.5 24.5 
      11 42.2c0 0 13.8 10.2 20.8 9 0 0-4.5 11-12 16.2l-3.5 3.2 9.5 11c0 .2 
      20.7-15.3 23.2-30.3z"/>
 <path fill="#D62D0E" d="M127.8 212s44-5.2 65.2 57.8c0 0 11.8 
      44.5-62.2 48.5 0 0-70.2 1.2-66.2-43.8-.1 0 
      6.6-54 63.2-62.5z"/>
 <circle fill="#FFFFFF" cx="103.8" cy="265.1" r="23.5"/>
 <circle fill="#FFFFFF" cx="153.6" cy="264.1" r="23.5"/>
 <circle cx="105.2" cy="263.8" r="14.8"/>
 <circle cx="152.2" cy="262.5" r="14.8"/>
 <ellipse transform="rotate(-45.37 157.15 256.57)" fill="#FFFFFF" 
      cx="157.1" cy="256.6" rx="4.7" 
      ry="7.2"/>
 <ellipse transform="rotate(-45.37 110.35 257.456)" fill="#FFFFFF" 
      cx="110.3" cy="257.4" rx="4.7" 
      ry="7.2"/>
 <path d="M78.5 290s12.7 20 51.6 19.5c0 0 34.2 1.5 49.2-19.5 0 0-15.8 
      17.5-49.2 17.2 0 0-36.1.3-51.6-
      17.2z"/>
 </svg>
  </figure>
  <h2>The Crab</h2>
  <p>Lorem ipsum dolor... </p>
  <a href="#" class="button">Crabby &raquo;</a>
</div>

哇,这是很多的代码... SVG 的缺点是你直接将大量代码放入你的标记中。你仍然会获得更好的性能,因为你不需要为它发出 HTTP 请求,但我们为此添加了接* 30 行的代码。

在 Chrome 中我们看不到任何变化;螃蟹看起来完全一样。所以我们不妨检查一下这个元素。现在我们可以看到它是内联 SVG 代码:

你还可以看到你可能会认为你可以使用 CSS 来改变这些属性,因为每个路径实际上都是 Dom 中的一个单独的节点:

例如,如果我们想的话,我们可以改变这行代码的填充颜色:

让我们把它变成绿色:

现在你得到了一个绿色的爪子:

所以你可以看到你可能如何改变 SVG 的属性,对其进行动画处理,或者创建一个酷炫的悬停状态。你不能用 SVG 作为background-imageimg标签来做到这一点,但你可以用内联 SVG 来做到这一点。

由于这是一种不同的媒体格式,它不是img标签,也不是video标签。它实际上是一个SVG标签。让我们转到样式表的顶部,进入我的重置。这是我们在媒体上设置max-width: 100%的地方,如下面的代码所示。我们还将向此列表添加一个 SVG:

img, iframe, video, object, svg {
  max-width: 100%;
}

在下一节中,我们将讨论如何在img标签上使用srcset属性,以向高密度显示器提供视网膜图像,并向普通密度显示器提供正常大小的图像。

源设置属性(srcset)

SVG 仍然是向 HiDPI 设备提供视网膜图像的最受欢迎的方式,因为文件大小几乎总是比 JPG 和 PNG 小,而且对于视网膜和非视网膜设备只需要一个图像。但还有另一个非常好的选择出现了,叫做srcset。这个选项并不是要取代 SVG,而是要补充它,因为 SVG 不能用于传统的光栅图像和照片,这些更适合于 JPEG 和 PNG。

什么是 srcset?

srcset属性只是一组图像,就像名称所暗示的那样。我们可以提供不止一个图像供浏览器选择,而是一组图像,浏览器可以从中选择,并且只获取浏览器认为最适合设备的图像。

我们将专注于我们电影页面上的三个电影图像,它们都是光栅的、摄影的图像。

movies.html中,我们有一个img标签和每部电影的适当图片。所以对于 Sharknado,我们有sharknado.jpg

<img src="img/sharknado.jpg" alt="Sharknado movie">

对于 Jaws,我们有jaws.jpg

<img src="img/jaws.jpg" alt="Jaws movie">

让我们更新 Jaws 图像,并添加一个名为srcset的新属性,然后将我们的 Jaws 图像作为该属性的值:

<img src="img/jaws.jpg" srcset="images/jaws.jpg" alt="Jaws movie">

正如我提到的,srcset是一组图像选择,供浏览器决定哪个最适合情况。让我们添加一组图像。

向 srcset 添加一组图像

要向image标签添加一组图像,用逗号分隔每个图像。我们首先提供常规大小的图像。然后我们将添加images/jaws@2x.jpg

<img src="img/jaws.jpg" srcset="images/jaws.jpg, images/jaws@2x.jpg" alt="Jaws movie">

实际上,浏览器需要其他东西来让它知道这是一个更大的图像,称为像素密度描述符,或者只是X 描述符。让我们添加它,如下面的屏幕截图所示:

<img src="img/jaws.jpg" srcset="images/jaws.jpg 1x, images/jaws@2x.jpg 2x" alt="Jaws movie">

在每个图像字符串后面,我将提供一个空格,然后是 X 描述符。因此,第一个图像字符串将是1x,第二个将是2x。X 描述符是我们提供给浏览器的提示。这意味着我们在1x或正常像素密度显示器上使用images/jaws.jpg,在2x或 retina 显示器上使用更大的图像images/jaws@2x.jpg

测试图像集

让我们看看图像集是否起作用。如果没有在 retina 显示屏上测试,这将会很困难。但让我们看看是否可以进行一些粗略的测试。Chrome 有一个很好的功能,如果我们检查一个图像,我们可以看到它的src属性和srcset属性。看看下面的代码:

在这里,如果我们将鼠标悬停在每个图像的路径上,你会看到一个弹出窗口出现在正在提供的图像上。但是当我们悬停在jaws@2x上时,没有弹出窗口出现,因为该图像没有被提供:

这是有道理的,因为我不是在 retina 显示屏上,所以它正在使用非 retina 图像。

让我们使用浏览器缩放技巧,这是我们在前面的部分中使用的,看看是否可以伪造一个 retina 设备。让我们放大到 200%:

然后,刷新页面,以便它获取它认为最好的图像:

当我在srcsrcset中悬停在jaws.jpg上时,我们没有弹出窗口。但是当我们悬停在jaws@2x.jpg的路径上时,我们就会看到,如下所示:

这告诉我,更大的图像正在被获取。这是好东西。

简化 srcset 属性

让我们再看一下代码,以简化srcset属性:

<img src="img/jaws.jpg" srcset="images/jaws.jpg 1x, images/jaws@2x.jpg 2x" alt="Jaws movie">

我们需要保留原始的src作为不支持srcset的浏览器的备用。我们稍后会在本节中讨论浏览器支持有多好,但重要的是要记住,src属性是为了让不支持的浏览器不被忽视。

另一件需要注意的事情是,我们可以简化这个代码方程式。W3C 规范提到,对于新的用户代理,src属性参与资源选择,就好像它是在srcset中用1x描述符指定的一样。由于我们有传统的src属性,可以提供图像的1x版本,我们可以从srcset属性中删除第一个图像字符串,并简化我们的标记:

<img src="img/jaws.jpg" srcset="images/jaws@2x.jpg 2x" alt="Jaws movie">

换句话说,我们可以从srcset属性中删除带有1x描述符的常规大小图像字符串,因为这已经在src属性中指定了。这样简化了它,这是好事。

现在让我们用类似的标记更新我们的另外两部电影,从《Sharknado》开始:

<img src="img/sharknado.jpg" srcset="images/sharknado@2x.jpg 2x" alt="Sharknado movie">

我们将对电影《Open Water》做同样的事情:

<img src="img/open-water.jpg" srcset="images/open-water@2x.jpg 2x" alt="Open Water movie">

浏览器支持

让我们讨论浏览器支持caniuse.com显示了大量绿色浏览器:

Microsoft Edge 支持srcset,Chrome,Firefox,Safari,Opera,以及 iOS Safari 8 及更高版本也支持。

将选项更改为日期相对,显示支持在 iOS 上更早:

它从 Safari 8.1 到 8.4 提供了部分支持。它支持"分辨率切换",这就是我们使用 X 描述符所做的;然而,它不支持完整的语法,我稍后会在本节中详细讨论。一个值得注意的不支持的浏览器是 IE,甚至是 IE11。但是,Internet Explorer 将获取我们在传统源属性中指定的常规大小图像。

好处在于绝大多数高密度设备最终都会得到2x版本,而不支持的浏览器,很可能是非视网膜设备,将收到1x版本。

我要指出的是,你不一定只使用一个或两个图像路径。我将复制并粘贴三个图像路径,如下所示:

<img src="img/sharknado.jpg" 
srcset="images/sharknado@1.5x.jpg 1.5x, 
        images/sharknado@2x.jpg 2x, 
        images/sharknado@4x.jpg 4x" 
alt="Sharknado movie">

正如你在前面的代码中看到的,我已经指定了一个可以用于4x显示、2x显示和1.5x显示的image。这让我想到另一点——你不一定要使用整数。你可以使用 1.5 或 1.325。

此外,我们只是向浏览器提供提示,所以即使我们看起来对哪个图像被服务有很大的控制,最终决定权还是在浏览器手中。这是基于除用户屏幕像素密度之外的因素,例如,缩放级别,正如我们已经看到的,以及其他因素,比如用户的网络条件。

因此,理论上,如果用户有一个视网膜设备,但网络条件不佳,浏览器会提供一个较小的图像,因为它会优先考虑图像的快速加载——加载会更快,但不会那么清晰。我们很可能在其他技术中看到了这种优先级的情况。例如,Netflix 可能会显示电影的模糊版本,直到它获得足够的带宽来向您展示同一部电影的高清版本。我们喜欢这样,因为我们宁愿快速得到一些东西来观看,而不是等待最好的版本来观看。

使用srcset属性的W描述符和 sizes 属性

请注意,srcset属性不是一个单一的技巧;我们已经讨论了它如何轻松处理视网膜图像。但srcset属性还有另一个用例,它使用W描述符和sizes属性:

<img src="img/medium.png"
    srcset="images/big.png 1600w,
 images/small.png 600w"
 sizes="(min-width: 1000px) 1600px, 
 600px" />

它允许你根据浏览器的宽度来处理不同图像的服务。在桌面上,一个巨大的、英雄式的、全屏的图像看起来很漂亮,但如果你把它缩小并在更小的移动设备上提供服务,那么它的性能就会很差,因为小设备不需要超大的图像。

w描述符是对浏览器关于图像大小的提示;这里的w代表宽度。sizes属性添加了媒体查询和一个维度,告诉浏览器我们首选的图像渲染大小,如果浏览器宽度与媒体查询匹配,最后是如果浏览器宽度不匹配媒体查询的首选渲染大小。

我的意图不是解释srcset属性的这种替代用法的细节,而是让你知道srcset属性有更深层次。如果你想深入了解,我在我的网站上写了一篇文章,网址是richfinelli.com/srcset-part-2/。我还写了一篇关于 X 描述符的文章,网址是richfinelli.com/srcset-part-1/,如果你还想更深入地了解我们刚才谈到的内容。

总结

为视网膜设备开发需要额外的工作。我的建议是尽可能使用 SVG 作为为视网膜设备提供超清晰图像的首选。在 SVG 不可行的情况下——即照片——使用img标签的srcset属性,让浏览器能够智能地决定提供图像。srcset的浏览器支持很好,不支持的浏览器将退回到src属性。浏览器根据像素密度、缩放级别和其他因素,比如网络条件,最终决定使用哪个图像。

在下一章第九章,FlexboxPart 1,我们将看到一个用弹性盒子布局网页部分的替代和更好的解决方案。

第九章:Flexbox,第一部分

Flexbox 是用于页面部分布局的模块,目前在 Internet Explorer 10 及以上版本中有很好的浏览器支持。从技术上讲,它并不是为全页面布局设计的;它更多用于页面的部分布局或给定组件的布局。

例如,以下三列(章鱼,螃蟹和鲸鱼)是使用浮动布局的,但我们将使用 flexbox 来完成完全相同的事情:

Flexbox 是一个大主题,所以我们将在两章中涵盖它。本章将介绍基础知识,我们将解决实现 flexbox,从浮动切换到 flexbox,并介绍所有 flexbox 属性和简写。在下一章中,我们将构建一个新的部分-以下产品列表-以演示如何使用 flexbox 构建不同的东西。

我将在最新版本的 Chrome 中编码,目前支持所有 flexbox 属性的非前缀版本。这将简化学习体验。但在完成之前,我们需要为最大的浏览器兼容性添加供应商前缀。

我们将涵盖以下主题:

  • CSS 的弹性盒布局模块概述

  • 从浮动切换到 flexbox

  • Flexbox 属性和简写

弹性盒布局模块概述

什么是 flexbox?通常称为 flexbox,它的真实名称是flexible box layout module。它提供了一种更有效的方式来布局,对齐和分配父元素的子元素之间的空间,即使它们的大小和数量是未知的或动态的。Flexbox 定义了一种全新的布局模式。

传统上,有块模式,用于文档布局。有内联模式,用于文本;表模式,用于表格数据(表格);和定位模式,用于明确位置而不太关心周围的其他元素。现在有flexbox 模式。那么 flexbox 做什么?它可以做很多真正有用的事情。在核心,flexbox 用于布局和对齐。以下列表说明了它更常见的用例:

  • 元素的垂直或水*布局。

  • 元素的左对齐或右对齐,就像您可以使用浮动一样,但没有浮动带来的额外麻烦。您还可以水*或垂直居中元素。

  • 此外,您可以控制显示方向。例如,默认情况下,您可以按源顺序显示元素,也可以按相反方向显示。

  • 此外,您可以明确控制元素并更改它们的显示顺序。

  • 它轻松实现的另一件事是给你相等高度的列,这以前只能通过使用黑客来实现

  • 它真正的乐趣在于它如何在父元素中分配元素以适应可用空间。

  • 面向响应式设计

弹性术语

所以这都是令人兴奋的事情,我相信你想开始看到它的实际效果,但在我们跳入之前,我们需要做一些功课并学习弹性术语。

Flex 容器和 flex 项目

首先,有一个称为flex 容器的东西,它本质上是包含所有flex 项目的元素。换句话说,它是一组元素的父元素;flex 项目是其父元素或 flex 容器的子元素。

主要尺寸和交叉尺寸

这里有一个称为主尺寸交叉尺寸的东西,如下所示:

默认情况下,主尺寸是宽度,交叉尺寸是高度,但如果修改flex-direction,这可能会改变,这是我们将在下一节学习的内容。

主轴和交叉轴

此外,还有一个称为主轴的东西,默认情况下水*运行,以及交叉轴,默认情况下垂直运行,如下图所示:

Justify-content 和 align-items

在本章节中,您将学习到一个叫做justify-content的属性,它控制沿主轴的对齐方式;align-items属性控制沿交叉轴的对齐方式。这是一个重要的概念。主轴和交叉轴可以根据flex-direction设置为columnrow来切换。因此,主轴默认始终是水*轴,除非您使用flex-direction: column,垂直轴就成为主轴!

如果这是您第一次接触 flexbox,您可能会说:“嘿,慢点!”不用担心,这只是术语和一些属性和概念的介绍;不要指望立刻就能理解这一切。随着我们在接下来的章节中开始使用不同的属性,我们将更多地参考前面的图表,并且我们将在接下来的章节中深入讨论每一个属性。

从浮动到 flexbox

在本节中,我们将开始工作,将我们的列模块从基于浮动的布局更改为基于 flexbox 的布局(我很兴奋)。

首先,我们将从列中删除所有与浮动相关的属性,并将它们分解到最初的状态;然后,我们将使用display: flex将浮动布局转换为基于 flexbox 的布局,以立即看到结果。最后,我们将探讨flex-direction在响应式设计中的用途;当我们开始讨论较小的屏幕尺寸时,我们将讨论这一点。

从列部分删除与浮动相关的属性

好的,这是我们的三列布局:

让我们回想一下,它在较小的宽度下变成了一个一列的管道:

好了,让我们去 CSS 文件。现在我们将从我们的列中删除所有基于浮动的属性。

从这开始:

****************
3 columns
****************/
.column {
  float: left;
  width: 31.25%; /* 300/960 */
  margin-left: 3.125%; /* 30/960 */
}
.column:first-child {
  margin-left: 0;
}
.columns figure {
  margin: 0 auto;
  width: 100%;
  max-width: 250px;
}

让我们基本上删除所有内容,使其看起来像这样:

****************
3 columns
****************/
.column {

}

接下来,让我们在响应式媒体查询中删除基于浮动的代码。所以从这开始:

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
 .column {
 float: none;
 width: auto;
 padding: 0 50px;
 }
 .column figure {
 margin: 0 auto;
 width: 100%; 
 max-width: 250px;
 }
 .column h2 {
 text-align: center;
 }
}/* end of media query */

让我们将它改成这样:

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
}/* end of media query */

并在一个非常小的宽度的媒体查询中,让我们删除对列的最后一个引用。所以,从这开始:

@media screen and (max-width: 550px) {
  h1 {
    font-size: 40px;
  }
  h2 {
    font-size: 26px;
  }
 .column {
 padding: 0;
 }
  .content-block .figure {
    width: 200px;
    display: block;
    margin-left: auto;
    margin-right: auto;
    float: none;
  }
  .content-block h1 {
    text-align: center;
  }
  .button-narrow {
    width: 100%;
  }
}/* end of media query */

让我们删除.column {}规则集,使其看起来像这样:

@media screen and (max-width: 550px) {
  h1 {
    font-size: 40px;
  }
  h2 {
    font-size: 26px;
  }
  .content-block .figure {
    width: 200px;
    display: block;
    margin-left: auto;
    margin-right: auto;
    float: none;
  }
  .content-block h1 {
    text-align: center;
  }
  .button-narrow {
    width: 100%;
  }
}/* end of media query */

好了,如果我们刷新浏览器并扩大它,我们将回到堆叠布局:

我们已经成功地从本节中删除了基于浮动的布局,因为我们的三列已经消失了。

使用display: flex打开 flexbox

现在我们将使用 flexbox 重新构建列。我们将查看我们的 index.html 文件。这是我们称之为列的区域的标记:

<!-- 
===============
Secondary Sections
===============
-->
<section class="secondary-section grouping">
  <div class="wrapper">
    <div class="column">
      <figure>
        <img src="img/octopus-icon.png" alt="Octopus">
      </figure>
      <h2>The Octopus</h2>
      <p>Lorem ipsum dolor...</p>
      <a href="#" class="button">Tenticals &raquo;</a>
    </div>
    <div class="column">
      <figure>
        <img src="img/crab-icon.png" alt="Crab">
      </figure>
      <h2>The Crab</h2>
      <p>Lorem ipsum dolor...</p>
      <a href="#" class="button">Crabby &raquo;</a>
    </div>
    <div class="column">
      <figure><img src="img/whale-icon.png" alt="Whale"></figure>
      <h2>The Whale</h2>
      <p>Lorem ipsum dolor...</p>
      <a href="#" class="button">Stuart &raquo;</a>
    </div>
  </div><!-- end wrapper -->
</section>

每个<div class="column"></div>将成为我们的 flex 项目;<div class="wrapper">将成为我们的 flex 容器。为了便于理解,我将简化我们的标记如下:

<div class="wrapper"> <!--flex container-->
  <div class="column">...</div> <!--flex item-->
  <div class="column">...</div> <!--flex item-->
  <div class="column">...</div> <!--flex item-->
</div> <!--end of flex container-->

让我们为 flex 容器添加一个新的类名"columns",我们将使用它来定位我们的 flex 容器与我们的 flexbox 代码:

<div class="wrapper columns"> <!--flex container-->
  <div class="column"></div> <!--flex item-->
  <div class="column"></div> <!--flex item-->
  <div class="column"></div> <!--flex item-->
</div> <!--end of flex container-->

让我们添加一个新的规则集,以定位我们将要成为 flex 容器的元素。要将某物转换为 flex 容器,只需添加display: flex

/****************
3 columns
****************/
.columns {
  display: flex;
}
.column {

}

flex 容器的子元素将自动成为 flex 项目。

请注意,子子孙孙的元素不会被视为 flex 项目,只有直接的子元素。

这就是我们得到的:

我们基本上通过一个简单的属性display: flex实现了我们的浮动布局。间距有点紧,但我们仍然有一个水*布局。

Flex 覆盖浮动。假设我们有许多浮动,即float: leftfloat: rightfloat: none;无论是什么,flex 项目都会忽略浮动,也就是说,它们没有任何影响。因此,一旦容器元素设置为display: flex,使子元素成为 flex 项目,浮动现在将被忽略在这些 flex 项目上。我可以随心所欲地浮动,但它对 flex 项目没有任何影响。

还有一件事要记住的是,每列现在都是相等的高度。但让我们做一件事。让我们在 flex 项目周围添加一个边框:

/****************
3 columns
****************/
.columns {
  display: flex;
}
.column {
  border: 1px solid pink;
}

这就是它的样子:

等高列,对吧?嗯,每列的内容量完全相同。所以即使我们使用浮动布局,我们也会得到等高的列。不等高是因为每列的内容量不同。我将删除螃蟹列中的一些段落文本:

做完这些之后,你会发现即使它的内容少了很多,它的高度仍然相同。尽管在这里发生了一些其他事情,特别是螃蟹列的宽度与其他两列不同。我们将在本章后面讨论这个问题,但是我们默认情况下确实获得了等高,这是一个很难通过基于浮动的布局来实现的快速胜利。

改变 flex-direction

让我们看看通过添加flex-direction属性并将其值设置为column来改变布局方向有多简单。这个属性适用于.columns的 flex 容器。我还删除了粉色的border

/****************
3 columns
****************/
.columns {
  display: flex;
 flex-direction: column;
}
.column {
}

我们保存这个设置,哇!我们从水*变成了垂直:

一些我们可能想要的居中对齐已经消失了;然而,布局仍然是垂直的。所以这很有趣。

另一件事是我们可以将flex-direction设置为column-reverse

/****************
3 columns
****************/
.columns {
  display: flex;
  flex-direction: column-reverse;
}
.column {
}

之前,我们的章鱼首先出现;现在如果我们刷新浏览器,我们的鲸鱼首先出现,章鱼最后出现:

然而,如果我们查看我们的 DevTools,我们会发现章鱼仍然是源顺序中的第一个,但是最后一个被显示出来:

因此,源顺序没有改变,只是显示顺序改变了。

现在是一个好时机来谈谈我们的 flexbox 图表。当flex-direction设置为row时,这个图表适用——flex 项目水*排列:

然而,当flex-direction改为column时,图表也会改变:

交叉轴现在从左到右,主轴从上到下,flex 项目现在堆叠在彼此上方。

flex-direction的默认值是rowflex-direction: row

我们还可以将flex-direction设置为row-reverse,它会按你所想的那样:水*排列 flex 项目,但顺序相反。让我们看一下下面的图片;我们有鲸鱼、螃蟹和章鱼的顺序相反:

让我们从.column的 flex 容器中删除flex-direction属性,它将默认为行,这正是我们想要的:

/****************
3 columns
****************/
.columns {
  display: flex;
}
.column {
}

浏览器缩小

现在让我们考虑一下更小的设备,缩小我们的浏览器。在接**板尺寸时,会有点紧:

在我们的媒体查询中,我们最初删除了所有的float内容。让我们将flex-direction改为column

@media screen and (max-width: 550px) {
  h1 {
    font-size: 40px;
  }
  h2 {
    font-size: 26px;
  }
  .columns {
 flex-direction: column;
 }
  .content-block .figure {
    width: 200px;
    display: block;
    margin-left: auto;
    margin-right: auto;
    float: none;
  }
  .content-block h1 {
    text-align: center;
  } 
  .button-narrow {
    width: 100%;
  }
}/* end of media query */

我们又回到了较窄浏览器宽度下的一列堆叠布局:

正如你所看到的,仍然存在一些间距和对齐的问题,我们将在下一节中使用 flexbox 来解决这些问题。

总之,我们从列部分中删除了所有基于浮动的布局 CSS,并添加了使用display: flex的 flexbox 布局。我们还改变了flex-direction,正如我们所看到的,它决定了主轴和交叉轴的方向。

理解 flex-grow、flex-basis、flex-shrink 和 flex

让我们试着理解 flexbox 的尺寸属性。在这一节中,我们将使用flex-growflex-shrinkflex-basis以及它们的快捷方式flex来调整 flex 项目的尺寸。所有这些属性都适用于 flex 项目,而不是 flex 容器。

使用 flex-grow

首先,我们来看一个新页面——flexbox.html。你可能已经猜到了,有一个<section>将成为 flex 容器,还有 5 个<div>将成为 flex 项目。

<!--
====================
Flexbox Demo
====================
-->
<section class='flex-container'>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div>
    <div class="flex-item flex-item5">item 5</div>
</section>

这是我们在添加 flexbox 属性之前将要开始的 CSS:

/***************
Flexbox demo
***************/
.flex-container {
  margin-top: 200px;
}
.flex-item {
  padding: 20px;
}
.flex-item1 { background: deeppink;}
.flex-item2 { background: orange; }
.flex-item3 { background: lightblue; }
.flex-item4 { background: lime; }
.flex-item5 { background: olive; }

这是在浏览器中的样子:

通过在我们的 CSS 文件中的 flex 容器中添加display: flex来打开 flexbox:

.flex-container {
  margin-top: 200px;
 display: flex;
}

好了,如果我们刷新浏览器,这为我们创建了一个水*行,如下面的截图所示:

flex-grow 是我们将要看的第一个 flexbox 大小调整属性,它是一个因子。它确定如何沿着 flex 容器的主轴分配“剩余空间”。让我们明确一下,当我说“剩余空间”时我的意思是什么。那就是 flex 容器内未填充的空间,即 flex 项没有占用的空间。在我们的例子中,就是右侧的这个空白空间:

再次,flex-grow决定如何将剩余空间分配给 flex 项。让我们应用它到我们的 flex 项,使用值1

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-grow: 1;
}

flex-grow: 1将强制将剩余空间均匀分配给所有的 flex 项。每个 flex 项都会得到之前未占用的空间的相等部分:

当我缩小浏览器时,我们可以看到我们实现了一个完全流动的网格,而不使用width属性和计算 100 如何*均分成 5 的确切百分比!

让我们为第二个 flex 项创建一个新的规则集(每个 flex 项都有一个唯一的类,第二个是flex-item2)。我们将添加一个flex-grow属性,值为2,这将把剩余空间的两倍分配给第二个 flex 项:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-grow: 1;
}
.flex-item2 {
 flex-grow: 2
}

如果我们刷新浏览器,它应该看起来像这样:

注意,flex-item2的宽度不一定是其他项的两倍;它只是得到了其他项两倍的剩余空间。这是一个值得注意的区别。而且,如果我们缩小浏览器窗口,我们可以看到随着浏览器窗口的缩小,它变窄,直到达到一定的宽度,然后它们大致相同。当有额外的空间时,它会尝试分配更多的空间给flex-item2,因为它具有更高的flex-grow因子:

我们也可以将flex-item2flex-grow设置为0,这是flex-grow的默认值。这基本上表示不要将任何剩余空间分配给这个 flex 项:

.flex-item2 {
  flex-grow: 0
}

第二个 flex 项不会增长以占用任何额外的空间;剩下的四个项会占用可用的额外空间:

使用 flex-basis

让我们再看一下 flex 项的另一个属性:flex-basis。请注意,flex-basis是在根据flex-growflex-shrink进行剩余空间分配之前的 flex 项的初始主尺寸;我们很快会讨论后者。现在,让我们把flex-basis简单地看作是宽度。所以,对于flex-item2,让我们给它一个flex-basis400px并移除它的flex-grow因子:

/***************
Flexbox demo
***************/
.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-grow: 1;
}
.flex-item2 {
 flex-basis: 400px;
}

如果你刷新浏览器,它将把第二个 flex 项的大小设置为400px。但是如果我们真正看一下,它的大小要比 400 像素多一点:

然而,我仍然将flex-grow应用到所有的 flex 项,包括这一个。让我们改变一下,让我们的第二个 flex 项具有默认值flex-grow: 0;

.flex-item2 {
 flex-grow: 0;
    flex-basis: 400px;
}

现在当你刷新浏览器,你会看到它确切地是 400 像素:

它会一直是 400 像素,直到我们开始缩小浏览器;在某个时候,它会开始让步。一旦空间开始变得非常有限,它决定将它缩小到小于 400 像素;这就是flex-basis定义中初始主尺寸部分发挥作用的地方。我们告诉 flexbox 我们希望第二个 flex 项的宽度为 400 像素,flexbox 会遵守这一点,直到 flex 容器没有足够的空间来容纳它。然后,它开始减小第二个 flex 项的宽度,以适应最佳布局。

让我们再次移除flex-grow

.flex-item2 {
  flex-basis: 400px;
}

请注意,flex-basis不仅仅是宽度:当flex-direction设置为row时,它是宽度,这是默认值,当flex-direction设置为column时,它是高度。从技术上讲,因为它不是宽度或高度,它是主要尺寸。

你开始明白为什么我们花了那么多时间来学习 flex 术语了吗?如果其中有任何内容让您感到困惑,我建议您回到本章的开头复习 flex 术语。

所以让我们将flex-direction更改为column。我们将在 flex 容器上执行此操作:

.flex-container {
  margin-top: 200px;
  display: flex;
 flex-direction: column;
}
.flex-item {
  padding: 20px;
  flex-grow: 1;
}
.flex-item2 {
  flex-basis: 400px;
}

现在,由于主轴是垂直运行,400pxflex-basis现在是第二个 flex 项目的高度。您可以在以下截图中看到:

因此,flex-basis会覆盖任何设置的高度。让我们举个例子,为第二个 flex 项目输入一个height800px

.flex-item2 {
  flex-basis: 400px;
 height: 800px;
}

我们看到高度仍然是 400 像素。实际上,我应该说主要尺寸是 400 像素,它应该看起来像这样:

因此,flex-basis还接受两个关键字:autocontentauto关键字的意思是,“去查看widthheight属性”。由于flex-direction目前是column,当我们将flex-basis更改为auto时,800pxheight不应再被忽略:

.flex-item2 {
 flex-basis: auto;
  height: 800px;
}

高度现在是 800 像素:

因此,autoflex-basis的默认值。还有另一个可用的关键字叫做content;这意味着 flex 项目的大小是基于 flex 项目内容的大小。目前最新版本的 Chrome 不支持这一点,所以我不会演示它;但是,一旦浏览器开始实现它,它似乎会在未来变得有用。

好了,我将删除heightflex-basis。我还将删除flex-direction,最终得到我们的 CSS 如下:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-grow: 1;
}
.flex-item2 {

}

这是它的样子:

使用 flex-shrink

flex-shrink可以被认为是flex-grow的相反。虽然flex-grow确定了当有剩余空间时,flex 项目应该消耗多少额外空间,与其他项目成比例,flex-shrink确定了当没有剩余空间时,flex 项目本身应该如何与其他项目成比例收缩。因此,让我们看看这个过程并逐步进行。

首先,让我们为每个 flex 项目添加flex-basis200px,并临时删除flex-grow

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-basis: 200px;
}
.flex-item2 {

}

因此,如果flex-basis设置为 200 像素,每个 flex 项目将是 200 像素宽,任何额外的空间都不允许在任何 flex 项目中,因为flex-grow已被移除。它应该看起来像这样:

让我们将flex-grow的值重新添加到我们的flex-item类中:1

.flex-item {
  padding: 20px;
  flex-basis: 200px;
  flex-grow: 1;
}

再次,额外的空间分配给每个 flex 项目。flex-basis属性只是初始主尺寸的起点(请注意我没有说“初始宽度”,而是“宽度”)。但是每个 flex 项目都变得更宽,以吸收均匀分配给每个项目的额外空间。这是您的页面目前应该看起来的样子:

让我们在第二个 flex 项目上添加一个flex-shrink属性。我们将使用一个因子2,如下面的代码所示:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-basis: 200px;
  flex-grow: 1;
}
.flex-item2 {
  flex-shrink: 2;
}

随着浏览器尺寸的减小,所有项目都会收缩。除了第二个 flex 项目,它的收缩量是其他 flex 项目的两倍,如下面的截图所示:

如果没有为 flex 项目指定,flex-shrink的默认值为1。因此,让我们为所有 flex 项目添加flex-shrink: 1,除了第二个 flex 项目,它的flex-shrink设置为2,只是为了证明没有任何变化:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-basis: 200px;
  flex-grow: 1;
 flex-shrink: 1;
}
.flex-item2 {
  flex-shrink: 2;
}

我们可以看到,当我们使浏览器变小时,实际上没有任何变化;第二个 flex 项目仍然比其他项目收缩得更多,如下面的示例所示:

您还可以做的一个好玩的事情是将flex-shrink设置为0,以确保项目不会收缩。让我们为第二个 flex 项目这样做:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex-basis: 200px;
  flex-grow: 1;
  flex-shrink: 1;
}
.flex-item2 {
 flex-shrink: 0;
}

现在刷新浏览器。当空间有限时,所有其他 flex 项目都会收缩,除了项目 2;它保持着flex-basis: 200px

使用 flex 快捷方式

还有一个名为 flex 的快捷属性,可以替代使用 flex-growflex-shrinkflex-basis。让我们用 flex 替换 flex-basisflex-growflex-shrink

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
 flex: 1 1 200px;
}
.flex-item2 {
  flex-shrink: 0;
}

因此,flex 中值的顺序如下:flex-growflex-shrinkflex-basis

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
 flex: 1 1 200px; /* order: flex-grow, flex-shrink, flex-basis */
}

如果我们刷新浏览器,它将做与我们使用非快捷属性时完全相同的事情:

对于第二个 flex 项目,它只有 flex-shrink,所以我们可以使用 flex: 1 0 的快捷方式。flex-basis 将智能地设置为其默认值 auto,可以省略。我们需要将值设置为 1 0,因为 flex-grow 的默认值是 1,因此即使我们没有显式设置 flex-grow,我们也需要将其值添加到我们的快捷方式中。我们还将删除现有的 flex-shrink

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
  flex: 1 1 200px; /* order: flex-grow, flex-shrink, flex-basis */
}
.flex-item2 {
 flex: 1 0; /* order: flex-grow, flex-shrink */
}

同样,在浏览器中我们看不到任何变化,这正是我们从小的重构中想要的:

因此,flex: 1 0 意味着 flex-grow = 1flex-shrink = 0。如前所述,flex-basis 默认为 auto,因此我们不需要在这里添加它。还有一个关键字 none,基本上是说不要增长、不要收缩,并且查看我的宽度或高度作为主要大小,换句话说,不要伸缩。这个快捷方式很简洁,但在开始使用 flexbox 时,我建议使用每个属性单独使用,直到完全理解每个属性在做什么。

更多布局,更多定位

本节介绍了使用 flexbox 进行更多布局和更多定位。在这里,我们将查看一个新属性 justify-content,以及如何在彼此之间嵌套 flexbox,最后使用自动边距。

在开始之前,让我们通过去掉我们的 flex 快捷方式来重置一些 flex 属性:

.flex-container {
  margin-top: 200px;
  display: flex;
}
.flex-item {
  padding: 20px;
}
.flex-item2 {

}

通过移除 flex 快捷方式,每个 flex 项目都不再关心增长、收缩或它们的初始主要大小应该是什么:

使用 justify-content 属性

首先是 justify-content,这是一个决定内容是否在主轴的起始位置、结束位置或中间位置对齐的 flex 容器属性。让我们添加 justify-content 并将其设置为 flex-start,如下面的代码片段所示:

.flex-container {
  margin-top: 200px;
  display: flex;
 justify-content: flex-start;
}

flex-startjustify-content 的默认值,因此没有任何变化:

flex-start 将 flex 项目定位在主轴的起始位置。请记住,当未指定 flex-direction 或指定为 row 时,主轴水*从左到右。因此,flex-start 将是左边缘,flex-end 将是右边缘:

现在让我们将值更改为 flex-end

.flex-container {
  margin-top: 200px;
  display: flex;
 justify-content: flex-end;
} 

内容现在定位到右侧:

这很像使用 float:right,只是没有所有额外的麻烦和与浮动相关的问题:没有清除、没有折叠,也没有重新排列浮动项目。基本上,我们只是将 flex 项目定位到右侧。

这非常有用,但真正的魔力是当我们使用 justify-content: center 时发生的:

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: center;
}

哦,天哪,我们刚刚将 flex 项目居中了!

从来没有 float: center。诚然,我们可以通过在容器上将左右边距设置为 auto 来居中物品。但问题是,这样做时我们总是必须指定容器的 width;因此,如果容器中的项目数量发生变化,我们还必须更改 width 属性。有其他居中的技巧,但没有一个像这样简单和灵活。

Flexbox 本质上更适合动态内容,不需要定义任何 width;让我们在 HTML 中添加另一个 flex 项目来证明这一点:

<section class="flex-container">
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div>
    <div class="flex-item flex-item5">item 5</div>
 <div class="flex-item flex-item1">item 6</div>
</section>

现在我们有六个项目,它们都仍然居中:

但等等,还有更多!有一个名为 space-between 的关键字可以使用:

.flex-container {
  margin-top: 200px;
  display: flex;
 justify-content: space-between;
}

这个关键字space-between在每个项之间均匀分配了额外的空间。因此每个元素之间都有"空间":

注意第一个和最后一个元素紧贴边缘;第一个 flex 项紧贴其容器的最左边缘;最后一个 flex 项紧贴其容器的最右边缘。

还有另一个值,space-around做了一些略有不同的事情:

.flex-container {
     margin-top: 200px;
     display: flex;
    justify-content: space-around;
 }

请注意,space-around重新分配了容器周围所有 flex 项的额外空间,甚至是第一个和最后一个,而space-between只在每个项之间插入额外的空间。

让我们回到主页,在一个更实际的例子中实现这一点,也就是我们的三列:

我们的三列设置为display: flex,但没有应用其他 flex 属性。flex 项已经有点居中,因为 flex 容器已经居中。然而,我们希望每个 flex 项之间有一些空间。因此,在我们的 CSS 区域中,让我们说justify-content: space-between。与我们目前正在做的无关。我还在我们的海洋生物上设置了max-width: 50%,这样它们就不会太大。但更重要的是justify-content

.columns {
  display: flex;
 justify-content: space-between;
}
.column {

}
.column figure {
 max-width: 50%;
}

没有任何变化!

这是因为每列中的文本。内容推动每个 flex 项填充可用的空间。因此,我们需要为这些项添加widthflex-basis,以明确定义我们希望每列有多宽。这是因为由于没有额外的空间,flexbox 无法重新分配 flex 项以在每个 flex 项之间放置额外的空间。我们需要一些额外的空间。

让我们通过向每列添加flex-basis: 30%来实现这一点:

.columns {
  display: flex;
  justify-content: space-between;
}
.column {
 flex-basis: 30%;
}
.column figure {
  max-width: 50%;
}

刷新页面,你应该看到这个:

注意空间在每个项之间均匀分布。太棒了!我们还有一点清理要做。底部的按钮在每列底部的位置不一致;现在这并不太明显,因为每列内的内容相对相同;然而,如果我们使每列中的内容量有很大的不同,这将变得更加明显:

我们如何解决这个问题?好吧,记住,在我们的情况下,flex 容器是列,每列是一个 flex 项。按钮不是一个 flex 项,因为它在列内。这就是嵌套的 flexbox 发挥作用的地方。

嵌套的 Flexbox

让我们将列转换为嵌套的 flex 容器:

.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
 display: flex;
}.column figure {
  max-width: 50%;
}

当然,容器的 flex 项默认设置为flex-direction:row,因此它们都水*地坐在一起,这完全破坏了事情:

显然,这不是我们想要的样子,但我们可以很容易地解决这个问题。让我们将flex-direction更改为column,如下面的代码片段所示:

.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
}

很好,我们又回到了正轨。看起来和我们将列设置为 flex 容器之前一样:

这对我们有什么帮助?嗯,我们可以开始使用justify-content,也许我们可以说justify-contentspace-between

.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
 justify-content: space-between;
}

这使按钮在底部很好地放置,但现在在内容的中间。每个 flex 项之间的空间均匀分布,这对于每列来说是不同的,因此看起来不太好:

让我们恢复justify-content的默认值:

.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

这将所有内容移回顶部,因为flex-directioncolumn,主轴现在是上下方向的:

使用自动边距

关于 flexbox 的一个显著特点是它对marginauto关键字进行了全新的处理。自动边距现在与 flexbox 密切配合。我现在可以将我的按钮作为选择器,并给它margin-topauto

.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
  display: flex;
}
.column figure {
  max-width: 50%;
}
.column .button {
 margin-top: auto;
}

砰!按钮上方的空间现在自动计算,按钮位于每列的底部:

当 flex-direction 是 row 时,这也适用;您可以使用margin-left: auto;margin-right: auto将 flex 项紧贴到其 flex 容器的外边缘。

为了举例说明,让我们回到我们的 flexbox 演示示例,我们可以将 flex 容器的justify-content更改为flex-start,然后添加另一个规则集,使用margin-left: auto将最后一个 flex 项推到右边缘:

/***************
Flexbox demo
***************/
.flex-container {
  margin-top: 200px;
  display: flex;
 justify-content: flex-start;
}
.flex-item {
  padding: 20px;
}
.flex-item:last-child {
 margin-left: auto;
}

所有的 flex 项都排在左边-在它们的flex-start处-除了最后一个 flex 项,它紧贴在右边-或者在flex-end处-因为我们自动计算它的左边距:

让我们回到主页,看看我们的列。关于这些列的最后一件事:红色标题不是每个都在同一垂直位置对齐,因为我们的每个海洋生物 SVG 的高度略有不同:

让我们给每个海洋生物一个flex-basis150px。由于flex-directioncolumnflex-basis可以被视为高度;因此,我们基本上给每个图形相同高度的150px

.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
}
.column figure {
  max-width: 50%;
 flex-basis: 150px;
}
.column .button {
  margin-top: auto;
}

现在这些红色标题将整齐地排在一起:

总之,justify-content沿着主轴定位和重新分配额外的空间。您可以整天嵌套 flexbox,并且自动边距非常方便,可以让您将内容定位到 flex 容器的相反边缘,这是一个非常常见的 UI 模式。

总结

在本章中,我们已经涵盖了 flexbox 的大量内容,包括所有基本的 flexbox 属性。在下一章中,当我们学习如何对齐和流动 flexbox 内容以及所需的属性时,我们将继续进行。我们还将创建一个新的 UI 模式-产品列表-并看看 flexbox 如何在那里发挥作用。

第十章:Flexbox,第二部分

让我们继续探索伸缩盒和它所提供的功能。你现在应该已经掌握了基础知识,所以在本章中,我们将继续构建一个新的部分——下面你看到的产品列表,以便获得一些使用伸缩盒构建实际内容的实践经验:

我们还将讨论在使用伸缩盒时需要添加前缀的内容,以及如何以最简单的方式添加前缀。

我们将涵盖以下主题:

  • 使用伸缩盒构建一个新的部分

  • 使用 flex-wrap 和 align-content

  • 更改伸缩项的显示顺序

  • 处理供应商前缀

构建产品列表

让我们用伸缩盒构建一个产品列表。我们将通过创建一个产品列表来看看我们还可以用伸缩盒构建什么。我们将探讨两个新的伸缩盒属性:align-itemsalign-self

使用 align-items

为了构建产品列表,我们将从一些新的标记开始,这些标记将直接位于页脚上方:

<!-- 
===============
Product Listing
===============
-->
<section class="secondary-section grouping">
    <ul class="wrapper product-list">
        <li class="product-list-item">
            <figure>
                <img src="img/octopus-icon.svg" alt="Octopus">
            </figure>
            <h2>The Octopus</h2>
            <p>Lorem ipsum dolor sit.</p>
            <a href="#" class="button">Tenticals &raquo;</a>
        </li>
        <li class="product-list-item">...</li>
        <li class="product-list-item">...</li>
        <li class="product-list-item">...</li>
        <li class="product-list-item">...</li>
        <li class="product-list-item">...</li>
    </ul><!-- end wrapper -->
</section>
<!-- 
================ 
Footer
================
--> 

标记相当多,但并不是很复杂。有一个无序列表,其中包含六个列表项(<li>标签)。每个列表项都有一个 SVG 图像(<figure><img></figure>)、一个标题(<h2>)、一个段落(<p>)和一个锚点(<a>)。在前面的代码片段中,我省略了除第一个之外所有列表项的内容。

我们还将从一些 CSS 开始引导这一部分:

/****************
Product Listing
****************/
.product-list-item {
  border-bottom: 1px solid #766e65;
}
.product-list-item figure {
  width: 50px;
  margin-right: 20px;
}
.product-list-item h2 {
  margin: 0;
}
.product-list-item p {
  margin: 0;
}
.product-list-item .button {
  transform: scale(1);
  width: 130px;
}

以下是我们产品列表的初始内容:

我们希望每个列表项的内容都水*排列。我们可以选择类product-list-item,并使用display: flex

.product-list-item {
  border-bottom: 1px solid #766e65;
 display: flex;
}

这个规则集是针对具有product-list-item类的六个不同的li标签。这很重要,因为我们有六个不同的伸缩容器。添加display: flex应该会水*排列每个伸缩容器中的所有不同伸缩项。因为这就是伸缩盒的作用。默认情况下,flex-directionrow,所以一切都是水*排列的:

好吧,看起来不太好。我们可以做的一件事是给h2添加一个值为250pxflex-basis

.product-list-item h2 {
  margin: 0;
  flex-basis: 250px;
}

这应该增加一些组织性,而且确实做到了:

现在,让我们使用自动边距将按钮对齐到最右边缘:

.product-list-item .button {
  transform: scale(1);
  width: 130px;
 margin-left: auto
}

回顾我们在上一节学到的内容,margin-left: auto将自动计算按钮左侧的边距,并将其推到最右边。这好多了,但是仍然有点紧:

让我们用一个叫做align-items的新属性来解决这个问题。所以这些按钮彼此之间太*了,而这段落却高高地坐着。我们希望图片、标题、段落和按钮都垂直居中。align-items是一个可以用在伸缩容器上的属性,它控制了伸缩项沿交叉轴的定位。这里再次提醒我们,当flex-direction设置为row时,交叉轴的方向是怎样的:

正如我们所看到的,当flex-direction为行时,交叉轴从上到下。我们将添加一个值为centeralign-items。这实际上不会有太明显的效果,除非我们添加一个height80px。所以我们也来做这个:

.product-list-item {
  border-bottom: 1px solid #766e65;
  display: flex;
 align-items: center;
 height: 80px;
}

因此,使用align-items: center将使项目在交叉轴中间对齐:

好吧!我们所有的伸缩项都是垂直居中的,只用了一个属性,而且每个项目的高度都不同。另外,我想指出align-items的默认值是stretch,它会强制伸缩项从交叉轴的起始位置拉伸到结束位置。这就是为什么伸缩盒默认提供了等高列。

我们还可以使用flex-start,它将所有伸缩项对齐到伸缩容器的顶部或交叉轴的起始位置:

.product-list-item {
  border-bottom: 1px solid #766e65;
  display: flex;
  align-items: flex-start;
  height: 80px;
}

以下是前面代码的输出:

让我们尝试flex-end,这将使所有的伸缩项目对齐到底部或交叉轴的末尾:

.product-list-item {
  border-bottom: 1px solid #766e65;
  display: flex;
  align-items: flex-end;
  height: 80px;
}

我们的伸缩项目现在对齐到了交叉轴的末尾——底部:

让我们把这个改回到center

.product-list-item {
  border-bottom: 1px solid #766e65;
  display: flex;
 align-items: center;
  height: 80px;
}

现在让我们回到我们的三列;我们仍然有图片和标题对齐到左边的问题,我们希望它们居中:

让我们看看如何使用align-items来在flex-direction设置为column时居中我们的海洋生物和标题。在这种情况下,交叉轴是水*的。

这是个好消息。因为align-items用于在交叉轴上对齐,而对于flex-direction: column来说,交叉轴是水*的,这应该会使我们的海洋生物图片和标题居中。

记住每个.column都是.columns的一个伸缩项目,但也是其自己的伸缩项目,拥有自己的伸缩项目。这些伸缩项目包括海洋生物图片、标题、段落和按钮等。所以每一列都是其自己的伸缩项目。我们可以使用align-items: center

/****************
3 columns
****************/
.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
 align-items: center;
}
.column figure {
  max-width: 50%;
  flex-basis: 150px;
}
.column .button {
  margin-top: auto;
}

这就是我们最终得到的结果:

事情都居中了,就像标题和底部的按钮一样,但我们的海洋生物图片完全消失了,我们的按钮也变小了。让我们逐个解决这些问题,首先考虑一下为什么我们的海洋生物消失了。让我们在海洋生物应该出现的地方附*检查并找到图片:

在 DevTools 中检查包含img元素的figure元素,显示宽度为 0,高度为 150。那么如果我们已经设置了这个 CSS,为什么宽度会是 0 呢?

.column figure {
  max-width: 50%;
  flex-basis: 150px;
}

嗯,我们没有为这些 SVG 设置明确的width。我们设置了max-width,但这实际上并没有强制设置宽度。请记住,max-width只是说“永远不要超过 x 像素宽度”,但不会强制在该阈值以下设置任何宽度。我们的flex-basis150px,因为flex-directioncolumn,所以它控制了高度。我们根本没有设置真正的宽度。当我们设置align-items: center时,它会强制元素只占据它们需要的宽度或高度,几乎就像当你将块级元素floatleftright时一样。此外,SVG 在图像的宇宙中是独一无二的。传统的 PNG 和 JPG 图像即使在 CSS 中没有指定任何尺寸,也有固定的尺寸。而 SVG 可以按比例缩放到任何大小,因此没有基准尺寸。由于figureimg都没有设置宽度或高度,align-items属性会将宽度挤压为 0,这就是它们消失的原因。

这很容易解决;我们只需要添加一个width。让它比以前的尺寸更小一点,大约是其容器的 50%:

.column figure {
  max-width: 50%;
  flex-basis: 150px;
 width: 50%;
}

我们的海洋生物又回来了!

底部的按钮和我们刚刚遇到的图片有相同的问题;它们没有设置任何paddingwidth,所以align-items强制宽度只能与内容一样宽,这就是为什么它们看起来都被挤压得很小。

解决方法是一样的:只需设置一个width。在这种情况下,让width90%

/****************
3 columns
****************/
.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
}
.column figure {
  max-width: 50%;
  width: 50%;
  flex-basis: 150px;
}
.column .button {
  margin-top: auto;
 width: 90%;
}

问题解决了:

使用align-self伸缩属性

看起来不错,但如果我不想让所有的伸缩项目都居中怎么办?我可能更喜欢h2对齐到flex-start(事实上我是这样做的)。align-items是一个用于控制所有伸缩项目在交叉轴上对齐的属性。另一个名为align-self的 flexbox 属性控制沿交叉轴的对齐,但是直接用于伸缩项目。这将帮助我们只将我们的h2对齐到左边。

让我们为h2创建一个新的选择器,并添加align-self: flex-start

/****************
3 columns
****************/
.columns {
  display: flex;
  justify-content: space-between;
}
.column {
  flex-basis: 30%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
}
.column figure {
  max-width: 50%;
  width: 50%;
  flex-basis: 150px;
}
.column h2 {
 align-self: flex-start;
}
.column .button {
  margin-top: auto;
  width: 90%;
}

请注意,align-self是仅适用于 flex 项的属性;它的默认值是auto,这意味着它告诉它检查align-items的值以进行交叉轴对齐。它还接受stretchflex-startflex-endcenterbaseline。它允许我们覆盖单个 flex 项的align-items值。

如果我们现在刷新浏览器,我们会看到我们的h2标签对齐到左侧-在它们的flex-start

我们现在看起来不错。让我们花一分钟快速修复一下我们之前创建的一个错误。我们将通过右键单击并选择检查来查看这个错误;我们将在 Chrome 中将 DevTools 移动到右侧。我只是将它调整到*板尺寸;我们现在可以看到问题了,我们的海洋生物失控了!

我们的列都堆叠在一起。所以我们必须弄清楚为什么会发生这种情况。这是因为我们为每列将flex-basis设置为30%。当flex-direction为行时,它运行得很好,但是您可能还记得从Floats to Flexbox部分,我们在较小设备的媒体查询中将flex-direction更改为column。当flex-directioncolumn时,flex-basis控制的是高度而不是宽度,因为在这种情况下,主轴是垂直而不是水*的。

所以让我们在媒体查询中修复这个问题。让我们创建一个新的选择器并将flex-basis设置为auto

@media screen and (max-width: 1023px){
 .column {
 flex-basis: auto;
    margin-bottom: 50px;
 }
}/* end of media query */

您会记得,将flex-basis设置为auto意味着这样做:看看我的宽度或高度。因为我们没有明确设置高度;高度由内容确定,正是我们想要的——只需使高度成为内容的大小。此外,我偷偷加了margin-bottom50px,以在它们之间提供一点间隙:

在本节中,我们使用 flexbox 构建了我们的产品列表,并介绍了两个新的 flex 属性:align-itemsalign-self。在下一节中,我将介绍另外两个属性:flex-wrapalign-content

使用 flex-wrap 和 align-content

flex-wrap属性允许我们确定我们是将内容包装到第二行还是将所有 flex 项挤入单行;align-content确定被包装到多行的行的对齐方式,从而变成多行。它们基本上是最好的朋友。

使用 flex-wrap

我们将返回并使用我们的 flexbox 示例页面(flexbox.html)作为测试这些属性的游乐场。这是我们在这个区域最终得到的 CSS:

/***************
Flexbox demo
***************/
.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: flex-start;
}
.flex-item {
  padding: 20px;
}
.flex-item:last-child {
  margin-left: auto;
}
.flex-item1 { background: deeppink;}
.flex-item2 { background: orange; }
.flex-item3 { background: lightblue; }
.flex-item4 { background: lime; }
.flex-item5 { background: olive; }

flex 容器将所有内容都对齐到flex-start,或者在我们的情况下是左侧。这是因为flex-direction没有设置,因此默认为row。最后一个 flex 项被推到最右边,使用margin-left: auto;。这是我们的flexbox.html页面目前应该看起来的样子:

flexbox.html中,让我们添加更多的 flex 项并保存它们:

<section class='flex-container'>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div> 
    <div class="flex-item flex-item3">item 3</div> 
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
</section>

现在我们看到 flexbox 是如何真正挤压 flex 项以适应在 flex 容器内的一行上的。

所以我们将通过将flex-wrap属性添加到 flex 容器中并将值设置为wrap来包装多行。此外,让我们通过删除整个规则集来摆脱最后一个 flex 项上的margin-left: auto

/***************
Flexbox demo
***************/
.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: flex-start;
 flex-wrap: wrap;
}
.flex-item {
  padding: 20px;
}
.flex-item1 { background: deeppink;}
.flex-item2 { background: orange; }
.flex-item3 { background: lightblue; }
.flex-item4 { background: lime; }
.flex-item5 { background: olive; }

因此,所有先前收缩以适应一行的 flex 项现在会扩展到其自然大小;这意味着文本的宽度加上文本两侧的padding20px。这创建了两行内容。很好,正是我们想要的!

flex-wrap的默认值是nowrap;。这是有道理的,因为在将其设置为wrap之前,它强制所有我们的 flex 项适合一行。就好像我们根本没有省略flex-wrap一样。让我们换成nowrap来测试一下:

.flex-container {
    margin-top: 200px;
    display: flex;
    justify-content: flex-start;
 flex-wrap: nowrap;
}

就好像我们根本没有指定flex-wrap一样:

还有wrap-reverse;让我们试试:

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: flex-start;
 flex-wrap: wrap-reverse;
}

最后一个项目现在是第一个,第一个项目是最后一个。从技术上讲,最后一个项目现在是第一行的第四个:

使用flex-wrap的好处是,现在每一行在flex-growflex-shrinkjustify-content等属性方面都可以独立工作。

让我们测试一下,添加flex-grow: 1

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap-reverse;
}
.flex-item {
  padding: 20px;
 flex-grow: 1;
}

这会重新分配任何额外的空间,以确保它们填满所有剩余的空间:

每一行都将它们的 flex 项目扩展以占据额外的空间。正如你所看到的,第一行的 flex 项目被拉伸得更远,以填补比下面一行更多的额外空间。在它下面的另外两行中,flex 项目只被拉伸了一点点来填补额外的空间。

让我们再次看看这些行如何独立于彼此地工作,通过将justify-content改为space-between在 flex 容器上。我们还将在 flex 项目上去掉flex-grow

.flex-container {
  margin-top: 200px;
  display: flex;
 justify-content: space-between;
  flex-wrap: wrap-reverse;
}
.flex-item {
  padding: 20px;
}

因此,在每个 flex 项目之间都有额外的空间分配:

第一行有很多额外的空间,而第二行在每个 flex 项目之间只有一点额外的空间。同样,每一行在处理flex-growflex-shrinkjustify-content时都是独立的。这种情况非常适合来自内容管理系统CMS)的动态、未知数量的内容。

使用 align-content

好的,让我们再看看另一个叫做align-content的属性。像flex-wrap一样,align-content是一个只在 flex 容器上工作的属性;然而,align-content依赖于flex-wrap被设置为wrapwrap-reverse,这意味着在所有其他情况下align-content都会被忽略。此外,align-content类似于align-items,因为它控制沿交叉轴的排列或对齐。唯一的区别是,它不是在交叉轴上重新分配每个flex 项目,而是在交叉轴上重新分配每个

让我们将align-content设置为space-between。我们还将其高度设置为300px,并给它一个深灰色的边框:

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap-reverse;
 align-content: space-between;
 height: 300px;
 border: 1px solid #777; 
}

我还要把 flex 项目的数量加倍,以保持事情的趣味性:

<!--
====================
Flexbox Demo
====================
-->
<section class='flex-container'>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div> 
    <div class="flex-item flex-item3">item 3</div> 
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
    <div class="flex-item flex-item1">item 1</div>
 <div class="flex-item flex-item2">item 2</div>
 <div class="flex-item flex-item3">item 3</div>
 <div class="flex-item flex-item4">item 4</div> 
 <div class="flex-item flex-item5">item 5</div> 
 <div class="flex-item flex-item1">item 6</div>
 <div class="flex-item flex-item1">item 1</div>
 <div class="flex-item flex-item2">item 2</div>
 <div class="flex-item flex-item3">item 3</div>
 <div class="flex-item flex-item4">item 4</div> 
 <div class="flex-item flex-item5">item 5</div> 
 <div class="flex-item flex-item1">item 6</div>
 <div class="flex-item flex-item1">item 1</div>
 <div class="flex-item flex-item2">item 2</div> 
 <div class="flex-item flex-item3">item 3</div> 
 <div class="flex-item flex-item4">item 4</div> 
 <div class="flex-item flex-item5">item 5</div> 
 <div class="flex-item flex-item1">item 6</div>
</section>

现在我们有 3 行,并且由于align-content,每行之间有空间:

height属性是相关的,因为如果省略,flex 容器的高度只会和其 flex 项目一样高;因此,align-content不会起作用,因为没有额外的垂直空间可供使用。除了space-between之外,align-items的其他值包括flex-startflex-endcenterspace-around。这些值应该是我们在学习justify-content属性时熟悉的。默认值是stretchspace-around值会均匀地重新分配所有项目周围的额外空间,包括第一个和最后一个。

所以让我们把它从space-between改为space-around

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap-reverse;
 align-content: space-around;
 height: 300px;
 border: 1px solid #777; 
}

你可以看到,使用space-around,在 flex 容器的顶部和第一行之间以及容器底部和最后一行之间有一些空间:

space-between让第一行和最后一行紧紧贴着 flex 容器,这是一个细微的差别。我们之前在学习justify-content时也注意到了这种微妙之处。

现在让我们将align-content的值改为center

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap-reverse;
  align-content: center;
  height: 300px;
  border: 1px solid #777; 
}

正如我们所期望的,我们的行是居中的:

现在让我们将flex-direction改为列,看看在这种情况下flex-wrapalign-content是如何一起工作的:

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  flex-wrap: wrap-reverse;
  align-content: center;
  height: 300px;
  border: 1px solid #777; 
}

这里发生了很多事情,很难准确地说是什么,但我们可以说的一件事是我们在水*方向上是居中的:

让我们简化一下,以理解发生了什么。首先,让我们将flex-wrapwrap-reverse改回wrap

.flex-container {
    margin-top: 200px;
    display: flex;
    justify-content: space-between;
    flex-direction: column;
 flex-wrap: wrap;
    align-content: center;
    height: 300px;
    border: 1px solid #777; 
}

flexbox.html中,我们将大大减少 flex 项目的数量:

<section class='flex-container'>
    <div class="flex-item flex-item1">item 1</div>
    <div class="flex-item flex-item2">item 2</div>
    <div class="flex-item flex-item3">item 3</div>
    <div class="flex-item flex-item4">item 4</div> 
    <div class="flex-item flex-item5">item 5</div> 
    <div class="flex-item flex-item1">item 6</div>
</section>

现在,更容易看到flex-directioncolumn,这会强制两个垂直列,因为flex-wrap设置为wrap,并且我们没有足够的空间容纳所有 6 个伸缩项目:

我们为justify-content设置的space-between正在在每个伸缩项目之间重新分配额外的空间。请注意,两列都独立地重新分配了它们的额外空间,如下图所示。

当交叉轴上有额外空间和多行时,使用align-content来排列交叉轴上的行。正如我们所知,当flex-direction为行时,交叉轴从上到下运行。

flex-direction为列时,交叉轴从左到右运行:

如果我开始听起来像一个重复的唱片,我很抱歉,但我觉得重复强调伸缩盒的运作方式是很重要的。

使用flex-flow缩写

之前,我们看到flex缩写如何将flex-growflex-shrinkflex-basis组合在一起。让我们介绍另一个缩写,flex-flow,它将允许我们通过将flex-directionflex-wrap组合在一起来减少一些属性。无论如何,这会简化我们的 CSS 代码:

.flex-container {
  margin-top: 200px;
  display: flex;
  justify-content: space-between;
  flex-flow: column wrap;
  align-content: center;
  height: 300px;
  border: 1px solid #777; 
}

没有变化,这正是我们在使用缩写重构时想要的:

考虑到align-contentflex-wrap的密切依赖,我期望align-contentflex-flow的一部分。然而,align-content并不是flex-flow的一部分,它与flex-directionflex-wrap一起。

在本节中,您学习了flex-wrap如何允许我们创建多个流或行的内容,而align-items则将这些多行定位在其容器的交叉轴上。

更改伸缩项目的显示顺序

在本节中,我们将讨论如何更改伸缩项目的显示顺序以及这如何有助于响应式网页设计。我们还将讨论这如何影响网页可访问性。

在较宽的屏幕宽度下,内容水*显示:首先是章鱼,然后是螃蟹,然后是鲸鱼:

在较窄的设备宽度下,内容以与源顺序相同的顺序显示,只是垂直显示,如下面的屏幕截图所示:

在这两种情况下,显示顺序与源顺序相同,在这种情况下是有意义的。

在这里做点不同的事情。假设我们正在与一位设计师合作,他希望本周突出显示螃蟹,并在视觉上优先于章鱼和鲸鱼。这就是我们在这里所做的。我在 HTML 和 CSS 中添加了一些额外的内容来实现这种新的特色处理:

在标记中,我为每列的标题添加了一个数字,这样我们在浏览器中查看时就可以轻松记住源顺序。然后,对于螃蟹,我添加了一个名为featured的类和一个名为ribbondiv标签。

<div class="column">
    <figure>
        <img src="img/octopus-icon.svg" alt="Octopus">
    </figure>
    <h2>The Octopus 1</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Tenticals &raquo;</a>
</div>
<div class="column featured">
 <div class="ribbon">Featured</div>
    <figure>
        <img src="img/crab-icon.svg" alt="Crab">
    </figure>
    <h2>The Crab 2</h2>
    <p>Lorem ipsum dolor...</p>
    <a href="#" class="button">Crabby &raquo;&lt;/a>
</div>
<div class="column">
    <figure>
        <img src="img/whale-icon.svg" alt="Whale">
    </figure>
    <h2>The Whale 3</h2>
    <p>Lorem ipsum dolor sit...</p>
    <a href="#" class="button">Stuart &raquo;</a>
 </div>

我添加了一些 CSS 来样式化丝带。

/*featured column*/
.featured {
  padding: 0 0 20px 0;
  background-color: #d3d3d3;
  overflow: hidden;
}
.featured h2, 
.featured p {
  margin-left: 20px;
  margin-right: 20px;
}
.ribbon {
    background-color: #ffc0cb;
    padding: 10px 50px;
    margin-bottom: 20px;
    align-self: stretch;
    text-align: center;
    font-family: 'Maven Pro', Arial, sans-serif;
    font-weight: bold;
    box-shadow: #b7b7b7 0px 2px 15px 0px;
}

您可能已经注意到,特色丝带被拉伸横跨顶部;这是使用align-self: stretch完成的。正如我们已经讨论过的,align-self沿着交叉轴对齐伸缩项目,在我们的情况下,由于flex-direction设置为column,交叉轴从左到右。align-self属性类似于align-items,不同之处在于它用于伸缩项目并覆盖align-items属性。

在桌面或更宽的视图上,当我们的业务合作伙伴和设计师看到这一点时,他们真的很高兴。但在手机上,他们说,“嗯,我不知道,螃蟹仍然显示为第二。”他们可能是对的,这是特色内容,所以它不仅应该在视觉上突出,而且还应该首先出现。如果我们的内容来自数据库,我们可以更新它,使螃蟹首先出现;或者,我们可以使用一些 JavaScript 来重新排列我们的特色内容,使螃蟹首先出现。这两种解决方案,至少不是理想的。

Flexbox 在这里派上了用场。在我们针对较小设备的媒体查询中,我们可以使用一个名为order的 flex 项目属性:

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
  .columns {
    flex-direction: column;
  }
  .column {
    flex-basis: auto;
    margin-bottom: 50px;
  }
  .featured {
 order: -1;
 }
}/* end of media query */

好了,当我刷新浏览器时,它立即将我们的螃蟹移动到了第一个位置,如下面的截图所示:

默认情况下,所有 flex 项目都被赋予order0,所以提供-1将螃蟹移到了顶部。项目的顺序与主轴一起运行;最低顺序的 flex 项目将位于主轴的开始,而最高顺序的 flex 项目将出现在主轴的末尾。

同样,在我们的情况下,由于flex-directioncolumn,主轴从上到下运行。让我们把order改为1

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
  .columns {
    flex-direction: column;
  }
  .column {
    flex-basis: auto;
    margin-bottom: 50px;
  }
 .featured {
 order: 1;
 }
}/* end of media query */

这将螃蟹移到底部,因为默认情况下章鱼和鲸鱼都是0,而我们已经指定螃蟹为1—所以现在它被放在了最后:

好了,让我们再添加两个规则集:

.column:nth-child(1) {
 order: 3;
}
.featured {
  order: 1;
}
.column:nth-child(3) {
 order: 2;
}

我们使用nth-child伪类来改变顺序。现在刷新浏览器后,螃蟹在显示顺序中是第一个(源顺序中是第二个),鲸鱼是第二个(但源顺序中是第三个),章鱼是第三个(但源顺序中是第一个)。这就是它应该看起来的样子:

所以我也可以按相反的顺序放置它们。我已经把第一个作为第三个,我可以把第二个作为第二个,第三个作为第一个:

.column:nth-child(1) {
  order: 3;
}
.featured {
  order: 2;
}
.column:nth-child(3) {
  order: 1;
}

这就是我们应该看到的:

现在我们把第三个作为第一个,第二个作为第二个,第一个作为第三个。相反的顺序。但请记住,我有一个更简单的方法来做到这一点;我实际上可以摆脱这三个规则集,只需指定flex-directioncolumn-reverse

@media screen and (max-width: 1023px){
  .intro-content {
    width: auto;
    float: none;
    padding-bottom: 0;
    margin-bottom: 30px;
  }
  .go-premium {
    width: auto;
    float: none;
    margin-top: 0;
  }
  .columns {
 flex-direction: column-reverse;
  }
  .column {
    flex-basis: auto;
    margin-bottom: 50px;
  }
}/* end of media query */

现在当我刷新浏览器时,它们仍然是相反的顺序:

无障碍影响

我想提一下的一件事是,改变显示顺序有一个缺点,你可能需要注意:通过内容进行制表。制表顺序仍然基于源顺序,这成为一个无障碍问题。现在你可能会认为在我们的例子中,制表顺序在桌面上是合乎逻辑的,但在较小的设备上,比如手机上,它可能会变得不合逻辑,尽管大多数情况下不会通过字段进行制表。也许这大多数是正确的;然而,改变显示顺序对于屏幕阅读器,比如 JAWS,也是一个问题,它根据源顺序而不是显示顺序向视力受损的用户读取内容。因此,你的内容仍然会根据源顺序被屏幕阅读器宣布,这将与视觉顺序不同步。这可能是一个无障碍问题。因此,如果改变顺序,最好记住这一点。

在这一部分,你学会了order属性如何改变 flex 容器中 flex 项目的显示顺序,以及它对制表顺序和无障碍的影响。

供应商前缀

让我们谈谈供应商前缀。在这一部分,我们将讨论 flexbox 的浏览器支持以及如何在我们的 flexbox CSS 中添加供应商前缀以获得更深入的 flexbox 支持。我们还将谈论一个叫做Autoprefixer的东西,它可以帮助我们添加这些前缀。

Flexbox 支持从 IE10 开始,如果我们使用-ms-供应商前缀。但这可能不是您想要添加的唯一供应商前缀,因为自从浏览器首次实现以来,W3C 规范实际上已经发生了变化。当它被实现时,语法和属性名称与今天的不同。为了获得深度浏览器支持,我们可以使用旧语法结合新语法来支持一些早期采用的浏览器。

让我们更新我们最初添加 flexbox 的原始规则集,这是我们的.columns

.columns {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: justify;
  -webkit-justify-content: space-between;
  -moz-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
}

哇!这里有很多事情。我们不仅仅是在属性的开头添加-ms--moz--webkit-。当涉及到display属性的值时,我们将供应商前缀添加到值的开头。值本身与我们的非前缀版本并没有太大不同。还有 2 个-webkit-值!Chrome 和 Safari 真的是 flexbox 的早期采用者,所以实际上有两个不同的前缀,WebKit 浏览器支持:-webkit-box-webkit-flex。所以,这是很多前缀和很多记忆,对于justify-content属性看起来也很疯狂。这是很多。棘手的部分是学习和记住旧的语法,特别是因为不明显哪些前缀仍然是必需的。

Autoprefixer

这就是 Autoprefixer CSS 在线工具(autoprefixer.github.io/)可以非常有帮助的地方。它根据浏览器的总市场份额和我们想要为每个浏览器回溯的版本数量提供我们需要的前缀。让我们将这个过滤器更新为.01%

让我们摆脱所有这些前缀,只需将这个规则集复制粘贴到 Autoprefixer 工具的左边框中:

在右侧,它提供了我们应该使用的前缀:

让我们将它复制回我们的 CSS:

.columns {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: justify;
      -ms-flex-pack: justify;
          justify-content: space-between;
}

这非常方便,比记住所有 flexbox 属性的不同语法要容易得多。如果有一种方法可以自动为我们添加供应商前缀,而不必我们进行所有这些复制和粘贴,那将是很好的。我们可以做的一件事是使用预处理器,比如Sass,编写一个称为mixin的东西,为我们添加供应商前缀,这样我们就不必太在意这个了。我们将在下一章中看看 Sass mixins

Gulp

现在我想提一下你可能听说过的东西:Gulp。

Gulp (gulpjs.com/)及其朋友 Grunt(gruntjs.com/),允许我们创建构建流程,例如压缩我们的 CSS 和 JavaScript 文件,将 Sass 编译成 CSS,甚至自动为 CSS 添加供应商前缀,使用 Autoprefixer。Gulp 在 Node 上运行,需要您下载它,然后下载 Gulp。然后您可以下载单独的任务,比如 Autoprefixer。学习 Gulp 远远超出了本书的范围,但 Gulp 真的非常有用,我非常喜欢它。为了开始使用它,我强烈建议您阅读Getting Started with GulpTravis MaynardPackt Publishing,这本书非常好地解释了如何安装 Gulp,设置它并使用它。这就是我学会如何使用 Gulp 的方式,通过阅读这本书。

Gulp 是一个命令行工具,你可以配置它在每次保存 CSS 文件时运行 Autoprefixer。所以,如果我在我的 CSS 中写入一个 flexbox 属性并按下Ctrl + S,Gulp 将会监视我的文件是否有任何变化,如果它检测到变化,它将告诉 Autoprefixer 运行并使用必要的供应商前缀更新我的 CSS 文件。我知道这听起来很奇怪——用供应商前缀更新我的 CSS 文件——但从技术上讲,它所做的是创建一个新的 CSS 文件,其中包含所有的供应商前缀。这里有比我解释的更多,但是查看 Travis Maynard 的书来设置它。这样,你就再也不用考虑供应商前缀了,因为 Autoprefixer 和 Gulp 会为你考虑这些事情。

Flexbox 作业

我们已经建立了一个了不起的网站:

然而,它并非没有问题。你可能已经注意到,在产品列表部分,当我们缩小浏览器时,它开始看起来有点怪异,如下面的截图所示:

最后一个带回家的测试是更新媒体查询内的 CSS,使得在较小的设备尺寸下显示效果良好。

在这一部分,我们谈到了我们需要为我们的 flexbox 属性添加供应商前缀,以便获得更深层次的浏览器支持。然而,供应商前缀可能会很棘手,最好将前缀添加工具交给像 Autoprefixer 这样的工具。更好的是,最好自动化 Autoprefixer,这样每次保存 CSS 文件时都会执行它。你可以使用任务运行器,比如 Gulp 或 Grunt 来实现这一点。

总结

这完成了我们对 flexbox 的探索。我们现在已经看到了与之相关的每一个属性,并将它们应用到了为我们的网站构建新产品列表中。在下一章中,我们将看看在结尾中关于 CSS 技能和工具的下一步:第十一章,总结

第十一章:总结

欢迎来到本书的最后一章。由于我们已经完成了这段旅程,我想带您走过您在 Web 开发学习过程中的一些步骤,并浏览一些链接和资源,以获取有关到目前为止学到的所有内容的更多信息。

下一步

学习 CSS 的下一个逻辑步骤是转向 CSS 预处理器,如 Sass 或 Less。CSS 预处理器允许您使用编程功能编写 CSS,如嵌套、导入、变量和混合,这些功能会被编译成常规 CSS。前端开发的另一个逻辑步骤是学习 JavaScript。不过,首先让我们谈谈 CSS 预处理器 Sass。

CSS 预处理器

我既使用了Less又使用了Sass,但我现在已经使用 Sass 一段时间了。当我开始为这本书创建课程材料时,我几乎忘记了没有 Sass 的情况下编写 CSS 是什么感觉。毋庸置疑,使用 Sass 编写 CSS 要容易得多。它使您的代码更有组织性和清晰,并且我强烈推荐它。让我们来看看 Sass 的一些显着特点。

变量

Sass 的一个简单而强大的功能是变量。让我们设置名为$blue$red的变量,分别等于我在整个站点中使用的蓝色或红色的颜色:

//colors
$blue: #0072AE;
$red: #EB2428;

现在,当我需要输入难以记住的十六进制值#0072AE时,我只需输入$blue,Sass 就会神奇地处理它。变量的另一个很好的用途是它们与字体一起使用,这就是我认为它真正强大的地方。对于字体,通常可以输入font-family,然后创建一组字体。但是,这可能会变得冗长和重复。因此,将所有这些信息插入变量中,例如$maven$droid这样的非常简单的变量,使得快速使用字体变得非常容易,随时都可以使用:

//fonts
$serif: 'Times New Roman', Georgia, serif;
$sans: Arial, Helvetica, sans-serif;
$maven: 'Maven Pro', $sans;
$droid: 'Droid Serif', $serif;

然后我可以在设置font-family的任何地方使用这些变量:

h1, h2 {
  font-family: $maven;
}
p {
  font-family: $droid;
}

这将被编译为整个字符串:

h1, h2 {
  font-family: 'Maven Pro', Arial, Helvetica, sans-serif;;
}
p {
  font-family: 'Droid Serif', 'Times New Roman', Georgia, serif;
}

混合

Sass 中还有一个更好的功能,称为混合。基本上,它们是一种抽象重复的方法。例如,为 CSS3 输入供应商前缀很麻烦,但我可以使用@mixin关键字声明一个混合,然后创建一个充满供应商前缀的模板。在这里,我声明了一个名为transition的混合:

@mixin transition($property: all, $duration: .25s, $timing-function: ease) {
    -webkit-transition: $property $duration $timing-function;
    transition: $property $duration $timing-function;
}

混合带有括号,括号内有参数$property$duration$timing-function。每个参数都有默认值,all.25sease。然后我有-webkit-前缀的过渡属性和未前缀的版本。两者都将混合的参数作为它们的值。

这使我可以进入我的 CSS,并且,如果我想使用过渡,只需添加@include transition

.button {
    @include transition();
}

这将编译为:

.button {
  -webkit-transition: all .25s ease;
  transition: all .25s ease;
}

我还可以在任何时候调用此混合时更新其默认值:

.button {
    @include transition(background-color, .5s, ease-in-out);
}

这将编译为:

.button {
  -webkit-transition: background-color .5s ease-in-out;
  transition: background-color .5s ease-in-out;
}

SASS 嵌套

除了变量和混合,还有嵌套,表面上看起来可能不太强大,但非常方便。您可以将选择器嵌套在彼此内部,而不是编写后代选择器。您可以在以下 CSS 代码中看到,我实际上将focushover选择器嵌套在.button内部:

.button {
  &:focus,
  &:hover {
    background-color: #333;
    color: #fff;
    transform: scale(1, 1) translate(0, -5px);
  }
}

这将编译为以下内容:

.button:focus, 
.button:hover {
  background-color: #333;
  color: #fff;
  transform: scale(1, 1) translate(0, -5px);
}

作为一个经验法则,如果不必要,不要嵌套,因为选择器每次嵌套都会变得更具体和更重要。模块化 CSS 的技巧是保持选择器的轻量级。有关 Sass 中嵌套和使用特殊的&字符的更多信息,请查看我为 CSS-Tricks.com 撰写的文章The Sass Ampersandcss-tricks.com/the-sass-ampersand/)。

使用 SASS 创建和导入部分文件

在第七章的Web Fonts部分的Font kits and font services中,我们还讨论了 Sass 允许您为 CSS 的不同部分创建部分文件,并将它们导入到您的主 Sass 文件中:

//Imports
@import '_variables.scss';
@import '_mixins.scss';
@import '_icons.scss';
@import '_reset.scss';
@import '_modular.scss';
@import '_modal.scss';

Sass 将所有部分 Sass 文件编译成一个主 CSS 文件。所以我的 CSS 被分解成更小的块。它们都编译成style.css

拥有这些多个组织良好的文件的最大好处是它们会编译成一个文件,这意味着只有一个 HTTP 请求,而不是多个请求。这就是性能的提升。更不用说它使我的 CSS 非常有条理。

这些只是预处理器(特别是 Sass)的一些非常好的特性。在这一点上,使用 Sass 或 Less 绝对是最合乎逻辑的步骤。您编写的 Sass 样式表需要通过编译器处理,将其转换为普通的 CSS;否则,浏览器将无法理解 Sass 代码。对于编译,您有几个选项。您可以安装 Ruby 和 Sass,并使用命令行来监视对 Sass 文件所做的任何更改。您还可以查看类似 CodeKit 的软件来执行相同的操作。或者您可以使用像 Gulp 这样的任务运行器,就像我们在上一节末讨论的那样。

要了解更多关于 SASS 的信息,我建议在 Packt 图书馆中观看Brock Nunn 的 Rapid SASS视频课程:

还要查看Dan CederholmSASS for Web Designers。这本书非常好地以简单的方式解释了 Sass,并且阅读起来很快:

JavaScript 和 jQuery

前端开发人员的另一个合乎逻辑的步骤是学习 JavaScript,如果您还没有学习的话。通过 CSS,我们可以通过hoverfocus状态添加交互性,但我们无法进行单击或滑动等操作。这就是 JavaScript 和 jQuery 的用武之地。我建议您学习 JavaScript 的基础知识;但是,如果您想快速入门,可以先学习 jQuery。

所以假设我们想要做的事情是在单击“了解更多>>”链接时显示一个模态框:

我们可以有一个显示模态框:

所以我们有一个会淡入淡出的动画。因此,使用 jQuery 设置动画相对比使用 JavaScript 更容易。这个想法是在 HTML 和 CSS 中创建一个模态框,就好像它一直存在一样。我创建了模态框,并将 HTML 放在 HTML 文件的最底部:

<div class="modal modal-learn-more">
    <span class="close-modal">close</span>
    <h2>Premium Benefits</h2>
    <ul>
        <li>More shark teeth than you can handle</li>
        <li>13 Species of octopus</li>
        <li>Giant, ancient lobsters!</li>
        <li>4 Whales</li>
    </ul>
</div>

然后我有一个名为modal.scss的 Sass 部分文件,它样式化了模态框并将其定位到它应该在的位置:


//learn more modal
.modal {
  display: none;
  width: 40%;
  margin: 0 auto;
  position: absolute;
  top: 200px;
  left: 50%;
  @include translateX(-50%);
  background: #fff;
  @include box-shadow;
  @include border-radius;
  overflow: hidden;
  .close-modal {
    position: absolute;
    right: 10px;
    top: 10px;
    color: #fff;
    cursor: pointer;
    text-decoration: underline;
  }
  h2 {
    background: $blue;
    color: #fff;
    padding: 10px;
  }
  ul {
    padding: 10px 30px 30px 30px;
  }
}

.modal类也设置为display: none,因此默认情况下它是不存在的。就像我们的下拉菜单一样;默认情况下,它是隐藏的。

这里有一些 jQuery 来打开模态框:

//open modal//
$(".learn-more").on("click", function(event){
    event.preventDefault();
    $(".modal-learn-more").fadeIn();
});

基本上,这会监视具有learn-more类的链接的单击,然后淡入具有modal-learn-more类的元素。如果我们回到 HTML,我们会看到我们在模态框的最外层父div上有modal-learn-more类:

<div class="modal modal-learn-more">
    <span class="close-modal">close</span>
    <h2>Premium Benefits</h2>
    <ul>
        <li>More shark teeth than you can handle</li>
        <li>13 Species of octopus</li>
        <li>Giant, ancient lobsters!</li>
        <li>4 Whales</li>
    </ul>
</div>

这是可读性很强的一小部分 jQuery。如果我们想要告诉模态框我们要在单击关闭链接时关闭它,也是同样的操作。

//close modal//
$(".close-modal").on("click", function(event){
    event.preventDefault();
    $(".modal-learn-more").fadeOut();
});

基本上我们是在说当你单击关闭模态框时,我们将使modal-learn-more淡出。jQuery 通过它们创建的预定义方法来处理淡入和淡出的动画。在 jQuery 中非常容易选择我们要淡出的div和我们要单击的项目或元素。要了解更多关于 jQuery 的信息,我建议查看 Packt 图书馆中的 jQuery 书籍,特别是jQuery for Designers: Beginner's Guide

Sass 和 jQuery 是接下来的逻辑步骤。Sass 将 CSS 编写提升到了一个新的水*,而 jQuery 将为您的网站添加功能和更深入的交互能力。更不用说它将使您成为一个全面的前端开发人员。在下一节中,我将通过总结我们讨论过的所有内容,并指出一些可以获取更多信息的好资源来结束。

结论和链接

感谢阅读《精通 CSS》。我真的很享受整理这本书。我们涵盖了很多内容,所以我将对我们学到的东西进行总结,并指引你获取更多关于这些主题的信息的方向。

盒模型和块级与内联元素

我们从回顾基础知识开始这本书,比如盒模型,以及块级和内联元素之间的区别。学习更多关于这两个重要的基础知识的好地方是 Sitepoint 的 A 到 Z CSS 视频。关于块级与内联元素:www.sitepoint.com/atoz-css-screencast-display/,关于盒模型:www.sitepoint.com/atoz-css-screencast-box-model/。在这里,你可以观看一些非常有帮助的盒模型和显示视频。

浮动

我们还讨论了很多关于浮动以及如何使用它们来创建多列布局,就像我们在主页上做的那样:

我们讨论了浮动带来的问题,比如塌陷,以及其他内容围绕浮动流动。我们还讨论了不同的方法,比如使用clear-fix,来避免这些问题。要了解更多关于浮动的知识,我将直接指向 Sitepoint 的 A 到 Z CSS,以及短短六分钟的视频(www.sitepoint.com/atoz-css-screencast-float-and-clear/),它涵盖了浮动的基础知识以及如何适应它们的怪癖。

模块化 CSS

接下来,你学会了如何创建模块化的 CSS。我们不想为网站的一个部分设置样式,然后如果我们想为网站的另一个类似的部分设置样式,就重新设置所有这些样式。我们希望能够通过采用模块化技术来重用我们已经创建的 CSS。当我们使用模块化类构建我们的按钮时,我强调了这一点。要了解更多关于模块化 CSS 的知识,你可以在SMACSS可扩展和模块化的 CSS 架构)上找到更多信息;请参阅smacss.com网站。

CSS3

在这一点上,我们最终使用了大量的 CSS3 来制作我们的按钮。我们在整个网站上使用了很多悬停效果。在电影页面上,我们为其中一部电影的图片添加了一个悬停效果。如果你想了解更多关于 CSS3 的知识,Packt 图书馆中有一本很棒的书,名为《使用 CSS3 设计下一代 Web 项目》(Designing Next Generation Web Projects with CSS3),作者是 Sandro Paganotti。

另外,你可能想看看丹·塞德霍姆(Dan Cederholm)的《网页设计的 CSS3》,第二版,可以通过abookapart.com获取。

创建导航

我们继续构建了一个固定在顶部的导航,内容在其下滚动。它有一个漂亮的下拉菜单,使用 CSS 动画向下精美地展开:

我们还让我们的鲨鱼在浏览器刷新时动起来;我们让它看起来像在游泳,这非常有趣。我为我在 CSS 动画上写的一篇文章感到自豪:www.richfinelli.com/css-animations-arent-that-tough/。在这篇文章中,我详细介绍了所有的动画属性,并逐渐进展到创建一个相当复杂的动画:

我发现自己经常参考Mozilla 开发者网络MDN)网站,快速查阅动画属性。我认为 MDN 是一个非常可靠和深入的网络资源。

使网站响应式

我们在使我们的网站响应式方面做得很好,特别是当我们完全将我们的菜单转换成小屏幕以适应移动设备时:

在我看来,学习更多关于响应式网页设计的最佳地方就是从发明它的人那里学习-Ethan Marcotte。看看那本开创性的书- Responsive Web Design. 该书的第二版于 2014 年底发布。

网络字体

在第七章中,Web Fonts,我们谈到了网络字体和图标字体。我们了解到一个好的字体确实可以让网站看起来很棒。回到abookapart.com网站,有一本非常好的书,你可以用来学习如何设置和配对字体。它叫做On Web Typography,作者是Jason Santa Maria

HiDPI 设备

最后,在第八章中,Workflow for HiDPI Devices,我们通过学习如何处理图像,使它们在 iPad Retina 等双倍密度显示屏上看起来清晰,使我们的网站“retina ready”。我们研究了许多应对 Retina 的方法。在网页开发中,我最激动的事情之一就是 SVG。它真的解决了一些 Retina 的明显问题。CSS 技巧的 Chris Coyier(css-tricks.com)写了一些关于 SVG 以及如何使用它的很棒的文章,包括一篇标题为- Using SVG 的文章。

此外,关于srcset属性的更多信息,我写了两篇文章。一篇是关于 W 描述符和sizes属性的(www.richfinelli.com/srcset-part-2/),另一篇是关于 X 描述符的(www.richfinelli.com/srcset-part-1/)。

Flexbox

Flexbox 太有趣了!我们将基于浮动的 3 列布局转换为基于 flexbox 的布局,并使用 flexbox 构建了一个新的部分,我们的产品列表。关于 flexbox 的更多信息,我建议查看 Wes Bos 的视频课程,What the Flexbox! at flexbox.io,或者快速全面地参考所有 flexbox 属性,请查看CSS 技巧的 A Complete Guide to Flexbox,网址为css-tricks.com/snippets/css/a-guide-to-flexbox/

最后的建议:音频播客非常棒

如果你和我一样,渴望学习并且要长时间开车上班,音频播客可以是一个很好的资源。我最喜欢的前端开发播客是 Shoptalk (shoptalkshow.com/)和 Syntax (syntax.fm/)。两者都非常有趣和富有信息。在上班的路上听播客是我保持了解网页开发动态和学习新知识的方式。

总结

最后,我认为我们在这里创建了一个非常棒的小网站,学到了很多关于 CSS 和网页开发的知识。再次感谢阅读。我真的很享受把它放在一起的过程。祝你们成功,并希望你们继续磨练你们的 CSS 技能。

posted @ 2024-05-24 11:14  绝不原创的飞龙  阅读(18)  评论(0编辑  收藏  举报