HTML5 和 VS2015 高级教程(二)
四、级联样式表
在第二章第一章和第三章第三章中,我向你展示了一些新的 HTML 元素以及如何在 ASP.NET 应用中使用它们。HTML5 的第二个主要方面包括样式表的改进。正如我在第一章中解释的,CSS3 推荐标准被分解成 50 多个模块,其中大部分仍在草案中(在撰写本文时)。然而,大多数浏览器已经提供了相当多的新功能。
在这一章中,我将演示许多更有用的特性。我将从解释创建样式表的基础开始。如果你有一些 CSS 的经验,这可能看起来像是复习,但其中一些是 CSS3 的新内容,尤其是选择器,它在 CSS3 中得到了显著的改进。然后,您将使用一些新的结构元素创建一个单独的 web 页面,比如nav、aside和footer。页面内容完成后,我将解释一些你可以用 CSS 做的有趣的事情。
查看样式语法
样式表由一组规则组成。每个规则由一个选择器和一个或多个声明组成,选择器指示规则适用于哪些元素。每个声明都包含一个属性值对。使用以下语法指定规则:
<selector> {<property:value>; <property:value>; ... }
例如,如果您希望所有段落标签使用绿色 12px 字体,则规则如下所示:
p {color:green; font-size:12px;}
与 HTML 一样,样式表中的空白会被忽略,因此这条规则也可以写成如下形式:
p
{
color:green;
font-size:12px;
}
我将在本章的其余部分使用这种格式,因为我认为它更容易阅读。
使用选择器
从文档中选择元素有很多不同的方法,CSS3 规范几乎是这个列表的两倍。我将概述可用的选择器。其中许多将在本章的后面演示。
元素选择器
我刚刚展示的第一个是元素选择器。要使用它,只需指定元素类型,如p、h1、input、ol、div等等。HTML5 引入了大量新标签,您可以在应用样式时加以利用。这些特定于上下文的元素,如article、footer和nav,更清楚地传达了它们的目的,因此更有可能将一致的格式应用于所有页面。这些新元素类型如下:
article:内容的独立部分,例如博客条目aside:内容通常放在页面的一边;通常用于相关信息details:用于根据用户输入隐藏或显示的可扩展内容figcaption:与figure一起使用,将标题与图像相关联figure:用于包装嵌入内容,如图像或图形footer:页面或章节页脚header:页面或章节标题hgroup:用于对h1、h2等表头元素进行分组nav:用于包含导航链接output:包含输出,例如用户操作的结果section:用于将内容组织成逻辑部分summary:通常与一个或多个details元素结合使用
使用组合子
如果要将相同的声明应用于多个元素类型,可以按如下方式对它们进行分组:
p, h1, h2
{
color:green;
font-size:12px;
}
逗号(,)字符用作逻辑或运算,例如,“类型为p或h1或h2的所有元素”。这只是选择器组合子的一个特例。您还可以组合选择器来指定某些元素层次结构。通过将元素与下列运算符之一组合,可以创建更复杂的选择器:
,(例如p, h1):选择所有p元素和所有h1元素。- space(例如,
header p):当第二个元素位于第一个元素内部时,选择第二个元素。例如,如果您想要一个header元素中的所有p元素,使用header p。header元素不一定是直接的父元素,只是在节点的父元素中的某个地方。 *(例如header*p):当第二个元素是第一个元素的孙元素或后续元素时,选择第二个元素。>(例如header>p):当第一个元素是直接父元素时,选择第二个元素。header>p选择器返回其父元素(直接)是header元素的所有p元素。+(例如header+p):当第一个元素是前面的兄弟元素时,选择第二个元素。∼(例如p∼header):在第一个元素之后选择第二个元素(不一定是立即)。
为了说明最后两个,如果您的文档如下所示,h1+p选择器不会返回任何元素,但是h2+p和h1∼p都将返回p元素:
<h1>Some header</h1>
<h2>Some sub-header</h2>
<p>Some text</p>
类别和 ID 选择器
类选择器允许您选择具有特定class属性的元素。因此,class 属性通常被称为 CSS 类。类选择器是通过在类名前面加一个点(.)像这样:
.featured
{
background-color:yellow;
}
这将为所有具有class="featured"属性的元素应用背景色。类选择器查找与选择器值匹配的整个单词。像class="the featured article"一样,一个元素在class属性中可以有多个单词,并且.featured选择器将返回它。
Caution
在 HTML 文档中,class属性是一个字符串,它可以有你想要给它的任何值。但是,为了能够在类选择器中使用它,它不能有任何空白或其他与 CSS 语法不兼容的字符。例如,您不能在类选择器中选择整个class="featured content"。如果你真的想要一个特色内容的类,使用featured_content或featuredContent。然而,你不能用一个类选择器只选择featured。相反,您需要使用一个属性选择器,我将在后面演示。
ID 选择器的工作方式类似于类选择器,只是它使用了id属性而不是class,并且在它前面加上了一个哈希符号(#,如下所示:
#Submit
{
color:blue;
}
ID 选择器根据元素的唯一 ID 来指定单个元素,因此,根据定义,该样式不会被重用。最好是基于元素或类来定义样式,这样相似的元素可以用相同的方式进行样式化。ID 选择器应该尽量少用,只在不需要重用样式的特殊情况下使用。
使用属性选择器
属性选择器为您提供了很大的灵活性,允许您根据元素的任何属性来选择元素。这些被指定为[attribute=value],如下所示:
[class="book"]
{
background-color:yellow;
}
这在功能上等同于使用.book类选择器;但是,属性选择器允许您仅使用属性值的一部分进行选择。为此,请在等号(=)前添加以下内容之一:
∼(例如[class∼="book"]):属性值必须包含选择器值指示的单词(例如class="somebooktitles")。这正是类选择器的工作方式。- |(例如,
[class|="book"]):属性值必须以匹配选择器值的单词开头(例如,class="booktitles") ^=(例如[class^="book"]):属性值必须以选择器值开头(例如class="books")$(例如[class$="book"]):属性值必须以选择器值结尾(例如class="checkbook")*(例如[class*="book"]):属性值必须包含选择器值(例如class="overbooked")
您可以指定不带值的属性,这将返回具有该属性的所有元素。一个很好的例子是[href]选择器,它将选择所有具有href属性的元素,不管其值如何。您还可以在属性选择器之前包含一个元素选择器,以进一步限制选定的元素。例如,img[src^="https"]将返回所有src属性以https开头的img元素。
伪类选择器
相当多的选择器基于元素的动态属性。例如,考虑一个超链接。如果链接引用的页面已经显示,链接通常以不同的颜色显示。这是通过使用 CSS 规则实现的,CSS 规则使用如下的visited属性:
a:visited
{
color: purple;
}
这将改变设置了visited标志的所有a元素的颜色。这些选择器中有几个已经存在一段时间了,但是 CSS3 定义了相当多的新选择器。以下是完整的列表:
:active:选择活动链接:checked:选择选中的元素(适用于复选框):disabled:选择当前禁用的元素(通常用于输入元素):empty:选择没有子元素的元素(不选择包含文本的元素):enabled:选择启用的元素(通常用于输入元素):first-child:选择其直接父元素的第一个子元素<tag>:first-of-type:选择其父元素中第一个指定类型的元素:focus:选择当前有焦点的元素:hover:选择鼠标当前悬停的元素:in-range:选择值在指定范围内的输入元素:invalid:选择没有有效值的输入元素:lang(value):选择具有以指定值开始的lang属性的元素:last-child:选择作为其父元素中最后一个子元素的元素:link:选择所有未访问的链接<tag>:last-of-type:选择其父元素中指定类型的最后一个元素:nth-child(n):选择其父元素中的第 n 个子元素:nth-last-child(n):选择其父元素中的第 n 个子元素,反向计数<tag>:nth-last-of-type(n):在父级中选择指定类型的第 n 个子级,反向计数<tag>:nth-of-type(n):选择其父级中指定类型的第 n 个子级:only-child:选择其父元素的唯一子元素<tag>:only-of-type:选择其父元素中指定类型的唯一兄弟元素:optional:选择不需要的输入元素(即没有required属性):read-only:选择具有readonly属性的输入元素:read-write:选择没有readonly属性的输入元素:required:选择具有required属性的输入元素:root:选择文档的根元素:target:选择具有目标属性的元素,其中目标是活动元素:valid:选择具有有效值的输入元素:visited:选择所有访问过的链接
nth-child(n)选择器计算父元素的所有子元素,而nth-of-type(n)只计算指定类型的子元素。这里的区别是微妙但重要的。对于only-child和only-of-type选择器也是如此。
Caution
有四个伪类可以与锚(a)元素一起使用(:link、:visited、:hover和:active)。如果使用多个,它们应该在样式规则中按此顺序出现。例如,如果使用了:link和:visited,则:hover必须在它们之后。同样,:active必须跟在:hover后面。
这些伪元素可用于返回选定元素的一部分:
:first-letter:选择每个选中元素的第一个字符:first-line:选择每个选中元素的第一行:selection:返回用户选择的元素部分
您可以将:before或:after限定符添加到选择器中,以便在文档中所选元素之前或之后插入内容。使用content:关键字来指定内容并包含任何所需的样式命令(样式仅适用于插入的内容)。比如要加上“重要!”在紧跟在header标签之后的每个p标签之前,使用以下规则:
header+p:before
{
content:"Important! ";
font-weight:bold;
color:red;
}
您还可以在选择器前面加上:not来返回所有未选中的元素。例如,:not(header+p)选择除了紧跟在header标签后面的p标签之外的所有元素。
理解工会
您还可以通过用逗号分隔复杂的选择器,将它们组合成逻辑 OR 关系。例如,我在本章前面展示的p, h1, h2选择器就是一个联合的例子。它将返回满足任何包含的选择器的所有元素。每个选择器可以是任何更复杂的类型。这也是一个有效的选择器:
header+p, .book, a:visited
它将返回所有元素,要么是紧跟在header元素之后的p元素,要么是带有图书class的元素,要么是已访问的a元素。
Tip
有关可用选择器的确切列表,请参见位于 www.w3schools.com/cssref/css_selectors.asp 的文章。
使用 CSS 属性
提供了所有这些选择器,以便您可以指定想要应用所需样式属性的适当元素。这才是 CSS 真正的肉。可用的 CSS 属性有数百个,我无法在此一一描述。在本章的剩余部分,我将演示许多更新的、更有用的特性。你会在 www.w3schools.com/cssref/default.asp 找到所有 CSS 属性的很好的参考。
使用供应商前缀
哦,生活在边缘的乐趣!与 HTML5 的其他领域一样,浏览器供应商将对 CSS 规范提供不同的支持。然而,在许多情况下,这些供应商在新属性成为官方推荐的一部分之前就实现了它们。事实上,CSS3 规范中包含的大部分内容已经可以从一个或多个浏览器中获得。
当浏览器供应商添加不属于 CSS3 建议的新功能时,该属性会被赋予一个特定于供应商的前缀,以指示这是一个非标准功能。如果这成为建议的一部分,前缀最终会被删除。为了利用一些较新的属性,您可能需要使用特定于供应商的属性,并且因为您希望您的页面在所有供应商上工作,所以您需要添加所有这些属性。例如,要指定边框半径,除了标准的border-radius属性,您可能还需要设置所有供应商特定的属性,如下所示:
header
{
-moz-border-radius: 25px;
-webkit-border-radius: 25px;
-ms-border-radius: 25px;
border-radius: 25px;
}
表 4-1 列出了最常见的前缀。还有其他的,但是这个表涵盖了绝大多数的浏览器。
表 4-1。
Vendor Prefixes
| 前缀 | 浏览器供应商 | | --- | --- | | `-moz-` | 火狐浏览器 | | `-webkit-` | Chrome、Safari、Opera | | `-ms-` | 微软公司出品的 web 浏览器 |您不能盲目地假设所有带有供应商前缀的属性都与标准属性同名,只是添加了前缀,尽管大多数情况下确实如此。这里有一篇很好的文章,列出了许多特定于供应商的属性: http://peter.sh/experiments/vendor-prefixed-css-property-overview 。遗憾的是,此页面已有一段时间没有更新,可能已经过时。如果您发现某个标准属性在特定的浏览器中不起作用,您可能需要做一些研究,看看他们的开发人员的网站上是否有前缀属性。例如,使用 Webkit 扩展的链接: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Webkit_Extensions 。
Caution
您应该始终将标准属性列在最后,这样它将覆盖特定于供应商的版本。有些浏览器会支持这两者,虽然大多数时候实现是相同的,但有时特定于供应商的版本表现不同。
了解盒子模型
文档中的每个元素都占用一定的空间,这取决于该元素的内容。此外,填充和边距等因素也会对此产生影响。填充是内容和元素边框之间的空间。边距是边框和相邻元素之间的空间。如图 4-1 所示。
图 4-1。
The box model
您可以使用margin声明来指定边距,并以像素或页面大小的百分比来指定值。您可以使用margin-top、margin-right、margin-bottom和margin-left声明单独指定上边距、右边距、下边距和左边距,或者使用margin声明指定所有四个值(按此顺序—上边距、右边距、下边距和左边距)。您也可以使用带有单个值的margin声明,这会将所有四个边距都设置为该值。如果您传递两个值,第一个将设置上边距和下边距,第二个将指定左边距和右边距。使用padding声明以同样的方式设置填充。
确定使用的空间时,记得包括边框宽度。例如,如果填充设置为 10px,边距设置为 5px,边框宽度设置为 3px,则使用的空间(除实际元素内容之外)将为(2 * 10) + (2 * 5) + (2 * 3) = 36px。
应用样式规则
样式是从各种来源以几种不同的方式指定的,顾名思义,它们是级联的,或者说是继承的。理解这是如何工作的很重要,特别是当有冲突的声明时。
包括样式规格
样式表有三个来源:
- 作者:这些是 web 开发人员创建的样式表,也是您在提到样式表时通常会想到的。
- 用户:用户也可以创建一个样式来控制网页的显示方式。
- 用户代理:用户代理(web 浏览器)将有一个默认的样式表。例如,如果您创建一个没有样式规则的文档,浏览器将使用默认字体系列和大小显示内容。这些实际上是在特定于浏览器的样式表中定义的。
对于作者样式,这是您可以控制的唯一来源,有三种方法可以在 HTML 文档中包含样式规则。
- 内联:样式是使用
style属性直接在元素中设置的,比如:<p style="color:red">This is red text</p>。当然,使用这种方法,不需要使用选择器,因为样式只适用于当前元素(以及所有子元素)。 - 内部:可以使用
style元素将样式规则包含在实际的 HTML 文档中。这通常放在head标签中,并应用于整个文档。以这种方式定义的样式将需要一个选择器来指示应该在哪些元素上使用该样式。这种方法有时被称为嵌入式样式。 - 外部:应用样式最常见的方式是将所有的样式规则放在一个扩展名为
.css的单独文件中。样式规则的格式就像内部样式一样。使用外部样式表的明显好处是同一组规则可以应用于多个页面。每个页面都引用了这个带有link元素的样式表,比如:<link rel="stylesheet" type="text/css" href="MyStyleSheet.css"
级联规则
当呈现页面时,浏览器必须处理来自所有这些来源的样式,以确定每个元素的适当样式。当存在冲突规则时,作者样式表优先于用户样式表,用户样式表优先于用户代理样式(浏览器默认)。可以使用我前面解释的三种方法(内联、内部和外部)来指定作者样式。在作者样式中,内联声明优先于内部声明和外部样式表。如果一个页面使用了一个内部的style元素,同时也使用了link元素来包含一个外部样式表,那么只要内部声明在link元素之后,它就会覆盖外部样式表中的冲突规则。
Caution
如果外部样式表在style标签后被引用,它将优先于内部样式。如果您既有外部样式表又有内部的style元素,那么您应该首先引用外部的样式表,这样优先级规则才能按预期工作。
此外,考虑到即使在单个样式表中也可能存在冲突声明。例如,样式表可能包括以下内容:
p
{
color: black;
}
header p
{
color: red;
}
一个header元素内的一个p元素被两个规则选中,那么使用哪个呢?在这种情况下,特殊性规则适用,它规定使用更具体的选择器,即header p选择器。在所有可用的选择器中,确定哪一个更具体并不像您想象的那样简单。ID 选择器被认为比类或属性选择器更具体,而类或属性选择器又比元素选择器更具体。如果只有元素选择器,那么元素最多的规则优先,所以包含两个元素的header p比只有p更具体。
最后,如果同一个选择器用不同的声明在同一个样式表中使用了两次会怎么样?假设p { color:black; }出现在样式表中,稍后p { color:green }出现。在这种情况下,最后出现的规则优先,因此您会看到绿色文本。
使用重要关键字
一种“王牌”是important关键字。如果在样式规则中使用,这将超越所有其他规则。您可以像这样添加important关键字:
p
{
color: red;
!important;
}
如果两个冲突的规则都有important关键字,那么优先级是基于我已经提到的规则确定的。然而,有一个显著的区别。通常,作者样式表中的规则会覆盖用户样式表中的规则。如果他们有important关键字,这是相反的;用户样式表将覆盖作者规则。乍一看,这似乎很奇怪,但它有一个重要的应用。这允许用户覆盖某些属性的作者样式。例如,有视觉障碍的人可能需要增加字体大小。标签important将确保这个样式不会被覆盖。
Caution
您可能会尝试使用important关键字来快速修复并覆盖级联样式规则。有了我刚刚描述的所有优先规则,您不应该需要这样做。我建议将此作为最后的手段。过度使用important关键字会使你的样式表难以维护。
创建网页
在这一章的剩余部分,我将向你展示如何构建一个单独的网页来展示 CSS 的许多新特性。为了多样化,我将使用 WebMatrix 应用而不是 Visual Studio 来创建单个网页。样式规则将使用内部的style元素,所以一切都可以放在一个文件中。少量的 JavaScript 也将包含在单个文件中。
我将在这个项目中使用 Chrome 浏览器,因为它支持我将演示的大多数 CSS 功能。在撰写本文时,其他浏览器还不支持这些特性中的一个或多个。当你读到这篇文章时,其他浏览器可能也支持这些。
Note
我在第一章中解释了如何安装 WebMatrix 应用。这是微软提供的免费下载。如果您愿意,也可以使用 Visual Studio 和 MVC 项目模板来实现网站。使用Index.cshtml文件遵循本章剩余部分的说明,你可以在Views\Home文件夹中找到这个文件,而不是Default.cshtml。也可以从 www.apress.com 下载源代码中包含的完整的 Visual Studio 项目。
规划页面布局
在创建一个新的网页之前,最好先勾画出基本的网页结构。这将帮助您可视化整体布局,并了解元素是如何嵌套在一起的。
你将在本章中开发的页面将在顶部使用header和nav元素,在底部使用footer元素。中间的主要区域将使用一个div元素,并有两个并排的区域,每个区域都有一系列的article标签。较大的区域将被另一个div元素包围,并提供组织成文章的主要内容。右边较小的区域将使用一个aside元素并包含一个section元素。这将包含一系列呈现相关信息的article元素。图 4-2 展示了页面布局。
图 4-2。
Planning the page layout Note
此图显示了每个元素之间的空间,以便于理解。在实际的 web 页面中,在大多数情况下,这个空间是通过将padding属性设置为 0 来移除的。
创建 Web 项目
规划好内容后,您就可以开始构建网页了。首先,您将使用 WebMatrix 创建一个项目。然后,您将进入基本的页面结构,并向每个元素添加内容。稍后,我将展示如何实现样式规则。
启动 WebMatrix 应用,点击新建图标,然后点击模板图库按钮,如图 4-3 所示。
图 4-3。
Launching the WebMatrix application Tip
为了便于将来参考,App Gallery 按钮将显示一个相当大的预构建 web 应用列表,您可以下载并使用它来构建您的 web 项目。这包括 WordPress、Joomla 和 Drupal 等软件包。
有几个模板可供选择。例如,起始站点模板将创建一个 ASP.NET MVC 项目。对于本章,您将使用空站点模板。选择此项,进入章节 4 作为站点名称,如图 4-4 所示。单击“确定”按钮创建项目。
图 4-4。
Selecting the Empty Project template
创建项目后,单击导航窗格中的“文件”按钮。为您创建的文件和文件夹应该如图 4-5 所示
图 4-5。
The initial files and folders
应该有一个名为Default.cshtml的网页。双击导航页面中的文件名将其打开。最初的内容如下所示:
@{
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Site's Title</title>
<link href="∼/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body>
</body>
</html>
定义页面结构
我发现在添加内容之前先输入结构元素会有所帮助。这将让你有机会清楚地看到结构,不被实际内容所干扰。打开Default.cshtml文件,输入清单 4-1 中所示的元素。
Listing 4-1. Entering the Page Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>``Chapter 4
</head>
<body>
<header class="intro">
</header>
<nav>
</nav>
<div id="contentArea">
<div id="mainContent">
<section class="rounded">
<header>
</header>
</section>
<section>
<article class="featuredContent">
<a id="feature"></a>
<header>
</header>
<div>
</div>
</article>
<article class="otherContent">
<a id="other"></a>
<header>
</header>
<div>
</div>
</article>
<article class="otherContent">
<a id="another"></a>
<header>
</header>
<div>
</div>
</article>
</section>
</div>
<aside id="sidebar">
<section id="titles">
<article class="book">
<header>
</header>
</article>
<article class="book">
<header>
</header>
</article>
<article class="book">
<header>
</header>
</article>
<article class="book">
<header>
</header>
</article>
<article class="book">
<header>
</header>
</article>
</section>
</aside>
</div>
<footer>
</footer>
</body>
</html>
这只是一个基本的 HTML 结构,你可以从图 4-2 中的图表推断出来。article元素被赋予了class属性,因为您将使用它进行样式化。我将id属性赋给了一些顶级元素。我还为每个主要内容文章添加了一个锚元素(<a id="feature"></a>)。您将在nav元素中设置这些内容的导航链接。
添加内容
内容没什么特别的。它包含大量文本(主要是 Lorem ipsum)、一些图片和一些链接。
在导航窗格中,右键单击 Chapter 4 项目,然后单击新建文件夹链接。输入文件夹名称的图像。可下载的源代码中包含一个Images.zip文件。将图片从这个文件复制到项目中新的Images文件夹。
我建议下载内容,而不是手动输入。源代码中有一个Default_content.cshtml文件。用该文件中的代码替换您当前的实现。它只包含该页面的内容,没有定义任何样式。如果要手动输入内容,可以在附录 a 中找到。
Note
我想指出内容中的一个小细节。footer元素使用 HTML5 中添加的新的time元素。开始和结束标签之间的文本(2015 年 3 月 7 日)被显示,但是datetime属性包含一个机器可读的格式,可以被浏览器、搜索引擎或 JavaScript 使用。更多详情请看这篇文章: www.sitepoint.com/html5-time-element-guide 。
添加内容后,单击功能区中的 Run 按钮,查看页面目前的外观。应该类似于图 4-6 。
图 4-6。
The initial page with only default styles
实现样式规则
现在你到了有趣的部分,增加了风格。您可以使用大量的样式属性,我将展示一些对 CSS3 来说新的更有用的技术。其中许多样式已经使用了一段时间,但是在 CSS3 之前,它们的实现更加复杂,通常需要 JavaScript。在指定一些基本的样式规则后,我将向您展示如何使用更多的高级功能,包括以下内容:
- 圆角
- 渐变背景
- 桌子
- 多列
- 方框阴影
- 斑马条纹文本装饰
- 3D 转换
- CSS 动画
添加基本样式
在开始添加新的样式特性之前,您需要定义基本的样式格式。在Default.cshtml文件顶部的head元素中添加一个style元素。然后添加清单 4-2 中所示的规则。同样,如果您愿意,您可以下载Default_styled.cshtml文件并从那里复制代码。
Listing 4-2. Adding the Basic Styles
<style>
/* Basic tag settings */
body
{
margin: 0 auto;
width: 940px;
font: 13px/22px Helvetica, Arial, sans-serif;
background: #f0f0f0;
}
h2
{
font-size: 18px;
line-height: 5px;
padding: 2px 0;
}
h3
{
font-size: 12px;
line-height: 5px;
padding: 2px 0;
}
h1, h2, h3
{
text-align: left;
}
p
{
padding-bottom: 2px;
}
.book
{
padding: 5px;
}
/* Content sections */
.featuredContent
{
background-color: #ffffff;
border: 2px solid #6699cc;
padding: 15px 15px 15px 15px;
}
.otherContent
{
background-color: #c0c0c0;
border: 1px solid #999999;
padding: 15px 15px 15px 15px;
}
aside
{
background-color: #6699cc;
padding: 5px 5px 5px 5px;
}
footer
{
margin-top: 12px;
text-align:center;
background-color: #ddd;
}
footer p
{
padding-top: 10px;
}
/* Navigation Section */
nav
{
left: 0;
background-color: #003366;
}
nav ul
{
margin: 0;
list-style: none;
}
nav ul li
{
float: left;
}
nav ul li a
{
display: block;
margin-right: 20px;
width: 140px;
font-size: 14px;
line-height: 28px;
text-align: center;
padding-bottom: 2px;
text-decoration: none;
color: #cccccc;
}
nav ul li a:hover
{
color: #fff;
}
</style>
我不会说太多,因为这都是很标准的 CSS 东西。它主要使用元素选择器,偶尔使用类选择器。如果你现在预览你的网页,它看起来应该如图 4-7 所示。
图 4-7。
The web page with only basic styling Note
为了简化示例代码,我将仅使用 Chrome 供应商前缀-webkit-,并且仅在当前版本(43)不支持标准属性时使用。我可以这样做,因为我希望这个页面只能在 Chrome 浏览器中运行。通常,您不能做出这种假设,需要包括所有的供应商前缀。
使用圆角
添加圆角用 CSS3 很容易做到;只需定义border-radius属性。你的网页将为aside、nav和footer元素以及rounded类的元素使用圆角。
Note
在第七章中,我将向你展示如何在不支持圆角功能的旧浏览器中实现圆角。读完那一章后,你可能会对浏览器中支持这样的特性有更好的理解。
将清单 4-3 中所示的规则添加到style元素的末尾。
Listing 4-3. Using Rounded Borders
/* Rounded borders */
.rounded
{
border: 1px solid;
border-color:#999999;
border-radius:25px;
padding: 24px;
}
aside
{
border: 1px solid #999999;
border-radius:12px;
}
/* Make the radius half of the height */
nav
{
height: 30px;
border-radius:15px;
}
footer
{
height: 50px;
border-radius:25px;
}
对于nav和footer元素,由于它们是相当短的部分,您将设置半径为高度的一半。这会在两端形成一个半圆。顶部导航部分应该如图 4-8 所示。
图 4-8。
Using rounded borders
使用渐变
使用 CSS3,您可以通过使用linear-gradient函数设置background-image属性来轻松创建渐变。使用此功能,您可以指定开始和结束颜色以及应用渐变的角度。你将在主标题中使用渐变,它有intro类。
在style元素的末尾添加以下规则:
/* Gradients */
.intro
{
border: 1px solid #999999;
text-align: left;
padding-left: 15px;
margin-top: 6px;
border-radius: 25px;
background-image: linear-gradient(45deg, #ffffff, #6699cc);
}
这将应用 45 度角的渐变。这也创建了一个圆形的边界。页面顶部现在应该如图 4-9 所示。
图 4-9。
Using a gradient background
创建表格
在标记中使用表格进行格式化通常被认为是不好的做法。这种格式最好在样式表中完成。如果需要不同的格式,可以更新样式。您可能已经注意到,当前 web 页面的主要内容后面有一个aside元素,而不是两个并排的元素。现在您将使用 CSS 设置一个表来纠正这种情况。
将清单 4-4 中所示的规则添加到style元素的末尾。
Listing 4-4. Creating a Table
/* Setup a table for the content and sidebar */
#contentArea
{
display: table;
}
#mainContent
{
display: table-cell;
padding-right: 2px;
}
aside
{
display: table-cell;
width: 280px;
}
这些规则在顶级元素上设置了display属性。contentArea元素被设置为table,mainContent和aside元素被设置为table-cell。然后,这些元素被呈现为整个内容元素中的单元格。为了完成对齐,mainContent上的填充设置为 2px,aside元素的宽度设置为 280px。使用剩余空间自动计算mainContent的宽度。
页面布局现在应该如图 4-10 所示。
图 4-10。
The page layout with the sidebar on the right
添加列布局
CSS3 的另一个新特性是能够像在报纸或杂志上看到的那样将内容格式化成列。这是使用column-count属性完成的。您还应该指定定义列之间垂直间距的属性column-gap。
在style元素的末尾添加以下规则:
/* Setup multiple columns for the articles */
.otherContent
{
text-align:justify;
padding:6px;
-webkit-column-count: 2;
column-count: 2;
-webkit-column-gap: 20px;
column-gap: 20px;
}
文章现在应该被格式化为两列,如图 4-11 所示。
图 4-11。
Using two columns
添加方框阴影
图像可能看起来有点粗糙,添加阴影可以柔化外观,使页面在视觉上更具吸引力。使用box-shadow属性很容易添加阴影,该属性采用以下值:
- 水平位置:水平阴影的位置。如果是负数,阴影在左边。
- 垂直位置:垂直阴影的位置。如果为负,阴影在顶部。
- 模糊:阴影后模糊区域的大小。
- 扩散:阴影的宽度。
- 颜色:阴影的颜色。
- 插入:使图像看起来比周围区域低,使阴影位于图像上而不是图像外。
这些值在逗号分隔的列表中指定。它需要两到四个位置/大小值、一个可选的颜色属性和可选的inset关键字。只需要前两个,分别是水平和垂直位置。如果未指定,模糊和扩散值将默认为零。将以下规则添加到style元素的末尾:
/* Add the box shadow */
article img
{
margin: 10px 0;
box-shadow: 3px 3px 12px #222;
}
.book img
{
margin: 10px 0;
display: block;
box-shadow: 2px 2px 5px #444;
margin-left: auto;
margin-right: auto;
}
aside
{
box-shadow: 3px 3px 3px #aaaaaa;
}
.book img规则还包括margin-left和margin-right属性,它们都被设置为auto。这导致图像水平居中。图 4-12 和 4-13 显示了特色内容和侧边栏项目中图像的特写。请注意,第一个图像比侧边栏图像有更大的模糊区域。
图 4-13。
The shadow on the sidebar images
图 4-12。
The shadow of the phone in booth image the featured content section
使用斑马条纹
一种已经使用了很长时间的样式方法是在有项目列表时替换背景,这有时被称为斑马条纹。这可以追溯到旧的蓝条纸用于进入会计期刊。交替的背景使得区分每个项目变得更加容易。在 CSS3 之前,这是通过 JavaScript 来实现的,它会以编程的方式改变每个其他元素的背景。
CSS3 引入了nth-child选择器,它非常适合这个应用,因为它每隔 n 个元素返回一次。在将n设置为 2 的情况下使用它将返回所有其他元素。将以下代码添加到style元素的末尾:
/* Stripe the title list */
#titles article:nth-child(2n+1)
{
background: #c0c0c0;
border: 1px solid #6699cc;
border-radius: 10px;
}
#titles article:nth-child(2n+0)
{
background: #6699cc;
border: 1px solid #c0c0c0;
border-radius: 10px;
}
这个规则使用一个复杂的选择器#titles article:nth-child(2n+1),它首先选择# titles元素。这是一个包含书名的section元素。每个书名都在一个单独的article元素中。然后article:nth-child选择器返回#titles元素中的每第 n 个article元素。然而,2n+1 参数可能看起来有点奇怪。要获取所有其他元素,需要指定 2n 作为参数,这将返回奇数项(第一项、第三项、第五项等等)。通过使用 2n+1,列表被偏移 1,所以你将得到偶数项(第二、第四、第六等等)。因此,第一个规则格式化偶数项,第二个规则使用 2n+0 格式化奇数项。你可以简单地用2n代替2n+0,因为它们是等价的,但是我喜欢用2n+0来保持一致性。这两种样式规则的唯一区别是背景和边框颜色。图 4-14 为效果图。
图 4-14。
Applying the zebra striping to the sidebar
添加文本装饰
文本装饰允许您用各种效果来修饰文本。已经定义了三种类型的修饰:线条(如下划线和删除线)、强调标记和阴影。官方推荐定义了这个功能,尽管浏览器的实现有点粗略和不一致。我将首先解释该标准是如何定义的,然后向您展示使其工作所需的变通方法。
Note
文字装饰细节在 WC3 推荐中有很好的解释,可以在 www.w3.org/TR/css-text-decor-3 访问。
线条装饰
线条装饰由三个属性的组合定义:
text-decoration-line:指定该行应该在文本上方(overline)、文本下方(underline)还是穿过文本中间(line-through)text-decoration-style:定义线条的样式,如solid、dashed、dotted、double或wavytext-decoration-color:表示线条的颜色
该建议还允许使用text-decoration快捷方式,您可以在一个属性中指定所有三个属性。例如,您可以用它来定义红色波浪下划线:
text-decoration: underline wavy red;
如果样式和颜色从快捷方式中省略,这是向后兼容 CSS 级别 1 和 2。并且,在撰写本文时,这是大多数浏览器所支持的全部内容。因此,您可以添加下划线、上划线或删除线,但不能调整其样式或颜色。要进行尝试,请在style元素的末尾添加以下内容:
h2
{
text-decoration: underline overline line-through;
}
这将显示文本中的所有三行。保存这些更改并刷新浏览器窗口,标题文本应该如图 4-15 所示。
图 4-15。
Adding line decorations
Firefox 支持一些线条修饰风格,但是你需要使用他们的供应商前缀。为了演示如何做到这一点,用以下内容替换您刚刚添加的h2选择器:
h2
{
text-decoration: underline;
-moz-text-decoration-line: underline;
-moz-text-decoration-style: wavy;
-moz-text-decoration-color: red;
text-decoration-line: underline;
text-decoration-style: wavy;
text-decoration-color: red;
}
第一行使用大多数浏览器都支持的向后兼容属性。这将使用与文本相同的颜色定义实心下划线。接下来的三行使用 Firefox 供应商前缀定义了相同的红色波浪线,所有其他浏览器都将忽略这一点。最后三行使用 CSS3 标准定义红色波浪线。如果您使用大多数浏览器显示这个页面,您将会看到一条像前面一样的黑色实线。如果你使用 Firefox,页面会如图 4-16 所示。
图 4-16。
Displaying line decorations in Firefox
然而,随着时间的推移,随着浏览器采用 CSS3 标准,波浪下划线将取代实线。这是一个很好的例子,说明了如何设计您的页面来使用当前可用的功能,并利用新兴的功能。
强调标记
强调标记的使用类似于添加线条;他们添加符号或标记来强调指定的文本。在撰写本文时,没有一个桌面浏览器支持这个特性。使用三种属性的组合来定义强调标记。
text-emphasis-style:指定要使用的符号类型,如dot、triangle、double-circle或sesame。text-emphasis-color:表示用于强调标记的颜色。text-emphasis-position:定义标记相对于文本的位置;可能的值有over、under、left和right。可以包含over right等组合。
也允许使用快捷方式定义,因此您可以使用如下所示的单个属性来指定它:
text-emphasis: dot red;
文本阴影
文字阴影的定义类似于方框阴影,我之前解释过。与其他一些文本修饰功能不同,所有主流浏览器都支持文本阴影。
文本阴影由包含以下参数的单个属性定义:
- 水平偏移
- 垂直偏移
- 模糊半径
- 颜色
偏移值可以是负值。负垂直偏移会将阴影置于文本上方,负水平阴影会将阴影置于左侧。如果省略 color 参数,阴影将与文本颜色相同。
Caution
文本阴影的模糊半径应该很小。除非你有一个非常大的字体,否则使用大于 1px 或 2px 的值会使文本不可读。可以指定 0px,这样会导致阴影一点都不模糊。
要演示文本阴影,请在style元素的末尾添加以下内容:
h3:first-letter
{
text-shadow: 2px -5px 1px blue;
}
保存更改并刷新浏览器。标题文本应如图 4-17 所示。
图 4-17。
Adding a text shadow Tip
这个例子还演示了first-letter伪类。这将从每个h3元素中选择第一个字母。
使用 3D 变换
添加 3D 变换可以为您的网页增添一些活力。我将演示一个相当简单的应用,您可以在其中翻转三维电话亭图像。您还将添加一些 JavaScript 来制作旋转动画。
要格式化 3D 变换,您需要指定几个属性。首先,您将在包含图像的div上设置perspective属性。这将建立用于确定 3D 效果渲染方式的消失点。然后,您将在图像本身上设置preserve-3d属性,它告诉浏览器在旋转图像时保持 3D 透视图。为此,将以下内容添加到style部分的末尾:
/* Transforms */
.rotateContainer
{
-webkit-perspective: 360;
perspective: 360px;
}
.rotate
{
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
}
Note
标准的perspective属性需要单位(例如,360px),但是 WebMatrix 中的 IntelliSense 不识别这一点,并将这显示为 CSS 验证错误。这个可以忽略。还要注意,以供应商为前缀的属性-webkit-perspective不需要单位。
现在,您将添加一个 JavaScript 函数,该函数将对图像旋转进行动画处理。在head元素中输入粗体代码:
<head>
<meta charset="utf-8" />
<title>``Chapter 4
<script type="text/javascript">
var angle = 0;
var t;
function rotateImage(value) {
document.getElementById("phone").style.transform
= "rotateY(" + value + "deg)";
}
function toggleAnimation() {
if (angle == 0) {
t = setInterval(function () {
rotateImage(angle);
angle += 1;
if (angle >= 360)
angle = 0;
}, 100);
}
else {
clearInterval(t);
angle = 0;
rotateImage(angle);
}
}
</script>
<style>
angle变量存储图像的当前旋转角度,t变量是对间隔定时器的引用。rotateImage()函数只是为图像元素设置rotateY样式。这相当于将它添加到 CSS 中:
transform: rotate(20deg);
这是在 JavaScript 中完成的,因为每次调用它都会指定一个不同的角度。toggleAnimation()功能将启动或停止动画。为了开始动画,它调用了setInterval()函数,提供了一个每隔 100 毫秒调用一次的匿名函数。匿名函数在当前角度调用rotateImage(),然后递增angle。要取消动画,调用clearInterval()方法,然后图像被设置回初始旋转。
最后,添加以粗体显示的代码,当单击图像时,该代码调用toggleAnimation()函数:
<div id="rotateContainer">
<p>This is really cool...</p>
<img class="rotate" id="phone"
src="img/phonebooth.jpg"
alt="phonebooth"
onclick="toggleAnimation() "/>
<br />
保存这些更改并刷新浏览器窗口。要开始播放动画,请单击图像。您应该看到图像缓慢旋转,如图 4-18 所示。
图 4-18。
The rotated phonebooth—in 3D! Tip
在撰写本文时,Chrome、Opera 和 Safari 有一个有趣的 bug。在检查元素之前,该变换是一个简单的 2D 变换。右键单击image元素并选择检查元素。一旦变换变为 3D,您就可以关闭检查窗口。
添加动画
对于最后一个效果,我将向您展示如何在不使用任何 JavaScript 的情况下创建动画效果。你不能用 CSS 修改图像,因为那被认为是内容,而不是格式。但是,您可以更改背景图像,并利用它来实现动画效果。
aside元素的div定义如下:
<div id="moon"></div>
因为没有定义内容或大小,所以目前这对页面布局没有影响。现在,您将使用 CSS3 中的动画特性来迭代显示月相的各种图像。
在 CSS 中,动画是通过定义一组keyframes实现的。每个框架定义一个或多个 CSS 属性。在这个应用中,您将指定适当的背景图像,但是您也可以轻松地更改颜色、大小或任何其他 CSS 属性。对于每一帧,您还可以指定该帧应该出现的动画持续时间的百分比。您应该始终有一个 0%和 100%的帧,它们指定开始和结束属性。您可以在中间包含任意数量的步骤。在此示例中,有八个图像,因此,为了保持帧间距均匀,帧将以 0%、12%、25%、37%、50%、62%、75%、87%和 100%的比例过渡。
一旦您定义了keyframes,您就可以在您想要制作动画的元素上设置动画属性。您将通过设置animation-name属性来指定keyframes的名称。您还可以使用animation-duration属性设置动画的持续时间(以秒为单位)。将清单 4-5 中所示的代码添加到样式部分的末尾。
Listing 4-5. Defining the Animation Effect
/* Animate the moon phases */
@@-webkit-keyframes moonPhases
{
0% {background-image:url("img/moon1.png");}
12% {background-image:url("img/moon2.png");}
25% {background-image:url("img/moon3.png");}
37% {background-image:url("img/moon4.png");}
50% {background-image:url("img/moon5.png");}
62% {background-image:url("img/moon6.png");}
75% {background-image:url("img/moon7.png");}
87% {background-image:url("img/moon8.png");}
100% {background-image:url("img/moon1.png");}
}
@@keyframes moonPhases
{
0% {background-image:url("img/moon1.png");}
12% {background-image:url("img/moon2.png");}
25% {background-image:url("img/moon3.png");}
37% {background-image:url("img/moon4.png");}
50% {background-image:url("img/moon5.png");}
62% {background-image:url("img/moon6.png");}
75% {background-image:url("img/moon7.png");}
87% {background-image:url("img/moon8.png");}
100% {background-image:url("img/moon1.png");}
}
#moon
{
width:115px;
height:115px;
background-image: url("img/moon1.png");
background-repeat: no-repeat;
-webkit-animation-name:moonPhases;
-webkit-animation-duration:4s;
-webkit-animation-delay:3s;
-webkit-animation-iteration-count:10;
animation-name:moonPhases;
animation-duration:4s;
animation-delay:3s;
animation-iteration-count:10;
}
这段代码将总持续时间设置为 4 秒,因此图像应该每半秒转换一次。它还指定在开始前等待三秒钟,并重复播放动画十次。当你刷新网页时,大约三秒钟后,它会循环显示月相,如图 4-19 所示。
图 4-19。
Animating the moon’s phases Tip
如果您希望动画无限期地继续,请将animation-iteration-count属性设置为infinite。
这里还有另外两个不适用的动画属性,timing-function和direction。如果你正在使用一个简单的动画并且只定义开始和结束值,那么timing-function定义了过渡的速度。例如,如果您正在将一个元素移动到不同的位置,将此设置为linear将会以恒定的速率移动对象。然而,使用默认值ease,过渡将开始缓慢,然后加速,然后在接近结束时减速。还有其他选择,比如ease-in,开始时会很慢,然后在剩余的过渡阶段会加速。direction属性,如果设置为alternate,将在交替迭代中反转过渡。默认值normal,每次都会重放相同的过渡。
摘要
在这一章中,我介绍了很多关于 CSS 的信息,尤其是 CSS3 中的新特性。选择器非常强大,在应用样式时提供了很大的灵活性。在 CSS3 之前,大部分工作都必须通过大量的 JavaScript 函数来完成。我还向您展示了如何使用大量新的结构化 HTML5 元素来规划和构建一个示例 web 页面。附录 B 显示了完整的style元素。
使用 WebMatrix 应用,您创建了一个简单的 web 页面,定义了基本结构,然后填充了内容。使用一些新的 CSS3 特性,您添加了一些重要的样式特性,包括:
- 圆形边框
- 梯度
- 桌子
- 多列
- 阴影
- 斑马条纹
- 文本装饰
- 3D 转换
- 动画
在下一章,我将介绍 HTML5 中一些与脚本相关的新特性。
五、脚本增强
在这一章中,我将展示一些影响 web 开发脚本方面的各种改进。到目前为止,我已经介绍了标记更改和 CSS 增强。脚本是整个 HTML5 的第三个支柱,这个领域受到了极大的关注。本章将解释一些应用广泛的改进。
- 查询选择器
- 网络工作者
- 管理包和版本
包管理实际上不是 HTML5 的一部分,而是通过 Bower 和 Gulp 等开源工具完成的,这些工具已经集成到 Visual Studio 中。
使用查询选择器
在第四章中,我解释了可以用来创建强大样式规则的 CSS 选择器。CSS3 在这方面引入了显著的改进。有了健壮的属性选择器和一些新的伪类,比如你在第四章的中使用的nth-child,选择 DOM 元素就有了相当多的功能。但是更好的是:所有这些功能都可以从 JavaScript 中获得。
HTML5 规范包括两个新函数,querySelector()和querySelectorAll()。querySelector()函数返回单个元素,第一个匹配指定选择器的元素。querySelectorAll()函数返回匹配元素的数组。对于这两个函数,都要传入 CSS 选择器,就像在样式表中一样格式化。所以,一旦你学会了如何使用 CSS 选择器,你就可以把同样的经验应用到 JavaScript 上。
为了尝试这些功能,您将使用在第四章中创建的同一个网页。如果你想使用,第四章项目的最终版本,可以从源代码下载中获得。
使用查询选择器
querySelector()功能可以用来代替getElementById()功能。当然,它比这有用得多,因为您可以传入任何类型的 CSS 选择器。
打开Default.cshtml文件,修改rotateImage()函数,替换getElementById()函数如下:
function rotateImage(value){
document.``querySelector("#phone")
="rotateY(" + value + "deg)";
}
Caution
不要忘记在 ID 前面加上前缀#。因为querySelector()函数可以与任何类型的选择器一起使用,所以您需要散列符号来表示这是一个 ID 选择器。
使用 Firefox 运行网页,并验证 3D 旋转仍然有效。
使用 querySelectorAll
这是一个相当简单的例子,所以现在我将演示一个更复杂的选择器。您将添加一个 JavaScript 函数来改变nav元素中所有内部链接的颜色。可以说,您可以在样式表中这样做,但有时您也需要在代码中这样做。例如,您可能需要根据用户输入以编程方式更改样式。
向Default.cshtml页面中的script元素添加以下函数:
function adjustInternalLinks(){
var links = document.querySelectorAll("nav ul li a[href ^='#']");
for (var i=0; i < links.length; i++){
links[i].style.color = "green";
}
}
CSS 选择器是nav ul li a[href ^='#'],它返回所有具有以#字符开始的href属性的a元素。这被进一步过滤为仅具有nav、ul和li父子关系的元素。这将排除可能出现在其他部分的链接。
querySelectorAll()函数返回一个数组,所以这段代码遍历数组,使每个元素都变成绿色。现在你需要调用这个函数。将以下粗体显示的代码添加到body元素中:
<body``onload="adjustInternalLinks()"
这将在页面加载时调用该函数,但是您也可以根据一些适当的用户输入来调用它,以使样式动态化。保存更改并重新加载页面。你现在应该有绿色链接。注意到 www.apress.com 的链接不是绿色的,因为是外部链接,不是以#开头。
创建 Visual Studio 项目
对于本章的其余练习,您将使用 Visual Studio 项目。启动 Visual Studio 2015,点击新建项目。选择 ASP.NET Web 应用项目模板,输入章节 5 作为名称,如图 5-1 所示。
图 5-1。
Creating the Chapter5project
在第二个对话框中,选择 ASP.NET 5 网站模板。
雇佣网络工作者
随着越来越多的工作在客户端完成,让客户端应用多线程化变得更加重要。幸运的是,使用 Web 工作器 是实现这一目标的一种便捷方式。CPU 密集型或可能需要一些时间才能完成的功能可以在后台线程上执行,让主 UI 线程可以响应用户操作。
网络工作者使用一个相当简单的概念。您创建一个 worker 并传递给它一个定义其执行的 JavaScript 文件。然后,网页可以通过消息与工作人员通信。worker 实现onmessage事件处理程序来响应来自页面的传入消息,并使用postMessage()函数将数据发送回调用者。调用者还必须处理onmessage事件来接收来自工作者的消息。如图 5-2 所示。
图 5-2。
Communicating with a dedicated web worker Tip
对于您将在本章中创建的演示应用,呼叫者和工作人员之间的消息将是简单的文本消息。然而,它们可以是您想要的任何格式,包括 JSON 编码的数据。
Web 工作器 最大的限制之一是他们不能访问 DOM,所以你不能用他们来更新页面内容或样式。此外,他们不能访问窗口对象,这意味着,除其他事项外,你不能使用计时器。考虑到这些限制,您可能想知道何时使用 web worker。
网络工作者非常适合执行检索数据等任务。例如,如果您需要从外部源(如数据库、本地文件系统或 web)查找信息,您可以将查找参数传递给 worker,当查找完成时,数据可以作为 JSON 消息传递回来。这允许网页在检索数据时响应用户操作。
网络工作者有两种类型:专用型和共享型。专用工作器只能由单个页面使用,而共享工作器可以由多个 web 页面使用。专用工作人员和共享工作人员的工作方式基本相同,但沟通方式略有不同。您将从实现一个专用的 web worker 开始。
使用专门的工人
一个专门的网络工作者,顾名思义,就是专门为创建它的网页服务的。网页创建它,需要时使用它,不再需要时关闭它。一个网页可以根据需要创建任意多的工作人员。
为了演示一个专用的 web worker,您将构建一个简单的 web 页面,允许您创建一个 worker 并向它发送消息。它还将显示响应,以便您可以看到双向通信。worker 实现很简单,只是简单地回显发送给它的消息。
EXERCISE 5-1. USING A DEDICATED WEB WORKERIn the Chapter 5 project you created earlier, open the Index.cshtml file, which you’ll find in the Views\Home folder. Replace the default implementation of this view using the code shown in Listing 5-1. This will create a simple form with a text area for displaying messages and three buttons for communicating with the worker.
清单 5-1。索引视图实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Chapter``5
<link rel="stylesheet" type="text/css" href="∼/css/Sample.css" />
<script type="text/javascript" src="∼/controlWorker.js"></script>
</head>
<body>
<header>
<h1>Web 工作器 Demo</h1>
</header>
<div>
<textarea id="output"></textarea>
</div>
<form id="control" method="post" action="">
<input id="create" type="button" class="button" value="Create Worker"
onclick="createWorker()"> <br>
<input id="send" type="button" class="button" value="Send Message"
onclick="sendWorkerMessage()">
<input id="message" type="text" class="text" value="Hello, World!"><br>
<input id="kill" type="button" class="button" value="Close Worker"
onclick="closeWorker()">
</form>
</body>
</html>
Replace the default implementation with the code shown in Listing 5-2. From the Solution Explorer, right-click the wwwroot\css folder and choose Add and New Item. Select the Style Sheet item and enter Sample.css for the file name, as shown in Figure 5-3. Click the Add button to create the file.
图 5-3。
Adding the Sample.css style sheet
清单 5-2。Sample.css 样式表
h1
{
font-size:22px;
color:purple;
}
#output
{
width: 500px;
height: 250px;
background-color:#dfcaca;
}
.button
{
width:125px;
height:25px;
color:green;
}
.text
{
width:260px;
}
For the contents of this file, enter the following code. This is the implementation of the worker. It handles both the onconnect event (when the worker is first created) and the onmessage event (when a message is sent to the worker). The implementation simply echoes the message back to the caller. Close the browser and stop the debugger. In the Solution Explorer, right-click the wwwroot folder and choose Add and New Item. In the Add New Item dialog box, select the JavaScript File item. Enter worker.js for the file name, as shown in Figure 5-5.
图 5-5。
Adding the worker.js file Press F5 to debug the application; the form should look similar to Figure 5-4.
图 5-4。
The initial form design
/* This file implements the web worker */
// This event is fired when the web worker is started
onconnect = sendResponse("The worker has started");
// This event is fired when a message is received
onmessage = function (event) {
sendResponse("Msg received: " + event.data);
}
// Sends a message to the main thread
function sendResponse(message) {
postMessage(message);
}
Using the same instructions, add a controlWorker.js file in the wwwroot folder. Enter the code shown in Listing 5-3 for its implementation. I will explain this in more detail later in the chapter.
清单 5-3。controlWorker.js 实现
/* This file contains functions used to
communicate with the web worker */
var myWorker;
function createWorker() {
if (typeof(Worker) !== "undefined") {
var log = document.querySelector("#output");
log.value += "Starting worker process… ";
myWorker = new Worker("worker.js");
log.value += "Adding listener… ";
myWorker.onmessage = function(event){
log.value += event.data + "\n";
}
log.value += "Done!\n";
}
else {
alert("Your browser does not support Web 工作器");
}
}
function sendWorkerMessage(){
if (myWorker !== null) {
var log = document.querySelector("#output");
log.value += "Sending message… ";
var message = document.querySelector("#message");
myWorker.postMessage(message.value);
log.value += "Done!\n";
}
}
function closeWorker(){
if (myWorker !== null) {
var log = document.querySelector("#output");
log.value += "Closing worker… ";
myWorker.terminate;
myWorker = null;
log.value += "Done!\n";
}
}
Press F5 to debug the application. Click the Create Worker button and then click the Send button. Modify the message and try clicking the Send Message button again. Finally, click the Close Worker button. The text area should look like Figure 5-6.
图 5-6。
The message log
controlWorker.js文件包含三个函数,为表单上的三个按钮提供实现。它首先声明一个myWorker变量,该变量包含对专用 web worker 的引用,然后实现以下函数:
createWorker():该函数首先通过查看是否定义了Worker类来检查浏览器是否支持 Web 工作器。否则,将发出警报。然后它创建一个Worker类的实例,将它的引用保存在myWorker变量中。工人实现通过引用worker.js脚本文件传递给它的构造函数。然后它实现onmessage事件处理程序,将传入的消息添加到output字段。sendWorkerMessage():这只是调用工人的postMessage()方法,传入在message字段中指定的文本。注意它使用了我在本章前面解释过的querySelector()方法。closeWorker():调用 worker 的terminate()方法,将myWorker变量设置为null。该工作线程会立即关闭,无法执行任何清理操作。
提示与添加一个onmessage事件处理程序的方式相同,您也可以创建一个onerror事件处理程序来响应来自 worker 的错误。网络工作者可以通过使用throw功能来报告错误。
通过这个简单的实现,您可以看到创建一个 worker 并使用消息与之通信是多么容易。
创建共享工作者
共享 web worker 允许您创建一个 worker,然后从其他页面重用它。使用共享工作人员有几个好处。最明显的一点是,多个页面可以共享同一个线程,而不必为每个页面创建一个新的工作线程。另一个,我将在后面解释,是跨页面共享状态信息。
Caution
IE 11 支持专用 Web 工作器,但不支持共享 worker。
现在您将创建一个共享的 worker,它是在一个 JavaScript 文件中实现的。概念本质上是一样的,只是交流的方式有点不同。您将向 web 页面添加几个按钮,并实现一组新的 JavaScript 函数来与共享的工作人员进行通信。
EXERCISE 5-2. CREATING A SHARED WORKEROpen the Index.cshtml file and add the following script reference in the head element after the previous reference:
<script type="text/javascript" src="∼/controlSharedWorker.js"></script>
Add the code shown in bold in Listing 5-4 to the form element. This will add another set of buttons to control the shared worker.
清单 5-4。Index.cshtml 中的附加按钮
<form id="control" method="post" action="">
<input id="create" type="button" class="button" value="Create Worker"
onclick="createWorker()"> <br>
<input id="send" type="button" class="button" value="Send Message"
onclick="sendWorkerMessage()">
<input id="message" type="text" class="text" value="Hello, World!"><br>
<input id="kill" type="button" class="button" value="Close Worker"
onclick="closeWorker()">
<br><br>
<input id="createS" type="button" class="button" value="Create Shared"
onclick="createSharedWorker()"> <br>
<input id="sendS" type="button" class="button" value="Send Shared Msg"
onclick="sendSharedWorkerMessage()">
<input id="messageS" type="text" class="text" value="Hello, World!"><br>
<input id="killS" type="button" class="button" value="Close Shared"
onclick="closeSharedWorker()">
</form>
From the Solution Explorer, add another file to the wwwroot folder named sharedWorker.js and enter the code shown in Listing 5-5. This is the implementation of the shared worker.
清单 5-5。sharedWorker.js 实现
/* This file implements the shared web worker */
var clients = 0;
onconnect = function(event) {
var port = event.ports[0];
clients++;
/* Attach the event listener */
port.addEventListener("message", function(event){
sendResponse(event.target, "Msg received: " + event.data);
}, false);
port.start();
sendResponse(port, "You are client # " + clients + "\n");
}
function sendResponse(senderPort, message) {
senderPort.postMessage( message);
}
Add another file in the wwwroot folder named controlSharedWorker.js and enter the implementation shown in Listing 5-6. I will explain this code later.
清单 5-6。controlSharedWorker.js 实现
/* This file contains functions used to
communicate with the web worker */
var mySharedWorker;
function createSharedWorker() {
if (typeof(SharedWorker) !== "undefined") {
var log = document.querySelector("#output");
log.value += "Starting shared worker process… ";
mySharedWorker = new SharedWorker("sharedWorker.js");
log.value += "Adding listener… ";
mySharedWorker.port.addEventListener("message", function(event){
log.value += event.data + "\n";
}, false);
mySharedWorker.port.start();
log.value += "Done!\n";
}
else {
alert("Your browser does not support shared Web 工作器");
}
}
function sendSharedWorkerMessage(){
if (mySharedWorker !== null) {
var log = document.querySelector("#output");
log.value += "Sending message… ";
var message = document.querySelector("#messageS");
mySharedWorker.port.postMessage(message.value);
log.value += "Done!\n";
}
}
function closeSharedWorker(){
if (mySharedWorker !== null) {
var log = document.querySelector("#output");
log.value += "Closing worker… ";
mySharedWorker.port.terminate;
mySharedWorker = null;
log.value += "Done!\n";
}
}
Press F5 to debug the application. Create a shared worker and send a message to it. It should work just like the previous exercise. Leaving the browser tab running, create a new tab and enter the same URL as the first tab. This will open the same page in a second tab. Create a shared worker from the second tab. Then click the Send Shared Message button to test the connection. Notice the message says that you are the second client, as demonstrated in Figure 5-7.
图 5-7。
Opening a second copy of the page
消息在工作者和调用工作者的页面之间传递。多个页面可以调用一个共享的工作器,但是消息是不共享的;每条消息仍然在单个页面和工作人员之间。但是,员工内部的数据是共享的,可以从多个页面访问。
现在让我们看看共享工作器是如何实现的。就像专门的工作者一样,它必须处理onconnect和onmessage事件。但是,您不能将ommessage处理程序直接附加到 worker 上;相反,您必须访问一个端口并连接到该端口。onconnect事件接收一个event参数,你通过event.ports[0]访问端口。一旦有了port,就可以将事件处理程序附加到它上面。您使用端口的addEventHandler()方法。这需要两个参数。第一个是事件的名称,在本例中是message。第二个参数是引发事件时将调用的函数。
发送消息时,还必须使用port对象。这个port对象在传入消息的event.target属性中提供。这个事件处理程序和onconnect事件处理程序都使用传入port对象的sendResponse()函数。
controlSharedWorker.js文件中的功能几乎与它们的专用对应物完全相同。然而,它们也必须使用port对象。端口包含在事件中。
注意在sharedWorker.js文件中,clients变量被声明,然后在onconnect事件处理程序中递增。这用于跟踪有多少客户端已经连接到共享工作器。我添加这个只是为了演示这个变量对于连接到 worker 的所有客户机是如何全局的。事实上,没有每个端口的实例数据;所有数据都是全球性的。
另外,当消息进来时,event参数包括响应应该发送到的端口。工作人员不会“记住”每个客户端的端口。它只是做它被指示做的事情,并在指定的端口上返回一个响应。
客户端包管理
Visual Studio 2015 和 ASP.NET 5 在如何完成客户端打包方面引入了一个非常重要的转变。NuGet 仍然存在,但已经被归入服务器端包。在客户端,您现在可以使用 Bower、Grunt、Gulp 和节点包管理器(NPM)等工具。项目模板为您预先配置了大多数基本功能。你不需要处理这些来实现本书中的例子。然而,我想给你一个这些工具做什么和它们如何工作的概述。让我们从查看为您创建的项目文件开始。
配置:图标配置
如果您看一看解决方案资源管理器,您可能会问自己,“web.config文件在哪里?”如果您查看文件列表的末尾,您会看到一个config.json文件,在这里您可以找到配置设置,比如连接字符串。配置数据可以存储在 JSON、XML 或 INI 文件中,并且可以有许多配置文件。
看一下Startup.cs文件。它定义了一个IConfiguration成员和一个定义要加载的文件的构造函数。
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This reads the configuration keys from the secret store.
// For more details on using the user secret store see
// http://go.microsoft.com/fwlink/?LinkID=532709
configuration.AddUserSecrets();
}
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
public IConfiguration Configuration { get; set; }
因此,您可以决定如何组织配置数据以及使用何种文件格式。项目模板生成的初始代码使用AddJsonFile()方法从config.json文件加载数据。它还加载任何可能使用AddEnvironmentVariables()方法定义的环境变量。
这里有一篇解释新配置模型的好文章: http://blog.jsinh.in/asp-net-5-configuration-microsoft-framework-configurationmodel/#.VQ3TUvnF9Cg 。
静态文件:wwwroot
wwwroot文件夹是 ASP.NET 5 的新增功能,它提供了一个存放所有静态内容的地方,比如 CSS、JavaScript、图片和静态 HTML。这里的想法是明确区分通过服务器端代码生成的内容和简单地按原样提供给浏览器的内容。
该文件夹被称为 web 根目录。这大致相当于以前版本的 MVC 使用的Content和Scripts文件夹。这些文件夹在解决方案浏览器中与Models、Views和Controllers处于同一级别。将它们向上移动一个级别,合并成一个级别,并将其命名为 web root,这样就可以更清楚地知道应该包含什么。
包管理:Bower
虽然 NuGet 是一个受欢迎的朋友。作为. NET 开发人员,Bower 在管理客户端依赖性方面一直很受欢迎。因此,在 ASP.NET 5 中,您将使用 Bower 来配置您的应用所需的客户端软件包。(您将继续对服务器端包使用 NuGet。)客户端依赖关系在bower.json文件中列出;清单 5-7 显示了初始的、模板生成的文件。
Listing 5-7. The bower.json Configuration File
{
"name": "WebApplication",
"private": true,
"dependencies": {
"bootstrap": "3.0.0",
"jquery": "1.10.2",
"jquery-validation": "1.11.1",
"jquery-validation-unobtrusive": "3.2.2",
"hammer.js": "2.0.4",
"bootstrap-touch-carousel": "0.8.0"
},
"exportsOverride": {
"bootstrap": {
"js": "dist/js/*.*",
"css": "dist/css/*.*",
"fonts": "dist/fonts/*.*"
},
"bootstrap-touch-carousel": {
"js": "dist/js/*.*",
"css": "dist/css/*.*"
},
"jquery": {
"": "jquery.{js,min.js,min.map}"
},
"jquery-validation": {
"": "jquery.validate.js"
},
"jquery-validation-unobtrusive": {
"": "jquery.validate.unobtrusive.{js,min.js}"
},
"hammer": {
"": "hammer.{js,min.js}"
}
}
}
使用这个文件的好处是智能感知支持。例如,打开这个文件并转到依赖项部分。在定义了bootstrap-touch-carousel的最后一行,转到行尾,输入一个逗号,然后按回车键。然后输入一个报价并开始输入一个包名。请注意,在您键入时,可用包的列表会自动显示。输入“modernizr”:”,注意显示的是可用的版本号,如图 5-8 所示。
图 5-8。
Bower IntelliSense support r
还要注意版本语义。撰写本文时,当前的稳定版本是 2.8.3。这些数字分别指定主要版本、次要版本和修补程序编号。在版本前面加上一个克拉符号(^)表示主要版本必须匹配。例如,如果指定了².8.3,则只要主版本是 2,就会使用等于或高于 2.8.3 的任何版本。因此,将使用 2.8.5 或 2.9,而不是 3.1。波浪号(∾)表示主要版本和次要版本必须匹配。所以,∞2 . 8 . 3 将使用大于或等于 3 的 2.8 的任何路径级别;因此,将使用 2.8.5,但不使用 2.9。省略这两者表明应该使用最新版本,只要它至少是 2.8.3。
构建任务:吞咽
对于大多数 web 应用需要的所有客户端文件,获取、组织和准备它们可能是一项单调乏味的任务。您已经将 Bower 视为管理依赖性的一个很好的工具。Gulp 是另一个有用的工具,它允许您自动化构建任务。Gulp 是一个基于 JavaScript 的框架,使用 Node.js 和 NPM。
Gulp 的一个典型场景是告诉 Bower 检查并下载依赖项。事实上,清单 5-8 中显示的初始gulpfile.js就是这么做的。
Listing 5-8. The Initial gulpfile.js File
/// <binding Clean='clean' />
var gulp = require("gulp"),
rimraf = require("rimraf"),
fs = require("fs");
eval("var project = " + fs.readFileSync("./project.json"));
var paths = {
bower: "./bower_components/",
lib: "./" + project.webroot + "/lib/"
};
gulp.task("clean", function (cb) {
rimraf(paths.lib, cb);
});
gulp.task("copy", ["clean"], function () {
var bower = {
"bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
"bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
"hammer.js": "hammer.js/hammer*.{js,map}",
"jquery": "jquery/jquery*.{js,map}",
"jquery-validation": "jquery-validation/jquery.validate.js",
"jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
}
for (var destinationDir in bower) {
gulp.src(paths.bower + bower[destinationDir])
.pipe(gulp.dest(paths.lib + destinationDir));
}
});
当然,您还可以完成许多其他任务,比如捆绑和缩小您的 JavaScript 或 CSS 文件。另一个例子是将较少的样式表预编译成 CSS 文件。
Note
除了 Gulp,Grunt 还用于客户端构建自动化。Grunt 可以执行与 Gulp 相同类型的任务,但以不同的方式完成。咕噜声和吞咽声可能会持续一段时间。如果你正在考虑使用这些工具中的一个,但不确定是哪一个,这里有一篇很好的文章,它描述了 Grunt 和 Gulp 之间的区别,并提供了一些关于你应该考虑使用哪一个的建议: https://medium.com/@preslavrachev/gulp-vs-grunt-why-one-why-the-other-f5d3b398edc4 。
摘要
在这一章中,您尝试了一些有用的技术,这些技术可能会在您的许多 web 项目中使用。
- 查询选择器在 JavaScript 代码中利用了同样强大的 CSS 选择器。
- Web 工作器 在单独的线程上执行 CPU 密集型或缓慢的操作,以提高整体响应能力。
我还介绍了一些可用于管理 web 应用的新的客户端工具。在第六章中,我将向您展示 HTML5 的改进如何用于创建移动友好的 web 应用。