原文:HTML5 Mastery Semantics, Standards, and Styling
协议:CC BY-NC-SA 4.0
五、多媒体:视频、音频和嵌入式媒体
网站只有带项目符号的文本列表,没有任何形式的图像的日子已经一去不复返了。但是如果你仔细想想,HTML 从来就不是真正的多媒体通。图像很早就出现了,也有像(现已不存在的)bgsound
这样的噱头元素,但是交互体验、网络应用、视频播放器——每一个都是由某种形式的插件提供的,在很大程度上扩展了 HTML 的功能。在这方面,HTML5 中增强的多媒体功能对于最终用户来说是 HTML 时代的到来。例如,video
和audio
元素分别提供了处理视频和音频媒体的标准化方法。这听起来像是网络媒体的一个巨大进步,的确如此,但也并非一帆风顺。尽管这些元素作为 HTML5 规范的一部分被标准化了,但是这些媒体元素中使用的文件格式却没有标准化,这导致了一些冗长的解决方案来提供替代内容。但是不用担心,这决不会使这些实现不可用,在本章中,您将看到处理回退内容的一致的最佳实践方法是可能的。
本章开始时,我将浏览 HTML 中的一些媒体元素,并向您概述发生了哪些变化。这将涵盖图像、图像映射、通用嵌入对象(处理视频的老派方式)和帧的状态等主题。然后,我将介绍 HTML 中引入的用于处理视频和音频的新元素。最后,我将简要介绍一下canvas
元素,它用于定义一个可以动态呈现图像的区域。与canvas
的互动将在第七章中更深入地讨论,但基础将在本章中讨论。
一切开始的地方:img
在 HTML 发展的早期,很明显平台需要支持某种形式的嵌入页面的混合媒体——至少是图像。实现可用于图像和其他媒体的 HTML 元素的工作始于 1993 年初,当时 21 岁的马克·安德森提出并在 Mosaic web 浏览器中实现了自结束img
元素。Mosaic 被认为是推动 20 世纪 90 年代互联网繁荣的核心力量,它将网络浏览器从基于文本的系统转移到更容易吸引非技术受众的图形应用。尽管对img
的局限性有所保留(其他人想要一个可以嵌入比图像更多种媒体的元素),但img
元素今天仍然存在。HTML5 没有太大的变化,除了删除了属性align
、border
、hspace
和vspace
,这些属性的功能应该通过 CSS 来创建。
1 Marc Andreesen 提议 img 元素的原始信息存档于1997 . web history . org/www . lists/www-talk . 1993 Q1/0182 . html
。
img
元素一般需要两个属性:src
和alt
。第一个是src
,指定实际图像文件的位置。该位置可以作为绝对或相对 URL 给出,这意味着它可以被指定为图像的绝对地址,如src="http://example.cimg/pic.jpg"
,或者是相对于当前 HTML 页面的地址,如src="img/pic.jpg"
。JPEG、GIF 和(相对较新的)PNG 图像是 Web 上流行的图像格式,但是很少有人知道img
元素并不局限于这些格式及其变体。根据网络浏览器的不同,可能会显示 TIFF、BMP、XBM,甚至 PDF 2 文件。这种多种多样的支持是因为 HTML 规范并没有指定该元素需要支持哪些图像格式,只指定要显示的文件实际上是一个图像。
何时使用一种图像格式而不是另一种
在决定网站上的特定图像使用何种图像格式时,您可以遵循一些通用规则。对于有许多色调的照片内容,JPEG 是可以选择的格式。如果图像中有连续的实心色块,如徽标或插图,请在 GIF 或 PNG 之间选择。PNG 提供了比 GIF 更大的灵活性,最初创建 PNG 是为了取代 GIF,因为 GIF 格式的算法存在许可问题。所以,总的来说,用 PNG。但是,GIF 有更多的遗留支持。包含透明度的 png 在 Internet Explorer 6 中将显示为粉红色;然而,可能是时候放弃对浏览器历史上如此久远的浏览器的支持,而只使用 PNG 了。PNG 一般有两种形式:PNG-8 和 PNG-24。如果图像中的调色板很小(256 色或更少)并且没有任何渐变区域,请使用 PNG-8。如果在图像中使用透明度,通常 PNG-24 将提供更好的结果,因为它将更好地处理图像的不透明和透明区域之间的过渡。
注意谷歌最近推出了一种新的网络图像格式,称为 WebP(读作“weppy”),是 JPEG 格式的竞争对手。值得关注,但目前浏览器支持太少(Google Chrome 和 Opera ),无法考虑将其作为主要的图像格式。你可以在[
code.google.com/speed/webp/](http://code.google.com/speed/webp/)
找到更多信息。
下一个属性alt
用于在图像不可用或不可见时为图像提供替代内容。属性中的文本应该合理地表示图像,如果图像被删除,也不会改变页面上内容的含义。如果alt
属性是对页面上其他信息的补充或冗余,或者如果它纯粹是装饰性的,那么它可以是一个空的文本字符串(""
),在这种情况下,CSS 可能是处理图像的更好方法(参见“?? 过时了吗?CSS 呢?”侧边栏)。在不可能提供图像的文本表示的情况下,也可以省略alt
属性,例如在由用户上传的动态添加的图像中,在这种情况下,图像的实际含义可能不会立即为人所知。如何在不同的上下文中正确使用alt
属性是一个令人惊讶的争论问题,但从根本上来说,它应该被认为是图像的替代物,而不是图像的描述。由title
属性提供描述更为正确。例如,对于显示安道尔地图的图像,如果目的是显示安道尔在世界上的位置,则提供“安道尔地图”的替代文本是不正确的。更好的替代文本将复制图像所传达的意思。在这种情况下,可能是这样的:“安道尔是一个内陆西欧国家,西南与西班牙接壤,东北与法国接壤”(图 5-1 )。
在支持的地方(例如,在 Safari 中),PDF 文档将只显示第一页,因为 img 不允许显示分页内容。
***图 5-1。*当图像不可用时,alt
属性显示的替代文本应该再现图像的目的,而不是描述图像。图像的描述性摘要最好由title
属性提供,当鼠标悬停在图像上时会出现。
虽然不是必需的,但是最好将width
和height
属性设置为源图像的宽度和高度(以像素为单位)。这将允许浏览器在图像完全加载之前呈现图像将占据的空间。这也意味着如果一幅图像丢失了,页面的布局也不会改变,因为一个空框width
和height
属性的尺寸将被呈现出来(图 5-1 显示了这样一个框)。
注意Google Chrome 和 Safari 的底层布局引擎 WebKit 存在一个问题,如果宽度和高度不够大,无法在一行中容纳所有替代文本,则无法在找不到图像时显示替代文本。如果当图像不可用时,您在这些浏览器中看不到替代文本,原因可能是尺寸设置。
IMG 过时了吗?CSS 呢?
img
元素是古老的——事实上它比层叠样式表(CSS)还要古老。自 20 世纪 90 年代早期以来,Web 上的内容表示已经有了很大的发展,与 CSS 相比,img
元素在许多方面非常有限。因为 CSS 关心的是页面的外观,包括图像,所以 CSS 提供了处理图像的能力,通常比使用img
元素更灵活。使用 CSS,图像可以应用于页面上任何元素的背景(通过使用background-image
和相关属性),图像可以平铺、偏移和等。正在开发的 CSS 规范的最新版本 CSS3 增加了更多的图像处理功能,例如将多个背景图像相互叠加的能力。 3
此外,HTML5 规范明确指出,应该避免出于布局目的使用img
元素。使用图像进行布局的示例包括通过使用基于图像的背景或边框来分隔内容区域,或者使用透明图像在页面上的内容旁边提供填充。这些任务更适合 CSS,通常通过background
、border
和padding
属性来完成。
此外,从性能的角度来看,重要的是要记住,img
元素在 HTML 页面中为外部资源提供了一个占位符。因为每个链接的资源都必须向 web 服务器发出请求,所以会发出对 HTML 页面的请求,并且页面上的每个图像都需要额外的请求。使用 CSS,一种被称为 CSS sprites 的技术,可以用于将多个图像合并成一个图像,以减少服务器请求。这些图像并排布置,并被裁剪以显示来自一个源图像的不同图像。因为这只需要一个请求,而不是每个图像一个请求,所以性能会有所提高。
那么,当 CSS 可以提供更多的灵活性时,为什么还要使用img
元素呢?答案在于img
的alt
属性。由于该属性用于以文本形式表示图像,它解决了 CSS 中丢失的可访问性问题。即使移除了 CSS 样式,HTML 文档中的信息也应该是易于理解的,因此对于页面上显示的信息至关重要的图像,比如文档文本中其他地方引用的图像或图形,应该显示在img
元素中。
在img
元素上剩下的属性是ismap
和usemap
,它们都用于图像映射,我们将在下面讨论。
影像地图
在 Adobe Flash 中内置的交互式动画和图像在 20 世纪 90 年代末风靡一时之前,交互性的高度是可点击的图像地图,其编码的“热点”根据用户点击图像的位置将用户链接到不同的页面。有两种图像映射:服务器端和客户端。在服务器端图像映射中,鼠标单击的像素坐标作为 x,y 坐标对发送到服务器。然后,服务器可以使用该信息来确定用户在图像上单击的位置,并以后续操作做出响应。启用此功能所需的全部工作就是将布尔型ismap
属性添加到包含在锚元素(a
)中的img
元素中:
<a href="process.php"><img ismap src="map.png" alt="" /></a>
点击图像后,图像被点击位置的坐标(相对于图像的左上角)作为 querystring 出现在 URL 中,类似于process.php?54,77
。(这个例子意味着点击发生在距离图像的左边缘 54 像素和上边缘 77 像素处。)然后,服务器端脚本可以访问这些坐标,并根据坐标区域的查找表使用它们来确定应该发生什么动作(如果有的话)。
正如你将在第六章中看到的,CSS3 不是一个规范,而是几个。然而,就像“HTML5”经常被用作涵盖一系列相关技术的总括术语一样,“CSS3”涵盖了许多不同但相关的规范。在多背景的情况下,实际的规范是“CSS 背景和边框模块级别 3”
客户端图像地图的工作原理与服务器端图像地图相同,但热点区域是在客户端(网络浏览器)而不是服务器上定义的。这是一种更可取的方法,因为热点坐标可以被无法查看图像的浏览者访问,并且它们提供了关于用户是否正在点击活动区域的即时反馈。然而,与服务器端图像映射相比,所需的标记更加复杂。有两个不同的部分:图像元素(img
)和关联的map
元素,两者都不嵌套。map
元素是一个具有name
属性的容器元素,它被图像元素的usemap
属性引用,以创建图像和图像映射区域坐标数据之间的关联。实际的图像映射热点坐标是通过任意数量的自闭area
元素在map
元素内定义的。这里有一个例子:
<img src="banner.png" alt="" width="300" height="272" usemap="#bannermap" /> <map name="bannermap"> <area shape="circle" coords="52,76,39" href="/about.html" alt="About" /> <area shape="rect" coords="120,56,187,102" href="/contact.html" alt="Contact" /> <area shape="poly" coords="265,148,221,99,221,42,266,24" href="/portfolio.html" alt="Portfolio" /> <area shape="default" href="/index.html" alt="Homepage" /> </map>
正如您在前面的例子中看到的,area
元素使用一个shape
属性来确定热点区域的形状,使用一个coords
属性来标出形状的尺寸。shape
属性可以具有值circle
、rect
、poly
或default
,其对应于绘制圆形、矩形、至少具有三个点的自由形状或整个图像(在default
状态中,没有给出坐标,因为它覆盖了整个图像)。在大多数情况下,这个区域还会包含一个href
属性来决定用户点击后应该被带到哪里。另一个属性nohref
在 HTML5 中已经过时,不再使用。这个属性已经被用来指定该区域不链接任何地方,但是仅仅去掉href
属性就足以提供这个功能(或者说缺少功能)。
注意为什么在图像地图中会有一个区域没有链接到任何地方?原因可能是在图像上提供工具提示,但不需要链接到任何地方。例如,考虑美国 50 个州的地图。如果创建的图像地图为每个州定义了一个区域,并将其title
属性设置为相应的州名,则该地图结束,但未设置href
属性,它将创建一个美国地图,当用户悬停在地图上时,该地图将显示用户所在的州名,但如果用户单击,则不会链接到任何地方。此外,创建一个没有href
属性的区域允许从其他区域中“冲出”区域,因此,例如,可以通过创建两个重叠的圆形区域来定义一个可点击的圆环形状,只有较大的圆形设置了其href
属性。
图像映射区域坐标点的意义取决于shape
属性的值。圆将有三个值,对应于圆形热点中心的 x 和 y 坐标,最后一个值决定圆的半径。对于矩形区域,coords
属性中有四个值,分别对应矩形左上角和右下角的 x,y 坐标。最后,poly
区域定义了形状中每个点的 x,y 坐标,这可能是相当多的!可以想象,手工编码图像地图是相当费力的,但是大多数 WYSIWYG web 创作软件只需通过指向、单击和拖动就能创建区域。图 5-2 显示了一个在 Adobe Dreamweaver 中创建的图像地图的例子。
***图 5-2。*使用 Adobe Dreamweaver 绘制的复杂图像地图区域
除了为热点定义形状坐标之外,area
元素的作用非常类似于锚点元素(a
)。如果定义了一个href
属性,那么可以定义以下附加属性:target
、rel
、media
、hreflang
和type
,它们的工作原理与第三章中描述的锚元素(a
)相同。
此外,应该在图像映射中的链接热点上设置alt
属性,以便在图像映射中的图像不可见的情况下,它给出链接的文本表示。
嵌入其他媒体
HTML5 包括两个用于嵌入非 HTML 内容的元素;也就是说,现有 HTML 元素无法处理的内容需要第三方插件来显示,如 Adobe Flash 内容,它需要 Adobe Flash Player。这两个元素是embed
和object
。一般来说,object
会比embed
更常用,因为它更灵活,并且可以提供后备内容(在不支持该元素的浏览器中显示的内容),但是我们会将它们都包括在内,这样您就可以看到它们的区别。
嵌入元素
虽然img
元素增加了网页的丰富性,但在它被引入的时候,很明显只支持静态图像是不够的。Web 需要一种方法来处理各种各样的嵌入式媒体。在 1993 年关于引入img
元素的讨论中,另一个元素是由蒂姆·伯纳斯·李(被广泛认为是万维网的发明者)提出的。他建议添加一个embed
元素来代替img
,这将解决后者的缺点,即它只支持嵌入图像,而不支持其他媒体或数据。最终,这两个元素都被网络浏览器制造商实现了,img
用于图像,embed
用于其他媒体(例如视频),但是一个官方支持的支持更丰富媒体的解决方案在未来几年将在几个方向上分裂。
embed
最早是由网景公司实现的,虽然其他浏览器也实现了它,但它有一些奇怪的地方,妨碍了它的标准化。具体来说,它可以包括任意属性(不仅仅是属性值,还有属性本身),这些属性可以根据所嵌入的媒体而有所不同。这些附加属性可以是任何东西,因为它们将作为参数传递给用于处理内容的插件,插件可能会响应它们。W3C 对这种行为并不放心,特别是当 XHTML 更严格的语法要求似乎是 HTML 的未来发展方向时。到 1999 年 HTML 4.01 规范出现时,embed
被认为是过时的,不鼓励使用。这可能是embed
元素的故事的结尾,但是 HTML5 通过将其正式标准化为 HTML 规范的一部分,将它带了回来,尽管是以精简的形式。HTML5 的目标是向后兼容并记录正在使用的东西——尽管有些奇怪,embed
今天仍在使用。
除了全局属性之外,HTML5 中的embed
还有一组简单的四个属性,而不是之前的实现所附带的 15 个属性。这四个属性是width
、height
、src
和type
。
width
和height
属性指定嵌入媒体在页面上占据的像素尺寸,而src
属性指定要嵌入的源媒体文件的地址。如前所述,可以添加附加属性来将设置传递给插件集,以处理特定类型的媒体。例如,下面的代码片段包括一个loop
属性,它被传递给 Adobe Flash Player(处理 SWF 内容的插件):
<embed src="game.swf" loop="true" />
在 HTML5 规范中,loop
属性没有被指定为embed
元素的属性,但是 Flash Player 将通过循环播放该 SWF 文件来做出响应。
注意在这个例子中没有指定type
属性。如果没有指定,浏览器将“内容嗅探”以确定哪个插件应该处理资源(在src
中指定)。这可能与在资源上查找任何指示其类型的元数据一样复杂,或者与查找可以处理特定文件扩展名的插件一样简单。在这种情况下,带有.swf
文件扩展名的资源将被移交给 Flash Player。当然,嵌入的文件类型可以通过在type
属性中提供合适的 MIME 类型来显式设置,如下所示:
<embed src="game.swf" type="application/x-shockwave-flash" loop="true" />
除了属性的模糊性,还有一个关于embed
的问题。它是一个自结束元素,像img
,但是它没有alt
属性。这意味着如果元素不受支持,什么都不会显示!为了解决这个问题,浏览器添加了一个noembed
元素,为不支持embed
的情况提供内容。然而,noembed
元素在 HTML5 中已经被标记为过时,不能再使用。
总之,尽管包含在规范中,embed
由于其局限性和怪癖,最好避免使用。一个更好的选择是object
元素,我们接下来会看到。
物体元素
因为embed
的问题和img
的限制,W3C 在 1996 年选择了object
元素作为两者的替代。尽管它从未取代img
, object
确实在很大程度上取代了embed
,因为它更灵活(尽管 Firefox 在支持上落后了一段时间)。F
例如,它有一个开始和结束标签,所以——与embed
不同——可以在元素的内容区域提供回退内容。
与embed
一样,object
在 HTML5 中被精简了,有几个属性被标记为过时,但也增加了一些。属性data
、type
、name
、width
、height
和usemap
是从先前的规范中保留的属性,而属性form
和typemustmatch
、被添加。正如您已经看到的,object
是一个比embed
更复杂的元素。object
不仅适用于插件媒体,因为它还支持图像和嵌套网页。
让我们进入object
的属性。data
属性类似于img
或embed
元素上的src
属性;它只是指定了要加载的资源的位置。type
属性的工作方式类似于它在embed
元素上的工作方式;为要嵌入的资源提供一个有效的 MIME 类型。data
或type
属性中的一个或另一个必须存在,但它们不需要都存在。如果两者都存在,可以添加布尔typemustmatch
属性,这在加载资源时增加了一层安全性。例如,可以使用object
来嵌入来自第三方网站的媒体和其他资源, 4 如果第三方网站声称他们正在提供一种类型的资源,而实际上这是一个恶意脚本或类似的伪装成无害的东西,这就带来了安全问题。当设置了typemustmatch
时,浏览器将对链接的资源进行内容嗅探,如果资源的内容类型不同于在type
属性中设置的值,则不会嵌入资源。
object
元素可能令人惊讶的一个方面是,object
意味着能够参与表单提交并随表单一起提交数据。这在 HTML5 中也不新鲜,因为它包含在 HTML 4.01 规范中,该规范将“对象控件” 5 列为表单控件的类型之一。新增的form
属性只是为了让object
和其他表单控件保持一致。如果您回忆一下上一章,各种表单控件(input
、textarea
等等)使用form
属性将它们自己与一个或多个表单关联起来,即使它们没有嵌套在它们所引用的表单中。该属性对object
同样有效。将object
作为表单的一部分的想法解决了让一些插件直接向服务器发送数据的需求。例如,可能会创建一个嵌入了某种复杂的浏览器内文字处理器的插件,该插件具有 HTML/CSS/JavaScript 所不具备的特性。然后,可以使用文字处理器来代替普通的老式textarea
,但是在表单提交时,文字处理器可以直接从插件向服务器提交数据(可能包括各种关于文本如何被奇妙地格式化的元数据)。
通常,当嵌入插件资源时,object
元素需要将定制参数发送给插件;不同的插件会有不同的功能。如前所述,embed
元素用一个不寻常的(也可能是混乱的)方法解决了这个问题。)允许向元素添加任何附加属性的解决方案,然后将这些属性交给插件。object
采取了不同的(也可以说是更干净的)方法。不允许添加任意的附加属性,object
使用了另一个元素param
,它被放在object
的开始和结束标记中,可以用来将值传递给嵌入的插件。param
有两个属性(除了全局属性):name
和value
。当嵌套在object
元素中时,在name
和value
属性中指定的值作为键/值对传递给相关的插件。在前面的embed
元素部分,有一个使用带有自定义loop
属性的embed
来告诉 Flash Player 循环播放的例子。这个例子可以用object
和param
元素重写,如下所示:
<object data="game.swf" type="application/x-shockwave-flash"> <param name="loop" value="true" /> </object>
在这个例子中,没有定制属性(不像类似的embed
例子);相反,自定义设置是通过param
上的name
/ value
属性来传达的。
您不太可能手工编写嵌入式插件的参数(例如,当您在创作环境之外发布内容时,Adobe Flash 会生成所需的param
元素),所以我在这里不详细介绍这种用法。请注意,在嵌入基于插件的媒体时,object
元素是您通常需要使用的。
例如,object 元素通常被用来嵌入 YouTube 视频(YouTube 后来转而使用 iframe 在其他网站上嵌入视频)。
5 见【www.w3.org/TR/html4/interact/forms.html】的。
为了总结关于嵌入式媒体的讨论,提到前面提到的object
元素的一个特性是很重要的:回退内容机制。这到底是怎么做到的?嗯,很直观,也很容易实现。您可以嵌套object
元素(或其他元素),允许 web 浏览器在无法呈现首选内容时显示替代内容。例如,您可以嵌套一个视频、一个图像和一些文本,如下所示:
<object data="video.mpg" type="application/mpeg"> <object data="picture.jpg" type="image/jpg"> Some descriptive text, and <a href="video.mpg">a download link</a>. </object> </object>
用户代理应该首先尝试显示视频,但是如果不能,它应该尝试显示图像,如果不能,它就显示文本——这里不需要alt
属性!在一个元素中嵌套替代内容的概念对于本章后面的元素来说特别重要,比如video
,所以请记住这一点。
注虽然理论上object
可以用来嵌入多种类型的媒体,但现实是 HTML5 中加入的许多新元素,以及现有的元素,已经在几个方面篡夺了object
。video
和audio
应该代替object
用于嵌入这些类型的媒体,而img
长期以来一直是嵌入式图像的普遍答案,尽管object
试图取代它。另外,iframe
(将在下一节讨论)是将一个网页嵌套在另一个网页中的更好的替代方法。然而,Java 小程序的applet
元素在 HTML5 中已经过时,对于这种类型的内容应该使用object
。它还应该用于基于插件的媒体,如 Adobe Flash 内容。
嵌入 HTML: iframe
Netscape Navigator 2.0 引入了创建框架的能力,这在当时看来是相当创新的。从大量其他页面中构造 HTML 页面的能力意味着,例如,在一个页面中有一个内容框架和一个导航栏框架,可以加载页面的内容而不需要重新加载导航栏。此外,页面内容可以滚动,而导航条保持不动。然而,伴随着这些优点而来的是许多问题,这些问题导致相框失宠。其中包括搜索引擎在导航框架集时遇到的困难,这导致了框架集上下文之外的特定框架可能会出现在搜索结果中。自从 Netscape Navigator 时代以来,web 浏览器和 web 服务器技术的进步已经淘汰了框架(例如,CSS 可以固定内容的位置,使其不滚动)。在 HTML5 中,frame
和frameset
元素已经过时,不能使用。HTML5 中剩下的是iframe
元素,称为内嵌框架。该元素允许将整个 HTML 页面(或 HTML 代码片段)嵌入到另一个页面中。将一个页面嵌入到另一个页面的能力对于将第三方 HTML 代码合并到您自己的网页中非常有用。例如,这个元素通常用于嵌入第三方广告(例如,Google 的 AdSense 广告)。其他用途包括脸书的“喜欢”按钮和 Twitter 的“推特”按钮,这些按钮经常被嵌入博客帖子或类似内容中,用于在这些社交网络上分享文章。
内嵌框架也可以用来在你的网站上嵌入更多的第三方内容,比如新闻提要或其他网站的文章。
处理 iframe 元素中的内容
元素创建一个具有设定宽度和高度的盒子,外部文档被加载到这个盒子中。它通过提供给src
属性的 URL 加载其内容。像object
一样,如果网页被不支持内嵌框架的浏览器浏览,可以在开始和结束标签之间添加内容作为后备机制。虽然所有主流浏览器都支持iframe
元素,但是从可访问性的角度来看,添加后备内容是一个好的做法。如果没有别的,它可以像注释一样提醒你链接的资源是什么。还可以考虑包含到嵌入文档的链接,这样即使不支持iframe
,用户也可以访问该文档。这里有一个例子:
`
View embedded web page.
如果在不支持iframe
的浏览器中查看之前的代码片段,链接将会显示;否则,embed.html
的内容将呈现在内嵌框架的边界内,这是一个 300 像素宽、150 像素高的框(在本例中,由width
和height
属性设置)。根据需要出现水平和垂直滚动条以适应内容,如图图 5-3 所示。
***图 5-3。*带有滚动条的内嵌框架的出现
因为它提供了一个固定的区域来显示内容,所以一个iframe
也可以用来在页面上的一个有限区域中显示大量的内容(通常是文本)。例如,也许你的网站有一个“条款和条件”声明,这通常是一个充满法律文本的长文档。通过页面底部页脚中的链接可以访问该文档,但是您也希望在注册表单中嵌入该文档,以便用户在您的站点上注册。你可以将现有的文档嵌入注册页面,如图 5-4 所示,而不是在你的网站上复制一个条款和条件页面——链接在页脚和用户注册表单上。
***图 5-4。*使用嵌入式框架在小区域嵌入现有的大量文本文档的示例
图 5-4 的源代码可能是这样的:
`…
View Terms and Conditions …`
新的 iframe 元素属性
就iframe
的属性而言,在 HTML5 中已经有了表示属性被标记为过时的惯例。不应使用以下属性:frameborder
、marginheight
、marginwidth
和scrolling
。此外,longdesc
属性已经被删除,这不应该是一个损失,因为它从来没有足够的浏览器支持。
HTML5 增加了少量与嵌入 HTML 代码片段和页面的能力相关的属性,这些代码片段和页面被沙箱,这意味着它们被阻止执行某些可能被第三方代码段用于恶意目的的操作。这个想法是使用内嵌框架作为一种方式来保护从第三方来源动态添加到网页的代码片段,就像在博客或论坛上作为评论系统的一部分所做的那样。这三个属性是srcdoc
、seamless
和sandbox
。
注意你可能会发现新的iframe
属性在你首选的网络浏览器中不起作用,因为它们还没有在主流浏览器中获得太多支持。在撰写本文时,sandbox
是三个新属性中唯一得到支持的属性(在 Safari 和 Google Chrome 中)。我们希望进一步的支持将很快到来,但与 HTML5 中的任何新功能一样,随着 HTML5 规范在未来几个月和几年中的进一步完善,请密切关注这些属性的变化。
srcdoc
属性允许将 HTML 片段直接输入到属性值中,不像src
需要一个指向另一个 HTML 文件的 URL,例如:
<iframe srcdoc="<p>This text is in an inline frame</p>"><p>This is regular text</p></iframe>
这段代码将创建一个 HTML 片段,它将被插入到一个内嵌框架中。如果srcdoc
和src
属性存在,srcdoc
将覆盖src
,但是src
仍然可以被包含并被赋予一个值,以便在srcdoc
属性不受支持时提供回退内容(目前所有主流浏览器都是这种情况)。
作为布尔属性的seamless
属性使包含的内容看起来是包含文档的一部分,这意味着,例如,在内嵌框架中单击的链接将加载到父文档中,而不是默认加载到iframe
中。
最后一个属性sandbox
,可以被视为布尔属性,但不一定必须是布尔属性(您马上就会看到)。当作为一个布尔属性处理时,它给iframe's
源内容添加了许多安全限制。这些限制如下:
- 受限的本地访问:内容被视为来自不同的服务器,这阻止了对本地服务器内容的访问,如 cookies 和其他与本地服务器域相关的 web 存储选项。
- 无表单提交:禁止从内嵌内容提交表单。
- 无 JavaScript :内联内容中的脚本被禁用。
- 无外部链接目标:例如,通过使用
target="_parent"
,阻止内联内容中的链接指向其他浏览上下文,例如包含文档。 - 无插件:需要插件的内嵌内容(如 Adobe Flash 内容)被禁用。
作为一个布尔属性,sandbox
只需要添加到iframe
中就可以启用这些限制,如下所示:
<iframe src="external.html" sandbox><!-- Fallback content --></iframe>
如果sandbox
属性不被视为布尔属性,可以设置许多文本关键字值,这将否定几乎所有先前的限制。表 5-1 显示了可用的关键字。可以添加多个关键字来否定多个限制,每个关键字之间用空格分隔。这里有一个例子:
`
前面的代码允许表单提交和嵌入内容中的外部链接目标,但是会有其他沙箱限制生效。
注意注意没有一个关键字可以覆盖禁用插件。这样做的原因是,可以构建一个插件来绕过其他一些限制,从而容易受到任何选择性限制的影响。因此,只要sandbox
属性存在,插件总是被禁用的。
瞄准内嵌框架
iframe
还有最后一个属性——不是新的——name
属性,它可以用来在页面上使用的其他内嵌框架中识别一个特定的内嵌框架。这样做意味着当target
属性被设置为特定内嵌框架的名称时,特定的内嵌框架可以被用作链接的目标。这里有一个例子:
`
View Terms and Conditions
在这种情况下,当点击“下一页”超链接文本时,page2.html
页面被加载到“terms”内嵌框架中。
视频
最重要的是,video
元素将 HTML5 带入了公众的视野。对于只使用网络的广大公众来说,YouTube 上的视频无法在 iPhone/iPad 上观看是众所周知的,因为 YouTube 使用 Adobe Flash 来显示视频。然而,有一种叫做“HTML5”的新解决方案可以在 iPhone/iPad 上显示视频。毫无疑问,对于大多数网络用户来说,这意味着什么,但对于你和其他像你一样的人来说,这意味着有一个新元素,video
,它允许视频文件嵌入到网页中,而无需使用任何第三方插件。这意味着在浏览器中播放原生视频。这与使用object
播放视频不同,因为object
只是将视频内容交给一个插件。
YouTube 仍然主要使用 Flash,但它在 www.youtube.com/html5 的 ?? 有一个 HTML5 播放器,用不支持 Flash 的设备观看视频将会退回到使用这个 HTML5 播放器。
这是对 HTML 规范的巨大补充,因为视频现在可以集成到任何可以使用其他 HTML 元素的地方。例如,CSS 和 JavaScript 可以与视频交互。然而,实施video
并非没有障碍。值得注意的是,标准的视频格式还没有出现,这导致需要以不同的格式编码视频来处理所有主要的 web 浏览器。让我们从那里开始讨论吧。
注意video
元素在各种主流视频浏览器中都有很好的支持,但是如果你想为老版本的浏览器提供支持,可以考虑一个解决方案,比如在[
html5media.info](http://html5media.info)
找到的,它提供了 JavaScript 代码来模拟老版本浏览器的video
(和audio
)元素的功能。
视频格式
视频文件可能是包含在网页上的非常大(文件大小)的一段内容。在网络之外,根据视频的长度和质量,一个特定的剪辑可以很容易地扩展到千兆字节的大小。因此,为了在 Web 上显示,需要对视频应用合适的压缩形式,以减小视频的大小,从而使通过 Internet 的传输变得可行。应用于视频的压缩形式被称为编解码器。由于视频是一种多媒体格式,因为它可以包含视频和音频,编解码器只是组成视频文件的一部分。视频和音频以所谓的容器格式放在一起,这就是视频文件的实际内容。Web 上有三种主要的视频容器格式:WebM、Ogg 和 MPEG-4。容器格式将包含一个用于视频压缩的编解码器和一个用于音频压缩的编解码器,以及关于视频的任何元数据,如字幕。特定容器格式中使用的编解码器可能会有所不同,但 Web 上常用的编解码器如表 5-2 所示,以及浏览器对这些格式的支持。
正如你在表 5-2 中看到的,目前没有一种格式可以在所有主流的网络浏览器上使用。遗憾的是,微软和苹果没有联合起来支持 WebM 或 Ogg, 8 但是,当前本地浏览器视频的现实情况是,如果广泛的浏览器兼容性是一个问题,那么网络开发者必须处理为同一视频文件提供多种格式的问题。在我们研究了许可问题之后,我们将看到如何做到这一点。
许可问题
除了浏览器支持问题,还有一个关于编解码器的问题需要注意。MPEG-4、H.264 和 ACC 中的编解码器包含专利技术。这意味着这些格式的某些使用受到 MPEG LA 联盟的版税,该联盟拥有这些专利的权利。你可以在网上免费播放这些视频,但如果你想对用户观看这种格式的视频收费,或者提供对这些格式的视频进行解码或编码的技术,情况就不同了。另一方面,WebM 和 Ogg 都是开放格式,不受任何已知专利的限制。
处理视频源
如果您只交付一个视频,那么标记就像在页面上放置一个图像一样简单。视频文件在src
属性中提供。和img
一样,也建议设置一个width
和height
,但要确保与视频源的长宽比相匹配:
如果你启动谷歌浏览器,发现它不能运行 MPEG-4 视频,不要对我提供的信息失去信心。传统上,Chrome 一直支持这种格式,但谷歌已经承诺取消对 H.264 编码视频的支持,转而支持其他两种开放格式,因此,今后还不能说它支持这种格式。
有一些方法可以让 Internet Explorer 9 和 Safari 至少支持 WebM,但这需要安装浏览器不附带的附加软件,在决定如何向这些浏览器中的用户提供视频内容时,这绝不是一个可以依赖的选项。
<video src="trailer.webm" width="320" height="240"></video>
然而,这并没有为不支持 WebM 格式的浏览器或不支持video
元素的浏览器提供任何后备内容。为了给视频提供多个源文件,另一个元素source
用于提供替代内容。source
元素有三个属性:src
、type
和media
。src
属性是指定视频文件的地方,就像在video
中一样。type
属性用于提供一个视频 MIME 类型,给 web 浏览器一个提示,告诉它正在处理什么样的视频格式。由于视频格式是一种容器格式,MIME 类型比其他文件类型更复杂。为了解决这个问题,MIME 类型定义了一个额外的codecs
参数来帮助缩小视频/音频格式的确切类型(使用容器格式和编解码器)。这看起来几乎像是source
上的一个附加属性,但是如果仔细观察嵌套的引号: 9 ,就会发现它包含在type
属性中
<video width="320" height="240"> <source src="trailer.webm" type='video/webm; codecs="vp8, vorbis"' /> </video>
此示例显示了源视频容器格式(WebM)以及所使用的视频和音频编解码器(VP8 和 Vorbis)。根据视频编码时设置的选项,codecs
参数中提供的实际值会有所不同。视频不仅是一种可以使用各种视频和音频编解码器的容器格式,而且这些编解码器也可以有自己的变体。特别是 H.264 有许多“配置文件”,可以选择这些文件在不同的环境中使用视频(例如,移动计算机和台式计算机)。表 5-3 显示了type
属性的公共值;显示的 H.264 编解码器是用于移动设备(基线)、标清数字视频广播(主)和高清视频广播(高)的配置文件。
type
属性不是必需的,但是如果您包含了它(建议您这样做),它将允许 web 浏览器在下载部分或全部文件之前确定是否可以播放特定的视频文件,从用户的角度来看,这将节省带宽和时间。
重要的是,引号应该是单引号,而不是双引号,因为 MIME 类型的编解码器参数希望它的内容在双引号中。
注意WHATWG 发布了一个 wiki,提供了 type 属性所需的 MIME 类型的有用摘要;可以在[
wiki.whatwg.org/wiki/Video_type_parameters](http://wiki.whatwg.org/wiki/Video_type_parameters)
找到。
最后一个属性media
,指定视频优化的设备和/或媒体。设置后,用户代理可以使用它的值来确定视频是否针对某一特定类型的设备(如移动电话)。与type
属性一样,用户代理可以检查该属性的值,以确定是否应该下载视频。这与锚元素中的属性相同(a
)。这将在第八章的中的“媒体询问”部分进行更深入的讨论。
我们仍然没有提供回退内容,因为最后一段代码仍然只提供一种视频格式。让我们改变这一点。web 浏览器将按顺序解析video
元素中的内容,在源文件列表中向下移动,直到找到一个可以播放的文件,因此在前面示例中的 WebM 文件下,我们将为 MPEG-4 和 Ogg 格式的视频再添加两行:
<video width="320" height="240"> <source src="trailer.webm" type='video/webm; codecs="vp8, vorbis"' /> <source src="trailer.mp4" type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"' /> <source src="trailer.ogv" type='video/ogg; codecs="theora, vorbis"' /> </video>
注意iOS 3 出现了一个 bug。 x 这导致浏览器在第一个视频源处停止,而不是在列表中向下移动。这一点已经得到了修补,但如果支持旧的 iOS 设备至关重要,请将 MPEG-4 视频移到来源列表的顶部。
同一个视频文件有很多副本!我们的目标是广泛的兼容性,但是如果你想精简,你可以不包含 Ogg 格式。旧版本的 Firefox 不能处理 WebM,所以 Ogg 的加入将帮助那些最近没有更新的 Firefox 用户。然而,我们可以用额外类型的回退内容来解决这些用户的问题,我们将在接下来讨论这些内容。
在视频源列表之后,可以提供额外的后备内容来处理不支持video
元素本身的情况。例如,可以包含object
元素来提供对 Flash 视频的回退:
<video width="320" height="240"> <source src="trailer.webm" type='video/webm; codecs="vp8, vorbis"' /> <source src="trailer.mp4" type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"' /> <source src="trailer.ogv" type='video/ogg; codecs="theora, vorbis"' /> <object type="application/x-shockwave-flash" data="videoplayer.swf" width="320" height="240"> </object> </video>
注意为了简洁起见,我简化了这里显示的 Flash 嵌入代码;在实践中,这可能会更加冗长,如果您使用 Adobe Flash IDE 生成的 HTML 包装器,肯定会如此,它包含额外的参数和格式来处理不同浏览器之间的特性。具体来说,Internet Explorer 不像其他浏览器那样处理data
属性,而是要求在嵌套的param
元素中定义 SWF,比如:<param name="movie" value="videoplayer.swf" />
。
虽然会包含一个额外的 SWF 文件来提供一个处理 Flash 视频的视频播放器,但幸运的是,不需要创建视频文件的第四个副本。Flash 方便地支持 H.264 视频格式,因此它将能够播放现有的 MPEG-4 视频。
注意对于一个优秀的 Flash 视频播放器,请查看开源媒体框架提供的播放器。甚至可以直接在浏览器中配置:[www.osmf.org/configurator/fmp/](http://www.osmf.org/configurator/fmp/)
。
可以添加进一步的后退;例如,可以添加海报图像(代替视频的静止图像),然后是文本回退。为了在文本回退中获得额外的可访问性,可以添加链接以允许下载视频文件。由于object
可以包含类似于video
的回退内容,该附加回退内容被放置在object
中,但是如果不包含object
,它可以被放置在视频源列表的下方:
<video width="320" height="240"> <source src="trailer.webm" type='video/webm; codecs="vp8, vorbis"' /> <source src="trailer.mp4" type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"' /> <source src="trailer.ogv" type='video/ogg; codecs="theora, vorbis"' /> <object type="application/x-shockwave-flash" data="videoplayer.swf" width="320" height="240"> <img src="poster.jpg" width="320" height="240" alt="" title="Movie trailer" /> <p><strong>Movie trailer.</strong><br /> Download video files as: <a href="trailer.webm">WebM</a>, <a href="trailer.mp4">MPEG-4</a>, or <a href="trailer.ogv">Ogg</a>. </p> </object> </video>
在不支持video
元素并且没有安装 Flash 播放器的浏览器中,浏览器会显示回退文本(图 5-5 )。
***图 5-5。*不支持video
元素的浏览器的后备内容可能包括图像、文本和下载视频的链接。
唷!这完成了用于处理各种不同用户代理的回退内容的级联。这不是看起来最简洁的标记块,但是如果访问您的视频内容是必不可少的,那么根据尽可能多的观众的需求对其进行定制是很重要的。
视频属性
此时,如果您测试这段代码,视频看起来不会比静态图像更壮观。这是因为默认情况下,视频不会自动播放,更重要的是,它不会有任何控制开始或暂停视频或调整声音。然而,正如您将看到的,这些和其他选项很容易通过video
元素上的各种可用属性来添加。
添加视频控制
要给视频添加控制按钮,只需要给video
添加布尔属性controls
。瞧。浏览器向视频添加一组基本但完整的控件。HTML5 视频的一个外在差异可能会让那些习惯于使用 Adobe Flash 时外观一致性的人感到有点震惊,那就是视频的控件在每个浏览器中看起来都不一样,因为它们都实现了自己的本地视频播放器(图 5-6 )。
***图 5-6。*不同浏览器中video
元素控件的外观:(从左到右)谷歌 Chrome、Safari、Firefox、Opera
如果你是一个有设计头脑的人,这种控件外观上的差异可能会有点令人失望,但重要的是要记住,因为视频是 HTML 格式的,所以可以用 CSS 和 JavaScript 创建一组自定义控件,在不同的浏览器上提供一致的外观。不过,这是另一个话题了(我们将在第七章中再次讨论)。
自动播放和循环播放视频
我提到当页面加载时视频不会自动播放,但是我们可以通过使用 Boolean autoplay
属性来改变这一点。通过添加这个属性…你猜对了…视频加载后立即开始播放。一般来说,包含这个属性并不是一个好主意,除非你的网站像 YouTube 或相关网站一样,用户点击一个链接就会被带到一个视频页面。在这样的站点中,视频是页面的焦点,期望的是视频会被立即播放。
另一个布尔属性(video
有很多)是loop
属性,它指定视频到达结尾时将会重播。就像自动播放视频一样,这通常只是在特定的场景下才是个好主意,比如视频是页面某种背景氛围的一部分。
预加载视频数据
preload
属性表明在用户点击播放视频之前,页面加载时是否预加载了任何视频数据。它可以取值none
、metadata
或auto
。如果它被设置为空字符串(""
,它将映射到auto
状态。包含这个属性是个好主意,因为预加载的默认行为是不标准的,由 web 浏览器自己决定;然而,尽管如此,它并不是一个严格的指令,而只是对网络浏览器的一个建议性属性。如果需要,web 浏览器可以基于任何用户偏好或其他因素(比如存在的autoplay
属性)覆盖这里的设置。例如,移动电话上的用户可能不希望预加载视频以节省带宽,移动浏览器可能会将此设置为首选项,然后在用户访问的页面上强制执行,尽管在preload
属性中设置了值。
因为preload
属性是建议性的,不同值的含义不能从字面上理解。值为none
表示根本不应该预加载视频(包括元数据信息)。值metadata
表示视频数据应该加载到视频本身,但不包括视频本身。元数据可以包括持续时间、维度等。值auto
表示任何内容都可能被预加载,这意味着可能只有元数据,但也可能意味着整个视频本身被预加载。
添加海报图像
poster
属性为视频设置海报图像,该图像在视频开始播放前代替视频显示(图 5-7 )。这个图像可以是任何东西,但它通常是视频的第一帧,因为海报图像旨在让用户了解视频的样子。给定的值是要使用的图像的位置,例如:
<video width="320" height="240" controls poster="trailer-poster.jpg"> …
这段代码将使用海报图像trailer-poster.jpg
,它位于包含这段代码的 HTML 页面所在的目录中。
注意在生产环境中,你会想把图像放在它们自己的目录中,以保持你的网站文件有条理。
***图 5-7。*图 5-6T5 中的飞鸟视频海报图片
将视频静音
布尔属性muted
在添加时,意味着在视频第一次播放时将声音设置为静音(用户可以单击控件中的声音图标来进行更改)。然而,在撰写本文时,各大浏览器尚未实现该功能。
设置跨产地策略
属性crossorigin
包含在称为跨源资源共享(CORS)的规范中,该规范确定了如何在不同的网站域之间共享视频。它有值anonymous
或use-credentials
(也可以给出一个空字符串"",它映射到anonymous
)。有关 CORS 的更多信息,请参见 W3C 在[www.w3.org/TR/cors/](http://www.w3.org/TR/cors/)
或[
enable-cors.org/](http://enable-cors.org/)
关于该规范的参考资料。
媒体团体
mediagroup
属性旨在支持将视频或视频轨道分组在一起,以便它们可以作为一个组同时被控制。它尚未获得浏览器支持,但当它获得支持时,想法是这样的:想象一个特定的视频演示,它也有一个伴随的视频,视频音频的手语翻译。播放主视频时,也应播放手语视频。属性将这两个视频联系在一起,因此浏览器知道如果一个视频播放,另一个应该同时播放。这可能意味着两个视频是嵌入在同一页面上的独立视频文件,也可能意味着每个视频都包含在一个视频轨道中。请记住,视频格式是一种容器格式,这意味着它至少包含两种类型的媒体:视频轨道和音频轨道。作为一种容器格式,附加的视频和音频轨道可以嵌入到单个文件中。使用前面的例子,特定视频可以具有用于主演示的视频和音频轨道,但是具有手语视频作为第二视频轨道。在这种情况下,页面上可能会出现两个video
元素,每个元素访问同一视频源文件中的不同视频轨道。然后,通过在它们的mediagroup
属性中设置相同的名称,将两个视频的回放联系在一起。
这个属性为 Web 上的视频广播提供了强大的可能性,所以请关注它的进一步开发和实现!
注意html 5 视频权威指南 (Apress)的作者 Silvia Pfeiffer 在她的博客上有一个关于如何使用这一属性的很好的总结:[
blog.gingertech.net/2011/05/01/html5-multi-track-audio-or-video/](http://blog.gingertech.net/2011/05/01/html5-multi-track-audio-or-video/)
。
音频
不可否认,HTML5 中的音频还有很长的路要走。对网页上的音频进行控制,就像 Adobe Flash 能够做到的一样,是目前开发的一个活跃部分。 11 基本音频,允许用户开始、停止和调整音频剪辑的音量,使用新的audio
元素可用。这个元素实际上只是没有运动图像部分的video
元素。这些控件看起来像由浏览器创建的控件;例如,比较图 5-8 中的和图 5-7 中的中的。它有以下几个属性,看“视频”部分应该都很熟悉:src
、preload
、autoplay
、loop
、muted
、controls
、crossorigin
、mediagroup
。同样像video
元素一样,audio
支持source
元素和嵌套在其开始和结束标记之间的回退内容。带有回退内容的audio
元素看起来与视频元素非常相似:
10 以 http://audiotool.com/app 见为例。Audiotool 是一个基于云的应用,用于在 Adobe Flash 平台上创建音乐。
11 参见 W3C 上的 Web Audio API:dvcs . w3 . org/Hg/Audio/rawfile/tip/Web Audio/specification . html
。
<audio controls> <source src="report.oga" type='audio/ogg; codecs="vorbis"' /> <source src="report.m4a" type='audio/mp4; codecs="mp4a.40.2"' /> <p>Audio not supported. Download audio files as: <a href="report.oga">Ogg</a>, <a href="report.m4v">ACC</a>p> </audio>
注意如果controls
属性被关闭,默认情况下什么都不显示!
这里和视频的区别在于,音频没有宽度和高度(可以用 CSS 调整回放栏的大小),显然源文件的格式也不一样。
***图 5-8。*谷歌浏览器audio
元素的出现
音频格式
音频格式在很大程度上是阅读“视频”部分所熟悉的。同样的浏览器支持也存在差异,但这并不值得庆祝。参见“视频格式”部分的表 5-2 并查看音频编解码器栏。与视频一样,可以使用 Ogg 和 MPEG-4 容器格式,但这次没有视频轨道。因此,Ogg 音频将在 Google Chrome、Firefox 和 Opera 中工作,而 MPEG-4 音频将在 Safari、Chrome 和 Internet Explorer 中工作。Ogg 格式使用 Vorbis 编解码器(WebM 对音频也使用这种编解码器),所以当谈到音频时,这种格式被称为 Ogg Vorbis,并具有文件扩展名.oga
(甚至.ogg
)。MPEG-4 容器对其音频编解码器使用高级音频编码(ACC)。由于是在 MPEG-4 容器中,所以一般用文件扩展名.m4a
来和.mp4
区分。ACC 是在数字音乐中广泛使用的 MP3 格式的继承者(用于便携式音乐播放器,如苹果 iPod)。ACC 编码的音频可以在网上自由播放(除了内容本身需要的任何许可),但它不是完全没有专利的。开发 ACC 编码和解码工具需要许可证(如果你好奇,这里列出了许可证费用:[www.vialicensing.com/licensing/aac-fees.aspx](http://www.vialicensing.com/licensing/aac-fees.aspx)
)。
注意WAV 音频格式也可以使用,有时会包含在回退内容列表中。WAV 是一种未压缩的音频格式,这意味着与 Ogg Vorbis 和 ACC 相比,这些文件相对较大。MP3 也可以使用,但 MP3 比 ACC 有更严格的许可问题,所以最好避免使用 ACC。WAV 和 MP3 也没有得到所有主流浏览器的普遍支持,所以在这方面使用它们没有什么好处。
字幕音轨
HTML5 增加了一个伟大的新功能,开放了视频的可访问性。HTML 规范中添加了track
元素,定义了一种向video
和audio
元素添加字幕和相关定时文本轨道的方法。所谓“添加”,我的意思是它已经被添加到规范中,因为 web 浏览器制造商仍在努力实现该元素的功能。总的思想是用文本信息来标记文本文件,然后将该文本信息与视频或音频一起加载,并且通常定时在媒体资源的整个回放过程中的点上呈现。然后,track
元素将被放置在video
或audio
元素中,并用于加载定时文本文件。track 元素定义了五个属性:kind
、src
、label
、srclang
和default
。
注意如果你想在今天的视频中提供隐藏字幕,请查看字幕项目([
github.com/cgiffard/Captionator](https://github.com/cgiffard/Captionator)
)。这个项目使用 JavaScript 来模拟track
元素的功能,以便在 web 浏览器赶上它的实现时可以使用它。
kind
属性决定了添加哪种文本轨道(见表 5-4);如果省略,默认将加载的文本文件标识为subtitles
类型。src
属性定义了要加载的文本文件的地址。文本文件的实际格式尚未定义,但它可能是一种称为 Web Video Text Track (WebVTT)的格式。文件扩展名为.vtt
。然而,也可以使用其他格式,例如定时文本标记语言(TTML), 12 。WebVTT 格式目前只在 HTML(5)的 WHATWG 版本中定义, 13 但是 W3C 已经公布了一个提议的 WebVTT 工作组章程。 14
12 见www . w3 . org/tr/2010/rec-TTF 1-dfxp-20101118/
记住 WHATWG 使用无版本开发,所以 HTML 和 HTML5 是一回事。
14 参见 www.w3.org/2011/05/google-webvtt-charter.html的
label
属性给出了文本轨道内容的人类可读描述,例如,网络浏览器可以向用户显示该文本轨道以改变成不同的字幕语言。布尔属性default
设置最初启用哪个文本轨道。一个特定的视频可以包含多种语言的字幕,因此这个属性可以用来设置要使用的默认语言。说到语言,最后一个属性srclang
正是为了这个目的:定义定时文本轨道使用的语言。
一个示例实现可能如下所示:
<video width="320" height="240"> <source src="trailer.webm" type='video/webm; codecs="vp8, vorbis"' /> <source src="trailer.mp4" type='video/mp4; codecs="avc1.4D401E, mp4a.40.2"' /> <track kind="captions" label="English Captions" srclang="en" src="trailer_cc_en.vtt" default /> <track kind="subtitles" label="English Subtitles" srclang="en" src="trailer_st_en.vtt" /> <track kind="subtitles" label="German Subtitles" srclang="de" src="trailer_st_de.vtt" /> <track kind="subtitles" label="French Subtitles" srclang="fr" src="trailer_st_fr.vtt" /> </video>
注意有一个提案正在讨论中,它允许track
元素包含source
元素,因此可以以各种格式提供定时文本。这将作为浏览器的后备机制,就像它对视频和音频媒体一样。
编码音频和视频
编码媒体文件是一个很容易写满一本书的主题,所以我将向您介绍一些工具,它们将帮助您生成 WebM、Ogg、Ogg Vorbis、MPEG-4 和 ACC 文件:
- 手刹:这是一个用于生成
.mp4
文件的开源代码转换器。参见[
handbrake.fr](http://handbrake.fr)
。 - 这是一个用于执行 ogg 编码的 Firefox 扩展。参见
[
firefogg.org](http://firefogg.org)
。 - FFmpeg :这是一个非常强大的开源工具套件,包括一个用于在不同媒体格式之间转换的命令行工具。它被用在其他工具中,比如 Firefogg。参见
[
ffmpeg.org](http://ffmpeg.org)
。 15 - Miro 视频转换器:这是一个易于使用的拖放式转换器,适用于网络和移动设备上的视频格式。然而,这只是苹果电脑。参见
[www.mirovideoconverter.com/](http://www.mirovideoconverter.com/)
。 - VLC :这是一款灵活的媒体播放器,可以轻松处理你可能遇到的所有网络视频和音频格式。它有一个导出向导,可以在不同格式之间转换。参见
[
videolan.org/vlc/](http://videolan.org/vlc/)
。 - Adobe Media Encoder :如果你的电脑上有 Adobe 软件,你可能有一个 Adobe Media Encoder 的副本,它可以导入流行的视频和音频格式,并以各种不同的格式进行编码。它为不同的视频传输情况提供了大量的预设。
最后但同样重要的是
新的canvas
元素用于将可脚本化的位图嵌入到网页中。这实质上意味着现在可以在特定维度的页面上放置一个空白图像,可以从 JavaScript 代码中绘制和图形化操作该图像。酷!元素本身是非常基本的;它只定义了两个属性,width
和height
,用于指定画布的尺寸。像本章前面的元素一样,它包含了通过在开始和结束标记之间放置额外的 HTML 来提供回退内容的能力,如下所示:
<canvas width="600" height="300"> <p>The canvas element is not supported!</p> </canvas>
像其他元素一样,在开始和结束标记之间可以放置比文本更多的后备内容,但是canvas
非常独特,所以可能很难找到文本之外的合适的替代内容。Flash 可以作为替代品,所以你可以,例如,使用canvas
构建一个交互式应用,并在 Flash 中复制该功能以提供后备内容。然后在canvas
中使用object
元素(或者更罕见的是embed
元素)。无论如何,如果您在页面上尝试前面的代码,它不会非常令人兴奋,因为它只会在页面布局中创建一个 600 x 300 像素宽和高的空白区域。canvas 的真正力量来自于使用 JavaScript 动态生成图像,我们将在第七章的中讨论。
总结
正如您所看到的,HTML5 使得在您的网页中包含多媒体变得更加容易。语义也得到了改进,因为有了专用于图像、视频和声音的元素,而不是像过去那样(至少就视频和音频而言)许多类型的媒体依赖于通用的object
元素。为了处理 web 页面向更丰富的媒体的过渡,新元素(和一些旧元素)包含了一致且直观的后退机制。object
、iframe
、video
、audio
、canvas
以及可能很快出现的track
都为处理替代内容提供了相同的机制。随着定时文本规范的发展和浏览器实现缺失的功能,还有更多的东西值得期待。也许有一天,一种媒体格式将出现在所有主流网络浏览器都支持的视频(和音频)中,就像静止图像一样,但与此同时,我们将不得不习惯于对媒体进行两到三次编码。
当使用 ffmpeg 命令行工具时,参考这个有用的帖子来获得命令列表:【www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs】??。
六、CSS3
在这一章中,我们将主要从 HTML 转向一种完全不同的语言和规范。层叠样式表(CSS)的时候到了。CSS 可能不是 HTML,但如果没有 CSS,HTML 就没什么可看的了,所以掌握其中一个就必须在一定程度上掌握另一个。
因为这本书是关于 HTML 的,所以我们将在这一章花大部分时间来研究 HTML 和 CSS 之间的关系以及两者是如何交互的,但是我们也将探索纯粹的 CSS 主题。CSS 规范目前正处于第三个迭代阶段,因此我们将浏览您应该知道的必要的核心 CSS 概念,然后深入了解新增内容的细节。这里有一点技术术语要讲,但是这样做,我们将为我们探索页面设计的可能性搭建舞台。从可用性的角度来看,这些方面和标记的语义一样重要。
当前状态:CSS2.1
CSS 被分成不同的层次,这些层次是类似于 HTML 的 W3C 版本的规范版本(HTML 3.2、HTML 4.01、HTML5 等等)。当前稳定和完整的级别是 CSS level 2 revision 1,也称为 CSS2.1。由 W3C 开发的文档会经过审查阶段,用于确定特定规范的稳定性(表 6-1 )。
开发一个完全成熟的规范通常需要几年的时间,并且随着技术条件的变化和新的工作草案的出现,一个特定的文档可能会在不同的成熟度级别之间来回徘徊。例如,CSS2 是 1998 年发布的推荐标准;CSS2.1(记住,CSS2 的一个修订版)花了十多年才成为 W3C 推荐标准,但最终在 2011 年 6 月 6 日完成了!显然,这并不意味着我们必须等到 2011 年 6 月才能使用 CSS2.1 中的特性,但这确实意味着规范在审查期间可能会发生变化。作为比较,考虑一下这个:W3C 的 HTML5 版本目前处于工作草案状态!预计其进展到推荐状态可能需要十年或更长时间,这并不是不合理的。然而,正如这本书所展示的,今天 HTML5 中有很多可用的东西。它的可用性还不到十年,但是规范(以及后续的实现)将在这段时间内不断发展。您还看到了尚未被任何主流 web 浏览器实现的特性(例如,track
元素)。如果规范处于 W3C 推荐状态,就不会发现未实现的特性(WHATWG 的 HTML 无版本规范对此持不同观点)。那么,现在你已经在更广阔的图景中了解了 CSS 的状态,下一个 CSS 级别,CSS 级别 3 的状态是什么?
CSS3 模块
CSS3 的定义与其前身不同。CSS3 不是一个覆盖整个规范的庞大文档,而是被分解成几十个处于不同成熟状态的模块。这允许 CSS 的自包含特性更快地成熟,而不会因为成为更大的草稿文档的一部分而受到阻碍。表 6-2 列出了从 W3C 选择的模块。这让您领略了 CSS3 中的一些特性。您可以在[www.w3.org/Style/CSS/current-work](http://www.w3.org/Style/CSS/current-work)
找到模块的完整列表。
使用 CSS
现在您已经从组织的角度对 CSS3 有了一个大概的了解,让我们来看看它的正确用法。这可能对您来说很熟悉,如果是这样,请随意跳到 CSS3 中特定模块的讨论,但至少浏览一下这一部分,因为 CSS3 中引入了使用 CSS 的新方面。
附加样式表
在 HTML 中使用 CSS 之前,您需要在页面上附加一个样式表,以便 CSS 样式可以访问与它们相关联的 HTML。有几种方法可以做到这一点,但是应该通过使用link
元素来实现。link
第一次出现在第二章,但是我们没有深入讨论使用它来链接样式表。您可能还记得,这个元素出现在页面的head
部分,而rel
属性用于指示链接的文档是一个样式表:
`
CSS rules!
` 注如第二章所述,当链接到一个样式表时,你可以省略type
属性,因为这是默认情况下链接的文档的属性。
title
属性对于样式表上下文中的link
元素有特殊的意义。该属性用于区分链接的不同种类的样式表。您可能会问自己,“有哪些不同种类的样式表?”放心吧,都是 CSS 没有别的语言可以学了!样式表的种类决定了特定样式表相对于页面上链接的其他样式表的优先级。有三种类型可以添加到文档中:
- 持久:始终应用于文档
- 首选:应用于文档,除非应用了替代样式表
- 替代:首选或持久样式表的替代
持久化、首选和备选样式表之间的主要实现差异在于,持久化样式表没有属性,而首选和备选样式表有。
所有持久的样式表都将应用于 HTML 文档,不管附加了多少个。这是应用样式表最常见的方式。上述代码片段中的样式表是一个持久样式表。相比之下,并非所有的首选和替代样式表都可以应用。如果多个首选和备选样式表链接到一个 HTML 文档,则只有一个样式表将被选择并在页面上使用。替代样式表的工作方式类似于首选样式表,还意味着它可以替代页面上指定的首选样式表。出于偏好或可访问性的原因,用户可以选择替代的样式表。
下面显示了一个附加了持久样式表、首选样式表和两个备选样式表的页面:
`…
…`
在这个示例页面中,main.css
是持久样式表,用于在所有情况下设置页面的所有样式。colors-generic.css
是一个首选样式表,它将颜色相关的样式添加到页面中。有两个可供选择的样式表供红绿光谱色盲用户选择:protanopia 的colors-protanopia.css
和 deuteranopia 的colors-deuteranopia.css
。据推测,激活这些样式将改变页面颜色,以补偿色盲可能使内容难以查看的区域。这些替代样式表如何变得可用取决于使用中的用户代理,但是一些浏览器提供了一个菜单选项,用户可以在其中选择替代样式(图 6-1 )。
***图 6-1。*在 Firefox 中,可以在视图菜单下选择首选和备选样式表。
您还可以使用meta
元素设置首选样式表。通过将content
属性设置为其中一个样式表的标题,可以使用http-equiv="Default-Style"
pragma 指令来设置首选样式表。例如,以下代码将标题为“蓝色主题”的样式表设置为默认样式表:
`…
使用meta
元素设置默认样式表提供了一个中心点,在这里可以选择页面上的首选样式。这提供了使用服务器端脚本动态更改一个值的可能性,例如,基于用户交互或其他变量。
CSS 样式规则
将样式表附加到页面后,您需要访问页面上的 HTML 元素并设置它们的样式。一个样式规则是你将创建的代码,用于样式化 HTML 内容的一部分。样式规则以一个选择器开始。选择器将在下一节中详细介绍,但简而言之,它们是一组用于选择 HTML 页面不同组件的规则。选择器后面是花括号({和}),用于括起特定样式规则的所有样式信息。括号之间是成对的 CSS 属性和值,它们合在一起被称为声明。为了提供各种样式效果,一个 CSS 样式规则可以有一个或多个声明,每个声明以分号结束。它们可以,而且通常会,跨越多行。图 6-2 总结了整个样式规则的语法和术语。
***图 6-2。*CSS 样式规则的术语和语法
在图 6-2 的例子中,选择器是p
,它访问页面上所有的段落元素(p
);然后,样式声明将其背景颜色设置为红色,文本颜色设置为黄色(这是一个简单的例子;我们稍后将讨论正确的颜色格式)。
一个警告:浏览器 CSS3 属性前缀
与 HTML5 一样,大多数 CSS3 模块规范都处于草案状态,并且正在积极完善,这意味着并非所有功能都可以在所有浏览器上实现。由于规范是不断变化的,web 浏览器制造商对在规范中实现特定的 CSS 属性保持警惕是有道理的,如果它在下一周可能会发生剧烈变化。折衷的办法是使用特定于特定浏览器的前缀约定,将一个属性标记为“正在进行的工作”如果 web 开发人员看到前缀,他们知道“啊,这个属性将来可能会改变。”一旦属性被标准化,web 浏览器就实现规范中列出的实际属性,web 开发人员可以停止使用带前缀的版本。主要浏览器的 CSS 属性前缀如下:
-webkit-
适用于 Safari 和谷歌浏览器-moz-
对于火狐来说-o-
对于歌剧来说-ms-
对于 Internet Explorer
例如,有一个名为transform
的 CSS 属性可用于旋转一段内容,以及其他效果。问题是该属性是处于工作草案状态的模块规范的一部分,因此 web 开发人员可以使用以下属性:
-webkit-transform
-moz-transform
-o-transform
-ms-transform
当创建 CSS 样式规则时,web 开发人员将使用每个浏览器前缀重复该属性,并在列表末尾使用相关规范中定义的实际属性结束:
-webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg);
随着时间的推移,当属性被标准化并且 web 浏览器供应商变得符合最终规范时,属性的前缀版本可以被移除。
注意如果你在本章中遇到一个 CSS 属性在你测试它的浏览器中不工作,试着添加适当的浏览器前缀,看看是否是这个问题。
基本 CSS 选择器语法
选择器是 CSS 样式声明和它们需要影响的 HTML 内容之间的桥梁。您将遇到的大多数选择器可能属于以下三类之一:类型选择器、ID 选择器和类选择器。在适当的时候,我们还会谈到其他类别,但这三个类别是最常见的。
类型选择器允许任何元素被它的 HTML 元素类型名引用,比如p
、article
、em
、li
、blockquote
等等。ID 选择器允许引用一个特定的元素,该元素的全局id
属性被设置为某个值。类选择器的工作方式与 ID 选择器相同,除了它们引用所有属性设置为某个值的元素。这三类选择器被称为简单选择器,并在表 6-3 中进行了总结。
正如您在表 6-3 中所看到的,ID 选择器在 ID 值前面加上一个散列符号。另一个例子可能是这样的:
#company-name { font-weight:bold; }
前面的代码将访问 HTML 中具有company-name
的id
属性的元素的文本内容,并将其加粗,例如这部分 HTML 中的“City Press”文本:
<h1 id="company-name">The City Press</h1>
类选择器以类似的方式工作,除了使用句点来引用class
属性值。下面的代码将在 HTML 元素周围添加边框和填充,这些元素的class
属性设置为alert
:
.alert { border:1px solid red; padding:5px; display:inline-block; }
注意你可能不熟悉代码display:inline-block
。它决定了样式元素在页面上的布局。确切的意思将在本章的“CSS 盒子模型”部分解释。
前面的选择器可能引用以下 HTML:
<p class="alert">Warning!</p>
与 id 不同,类的一个优点是单个元素可以应用多个类,因此样式可以在单个元素中一个接一个地层叠。例如,下面使用了前面的.alert
规则,外加一个额外的.critical
规则:
`
Warning! do not feed the kittens, they are
dangerous
附加样式规则大写文本并将其涂成红色:
.critical { text-transform:uppercase; color:red; }
总的来说,这在一些文本周围创建了一个警告框,其中一些文本仅仅是阅读的关键,而另一些是非常重要的警告。最终样式的文本看起来像图 6-3 。
***图 6-3。*通过在一个元素上设置多个样式化的class
属性值,类样式规则可以彼此“重叠”。
组合选择器
CSS 在如何定义选择器方面非常灵活,如果您小心使用 HTML 和 CSS 结构,有很多机会节省输入。例如,如果一组选择器应用了相同的 CSS 声明,这些样式规则可以合并到一个逗号分隔的列表中,称为选择器列表,它会将一组 CSS 声明分配给列表中的所有选择器。例如,以下代码会将页面上所有标题的颜色更改为红色:
h1,h2,h3,h4,h5,h6 { color:red; }
这就节省了反复输入相同 CSS 声明的空间和时间;选择器仍然是独立的实体。然而,可以将选择器组合成集合选择器,称为复合选择器,它可以用来匹配网页结构中的特定内容。考虑前面的一段代码:
<span class="alert critical">dangerous</span>
该元素应用了两个类,因此可以使用三个不同的选择器来设置样式:
… span.alert { } span.critical { } span.alert.critical { } …
前两个类选择器简单地挑选出一个或另一个类——它们将引用任何分配了两个类值之一的span
元素。第三个将两个类值链接在一起,创建一个复合选择器,该选择器将只引用应用了两个类的span
元素。
如果类型选择器被关闭(就像前面的例子一样),所有的元素都将被搜索匹配。这相当于将*选择器(称为通用选择器)与另一个选择器(在本例中为类别选择器)结合使用:
.critical { color:red; } /* match all elements for class 'critical' */ *.critical { color:red; } /* match all elements for class 'critical' */ p.critical { color:red; } /* match paragraph elements for class 'critical' */
注意你可能对此很熟悉,但是如果你不经常使用 CSS,你可能不知道前面代码中/* text */
部分的用途;这就是你如何在 CSS 中写一个被浏览器忽略的注释。
仔细看看最后一行代码,p.critical…
;这是两个放在一起的简单选择器,一个类型选择器和一个类选择器。这就是复合选择器得名的原因,因为它们是复合在一起的简单选择器。值得注意的是,它们总是以类型选择器、通用选择器或无选择器开始(在这种情况下,通用选择器是隐含的)。在类型选择器之后,它们可以有一长串附加的简单选择器。这里有一个例子:
em.alert.critical.item { }
该选择器仅引用正确类型的 HTML 元素,并且应用了正确的类,如下所示:
<em class="alert critical item">Rocket fuel</em>
注意两种以上的选择器可以组合;例如,em.alert.critical#item
是一个有效的复合选择器,但实际上这是不必要的冗长,因为页面上应该只有一个 id 为item
的元素,所以没有必要包含类选择器(甚至类型选择器)。
复合选择器非常强大,但是还有其他语法选项可以匹配各种 HTML 结构。让我们继续讨论复杂的选择器!
组合子和复杂选择器
表 6-4 列出了一组称为组合子的语法规则,它们决定了如何描述不同 HTML 元素之间的关系,以便用选择器引用它们。
复合选择器使用组合器将一个或多个复合选择器组合在一起。因为它们是由复合选择器组成的,所以它们总是在组合子的每一边至少使用一个类型选择器。虽然组合子的两端都包含一个类型选择器,但是复合选择器的其余部分可以包含链接在末尾的任何类型的选择器。它甚至可以包含组合子和选择器的混合。例如,想象一个允许客座作家在网站上创作内容的网站。可能会创建一个部分,并为其指定一个类,将其指定为包含来宾内容:
<body> <header>
`
Page header
Subtitle
Guest content
Guest article
`
一个复杂的选择器可以用来选择一篇文章中的页脚,这是页面上最重要的客人部分,目的是强调作者,或者你有什么:
header + section.guest article > footer { border:1px solid red; }
从右向左阅读,这将为页脚添加一个红色边框,该页脚直接包含在一篇文章中,该文章是设置了类guest
的部分的后代。该部分必须直接与标题相邻。因为页眉应该出现在页面的顶部,所以这将只选择顶部部分(如果页面上有多个部分)。要查看这一演示,请在前面的代码块中创建该部分的精确副本,并将其粘贴到现有代码块的下方。如果您应用样式,您将看到只有第一节的文章内的页脚应用了样式。要选择任何文章中的页脚,可以将相邻的兄弟组合符(+
)更改为新的通用兄弟组合符(~
),它将匹配任何包含标题兄弟类guest
的部分。
注意虽然创建长链的选择器和组合子是可能的,但是你应该尽可能地创建简洁的选择器。不必要的复杂选择器会变得难以理解,并且难以针对 HTML 中的变化进行维护。然而,重要的是要知道创建这样复杂的选择器是可能的,因为当你确实需要它们的时候!
高级选择器
除了简单的类型、ID 和类选择器之外,CSS 中还有各种各样的选择器,如果需要的话,它们都可以链接到一个类型选择器上。其他类型的选择器包括:
- 属性选择器:可以根据元素的属性、属性值或部分属性来访问元素。
- 伪类:可以根据元素结构中的模式、它们对用户交互的响应、它们被分配的语言或其他可能无法直接在元素上设置的属性来选择元素。
- Pseudoelements :可以访问标记中没有明确定义的元素部分,例如元素中的第一个字母或第一行文本。
还有更多!正如你很快就会看到的,CSS 有强大的逻辑规则,可以用来挑选特定的 HTML 块。让我们浏览一下其他选择器,这样您就可以看到新的特性了。
属性选择器
从技术上讲,前面的 ID 和类选择器是属性选择器,但是它们的语法不同于其余的属性选择器,所以我把它们分开了。这些选择器可以引用属性中具有特定模式的元素。表 6-5 总结了这一串。
正如您所看到的,属性选择器并不是新的,但是其中的三个是:[A^="V"]
、[A$="V"]
和[A*="V"]
。这将允许您匹配一个元素,该元素具有开始、结束或包含特定文本的属性值。值得指出的是,属性选择器(任何种类)和其他选择器一样,可以链接成复合选择器:
a[href][rel] { color:orange; }
这个选择器将匹配任何同时设置了href
和rel
属性的锚元素(a
)。通过在href
属性中包含 URL 的搜索模式,可以将搜索范围缩小到超链接锚点的特定子集。例如,在我自己的站点上,也许我想从我自己的域中预取页面,但是我想使这些链接的样式不同于页面上的其他链接。我可能会创建一个这样的规则:
a[href*="anselmbradford.com"][rel="prefetch"] { color:orange; }
这将只匹配链接到包含“anselmbradford.com”作为其 URL 的一部分并且将它们的rel
属性设置为prefetch
的资源的超链接锚点,如下所示:
<a href="http://blog.anselmbradford.com" rel="prefetch">Anselm's blog</a>
属性选择器的可能性很多。示例包括从a
、img
或object
源 URL 中挑选特定的文件类型,以便根据文件类型处理样式。作为另一个例子,多态的input
元素可以很容易地使用属性选择器来设计不同的类型。例如:
input[type="password"] { border:1px solid red; }
该样式规则将在所有密码输入表单控件周围添加红色边框。
当在一个元素上使用类选择器时,问问自己是否有一个已经存在的属性可以被挑选出来。如果可以使用现有的属性,仅仅为了样式的目的而添加一个额外的类属性会产生额外的代码,以后可能需要维护和更新这些代码。例如,如果前面的样式规则更改为使用类选择器而不是属性选择器,这意味着您必须记住将正确的类属性值添加到添加到页面的任何新密码输入表单控件中,而不是依赖于它们现有的类型属性,无论如何它们都需要有。
样式链接和片段标识符目标
下一组选择器与超链接的行为有关,称为位置伪类选择器 ( 表 6-6);这个小组处理引用未访问和已访问的链接和片段标识符。:link
和:visited
选择器是不言自明的,也不是什么新东西,所以让我们看看新的:target
选择器以及它如何处理片段标识符。
在第三章的“超链接”一节中介绍了片段标识符。在该章中,给出了一个锚元素的示例:
<a href="newpage.html#parttwo">link</a>
通过将片段标识符#parttwo
与id
属性值partwo
匹配,该元素链接到链接页面上的特定 HTML 元素,因此页面将滚动到如下元素:
<h3 id="parttwo">Part Two</h3>
因为这使用了一个片段标识符,所以这个元素可以使用:target
选择器进行样式化。此选择器的样式规则将应用于作为出现在 URL 中的片段标识符的目标的任何元素(它应该只是页面上的一个元素):
:target { text-decoration:overline; }
当片段标识符出现在页面的地址栏 URL 中时,页面上作为目标的元素将接受:target
选择器规则的样式。从 URL 中移除片段标识符,样式就消失了。这在用户可以滚动离开目标元素的情况下特别有用;通过对其进行不同的样式设计,可以使用户在再次寻找时不会看不到它(可以添加样式,以便将目标元素固定在适当的位置,这样即使页面滚动 1 ,它也会保留在视图中)。
这是使用 position:fixed property 和 value 来完成的,这将在本章的后面讲到。
选择用户交互中涉及的元素
除了:link
和:visited
之外,选择器:hover
和:active
( 表 6-7 )通常与链接相关联。但是,它们可以用来响应任何元素上的用户交互。
例如,下面是一个简短的无序列表:
`
- Milk
- Eggs
- Carrots
- Butter
- Almonds
- Flour
为了帮助用户直观地找出他们所悬停的项目,可以使用:hover
选择器来设置一个样式规则,将背景颜色改为浅灰色:
li:hover { background-color:#ccc; }
只有当用户将光标悬停在列表中的项目上时,才会应用这种方法,使用户悬停的行变暗(图 6-4 )。
图 6-4。:hover
选择器只允许当鼠标光标在一个元素上时应用一个样式。
:active
选择器用于对用户激活的元素进行样式化,例如,这意味着鼠标按钮已经被按在元素上,但是还没有被释放。这个选择器可以用在当元素被按下时,但在释放元素之前元素的外观应该改变的任何地方,比如在拖放应用中。它通常用在链接锚点上,当单击链接时,闪烁不同的颜色或其他样式变化。
选择表单控件元素
表 6-8 列出了一组选择器,用于设计用户界面组件的各种状态,特别是 web 表单控件。除了前四个选择器,从:indeterminate
开始的选择器是 CSS3 基本用户界面模块的一部分(本章中显示的其他选择器是选择器第三级模块的一部分)。
注WC3 的 CSS 工作组在 2011 年 8 月 12 日发布了一份文档,指出 CSS 的下一个版本将在同一模块中指定本章中的所有选择器,命名为选择器第 4 级。
2 在用户点击文本字段后应用。
在输入有效的电子邮件地址之前, 3 输入将无效。
选择器不只是用于表单,它的行为在 web 表单的上下文中是最容易看到的。应用于文本字段的:focus
选择器在用户点击字段并开始输入时添加一个样式。当用户单击另一个字段时,焦点会移动,样式规则会相应地应用。以出现在第四章中的 web 表单为例,我们可以添加这个简单的样式:
input:focus { border:5px solid black; }
该样式规则将在用户点击开始输入的任何文本字段周围添加一个突出的黑色边框。这将清楚地显示哪个文本字段当前有焦点(图 6-5 )。
***图 6-5。*应用于文本字段的:focus
选择器允许在用户选择字段开始输入时附加样式。
:enabled
和:disabled
选择器都使用布尔disabled
属性。:enabled
选择器将对没有具有disabled
属性的任何表单元素应用样式,而:disabled
将对具有属性的任何元素应用样式。
在 JavaScript 中,复选框需要将其不确定属性设置为 true。
:required
和:optional
选择器类似于:enabled
和:disabled
选择器,除了它们由布尔required
属性的存在或不存在来触发。
:read-only
和:read-write
选择器在布尔readonly
属性存在或不存在的情况下工作。一些表单控件,比如文件上传控件(input
中设置的type="file"
),默认情况下是只读的,将由:read-only
选择器选择,而不添加readonly
属性。
:valid
和:invalid
选择器的工作方式类似于:required
和:optional
选择器,除了它们没有直接绑定到required
属性。:valid
选择器将选择任何没有附加表单验证约束的元素(required
属性、pattern
属性等等),而:invalid
将选择任何有约束的元素,如果这些约束没有被满足的话。
:checked
和:indeterminate
选择器通常应用于复选框和单选按钮。当这些控件处于选中状态时,:checked
选择器将应用于这些控件。:indeterminate
稍微难一点触发。复选框可能既不处于选中状态,也不处于未选中状态。复选框控件有一个可从 JavaScript 访问的布尔型indeterminate
属性,可以设置该属性,这将触发该选择器的适用性。假设您有一个复选框控件:
<input id="undecided" type="checkbox" />
然后从 JavaScript 访问id
,将复选框的状态设置为不确定:
function setState() { var check = document.getElementById("undecided"); check.indeterminate = true; } window.onload = setState;
使用这个脚本,复选框将被置于不确定状态(图 6-6 ),然后可以使用:indeterminate
选择器应用样式。
***图 6-6。*处于不确定状态的复选框
注意你可能会发现应用于复选框的样式很无聊。Safari、Chrome 和 Firefox 不支持为复选框添加边框和背景。要查看您的样式是否被应用,请尝试设置高度,如height:100px;
。复选框周围的任何内容都应该被推开。
default:
样式将应用于在给定上下文中被指定为默认的元素;例如,单选按钮组中最初选中的单选按钮可以被认为是默认的。可以对这个按钮进行样式化,即使单击了另一个单选按钮,该样式也会保留在最初选中的按钮中。另一个场景是如果一个表单上有不止一个提交按钮(实际上不应该有——但这只是假设!),其中一个按钮将被视为表单的默认提交按钮。不幸的是,不同的浏览器会设置不同的默认值。例如,Opera 识别单选按钮的第一种情况,而 Safari 识别第二种情况,但是两种情况都不识别。
该组中的最后两个选择器:in-range
和:out-of-range
用于元素,这些元素对可以输入其中的值的范围有约束。例如,数字输入表单控件有min
和max
属性,设置可以输入到数字字段的值的范围。如果输入值落在最小和最大范围之间的这个范围内,将应用:in-range
选择器样式规则,而如果超出这个范围,将应用:out-of-range
选择器。理论上,范围输入表单控件也可以使用这些选择器;然而,在撰写本文时,只有 Opera 支持它(部分支持,因为:out-of-range
选择器被忽略了)。
模式匹配选择器
额外的逻辑可以通过使用“第 n-”类型选择器之一引入选择器,这是树形结构伪类选择器组的一部分(见表 6-9 )。这些选择器除了一个以外都是 CSS3 中的新特性。
这些选择器特别适用于列表、表格或任何其他具有重复信息行的 HTML 结构。它们允许样式按照重复的模式应用,例如,可以用来对一列项目进行斑马条纹处理。首先,可以给它们两个关键字之一:even
或odd
,它们将样式应用于特定父元素中的偶数或奇数元素。例如,下面是一个基本的 HTML 列表:
<ul> <li>Row 1</li> <li>Row 2</li>
`
- Row 3
- Row 4
- Row 5
- Row 6
- `
通过添加以下代码,可以将列表中奇数行涂成灰色:
li:nth-child(odd) { background-color:#ccc; }
给偶数行着色就像把关键字
odd
换成even
一样简单:li:nth-child(even) { background-color:#ccc; }
这些导致了图 6-7 中的列表。
图 6-7*。**使用
:nth-child(even)
和:nth-child(odd)
选择器*对列表中偶数和奇数行进行斑马条纹设计此外,
nth-
选择器包括一个模式公式,允许从偶数或奇数行中选择不同的行。可以用公式 an+b 代替even
或odd
。 n 等于要处理的子元素的数量(在前面的例子中是一个列表中的六行),从零开始计数。然后将 a 的值乘以 n 的每个值,并将 b 加到该值上。例如,在公式 3n+1 中,处理的第一行将值 0 赋给 n ,因此公式最终为(3 × 0) + 1,等于 1,意味着样式应用于第一行。对于下一行,公式变为(3 × 1) + 1,等于 4,意味着样式应用于第四行。接下来是(3 × 2) + 1,等于第七行。图 6-8 是应用了以下 CSS 规则的无序列表的外观:li:nth-child(3n+1) { background-color:#cccccc; }
***图 6-8。*无序列表上选择器
:nth-child(3n+1)
的结果也可以给
nth-
选择器一个他们应该选择的具体值,比如:nth-child(2)
只样式化第二行,:nth-child(3)
只样式化第三行,等等。第 n 种类型的选择器,比如
:nth-of-type(N)
,能够从一组共享相同父元素的某种类型的元素中挑选元素。因此,这些选择器不是挑选某个特定元素的子元素,而是挑选某个特定元素的兄弟元素。例如,下面的 HTML 可能是某种博客的结构,有页眉和页脚部分,以及夹在这两部分之间的一些文章:`
Article 1
Article 2
Article 3
`因为所有这些部分共享同一个父项(即
body
),所以:nth-of-type(N)
选择器可以与一个类型选择器结合起来挑选出一个特定的文章,同时忽略header
和footer
:article:nth-of-type(1){ background-color:#f00; };
这将挑选出第一篇文章,并将其背景涂成红色(十六进制颜色代码将在后面讨论)。
注意如果选择的只是特定类型的第一个项目,并且行为不会改变,那么可以使用新的
:first-of-type
选择器。但是,第 n 种类型的选择器可以使用 an+b 公式来挑选多个元素。伪 elemont 选择器
表 6-10 中的元素可能不太常见,因为它们的行为在不同的选择器类别中相当独特。您很快就会看到,它们使样式能够应用于元素中不可访问的部分。虽然您可能认识在表 6-10 中列出的选择器,但是您可能会注意到它们的语法已经改变,在选择器的开头包含了两个冒号而不是一个。请参阅“什么构成了伪元素?”侧边栏了解更多信息。
::first-line
和::first-letter
选择器是不言自明的,因为它们选择元素中文本的第一行和第一个字母。例如,它们可以用来在段落的开头创建“首字下沉”,并在风格上改变文本的第一行,如图图 6-9 所示。图 6-9*。**
::first-letter
和::first-line
选择器将样式应用于段落,创建首字母“首字下沉”和首行加粗*类似于图 6-9 所示的效果可以通过将下面的 CSS 应用到一个填充了文本的段落元素(
p
)来创建:p::first-line { font-weight:bold; } p::first-letter { margin-top:-0.2em; float:left; font-size:4em; margin-right:0.1em; }
该组中接下来的两个选择器
::before
和::after
,用于在元素前后生成和插入内容。在 CSS3 生成和替换的内容模块 5 中描述了如何实现这一点的精确行为,但简而言之,可以在元素周围插入图像或文本。使用 CSS 将文本插入到页面中是很多人不赞成的,因为这被视为使用 CSS 来做应该在 HTML 中完成的事情。如果您确实使用这些伪元素,请记住,出于可访问性的原因,即使样式表被禁用,内容也需要有意义,因此任何插入的内容都需要是无关紧要的。也就是说,让我们继续讨论如何使用这些选择器。这两个选择器通常都与 CSS
content
属性一起使用,该属性用于指定要插入的实际内容。内容属性可以采用引用文本、url()
和attr()
符号语法等等。url()
符号语法将图像文件路径放在括号中,就像url("img/pic.jpg")
。attr()
符号语法接受一个属性名,这个属性名出现在 parantheses 之间的元素上,它将被替换为属性值。例如,可以使用以下内容创建下载 PDF 文档的链接:<a href="presentation.pdf" type="application/pdf">Download the presentation</a>
然后可以创建一个选择器来挑选页面上的锚元素,这些元素的属性设置为 MIME 类型
application/pdf
(我们将使用通配符属性选择器,这样我们就不必键入整个 MIME 类型)。在这些元素上,伪元素选择器会在链接前生成一个图标(通过链接到外部图像),并在链接后生成一些文本,说明这是一个 PDF。这里有一个例子:
5 见【http://www.w3.org/TR/css3-content/】??
a[type*="pdf"]::before { content: url(doc.png); margin-right:5px; } a[type*="pdf"]::after { content: " (" attr(type) ")"; }
使用
content
属性,图标被嵌入到锚元素之前,括号和type
属性的值被插入到元素之后。图 6-10 显示了结果。图 6-10*。**
::before
和::after
选择器在链接周围插入图标和文件类型信息*什么构成了伪元素?
CSS level 1 (CSS1)定义了一个选择器
:first-letter
,在 CSS3 中变成了::first-letter
。正如你在表 6-10 中看到的,一些选择器使用::前缀,这是 CSS3 中的新语法。该语法用于将这些元素标记为选择器伪元素组的一部分。前缀是为了将它们与其他类型的选择器区分开来。伪元素在选择器中是独一无二的,因为它们访问的信息不是由文档的 HTML 中的元素专门标记的。例如,查看一段文本的 HTML,没有一部分被定义为文本的第一行;相反,第一行是由内容在浏览器中的布局决定的。然而,::first-line
选择器可以挑选出 HTML 的这一部分。从它的角度来看,元素中的第一行文本内容位于它可以设置样式的 ethereal 元素中(这就是为什么它被称为伪元素)。使用::first-letter
也会出现同样的情况。段落中的哪个字符是第一个字母是显而易见的,但是在 HTML 中没有明确地标记出来,所以使用::first-letter
选择器从其余的内容中挑选出第一个字母,就好像它包含在一个元素中一样。::before
和::after
伪元素选择器可用于在元素中的现有内容之前或之后(分别)插入内容(注意,它仅适用于可以容纳内容的元素。它不适用于自结束元素,例如img
。因此,它们是这个组的一部分,因为它们引用 HTML 中的空点。像第一个字母一样,HTML 中没有明确的标记来指定开始标记的结尾和内容的开头之间的位置,或者内容的结尾和结束标记的开头之间的位置。表 6-11 列出了 CSS3 生成和替换的内容模块规范中的几个伪元素,但它们对浏览器的支持如此之差,以至于你不太可能使用它们。我们会看到他们的未来。请记住,该规范处于草案状态,因此如果浏览器支持没有实现,它们可能会消失。
注意有一个名为::
selection
的伪元素选择器,它允许将样式应用于用户选择的文本。它最初包含在 CSS3 选择器模块中,但显然这种行为对于 W3C 来说太激进了,因此已经被移除了。然而,你会发现它有强大的浏览器支持。例如,尝试将样式规则::selection { color:#0f0; }
添加到您的页面并选择一些文本。它会变绿的!杂项选择器
两个额外的选择器不适合任何其他类别。
:lang(L)
选择器查找被指定为被搜索语言的内容(通过其lang
属性设置或从页面的元数据继承)。语言代码 7 放在选择器的括号之间。示例见表 6-12 。
标记选择器在另一个规范中有更详细的解释, CSS 列表和计数器模块级别 3,可从这里访问:【http://www.w3.org/TR/css3-lists/#marker-pseudoelement】??
参见 http://www.iana.org/assignments/language-subtag-registry 的关于语言代码的列表,这里显示的“子标签”是你可能会用到的。
表 6-12 中的另一个元素是新的
:not(S)
选择器,用于查找与某个选择器不匹配的所有元素。与其他选择器结合使用,它可以为一个样式创建非常复杂的匹配规则。例如,为了直观地识别我的网站上链接到我不想认可的外部资源的链接,我可能会选择并样式化那些在它们的href
属性中不包含“anselmbradford . com”URL,而在它们的rel
属性中包含nofollow
或noreferrer
属性值的所有链接:a[rel^="no"]:not([href*="anselmbradford.com"]) { color:red; }
第一部分是一个作用于
rel
属性的属性选择器,而第二部分包含另一个作用于href
属性的属性选择器,其结果被:not()
选择器否定。注意,我们只是搜索以“no”开头的值rel
,以选取两个可能的值。应用于以下代码片段,前面的选择器将跳过第一个锚点的样式,第二个和第三个锚点的样式,并跳过第四个:<a href="http://anselmbradford.com" rel="noreferrer"> Anselm's website </a> <a href="http://example.com/contact.html" rel="noreferrer"> Example.com contact </a> <a href="http://example.com/links.html" rel="nofollow"> Example.com links </a> <a href="http://example.com" rel="bookmark"> Example.com homepage </a>
正如您所看到的,关于您可以构建的选择器,可能性实际上是无限的!
有效地使用选择器
正如您在前面的简单选择器一节中看到的,选择器可以推广到所有元素,例如:
:only-child { color:blue; } /* match any element that is the only element contained in its parent element */ ::first-letter { font-size:2em; } /* match the first letter of all elements' content */
根据选择器的不同,以这种方式进行归纳可能会变得难以处理,因为所应用的样式可能会附加到具有完全不同的目的和意义的元素上。您可能会看到 ID 选择器以这种方式一般化,比如在代码片段
#item {}
中。然而,这样做是很常见的,因为 ID 选择器总是只选择一个元素,所以永远不会推广到页面上的其他元素。注意一些专业的 CSS 开发人员提倡使用类选择器而不是 ID 选择器。其原因本质上归结为灵活性。ID 选择器只能在页面上的一个元素上使用。可以选择将类选择器应用于一个元素,有效地使它们等同于 ID 选择器。但是,它们也可以应用于多个元素,这是一个额外的好处,如果您意识到某个特定的样式规则需要应用多次的话。
话虽如此,也应该避免反其道而行之和过于具体地使用一连串的选择器。这被称为选择器的特异性。如果选择者的特异性过于集中,这是一件坏事。避免使用如下 CSS 规则:
article section#one ul li.item::first-letter { color:blue; }
虽然这在它被使用的情况下会工作得很好,但是它是脆弱的。对 HTML 的更改很容易导致这个规则不再适用,这也意味着这个选择器不能轻易地移动到另一个上下文中。通过将一个类分配给无序列表(而不是列表项)并按类型挑选列表项,选择器可以缩短为:
ul.toc li::first-letter { color:blue; }
从长远来看,简洁的选择器可以帮助你正确应用 HTML5 的语义元素,从而节省你的时间。不需要创建复杂的选择器或依赖类或 id 来挑选特定种类的内容,您可以直接将样式应用于
em
、strong
、article
、aside
等等,而不需要任何更深层次的特性。显然,如果您想针对某一特定类型的文章,这就是类(可能还有 id)发挥作用的地方,但是首先看看哪些内容可以使用语义 HTML 元素、属性、定制 id 或类分组在一起。CSS 盒子模型
现在我们已经全面地介绍了选择器,您应该对如何从 CSS 访问 HTML 有一个全面的了解。此时,还有另一个值得探索的基本概念 CSS 盒子模型。
就 CSS 而言,组成网页的 HTML 元素包含在一个矩形框中,这个矩形框决定了它相对于页面上其他元素的位置。这种将页面内容放置在框中的范例就是 CSS Box 模型得名的原因。该模型中主要有三种类型的盒子:块级、行级和行内级。为了形象化这些区别,想象一段文字。默认情况下,块级框是围绕整个段落构建的,行级框是围绕段落中每个单独的文本行构建的,行内级框是围绕单行文本中的每个单词构建的。这是 CSS 查看内容和布局的方式,但这与您将使用的盒子模型略有不同。您将把您想要从 CSS 中操作的内容包装在一个 HTML 元素中,比如一个用于整个段落的
p
元素或者一个用于段落中的单个单词的em
元素。然后,您将使用选择器来样式化这些元素。通常,这意味着您将处理块级或内联级的盒子。行级框不是你可以轻易访问的东西(尽管有::first-line
选择器),所以记住它们是 CSS 内部用来布局内容的东西。您可以在一个em
中包含多个单词,甚至可能是一整行,但这仍然会被视为一个行内级别的框,因为它可能会也可能不会占据整行文本。块级盒子是最常见的盒子类型。当在块级框中设计元素样式时,您将可以访问 CSS 属性来填充、边距以及可能夹在两者之间的边框。框外是定位属性,可用于将框从默认位置偏移。 8 框内的宽度和高度属性用来设置框的大小。所有这些特性都显示在图 6-11 中。
***图 6-11。*显示填充、边框、边距、宽度和高度以及 HTML 元素周围的定位属性的块级框
块级框最常见于流动内容周围,如段落、标题或其他在内容块之间提供划分的元素。默认情况下,块级框将扩展以填充其包含元素的宽度,并将垂直堆叠。
一个内联级框通常出现在诸如
span
、q
、em
等短语内容周围。默认情况下,行内文本框将水平排列,而不是垂直排列,因为它们跟随内容中的文本行。内联级别框的框看起来与块级别框完全一样(图 6-11 ),除了应用于框顶部和底部的任何边距都将被忽略——只有左侧和右侧的边距有影响。此外,应用于顶部和底部的填充不会像块级框那样推开元素上方和下方的元素,而是将元素的边框(如果存在)重叠在相邻的内容上。最后,内联级别的框只占用与其中包含的内容一样多的空间,加上设置的任何填充,这意味着它们将忽略对它们的width
和height
属性的设置。图 6-12 显示了一个块级和行内级盒子的例子,以及它们是如何相互作用的。
8 定位属性(左、上、右、下)与位置属性结合使用,这将在后面讨论。
***图 6-12。*块级和内嵌级盒子。请注意,默认情况下,跨两行的行内级别框将会换行。
在图 6-12 中,整个内容和第一段(均为块级框)周围画有边框,单个词和几个词周围画有边框。拥有块级盒子的元素可以被称为块级元素。单词包含在内嵌级别的框中,并应用了 10 像素(px)的填充。正如您所看到的,尽管框的边框向远离文本的所有方向扩展了 10px,但顶部和底部的填充不会影响任何周围的元素,并且边框与页面上的其他内容重叠。如果你很好奇,下面是产生先前的 HTML:
`
This is the first paragraph in this page of content, it has a border drawn around it to
show that it is a block-level element.
`
This is a second paragraph, it is a block-level element as well, but does not have a
bordered style added. Instead, some of the inline-level boxes are
shown with borders added. Inline-level boxes may wrap around more
than one line if the layout requires them to.
CSS 如下所示:
.box { border:2px solid black; padding:10px; }
设置框类型
使用的盒子类型不是一成不变的。事实上,有一个名为
display
的 CSS 属性用于设置包含元素的盒子的类型。在一个元素上设置display:block
将把它视为包含在块级盒子中,而设置display:inline
将把它视为包含在内联级盒子中。在框类型之间切换的能力对于锚元素(a
)特别有用,锚元素在默认情况下是行内级别的元素,因此它们忽略宽度和高度设置。考虑下面的 HTML:
<a href="#">Link</a> <a href="#" class="block">Link</a>
前面的代码可以用下面的 CSS 进行样式化:
a { width:200px; /* set the width */ height:60px; /* set the height */ border:1px solid #000; /* set a black border */ background-color:#ccc; /* set a gray background */ padding:10px; /* set 10 pixels of padding */ text-decoration:none; /* remove the underline */ text-align:center; /* center align the text */ margin-top:20px; /* add margin to the top */ }
这段代码产生了图 6-13 的外观。width 和 height 以及 margin top 被忽略,因为它们是两个内联级别的元素。
***图 6-13。*默认情况下,两个行内元素会并排排列。
现在,如果添加一个额外的样式规则来将第二个框转换为块级元素:
a.block { display:block; /* treat as block-level element */ }
第二个链接的外观发生了变化,包括了宽度和高度(以及边距,这样元素就不会重叠),如图 6-14 所示。较大矩形形状的整个区域是链接活动区域的一部分,因此将锚元素显示为块级元素在链接区域的大小方面提供了更大的灵活性。例如,在创建菜单时,这一方面特别有用。
***图 6-14。*行内级和块级盒子。块级框移动到它自己的行。
设置内联级元素显示为块级元素的一个问题是,块元素将移动到它之前的内容之下,开始一个新的内容行,如图 6-14 所示。还有一个附加的 display 属性,可以将元素格式化为位于块级框中,然后将格式化后的框视为内联级框。将显示更改为
display:inline-block
会产生图 6-15 。***图 6-15。*内联级框和块级框在布局上被视为内联级框
显示属性还有其他几个值, 9 但是
block
、inline
和inline-block
是三个主要的值,可以用来改变大多数 HTML 元素的默认布局行为。设置框位置
为了调整元素的位置,使用了
position
属性。它有以下值(加上inherit
,它只使用其父元素的设置):static
:盒子处于默认位置。定位属性(left
、top
、right
和bottom
)被忽略。relative
:框从默认位置偏移 CSS 定位属性中指定的像素数,这些属性命名为left
、top
、right
和bottom
。absolute
:该框按照定位属性中指定的像素数进行偏移,但是从其position
属性设置为非static
值的第一个父元素的左上角偏移,或者从浏览器查看区域的左上角偏移,以先到者为准。fixed
:类似于绝对定位,增加了一个功能,即框不随页面上的其余内容滚动,而是固定在查看区域。
设置框分层
由于盒子的位置可能是相对的和绝对的,这意味着它们可能会相互重叠(这可以用来创造有趣的效果!).当两个元素相互重叠时,可以使用
z-index
属性设置它们的分层顺序。这个属性有一些注意事项,但是很容易理解:- 如果要改变分层,相互重叠的两个元素需要有相同的父元素(它们需要一起包含在同一个 HTML 块中)。
- 至少有一个元素需要将其
position
属性设置为relative
、absolute
或fixed
。 - 至少有一个元素需要设置其
z-index
属性。相对于另一个元素的较大数值会将一个元素放在另一个元素的前面,而较小的数值会将其放在后面。
在此查看所有 CSS 显示属性值:【http://www.w3.org/TR/css3-box/#display】??
背景和边框
背景和边界曾经非常有限。这些形状是带有纯色背景或单一重复图像的矩形。边界有一些变化(实线、虚线、虚线等等),但仍然是矩形。例如,众所周知,圆角的制作非常复杂。CSS3 使许多效果,包括圆角,变得更加容易。圆角是 CSS3 背景和边框模块的一部分,它还增加了添加多个背景的能力,以新的方式剪辑和重复背景,并添加投影,等等。
基本背景色和图像
属性设置背景的颜色。它将采用本章“颜色”一节中描述的任何颜色格式。颜色绘制在任何背景图像的后面,因此如果背景中附加了包含透明区域的图像,颜色就会显示出来。可以给定值
transparent
,使得没有颜色显示出来。属性用于将图像附加到元素的背景上。语法
url(“path/to/bgimage.png”)
用于附加图像,其中path/to/bgimage.png
是要包含的图像的文件路径。文件路径可能是相对于样式表的位置,也可能是绝对的(在路径的开头使用http://
或类似的)。看到相对文件路径并不少见,比如url("img/pic.jpg")
,因为样式表通常放在与图像不同的目录中。../
在进入images
目录之前向上移动一个目录。注意如果文件名或文件路径中没有空格,图像文件路径两边的引号是可选的。但是,如果文件路径中有空格,则需要用引号括起来。
background-repeat
属性决定背景图像(如果存在的话)如何重复。基本设置有repeat
、no-repeat
、repeat-x
和repeat-y
。它们分别确定图像是平铺、非平铺(仅显示一次)、水平平铺还是垂直平铺。CSS3 中的这个属性增加了两个新值:space
和round
。通常,当图像重复时,它会根据需要重复,以用图像的某些部分填充可用空间,即使这意味着图像的某些部分会被剪切到视图之外。space
和round
属性防止这种削波发生。值space
将使用图像填充可用空间,而不剪切图像,然后根据需要在重复的图像之间添加空间,以均匀填充可用区域。值round
的工作方式基本相同,但不是用空白空间填充多余区域,而是放大图像以填充可用区域。小心使用round
值,因为图像在放大时质量会迅速下降,变得模糊和像素化。不好!注意
background-repeat
属性的space
和round
值目前浏览器支持有限。截至本文撰写之时,Opera 是唯一支持这两种浏览器的主流浏览器。这是一个很好的提点,Modernizr ([
modernizr.com](http://modernizr.com)
),在第一章中提到的 HTML5 检测库,也有检测 CSS3 的能力!background-attachment
属性决定了当页面滚动时,背景图像是否随内容一起滚动。值fixed
固定背景位置,即使页面滚动,而scroll
允许它和内容一起滚动。此外,还添加了一个新值local
。该值仅适用于页面上有自己的滚动条的元素。 10 如果设置了该值,当用户滚动插入滚动条时,屏幕该区域的背景将随之滚动。注关于
background-attachment
不同值的演示,请访问[
people.opera.com/pepelsbey/experiments/bga](http://people.opera.com/pepelsbey/experiments/bga)
。background-position
属性用于偏移背景图像的起点。它采用两个值,分别对应于水平和垂直定位。您可以设定特定值或使用预设关键词:/* background image is offset 10 pixels left and 25 pixels down */ background-position: -10px 25px;
CSS3 中已经引入了属性
background-clip
和background-origin
。这两个属性采用相同的值,但各自有不同的功能。background-clip
用于指定“背景绘画区域”,即颜色和图像将被渲染的区域。background-origin
指定“背景定位区域”,用于确定背景的原点,即非重复图像左上角的起始点(如果您熟悉坐标空间,则为 0,0 点)。这些属性的三个值如下:border-box
:背景绘画区/定位区延伸至边框外缘。边框将覆盖图像的顶部,但它可以是半透明的。padding-box
:背景绘画区/定位区延伸到边框的内边缘。这是默认值。content-box
:背景绘画区域/定位区域仅延伸到内容区域的边缘,即应用任何填充之前的区域。
设置元素的宽度和高度属性,然后设置 overflow:auto 将导致滚动条出现,这样用户可以找到任何溢出的文本。
图 6-16 说明了这些不同的数值。
图 6-16*。**
background-clip
和background-origin
属性的三个可用值的位置含义。*为了演示这些值,我们可以创建两个框,稍后我们将对其应用背景:
`
`我们将设计两个框的样式,使它们具有宽度、高度、填充、边距和半透明边框(透明边框的颜色语法对您来说可能很陌生;这将在后面的“颜色”一节中介绍)。一个将被裁剪到内容区域,而另一个将被裁剪到边界区域:
.box { width:200px; /* set the width to 200 pixels */ height:200px; /* set the height to 200 pixels */ padding:20px; /* set the padding to 20 pixels */ margin:10px; /* set the margin to 10 pixels */ border:10px solid hsla(0 , 0% , 0% , 0.5 ); /*set a semi-transparent black border */ background-color: #ccc; /* set a gray background color */ background-image: url(logo.png); /* attach an image */ background-repeat:space; /* repeat image in available space */ background-origin:content-box; /* set image origin to content edge */ background-clip:content-box; /* clip image at content edge */ } .no-clip { background-clip:border-box; /* clip image at border edge */ }
这些框将会像图 6-17 中的一样出现。背景图像将从相同的原点开始重复,但是将在不同的点被剪切。
**图 6-17。**使用新的
background-clip
和background-origin
属性以及background-repeat
属性中的space
值对背景进行两次处理最后,在背景中,新的
background-size
属性可以用来在背景区域上拉伸背景,而不是重复它。这是背景的一个有用和必要的特性,但是要小心不要把你的图像拉伸或放大太多,否则它会看起来模糊,像素化,非常糟糕。属性接受一个或两个值。一个值一起设置水平和垂直尺寸,而两个值彼此独立设置。宽度和高度可以用百分比来设置,但是当设置图像的两个维度时,这有使图像失真的风险。为了解决这个问题,关键字auto
可以用于一维,因此图像保持其纵横比。当只给出一个值时,这是高度的默认值:/* Set width to 100% of the available area and height to 50% of the image height */ background-size:100% 50%; /* Set width to 100% and maintain the aspect ratio of the image */ background-size:100% auto; /* Set the width 50% and the height to auto */ background-size:50%;
注意前面代码中的第一个设置会扭曲图像。
background-size
属性也有关键字contain
和cover
。值contain
将缩放图像以完全适合背景区域,同时保持其外观区域,即使一侧有一些空白。cover
值将在保持纵横比的同时缩放图像,但它将缩放图像,使其完全覆盖背景区域,即使这意味着图像的一部分将被剪切出视图(图 6-18 )。***图 6-18。*在
background-size
属性上设置的contain
(L)和cover
®值。请注意放大图像时出现的突出的像素化。多重背景
CSS3 中增加了背景属性的功能,以支持指定多个图像,这些图像将在背景区域中彼此层叠。语法很简单——用逗号分隔每个图像。例如,图 6-19 中的所示的两幅图像,可以使用以下方法进行组合:
background-image: url(logo.png) , url(star.png);
***图 6-19。*两幅独立的图像将使用多种背景进行组合
使用这种技术将产生如图 6-20 所示的组合模式。
***图 6-20。*背景中叠在一起的两幅独立图像
颠倒
backgroud-image
属性中图像的顺序将改变它们的分层。指定的第一个图像将出现在顶部。其他背景属性可以提供逗号分隔的值,以单独设置每个图像的属性;例如,为了仅重复图 6-20 中的星形图像,可以添加以下内容:background-repeat: no-repeat , repeat;
这些值也可以通过 JavaScript 设置,这会产生一些非常有趣的动态效果!
圆角
拥有在元素上创建圆角的属性是网页设计的灵丹妙药。这曾经是一个痛苦的过程,将图像分割并放入由
div
元素组成的网格中,这样四个角就可以被隔离。恶心!新的border-radius
属性允许边框变圆。可以一次性、单独甚至以不一致的方式对边界进行圆角处理。给属性一个长度值会将所有四个角设置为该值:border-radius:20px; /* round all corners by 20 pixels */
给属性赋予四个值将设置四个角的舍入:
border-radius:100px 50px 25px 0px; /* each corner is rounded by a different amount */
在值之间使用正斜杠将允许为圆角的每一侧指定不同的值,从而导致不均匀的拐角:
border-radius: 100px / 20px; /* round one side of the corner more than the other side */
前面代码中的正斜杠将舍入分成两个值;第一个值是水平舍入量,第二个值是垂直舍入量。
前三个属性应用于一个背景色为空的
div
的结果看起来像图 6-21 。***图 6-21。*使用
border-radius
属性可以创建许多不同的形状。投影
属性可以用来在盒子周围创建阴影。它取的值是阴影的水平距离,垂直距离,要应用的模糊量,它应该扩散的量,最后是阴影的颜色。这里有一个例子:
box-shadow: 10px 15px 20px 5px #999999;
这将阴影向右移动 10 个像素,向下移动 15 个像素,模糊 20 个像素,向外扩展 5 个像素(这将向各个方向扩展阴影),并将其设置为深灰色。图 6-22 显示了应用于在“圆角”部分创建的形状的属性。
**图 6-22。**使用
box-shadow
属性应用了阴影的形状颜色
如果使用得当,颜色可以成为网页设计的强大补充,通过根据颜色对项目进行视觉分组来改善网页的结构,或者通过使用对比色来分隔布局中的内容。它也可能被滥用,使原本很好的布局变得华而不实,特别是如果使用了冲突的颜色,看起来很难。还有关于颜色的可访问性问题,因为页面的某些颜色选择会使色盲者难以阅读内容。红绿色盲是最普遍的,所以避免将内容分成亮度相似的红色和绿色阴影。
彩色屏幕
您在屏幕上看到的颜色是由加色模型中的红、绿、蓝三原色组合而成的,这意味着所有三种颜色以最大强度组合将产生白色(图 6-23 )。
***图 6-23。*在加色模型中,原色互相增加亮度。
在 CSS 中,颜色通常用十六进制的符号来定义。这种符号将颜色的红、绿、蓝分量分解成两位数,合起来形成一个六位数,前面加一个散列符号,形式为
#RRGGBB
。称为十六进制三元组,每个数字对的十六进制数值范围为 00–FF。例如,#ff00ff
代表全强度红色和全强度蓝色,没有任何绿色。结合起来,它们产生洋红色。如果你不清楚这个数字和字母序列是如何变成颜色的,请务必阅读“web 颜色值是如何计算的”边栏以了解更多信息。如何计算网页颜色值
你在屏幕上看到的所有颜色都是由红、绿、蓝三原色组成的。如图 6-23 所示,这是一种加色模型,即随着原色强度的增加,颜色变得越来越浅(增加了亮度),直到最终变成白色。为了在 web 浏览器中定义颜色,每个原色都存储在一个字节或 8 位(8 个 1 和/或 0)中。每个颜色分量(红色、绿色和蓝色)的可能值在 00000000 到 1111111 的范围内,这是十进制的范围 0–255。0 表示完全没有颜色,而 255 是颜色的最大强度。Decimal 是以 10 为基数的计数系统,这意味着在计数移动位置之前要使用十个数字(包括 0)。二进制是以 2 为基数的系统,意味着只有两位数(0 或 1)。十六进制的 Base-16 有 16 个数字,可用于将二进制数(如 1111)压缩为一个数字:f。下表显示了每个编号系统的前 16 个数字:
十进制的值 255 可以用二进制的 11111111 或十六进制的 FF 来表示。显然,在两位数长的情况下,十六进制是 255 的最简洁的表示。纯白是红色、绿色和蓝色的组合全强度,可以表示为红色= 255、绿色= 255 和蓝色= 255,或者以十六进制表示为红色= FF、绿色= FF 和蓝色= FF。凝聚在一起,我们得到 FFFFFF。在开头添加一个散列符号,这是用于网页颜色的十六进制符号。该系统中可用的颜色范围为 256 × 256 × 256(或 2563),相当于 16,777,216 种可能的颜色。(此处使用 256 而不是 255,因为计算需要在每个颜色分量中考虑零。)
注意有一个名为 ColorZilla (
[www.colorzilla.com](http://www.colorzilla.com)
)的方便的颜色工具,它集成到 Firefox 中,并提供了几个功能,如浏览器内颜色选择器、网页配色方案分析器,甚至是一个创建 CSS 渐变的工具!当一个数字在每个颜色分量中重复出现时,十六进制记数法也可以用三个数字来表示颜色。所以,洋红色也可以表示为
#f0f
。白色,十六进制的#ffffff
,可以随意缩写成#fff
。#99aaff
(浅蓝色)可以缩写成#9af
,以此类推。也可以使用预设的颜色关键字,如
white
、black
、aquamarine
等。虽然现代 web 浏览器支持许多选择颜色的关键字,但为了一致性起见,请坚持精确定义您的颜色(通过十六进制符号或其他方式),以确保它们与您的图像和其他内容中出现的颜色相同。(“伯里伍德”到底长什么样?是的,这是一个受支持的颜色关键字!)Color 在几个 CSS 属性中使用,但最常用于设置背景色和前景色。背景将在本章后面介绍,所以让我们先仔细看看前景色。前景色(用于页面上的文本内容)是使用
color
属性定义的,它是 CSS 颜色模块级别 3 的一部分。这个模块是 CSS3 中少数几个现在处于 W3C 推荐状态的模块之一。这是一个小模块,定义了color
和另一个属性opacity
。除了十六进制记数法之外,还有其他记数法系统可以用在颜色中,我们将在接下来讨论。功能符号语法
除了指定十六进制值,还有另一种语法可以使用,叫做功能符号。在这种形式的语法中,使用命令
rgb(R, G, B)
,其中“R,G,B”被替换为十进制表示,或者更有用的是,特定颜色分量的百分比。例如,洋红色可以用以下两种十六进制表示形式之一来表示:p { color: #ff00ff; } p { color: #f0f; }
使用函数符号,这可以重写为:
p { color: rgb( 255, 0, 255 ); } p { color: rgb( 100%, 0% , 100% ); }
函数符号在 CSS3 中并不新鲜,但它的下一种形式是:色调、饱和度、亮度(HSL)。
色调、饱和度、亮度
HSL 函数符号使用形式
hsl( H, S, L, )
,其中“H,S,L”代表一种颜色的色相、饱和度和明度。在进一步研究语法之前,您需要理解 HSL 的确切含义。在 RGB 颜色系统中,调整颜色的每一个成分以达到某种阴影或色调(同一颜色的较暗或较亮版本)是一个相当不直观的过程,尤其是对于没有经验的开发人员。根据颜色的不同,可能需要增加或减少三个 RGB 分量中的一个以上的分量,以实现颜色的适当阴影或色调。想象一个由红色和绿色组成的亮黄色。仅通过减少红色分量不能产生更暗的黄色,因为这只会产生更绿的黄色色调,而不是更暗的黄色。红色和绿色分量都需要减少。HSL 旨在使这种调整更容易。
在 HSL 中,RGB 颜色空间中所有可能的颜色都包含在一个圆柱体中。色调通常被认为是实际的颜色(可能更亮或更暗,或更饱和或更不饱和)。在 HSL 圆柱体上往下看,所有颜色都是可见的,选择哪一种是通过在圆柱体外侧来回移动来决定的。因为这是一个圆形的横截面,所以这个运动被赋予 0 到 360 之间的值。颜色按照彩虹的颜色顺序分布在圆圈周围,从红色开始,到红色结束。所以,0 和 360 都是红色的。
饱和度由一个百分比确定,该百分比表示颜色离圆柱体中心有多远;0%位于中心,这意味着它将缺少所有饱和度或灰度。相反,100%意味着它将处于边缘或颜色的最大可能强度。向下移动圆柱体会使颜色变暗,以黑色结束,而向上移动圆柱体会使颜色变亮,以白色结束。这也是以百分比来衡量的,其中 0%是黑色,100%是白色,中间的值是颜色的较暗或较亮的阴影和淡色。参考图 6-24 了解这些值之间的关系。
***图 6-24。*色调、饱和度和亮度(HSL)圆柱体定义了如何以更直观的方式调整 RGB 颜色
现在您已经了解了 HSL 是如何工作的,您可以在使用十六进制表示法的地方使用 HSL 函数表示法语法,例如:
p { color: hsl(300, 100%, 50%); } /* magenta */ p { color: hsl(300, 20%, 50%); } /* pastel magenta – less saturated */ p { color: hsl(300, 20%, 25%); } /* dark shade – less lightness */ p { color: hsl(115, 20%, 25%); } /* dark green – different hue */
请注意,亮度最初放在中间(50%);如果它是 100%或 0 %,它将使颜色纯白或纯黑,而不管其他值。
不透明度
任何元素都可以使用
opacity
属性调整其整体不透明度。可能的值介于 0.0 和 1.0 之间,对应于 0%到 100%的范围。以下是所有合法的值:p { opacity:1.0; } /* totally opaque */ p { opacity:0.0; } /* totally transparent */ p { opacity:0.5; } /* 50% transparent */ p { opacity:0.25; } /* 75% transparent */
定义颜色时,还可以使用前面概述的功能符号样式,通过添加不透明度组件(称为 alpha )来添加不透明度。这使得函数符号样式变成了
rgba( R, G, B, A )
和hsla( H, S, L, A)
,其中“A”是要添加的 alpha 值,它与opacity
属性具有相同的取值范围:p { color: rgba(0, 255, 0 , 0.1); } /* nearly transparent green */ p { color: hsla(225, 100%, 50%, 0.5); } /* semi-transparent blue */
在
color
属性和opacity
属性中设置颜色的区别在于opacity
会影响整个元素(包括背景和边框),而color
只会影响前景文本(图 6-25 )。图 6-25。
color
属性将只影响一个元素的内容(顶部),而opaque
属性将影响整个元素(中部和底部)。网页排版
如果有什么比从印刷设计到网页设计更令人失望的话,那就是印刷术。或者更确切地说,应该说,“更令人失望”,因为在 CSS3 中,控制排版的能力已经有了突飞猛进的发展。可用的字体选择现在可能是无限的,而且可以应用文本级的效果,如阴影和笔画。伴随着这种力量而来的是不要滥用排版,不要忘记页面上排版的目的,并利用这些效果来帮助用户处理页面上的内容,而不是阻碍他们的理解。
网络资源
多年来,字体是在样式表中引用的,而不是下载供样式表使用的。如果某个特定的操作系统没有安装参考字体,浏览器就会退回到少数通用默认字体之一。实际上,这意味着 Arial、Times New Roman 和 Courier New 以及其他一些字体是唯一“安全”的字体选择。精心策划的黑客如可扩展的因曼 Flash 替换(sIFR) 11 出现了,他们用可以显示嵌入字体的 Adobe Flash 内容替换页面上的文本。这种努力后来被放弃了,取而代之的是现代网络浏览器下载字体用于页面的能力。所谓的网络字体的可用性有了显著的提高,这要归功于
@font-face
规则的引入,它是 CSS 字体模块 Level 3 的一部分。此规则允许将字体文件链接到样式表中,以便包含在页面上。
11 在这里可以找到 sIFR 的信息:【http://novemberborn.net/sifr3】??,但是注意到该项目已经不再维持。
注意由于字体定义文件需要在显示之前下载,使用 web 字体可能会引入前端开发人员保罗·爱尔兰所说的 FOUT(无样式文本的闪烁), 12 从而页面上的文本在使用下载的字体之前以其默认字体闪烁。一些网络浏览器已经试图解决这个问题,所以你可能不会碰到它;但是,如果你这样做了,有一些脚本可以使用,比如 fout-b-gone (
[www.extensis.com/en/WebINK/fout-b-gone/](http://www.extensis.com/en/WebINK/fout-b-gone/)
),它们的目的是防止 fout 在浏览器中出现。根据浏览器的不同,可以下载各种格式的字体(TrueType、OpenType 等)。所有现代 web 浏览器都支持的较新格式是 Web 开放字体格式(WOFF),这是 2009 年开发的一种轻量级字体格式,专门用于将在 Web 上分发的字体。该格式目前正在 W3C 进行标准化。 十三
谷歌在
[www.google.com/webfonts](http://www.google.com/webfonts)
托管了一个网络字体库,可以远程链接到你的网站上。自推出以来,这个目录已经从少数几种字体成倍增长到数百种。这是开始尝试 web 字体的好地方。下载字体背后的一般想法是将
@font-face
指令放在 CSS 文件的顶部,指定下载的字体系列和字体文件源:@font-face { font-family: myFont; src: url('LunotrobeOutline.woff') format('woff'); }
然后可以将
font-family
属性设置为在@font-face
规则中设置的font-family
的值:body { font-family: myFont; }
这将把下载的字体应用到网页正文中的所有元素。
小心你一直在使用各种各样的字体,但是就像其他作品一样,字体是由某人设计的!这意味着法律不一定允许你使用你遇到的任何字体。确保您获得了使用您选择的字体的许可。例如,Adobe Systems 的字体不能直接用作网页字体,即使你已经在电脑上安装了它们!然而,基于订阅的服务,如 Typekit (
[
typekit.com](http://typekit.com)
)可以用作中介,以允许使用其他受限制的字体。
保罗·爱尔兰人关于 FOUT 的帖子可以在这里找到:【http://paulirish.com/2009/fighting-the-font-face-fout/】??
13 见【http://www.w3.org/TR/WOFF/】??
多列
创建一个有多列的文本块过去需要为布局中的不同列设置许多不同的
div
。为了减轻这种标记的痛苦,添加了一个新的属性,columns
属性。在最基本的情况下,可以将属性设置为指定要添加的列数的值,如下所示:p.col { columns: 3; }
这将把段落(将
class
属性设置为"col"
)在其可用区域内分成三列。有许多相关的属性可以更具体地说明列的显示方式;例如,可以使用column-gap
和column-rule
属性像控制边界一样控制列之间的间隙。还有一个column-count
属性,仅用于设置列数。(columns
属性是这些其他属性的简写属性)。例如,下面将产生图 6-26 中的布局:column-count: 3; column-gap: 2em; column-rule: 0.35em solid #000;
***图 6-26。*多列应用于一个段落
column-gap
属性指定列之间的间隙宽度,而column-rule
属性指定垂直标尺的宽度样式和颜色。文本效果
属性
text-shadow
在 CSS2 中被添加回来,但是在 CSS2.1 中被移除了。好吧,它在 CSS3 中又回来了!不要让名字欺骗了你;创建一个文本阴影可能是这个属性最没用的效果,但这是查看它的起点。该属性使用与“背景和边框”一节中显示的box-shadow
属性几乎相同的格式。唯一的区别是text-shadow
没有spread
值。如果您调整这些值,您可以创建类似缩进文本、浮雕文本等效果。此外,通过偏移几个分层的文本阴影,轮廓可以以这种方式添加到文本中。为此,可以使用逗号将四个文本阴影链接在一起,每个阴影向不同的方向偏移:
h1 { font-size:4em; color:#fff; text-shadow: -1px 0 0 #000 , 0 -1px 0 #000 , 1px 0 0 #000 , 0 1px 0 #000; }
记住,第一个值是阴影的水平偏移,第二个值是垂直偏移,第三个值是模糊,第四个值是颜色。这将创建类似图 6-27 的轮廓文本。
**图 6-27。**使用
text-shadow
属性创建的轮廓文本注意基于 WebKit 的浏览器(Safari 和 Google Chrome)已经实现了一个名为
text-stroke
的属性。它不在 W3C 的任何 CSS 规范中,所以你最好避免使用它,除非它出现在任何 CSS3 模块中。要使用它,你需要webkit
前缀;属性是-webkit-text-stroke
,它接受一个宽度和一个颜色,如在-webkit-text-stroke: 1px #f00
中。排版规则
在罗宾·威廉的书中提出了一个构图规则,非设计师的设计书指出页面上仅仅是相似的元素应该被避免。如果布局上的设计元素不同,就让它们非常不同,以便在布局中形成对比。使用设计元素之间的对比差异来帮助在视觉上组织页面。当你考虑你选择使用的字体时,这适合于排版。字体分为两大类:衬线字体和无衬线字体。衬线字体是对字母边缘进行修饰的字体(称为衬线)。这些包括时代新罗马,格鲁吉亚,帕拉蒂诺,等等。 Sans serif (意为“无衬线”)包括 Arial、Helvetica、Verdana 等。如果你开始在同一个设计中使用不同的衬线字体或不同的无衬线字体,停下来想一想,是否真的有必要在同一个布局中同时使用 Arial 和 Helvetica 字体。最好避免使用相似但差异明显的字体。同样,最好避免在页面上使用两种以上的字体。你可以用粗体、斜体、颜色等等做很多事情,以至于即使使用一种字体也会提供过多的风格选择和处理。
当考虑文本内容的整体组织时,使用版式在页面上创建信息的层次结构,清楚地确定哪些内容比其他内容更重要。这就是标题、题目、副标题等等的目的;它们创建了一个层次结构,你的眼睛可以快速浏览,从而越来越深入地了解页面上的信息。当你把文本作为页面顶部的信息时,不要害怕把它放大,但是就像你选择字体一样,避免使用几乎一样大但又不完全一样的字体,因为这可能会分散注意力。
在第三章中讨论的 HTML5 轮廓算法对于帮助你将你的页面组织成一个层次结构特别有用。在组织标题等内容时,您可能需要参考该部分。
最后,关于排版要遵循的最重要的一条规则是:如果文本是为了阅读,那就让它可读!
总结
这是本书中最大的章节之一,理由很充分!我希望您能体会到 CSS 微调页面元素外观的强大功能。这是众所周知的冰山一角;CSS 中还有很多未涉及的开发内容,例如在没有任何 JavaScript 代码的情况下从 CSS 中创建过渡和动画的技术(其优点可以讨论!),旋转和以其他方式变换 2D 和 3D 空间中的元素,从 CSS 创建渐变,将图像应用于边框,等等!请参见
[www.w3.org/TR/css-2010/#properties](http://www.w3.org/TR/css-2010/#properties)
获取您可以使用的可用 CSS 属性列表(因为 W3 使用“快照”,在再次更新之前,这可能不是一个完整的列表)。我们还没有完全完成 CSS,因为这是一个不断发展的领域,它和 HTML5 一起为未来的可能铺平了道路,这也是我们在下一章要去的地方!七、用户交互和 HTML5 APIs
HTML5 规范中包括关于如何从编程脚本访问 HTML 元素以及更广泛的 web 浏览器环境的文档。这是贯穿整个规范的规范的一个组成部分。每个可以编写脚本的组件都被分成个应用编程接口(API),这些接口定义了脚本如何与页面上的特定元素和 web 浏览器的特定方面进行交互。其中一些 API 是 HTML5 的一部分,如用于以编程方式操作浏览器前进和后退按钮的历史 API,而其他 API 是相关技术的一部分,包含在 HTML5 的独立(但相互链接)规范中,如地理位置 API,它为网页提供地理位置感知功能。这些 API 的构建思想是,页面及其环境由一系列对象表示,比如包含页面位置数据的
geolocation
对象。在页面内容的上下文中,这些对象形成了遍历网页文档对象模型(DOM)的方式,如第一章中的所述。本章的目标是让你精通如何探索和使用 HTML 规范中定义的脚本功能,这样你就有了必要的工具去探索本章中没有介绍的内容。作为例子,我们将看看使用脚本与浏览器的历史交互,以及
video
和canvas
元素。最后,我们还将了解如何向任何元素添加拖放支持。在本章中使用 JavaScript
JavaScript 在本书的各个章节中都有涉及,但是在这一章中,我们将更正式地使用它,所以你需要为 JavaScript 代码建立一个测试环境。正如在第二章中所讨论的,JavaScript 可以使用
script
元素嵌入,或者从外部文件加载,这是更好的方法。将它移动到自己的文件中可以在你的网站上创建一个更清晰的标记和脚本的分离。我们将创建一个基本的网页,作为本章示例的模板。创建一个名为
jstemplate
的目录(在你的桌面上或者任何你方便访问的地方)。您将使用该目录作为本章示例的起始模板。启动您最喜欢的文本编辑器,并创建以下网页:`
HTML5 Apprentice ` `Content goes here!
`在您创建的
jstemplate
目录中,将此代码保存为一个名为index.html
的文件。为名为script.js
的 JavaScript 创建第二个页面,并将其放在jstemplate
目录内名为js
的目录中。在
script.js
中编写以下 JavaScript:// init function runs when page has fully loaded function init() { // code to run } window.onload = init;
当页面完全加载后,这个脚本将运行函数
init
。最后,创建一个名为
styles.css
的空 CSS 样式表(在这个阶段只是一个空的文本文件),并把它放在jstemplate
目录内名为css
的目录中。访问 DOM 属性和方法
第一章提供了 DOM 的概述,它将页面上的元素描述为一个连接的树状结构(技术上是一个非循环的连通图),可以用来获得对页面元素、属性和文本的编程访问。还提到了 DOM 可以通过一个
document
对象访问,这个对象包含在另一个对象——window
对象中。没有提到的是,更多的抽象实体(除了网页文档)可以通过window
对象访问。例如,可以通过编程访问浏览历史、网站 URL 位置、屏幕尺寸等。这些实体中的每一个都可以通过window
作为一个独立的对象来访问。那么,对象到底是什么?对象仅仅是属性和方法的集合**。这是什么意思?属性是一系列的键和值,就像 HTML 元素的属性一样。他们是所拥有的对象。例如,屏幕宽度是一个保存特定值(以像素为单位的屏幕宽度)的属性。有些属性是只读的,而其他属性的值可以被覆盖。根据上下文的不同,属性也称为变量。**
方法描述了一个特定的对象可以做什么。例如,使用 JavaScript 弹出一个警告窗口是通过一个方法完成的。方法也被称为函数。它们是自包含的代码块,只有在被调用时才会运行,就像您在上一节的模板 JavaScript 代码中看到的函数一样。
例如,
window
对象有一个名为name
的属性,可以用来向特定的 web 浏览器窗口添加标识符。1这个属性不是只读的,所以可以读写。就方法而言,
window
对象有一个方法(在众多方法中)叫做alert()
,您可能已经见过它的使用。它会弹出一个对话框,显示一条消息和一个 OK 按钮。使用点符号(在第一章中描述),它们都可以这样使用:
1 这可以在超链接的目标属性中使用,以便在特定窗口中打开链接的资源
function init() { // sets the name of the window window.name = "MAIN"; // shows the name of the window window.alert("The name of this window is set to: " + window.name); } window.onload = init;
该属性和方法的具体用法在现阶段是题外话;重要的是属性和方法之间的概念差异,以及 JavaScript 可以访问 web 浏览器中的设置和行为,而不仅仅是页面上的 HTML 元素。
登录到控制台
第一章还提到了主流 web 浏览器可用的 web 开发工具和代码
console.log("message")
。让我们更仔细地看看这两个话题。在每个浏览器的 web 开发工具中,都有一个访问 JavaScript 控制台的方法,在那里可以记录来自 JavaScript 的消息。在主要的 web 浏览器中,可以找到这些文件,如下所示:- Chrome :选择查看开发者 JavaScript 控制台。
- Firefox :我推荐使用 Firebug 中的控制台标签。
- ie 浏览器(8 及以上):ie 浏览器开发者工具中有脚本标签,选择工具开发者工具即可进入。
- Opera :选择查看开发者工具错误控制台。Opera 在 Opera 蜻蜓中也有一个显示和隐藏控制台的开关。
- Safari :选择开发显示错误控制台。
如果您修改模板脚本来记录消息,那么在加载页面时,您应该会看到它出现在 JavaScript 控制台中。图 7-1 显示了在 Safari 中运行以下代码的例子:
function init() { console.log("message"); // log a message to the JavaScript console } window.onload = init;
图 7-1。Safari 的错误控制台显示记录的 JavaScript 输出。
注意记录 JavaScript 输出的另一个选项是 Firebug Lite,这是一个 Firebug 版本,可以用于 Internet Explorer、Firefox、Opera、Safari 和 Chrome。在这里下载:
[
getfirebug.com/firebuglite](http://getfirebug.com/firebuglite)
。虽然将消息记录到控制台很好,但是控制台的真正强大之处在于记录 DOM 的各个部分,并检查对象、属性和方法的值和存在。例如,尝试将前面代码片段中的控制台消息更改为以下内容,然后重新加载页面:
… console.log(window); // log the window object to the JavaScript console. …
这将产生(取决于 JavaScript 控制台)一个表示为属性和方法的可折叠列表的
window
对象,如图 7-2 所示。图 7-2。在 Safari 的错误控制台中记录的
window
对象滚动这个列表,你会发现关于你能从 JavaScript 中操作什么的有趣信息。例如,您将看到可以从 JavaScript 处理的数据类型,比如数组、布尔值等等。您还会看到一个 HTML 元素列表,以
HTMLAnchorElement
、HTMLImageElement
、HTMLParagraphElement
等形式显示。JavaScript 是面向原型的语言,这意味着它没有类(不像 Java 或 Adobe Flash 的 ActionScript ),而是利用“原型”对象为其他对象提供基础。这些原型对象可以相互构建,创建一个“原型链”,允许原型对象从其他原型对象继承属性和方法。这个过程类似于其他面向对象语言中类的继承能力。图 7-2 所示的控制台输出中类似 HTML 元素的条目是原型对象,定义了不同 HTML 元素在 DOM 中表示时的属性和方法。例如,为video
元素定义的stop()
和play()
方法可以通过研究 DOM 中表示的HTMLVideoElement
对象找到,这些方法用于停止和播放该元素中加载的视频(研究这个元素,您会发现它实际上继承了HTMLMediaElement
的这些方法)。注意当记录页面上出现的 HTML 元素时(例如,通过它们的 ID 引用它们),错误控制台通常会显示元素的实际 HTML 代码,看起来像 HTML 源代码的一部分。这种行为不能帮助您发现被引用的 HTML 元素所具有的属性,所以在这种情况下,使用控制台方法
console.dir()
而不是console.log()
。该方法将向您显示发送给它的特定对象的属性。在
window
对象中需要注意的其他对象包括:navigator
对象包含关于正在使用的网络浏览器的信息,例如安装的插件、供应商和版本信息。该对象中还包含地理位置信息。screen
对象包含关于尺寸、颜色深度的信息,以及关于用来查看当前网页的显示器的相关信息。- 对象包含以编程方式访问浏览器前进和后退按钮以及相关功能的方法。
location
对象包含关于网页 URL 地址的信息。通过在该对象中设置href
属性,可以重定向网站,比如location.href = ["http://www.apress.com";](http://www.apress.com)
。- 对象包含当前正在浏览的网页上所有 HTML 元素的 DOM 表示。
有些信息会被埋没;例如,您可能不会立即找到之前使用的
alert()
方法。为此,我们需要查看用于创建window
对象的原型,该对象可通过以下方式访问:… console.log(window.constructor.prototype); // log the window object's prototype. …
这将显示直接在
window
对象上定义的方法,如图图 7-3 所示。图 7-3。
window
对象的原型对象显示了为window
对象定义的方法注意访问对象的原型并不直接揭示该原型继承的方法。对于支持它的浏览器(它是非标准的),比如 Safari、Chrome 和 Firefox,您会发现一个
__proto__
属性,该属性允许您访问构建特定原型的父原型。这可以用来检查从父原型继承的方法。事件
事件是 JavaScript 中发生的通知,它是用户输入(鼠标点击、键盘按键等)或页面条件(页面已加载等)的结果,当事件发生时,可以“监听”这些事件以使某些事情发生。在
window
对象的其他地方,你会看到一长串以“on”开头的属性,比如onclick
、onscroll
、onload
等等。这些是window
对象可以响应的事件(分别是点击、滚动和加载页面)。除了将被设置为function init()
的onload
之外,大多数情况下,它们的值都是null
,因为这是在该行之前创建的代码中设置的:… window.onload = init;
这会将
onload
属性设置为我们的自定义事件处理init()
函数,这意味着当窗口完全加载并发送了onload
事件的通知时,init()
函数将会运行。每个可能的事件都可以通过这种方式与一个函数相关联。例如,下面将关联一个函数,该函数在页面被单击时弹出一个警告框:
function windowClickHandler() { alert( "Window was clicked" ); // pops up an alert box } window.onclick = windowClickHandler;
如果与事件相关联的函数被给定一个参数,那么该参数保存一个对象,该对象具有关于所发生事件的信息。例如,以下代码使用了一个参数(名为
e
),该参数包含一个pageX
和pageY
属性,用于指定鼠标光标在页面上的位置:// logs the event object function windowMouseDownHandler(e) { console.log(e); // log the mouse location console.log("Mouse is at: " + e.pageX + ", " + e.pageY); } window.onmousedown = windowMouseDownHandler;
当运行这段代码并点击页面时,事件对象(在本例中是一个
MouseEvent
对象)和鼠标位置将被记录到控制台。历史 API
让我们把这些 JavaScript 知识都用上吧!我提到过
window
对象包含一个history
对象,用于控制浏览器的前进和后退按钮。这实际上并不是使用history
对象所能完成的全部,HTML5 构建了历史 API 2 来允许实际的历史记录以编程方式更新。这意味着可以将页面添加到浏览器的历史记录中,如果用户真的点击了后退按钮,他们将转到已经添加到历史记录中的页面,而不是他们以前访问过的页面。这在使用 Ajax 动态加载内容的页面中非常有用。异步 JavaScript 和 XML (Ajax)是一种在无需重新加载整个页面的情况下向页面加载和注入内容的方法。例如,像 Twitter 和脸书这样的网站使用 Ajax 向它们的页面添加新帖子。
2 见【www.w3.org/TR/html5/history.html】的。
真正简单的 Ajax
Ajax 使用一个名为
XMLHttpRequest
(XHR)的对象通过 JavaScript 从 URL 加载内容。然后,可以通过使用 HTML 元素的innerHTML
属性将这些内容添加到页面中。最简单地说,使用 Ajax 可能如下所示:
var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = responseReady; xmlhttp.open("GET","content.txt",true); xmlhttp.send();
关键字
var
创建了一个新的变量,它就像一个属性——它是一个容器,保存一些可以读写的值。本例中的新变量包含一个新的XMLHttpRequest
对象实例(new
关键字创建了这个对象)。然后设置一个函数来处理onreadystatechange
事件,该事件将在 XHR 对象检索到一些内容时被触发。然后,open()
方法指定用于检索文件的方法种类、文件名,以及请求是异步(当外部文件加载时脚本继续)还是同步(脚本在继续之前等待外部文件加载)。将其设置为true
使其异步,而false
将其设置为同步。事件处理函数可能如下所示:… function responseReady() { document.body.innerHTML = xmlhttp.responseText; }
这个脚本会将页面上的内容设置为加载的文本文件中的内容。
作为一个完整的例子,我们将创建一个简单的页面,包含一个“下一步”按钮和一个“上一步”按钮,以及在这两个按钮之间加载的消息。复制前面的
jstemplate
目录并将其重命名为ajax
,然后编辑index.html
,如下所示:`
HTML5 Apprentice Previous Next `该页面包含两个按钮和两个按钮之间的空白段落,我们将使用 Ajax 填充内容。从
ajax/js
目录中打开script.js
。添加以下脚本:// create global variables var xmlhttp; var prevbutton; var nextbutton; var message; var messageID = 1;
`// initialize variables and add event listening functions
function init() {
prevbutton = document.getElementById(“prevButton”);
nextbutton = document.getElementById(“nextButton”);
message = document.getElementById(“message”);prevbutton.onmousedown = prevButtonDown;
nextbutton.onmousedown = nextButtonDown;
checkButtons();
loadFile();
}
// disable previous or next button depending on whether first or last message is displaying
function checkButtons() {
if ( messageID == 1 ) prevbutton.disabled = true;
else prevbutton.disabled = false;
if ( messageID == 3 ) nextbutton.disabled = true;
else nextbutton.disabled = false;
}
// decrement message ID when previous button is pressed
function prevButtonDown() {
messageID–;
if (messageID < 1) messageID = 1;
checkButtons();
loadFile();
}
// increment message ID when next button is pressed
function nextButtonDown() {
messageID++;
if (messageID > 3) messageID = 3;
checkButtons();
loadFile();
}
// load message files using Ajax
function loadFile() {
var file = “message”+messageID+“.txt”;xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = responseReady;
xmlhttp.open(“GET”,file,true);
xmlhttp.send();
}
// add Ajax loaded content to paragraph on the page
function responseReady() {
message.innerHTML=xmlhttp.responseText;
}
window.onload = init;`这个脚本使用一个变量(
messageID
)来跟踪点击上一个或下一个按钮时需要加载三条消息中的哪一条。您需要创建三个名为message1.txt
、message2.txt
和message3.txt
的文本文件来加载,并将它们保存在ajax
目录中。向每个文件添加一些文本;只要确保每个文本是不同的。该文本将被注入到页面上的空段落元素中。在网络浏览器中打开
index.html
来测试文件。你应该能够前后点击按钮,每次点击都会显示不同的信息(图 7-4 )。图 7-4。一个简单的 Ajax 应用在双击 next 按钮后的输出。当显示三条消息中的最后一条时,该按钮被禁用。
支持历史的 Ajax
如果您查看上一个示例中页面地址的 URL,您会发现它并没有随着不同内容的加载而更新。这并不理想,因为显示不同消息的页面不能被书签标记,并且单击浏览器的后退按钮不会返回到先前显示的消息,而是会转到先前查看的页面。然而,历史 API 可以用来克服这些问题。HTML5 引入了两种方法,
pushState()
和replaceState()
,用于添加和编辑浏览历史。第一个是pushState()
,允许向页面的浏览历史中添加一个新条目,而replaceState()
将用一个新条目替换历史中的当前浏览位置。让我们编辑上一个例子中的脚本,并包含
pushState()
以在单击下一个和上一个按钮时向浏览历史添加一个新条目。回到之前的脚本,编辑prevButtonDown()
和nextButtonDown()
方法:… function prevButtonDown() { messageID--; if (messageID < 1) messageID = 1; var obj = { page: messageID }; var title = "page"+messageID; var url = "#message"+messageID; window.history.pushState( obj , title , url ); checkButtons(); loadFile(); } function nextButtonDown() { messageID++; if (messageID > 3) messageID = 3; var obj = { page: messageID };
var title = "page"+messageID; var url = "#message"+messageID; window.history.pushState( obj , title , url ); checkButtons(); loadFile(); } …
pushState()
方法有三个参数。第一个是 JavaScript 对象(花括号是创建对象的简写符号),它可以包含关于被添加到历史中的页面的信息。在本例中,它是一个具有单个自定义属性page
和保存页面 ID 的值的对象。接下来是添加到历史中的页面的标题(例如,“第 1 页”)。最后,也是最重要的,是要添加到历史中的页面的 URL。我们将添加的页面与我们现在所在的页面是相同的,但是我们将为 URL 添加一个标签,因此对于每一次点击 next 按钮,它都变成了ajax.html#message1
、ajax.html#message2
和ajax.html#message3
。我们可以只添加 hashtag 而不指定当前页面,因为如果没有指定页面,浏览器会添加当前页面。好,编辑
loadFile()
函数,以便它从 URL 中的 hashtag 检索消息 ID:… function loadFile() { // retrieve the hashtag from the URL using the location object var messageHash = window.location.hash; if (messageHash == "") messageHash = "#message1"; var file = messageHash.substr(1)+".txt"; // strip out the "#" from the hashtag xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = responseReady; xmlhttp.open("GET",file,false); xmlhttp.send(); } …
现在测试页面,并使用下一页和上一页按钮。您应该看到带有 hashtag 的 web 页面地址 URL 更新。这意味着支持 Ajax 的内容页面可以加入书签,这太棒了!但是,如果您单击浏览器的前进或后退按钮,您会注意到页面上的消息不会更新,即使地址栏中的 URL 会更新。原因是当用户点击前进和后退按钮时,
onload
事件不会再次触发。这是因为浏览器已经缓存了该页面,这样当用户浏览历史记录时,它会加载得更快。我们需要添加的是一个在历史发生变化时触发的事件,谢天谢地有这样一个事件叫做onpopstate
。在脚本的最后,添加以下内容:… window.onpopstate = init;
这将在每次浏览历史记录时运行
init()
功能。这不是最有效的代码,因为会有对init()
函数的多余调用(例如,在第一次加载页面时调用两次),但是使用这个简单的例子来开发。构建自定义视频控制器
让我们看看与 HTML 元素相关的 JavaScript 方法的交互。前面我提到过,
video
元素有一个play()
方法和一个stop()
方法。我们可以使用这些从 JavaScript 控制视频回放,这意味着我们可以创建一个带有自定义播放和暂停按钮的控制栏。告别每个浏览器看起来都不一样的控制条!对于图形,我们将使用 CSS,但更具体地说,我们将使用一个叫做 CSS sprites 的概念,这是一种使用一个图像文件在页面的不同点显示两个或更多图像的技术。这种技术背后的想法是,一个图像文件可以包含一整串图像,然后可以以不同的方式裁剪和定位,以显示不同的图像。这有点像透过纸巾管看饼干摊在饼干纸上。您一次只能看到一个 cookie,但是您可以移动表单来显示不同的 cookie。这种技术给人一种已经下载了多个图像的错觉,但实际上只有一个图像被下载了,这意味着所有的图像将同时出现在页面上,并且只有一个请求被发送到服务器以获取图像。继续我们的例子…复制前面的
jstemplate
目录,并将其重命名为video_player
。为 sprite 创建一个图像,该图像包含视频控件的播放和暂停图形(图 7-5 )。将大小设置为 80 像素宽,30 像素高。***图 7-5。*一个图像精灵,有两个图像,用于简单视频播放器上的控件
更新
index.html
中的 HTML,如下所示:`
HTML5 Apprentice Video playback not supported.
正如您所看到的,这段代码包含了带有回退内容 3 的
video
元素,后跟一个用于自定义视频控件的div
区域。为了让这个例子正常工作,您必须包含三个大小为 320 x240 像素的视频文件,它们被命名为trailer.webm
、trailer.mp4
和trailer.ogv
,与index.html
在同一个目录中(为视频创建一个video
目录或类似的目录并不是一个坏主意,如果您这样做,请确保更新 HTML)。接下来,我们将创建用于启用该自定义控件的 JavaScript。从
video_player/js
目录中打开script.js
。对于代码,首先我们将创建变量来保存对页面上的video
、锚(<a>
)、span
和label
的引用。接下来,init()
函数被更新,这样它将每个 JavaScript 变量的值设置为一个 HTML 元素的引用,可以使用getElementById()
方法通过它们的id
属性检索该元素。将script.js
更新成这样:`// JavaScript variables used to hold references to HTML elements
var video;
var control_button;
var control_label;
var control_graphic;
// initialization function runs when the page has loaded
function init() {
video = document.getElementById(“my_video”);
control_button = document.getElementById(“control_button”);
control_label = document.getElementById(“control_label”);
control_graphic = document.getElementById(“control_graphic”);
control_button.onclick = playVideo;
}window.onload = init; // runs the init function when the page has finished loading`
注意类代替 id 可以用于所有的元素,并且
document.getElementByClassName()
函数可以用于在一个页面上自动启用多个视频控件的功能,但是这会使代码稍微复杂一些,所以为了简洁起见,使用了 id。当点击按钮控件时,
init()
函数中的最后一行调用另一个函数。这个函数被命名为playVideo()
。继续添加到script.js
:… function playVideo() { control_graphic.className = "pause"; control_button.onclick = pauseVideo; control_button.title = "Pause";
control_label.textContent = "Pause"; video.play(); return false; }
3 为简洁起见,回退内容已缩短;参见第五章了解视频元素的综合回退内容信息。
这个函数将控件图形上的 class 属性设置为一个名为
pause
的 CSS 类,它将 CSS sprite 放置在暂停按钮图像上。接下来,它设置文本内容(如果 CSS 样式表被禁用,它将用作后备内容),然后播放视频。return false
位确保链接(ID 为control_button
)在被点击时不会转到它链接的页面;然而,如果 JavaScript 被禁用,它将转到一个名为nojs-player.html
的页面,该页面可能包含一个无需 JavaScript 也能工作的视频播放器(您必须自己构建这个)。该函数还为再次单击按钮控件设置了一个新函数。这个新功能被称为pauseVideo()
,它暂停视频并反转playVideo()
功能中设置的更改。继续将它添加到您的script.js
文件中:… function pauseVideo() { control_graphic.className = "play"; control_button.onclick = playVideo; control_button.title = "Play"; control_label.textContent = "Play"; video.pause(); return false; }
这就是我们需要的全部 JavaScript 现在来看 CSS。首先,链接锚元素被设置为显示为块级元素,并设置了其宽度和高度。属性
overflow
被设置为hidden
,这样超出控件尺寸的内容就不会显示出来。这是隐藏回退文本所必需的,因为它将被推出控件的边界。从video_player/css
目录中打开styles.css
。添加以下 CSS 规则:.video_controls a { display: block; width: 40px; height: 40px; overflow: hidden; }
接下来,我们为
span
添加一个规则,使它也成为一个块级元素(将其他文本内容排除在外)。这是附加背景图像精灵(名为"controls.png"
)的地方:… .video_controls span { display: block; background-image: url(controls.png); background-repeat: no-repeat; width: 40px; height: 40px; background-color: #ccc;
}
最后,创建了两个 CSS 类来设置背景图像的位置,移动它来显示播放符号或暂停符号:
… .pause { background-position: -45px 5px; } .play { background-position: 5px 5px; }
就这样!播放和暂停按钮来自同一个图像,如图图 7-6 所示。
***图 7-6。*使用 CSS sprite 自定义视频控件的播放(L)和暂停®状态
脚本化 2D 画布 API
现在让我们来看看更具互动性的东西。正如你在第五章中看到的,
canvas
元素没有太多可以用 HTML 来处理的东西,因为它只有两个属性,简单地指定了画布区域的宽度和高度。canvas
的真正力量来自于 JavaScript 对元素的操作。请记住,画布是位图画布,这意味着它本质上是一个空白图像,我们可以通过一组绘图命令来操纵它的像素。要使用画布,需要从元素中检索特定的“上下文”,然后可以将它用作绘制操作的目标。上下文指定了我们正在处理什么样的图像。有两种选择:二维或三维图像。
让我们从寻找检索我们将要使用的上下文的方法开始。 4 首先从章节开始复制
jstemplate
目录,并重命名为canvas
。修改index.html
中的 HTML,添加一个canvas
元素(和一些后备内容)来代替文本段落:
对于没有耐心的人来说,检索上下文的方法称为 getContext()。
`…
The canvas element is not supported!
…`现在修改
canvas/js
目录中的script.js
,以便从 JavaScript 访问canvas
元素。我们将首先添加一个变量来保存对canvas
元素的引用,我们将使用getElementById()
方法来设置它:var canvas; // variable to hold a reference to the canvas element on the page function init() { canvas = document.getElementById( "canvas" ); // look up the canvas element by its ID } window.onload = init;
注意你可能早就注意到了
document
是window
对象的一个属性,那么为什么前面的代码块中没有语法window.document…
?原因是在查找属性或方法时,浏览器会自动查找window
对象,所以没有必要在window
对象的所有属性或方法前加上前缀window
。例如,console
也是window
的一个属性,但是为了简洁起见,window
通常被省略掉了(尽管它可以被包含在内而不会造成任何伤害)。这让我们可以从脚本中访问
canvas
元素。让我们通过查看其原型来看看为canvas
元素定义了什么方法,就像我们通过访问constructor.prototype
来处理window
对象一样。像这样编辑脚本:… function init() { // look up the canvas element by its ID canvas = document.getElementById( "canvas" ); // log the canvas element's prototype console.log( canvas.constructor.prototype ); } …
这就揭示了
getContext()
,这就是我们需要的检索上下文的方法!另一个方法是toDataURL()
,用于将画布图像数据转换成一个 URL,例如,该 URL 可以作为图像元素的源图像数据(img
)。如前所述,上下文将告诉我们正在处理什么类型的图像,可以是二维或三维图像。为了检索某个上下文,一个文本关键字作为一个参数被提供给
getContext()
方法。 5 使用文本关键字"2d"
来检索二维上下文。为了检索三维上下文,使用文本关键字"webgl"
来代替。如您所见,3D 上下文使用 WebGL 与图像进行交互。注意 WebGL 是一个网络浏览器的 3D 渲染规范,由 Khronos 集团监管。WebGL 是网络浏览器中的一个实验性功能,因此,
webgl
关键字可能在不久的将来不会起作用。如果您尝试这种上下文并发现它不起作用,请尝试使用"experimental"-webgl
,这是一个临时的上下文关键字,用于支持浏览器,直到 WebGL 进一步发展。让我们检索 2D 上下文,并使用 JavaScript 控制台检查它的属性和方法;编辑脚本,如下所示:
var canvas; var context; // variable to hold a reference to the canvas context function init() { canvas = document.getElementById("canvas"); // retrieve the 2D canvas context context = canvas.getContext("2d"); // inspect the canvas context console.log(context); // inspect the canvas context prototype console.log(context.constructor.prototype); } window.onload = init;
这将向控制台记录两个对象,
CanvasRenderingContext2D
和CanvasRenderingContext2DPrototype
(实际名称可能因浏览器而异)。第一个将显示上下文中可用的属性,包括添加到画布的线条和填充的颜色。CanvasRenderingContext2DPrototype
显示了可用的方法,包括一系列在画布上绘图和变换画布的方法。画布的图形功能包括以下内容:- 基础画法:有画矩形、直线、曲线、圆弧的方法。
- 填充和描边:有创建实心填充和轮廓的方法。
- 效果:有一些方法可以创建阴影、渐变、透明度,以及将图像叠加在一起。
5WHATWG 在 http://wiki.whatwg.org/wiki/运营一个 wikicanvas contexts,提供可用画布上下文关键字的概述。
WebGL 规范可从这里获得:【www.khronos.org/registry/webgl/specs/latest/.
- 变换:有缩放、旋转、平移(移动)图像的方法。
- 文本:有添加实心或轮廓文本的方法。
- Images :有一些方法可以在画布上绘制图像(甚至是视频或其他画布元素),然后可以对其进行转换或其他操作。
我们将探索前两个方面,但是一定要使用控制台来探索本章中没有涉及的可用方法和属性。其中很多的目的是相当不言而喻的!
在画布上绘画
让我们从在画布上画一个覆盖可用区域的矩形开始。与绘制矩形相关的方法如下:
fillRect(x,y,w,h)
:画一个实心矩形strokeRect(x,y,w,h)
:勾勒出一个矩形
使用这两种方法中的任何一种,我们都可以从画布上的指定位置开始创建一个给定宽度和高度的矩形。在使用这些方法之前,您可能需要设置矩形的样式,以指定填充和描边的外观(轮廓颜色)。为此,可以设置一些属性:
fillStyle
:填充颜色strokeStyle
:轮廓的颜色lineWidth
:轮廓的宽度
要设置样式属性,请将值设置为带引号的 CSS 样式颜色代码,例如#00000 表示黑色。
lineWidth
属性接受一个以像素为单位指定宽度的数字。要用实心矩形填充整个画布,需要指定起点为 0,0(左上角点),宽度和高度与画布的宽度和高度相同。在它的顶部添加一个矩形轮廓会得到如下结果:… canvas = document.getElementById( "canvas" ); context = canvas.getContext( "2d" ); // the color of the fill context.fillStyle = "#cccccc"; // the color of the outline context.strokeStyle = "#999999"; // the width of the outline context.lineWidth = 5; // fill the canvas area with a rectangle context.fillRect( 0, 0, canvas.width, canvas.height ); // outline a rectangle inside the canvas borders context.strokeRect( 30, 30, 200, 100 ); …
将这段代码添加到
canvas/js
目录下script.js
中init()
函数的内容中,看看它是如何工作的!画布使用带有反向 y 坐标的笛卡尔坐标系。这意味着画布上的每个像素都可以由 x 和 y 值指定,其中 x 是画布区域左侧的像素数量,y 是画布区域顶部的像素数量。例如,一个 30,30 的 x,y 坐标将指定一个距离画布区域左侧 30 像素和顶部 30 像素的位置(图 7-7 )。图 7-7。由
strokeRect(30,30,200,100)
在 300×300 像素的画布上绘制的矩形画比矩形更复杂的形状更复杂。需要创建一条线,也称为路径,它可用于逐段构建形状。以下是绘制带有直边的简单形状的相关方法:
beginPath()
:开始新的一行closePath()
:结束新的一行moveTo(x,y)
:移动到画布上的一个坐标lineTo(x,y)
:在画布上绘制一条到坐标的线段stroke()
:给线条上色fill()
:填充由线段创建的形状
beginPath()
方法首先用于告诉画布正在绘制一个新的形状,它由线段组成。然后使用moveTo()
和lineTo()
方法在画布上移动并绘制线段。最后,使用closePath()
方法完成一条线,如果该线被填充,则连接起点和终点。例如,要创建一个三角形,首先要创建一条路径,移动绘图点,绘制两条直线,然后闭合、填充路径并画出轮廓:… context.beginPath(); // start a new line context.moveTo(50,50); // move to 50 pixels from the left and top edge of the canvas context.lineTo(150,50); // draw a line to 150 and 50 pixels from the left and top edge context.lineTo(100,150); // draw a line to 100 and 150 pixels from the left and top edge context.closePath(); // close the line context.fill(); // fill the shape formed by the line with the fill style color context.stroke(); // outline the line with the stroke style color …
用于代替之前的矩形绘图代码,这导致了图 7-8 中所示的三角形。
图 7-8。使用画布路径绘制方法绘制的三角形
注意如果省略了
beginPath()
和closePath()
方法,线条将不会自动闭合以形成一个形状,这在你只想让线条没有填充的情况下可能是需要的。代替
lineTo()
方法,arcTo()
、bezierCurveTo()
或quadraticCurveTo()
方法可以用来创建具有弯曲边缘的形状。此外,可以设置
lineCap
和lineJoin
属性来影响线段末端和每个线段连接点的显示方式。可以将lineCap
属性设置为butt
、round
或square
来改变行尾。lineJoin
可以设置为miter
、round
或bevel
来影响两条线段连接点的形状(图 7-9 )。当设置为miter
时,另一个属性miterLimit
可用于指定形成一个点的角度。图 7-9*。
lineCap
和lineJoin
属性的值*三角学
在处理算法生成的图形时,一个值得你思考的数学领域是三角学。三角学是关于三角形的角和边的研究,这对于画圆周围的特定点是至关重要的。由于画布使用由行和列(x 和 y 坐标)组成的笛卡尔坐标空间,因此当一个点与另一个点之间的视线倾斜时,找到它们之间特定距离处的 x、y 坐标并不像它们彼此在水平或垂直线上那样简单,但是找到该点并没有那么困难。你所需要的是在两者之间画出的三角形内部形成的角度。一旦你有了这些,图 7-10 中的公式就是你所需要的。
图 7-10。使用三角函数确定圆上的点
在图 7-10 中,角度θ需要以弧度表示,等于角度角度乘以 pi 除以 180。翻译成代码,图 7-10 中的公式可能是这样的:
x = 50 + Math.cos( 45 * Math.PI / 180 ) * 20; y = 50 + Math.sin( 45 * Math.PI / 180 ) * 20;
这将找到一个点的 x 和 y 坐标,该点与画布上 x,y 坐标为 50,50 的另一个点成 45 度角,相距 20 个像素。
让我们来看一个完整的例子。这个例子在画布上画了一个螺旋,其结果如图 7-11 所示。它运行一个循环多次,增加与中心的距离(图 7-10 中的“c”)并增加角度度数:
`// declare the global variables
var canvas;
var context;
var centerX;
var centerY;
var degrees = 0;
var radius = 1;function init() {
canvas = document.getElementById( “canvas” );
context = canvas.getContext(“2d”);
centerX = canvas.width/2;
centerY = canvas.height/2;
// move to the center of the canvas
context.moveTo( centerX , centerY );
// loop two thousand times, drawing a line out further and further from the center
for (var i=0; i<2000;i++) {
degrees += 1;
radius += 0.02;
context.lineTo(
centerX+Math.cos(degrees*Math.PI/180)radius ,
centerY+Math.sin(degreesMath.PI/180)*radius
);
}
context.stroke();
}
window.onload = init; `图 7-11。使用三角函数在画布上绘制螺旋
画布状态
设置填充和描边样式将影响在当前画布上下文上创建的所有后续图形。为了处理临时改变填充和笔画样式,canvas 上下文有两个方法,
save()
和restore()
,用于保存样式集,然后在以后将它们恢复到保存的状态。当在画布上绘制多个形状时,这两种方法通常用于隔离每个形状应用的样式更改。例如,在下面的代码块中,绘制了三个正方形,分别用红色、绿色和蓝色标出轮廓。然后在所有的框周围画一个边框。因为使用了save()
和restore()
,最终的边框不需要样式集,因为它将使用原始样式:`…
context.save();
context.strokeStyle = “#ff0000”;
context.strokeRect(0,0,100,100);
context.restore();context.save();
context.strokeStyle = “#00ff00”;
context.strokeRect(50,50,100,100);
context.restore();context.save();
context.strokeStyle = “#0000ff”;
context.strokeRect(100,100,100,100);
context.restore();// Rectangular outline will be default black, because original style settings were restored context.strokeRect(0,0,200,200);
…`注画布是闪光杀手吗?你可能听说过这个问题,特别是苹果公司的史蒂夫·乔布斯在 2010 年的一封公开信中对 Adobe Flash 进行了著名的抨击,他在信中表示 Flash 是一种过时的技术,很快将被 HTML5 技术所取代。这在一定程度上受到了
video
元素的刺激——因为视频是 Flash 在网络上占主导地位的领域——但是canvas
元素也侵占了 Flash 的一点地盘,尽管重叠是有限的。Canvas 是一个可脚本化的位图图像,针对使用像素数据进行绘制进行了优化,它不包含任何用于动画的内置方法。Canvas 使用即时模式渲染,这意味着它不会将显示的图形存储为单独的实体。这实际上就像在真正的画布上绘画,其中颜料可以层叠在图像上,但一旦在画布上,它实际上是任何先前已应用的颜料的一部分。另一方面,Flash 内容传统上存储为矢量图像(尽管它也处理位图数据),并且它包括图形显示列表的概念,这非常类似于 HTML 的 DOM。这意味着 Flash 中的图形被分成多个节点,这些节点可以方便地从代码中访问,以实现交互和动画目的。在许多方面,SVG,另一种网络图形技术,更类似于传统的 Flash 空间。因此,canvas
本身并不能取代 Flash,但是和其他与 HTML5 相关的技术一起,使得传统上仅限于 Flash 的内容创作成为可能。画布互动
与画布图形交互就是在画布的一个与鼠标相关的事件上设置一个事件处理函数,比如
onmousedown
、onmouseover
或onmousemove
(在window
对象上使用console.log()
来探索可用的事件),然后在画布上的鼠标位置绘制一些图形。例如,以下脚本记录了鼠标光标位置的 x,y 坐标,并在用户每次移动鼠标时,从保存的位置到当前位置绘制一条线,创建一个简单的绘图应用(图 7-12 ):// declare global variables var canvas; // reference to canvas element on page var context; // reference to canvas context var cwidth; // reference to canvas width var cheight; // reference to canvas height var lastX = 0; // variable to hold an x coordinate value var lastY = 0; // variable to hold an x coordinate value // initialize the variables and add event handler function init() { canvas = document.getElementById( "canvas" ); context = canvas.getContext("2d"); cwidth = canvas.width; cheight = canvas.height; context.strokeStyle = "#000000"; context.strokeRect(0,0,cwidth,cheight); // call the draw function when the cursor moves over the canvas canvas.onmousemove = draw; } // draw on the canvas function draw(e) { // update the saved x, y coordinates to the position of the cursor // if it is first entering the canvas if (lastX == 0) lastX = e.pageX - canvas.offsetLeft; if (lastY == 0) lastY = e.pageY - canvas.offsetTop; // begin a new line and move to the last saved x, y coordinates context.beginPath(); context.moveTo(lastX, lastY); // set the saved x, y coordinates to the position of the mouse cursor lastX = e.pageX - canvas.offsetLeft; lastY = e.pageY - canvas.offsetTop; // draw a line context.lineTo(lastX, lastY); context.closePath(); context.stroke();
} window.onload = init;
图 7-12。用一个简单的画布绘图应用制作的涂鸦
画布动画
动画只是一系列静止图像,因此制作动画的基本步骤如下:
- 清除画布上的任何现有图形。
- 在画布上绘制新图形。
- 重复步骤 1 和 2
在画布上绘制使用前面介绍的绘制方法,但是清除画布引入了一种新方法:
clearRect(x,y,w,h)
。此方法清除矩形区域内画布上的所有图形。对于动画,这通常意味着清除整个画布区域,除非您能够可靠地知道在整个动画过程中只有画布的一部分会发生变化。全局
window
对象包含两种方法,用于根据动画的需要重复一段代码:setInterval(f,t)
和setTimeout(f,t)
。这两种方法都在指定的时间间隔(t
)后调用自定义函数(f
)。时间间隔以毫秒为单位设置,因此 1000 的长度为一秒。两者的区别在于setInterval(f,t)
连续运行,而setTimeout(f,t)
只在设定的时间量后执行一次。我们将使用setInterval(f,t)
创建一个基本的动画引擎。注意 Firefox、Chrome、Opera、Internet Explorer 都有一个实验方法叫做
requestAnimationFrame()
,用来告诉 web 浏览器重画窗口,以便重画一个动画的帧。这比使用setInterval()
方法有优势,因为动画仅在浏览器可用时运行,这意味着,例如,在隐藏的选项卡中运行的动画将会因为页面不显示而停止,从而节省计算机处理器不必要的资源消耗。你可以在[
developer.mozilla.org/en/DOM/window.requestAnimationFrame](https://developer.mozilla.org/en/DOM/window.requestAnimationFrame)
找到更多关于这种方法的信息。要创建一个基本的动画引擎,需要创建以下函数:
init()
设置初始变量并启动动画invalidate()
指定动画帧需要重画clear()
清除动画帧draw()
绘制动画帧update()
更新绘制动画帧时使用的变量
总的来说,一个基本动画引擎的代码看起来如下,它将在画布上动画显示两条线(图 7-13 ):
`// declare global variables
var canvas;
var context;
var cwidth;
var cheight;
var lastX = 0;
var lastY = 0;// initialize animation
function init() {
// set variable values
canvas = document.getElementById( “canvas” );
context = canvas.getContext(“2d”);
cwidth = canvas.width;
cheight = canvas.height;// start animation sequence by calling the update function every 30 milliseconds
setInterval(update, 30);
}
// clear and redraw the canvas
function invalidate() {
clear();
draw();
}
// clear the canvas
function clear() {
context.clearRect( 0 , 0 , cwidth , cheight );
}
// draw the graphics on the canvas using the saved x, y coordinate values
function draw() {
context.fillRect(0,lastY,cwidth,5);
context.fillRect(lastX,0,5,cheight);
}
// update the saved x, y coordinate values. Reset them to zero if they reach the canvas edge
function update() {
lastY += 1;
if (lastY > cheight) lastY = 0;
lastX += 1;
if (lastX > cwidth) lastX = 0;
}
window.onload = init;`图 7-13。三帧来自一个基本的动画引擎,在画布上扫过两条线
要查看这种工作方式,请编辑
canvas/js
目录中的script.js
。显然,这只是一个简单的例子,展示了一个基本的动画引擎,但是这种结构可以很容易地进一步开发,以创建更复杂的动画序列。拖放操作
HTML5 规范中出现的拖放功能实际上是微软的一个老的附加功能, 72 最初是在十多年前添加到 Internet Explorer 中的。从那时起,其他浏览器已经赶上并实现了相同的功能——除了 Opera,它目前还不支持这一特性。在支持的浏览器中,所有元素都可以通过全局
draggable
属性使用拖放功能,该属性的值可能是true
或false
。默认情况下,大多数元素将被设置为false
,但也有一些例外。默认情况下,图像元素(img
)的draggable
属性设置为true
,锚元素(a
)也是如此,但前提是它设置了href
属性。此外,虽然它没有属性,但默认情况下可以拖动用户选择的纯文本。拖动操作总是有一个开始和一个结束,这通常对应于某些东西被拾起、移动和放到其他地方。根据拖放序列开始和结束之间所需行为的复杂性,会涉及到相当多的事件。表 7-1 描述了相关事件及其触发时间。
让我们创建一个文件,将这些事件记录到控制台。复制前面的
jstemplate
目录,并将其重命名为dnd
。首先编辑index.html
,如下所示:`
HTML5 Apprentice Draggable
Drop Target
这个 HTML 页面创建了两个部分,一个部分的
draggable
属性被设置为true
,另一个部分被设置为false
(这个可以省略,但是我把它包括进来是为了明确区分这两个部分)。每个都有一个 ID,将在页面的 JavaScript 中被引用。现在编辑链接的 CSS 文件
styles.css
(在dnd/css
目录下)。我们将选取这些部分,并创建两个并排排列的黑框:section { width:200px; padding:10px; background-color:#cccccc; border:1px solid #000000; float:left; margin-right:4px; text-align:center; }
最后,编辑
script.js
(在dnd/js
目录中)并编写以下脚本:// global variable to hold reference to the two sections on the page var draggable;
`var droptarget;// initialize variable values and set event handling functions
function init() {
draggable = document.getElementById( “draggable” );
droptarget = document.getElementById( “droptarget” );draggable.ondragstart = dragStartHandler;
draggable.ondrag = dragHandler;
draggable.ondragend = dragEndHandler;
droptarget.ondragenter = dragEnterHandler;
droptarget.ondragover = dragOverHandler;
droptarget.ondragleave = dragLeaveHandler;
droptarget.ondrop = dropHandler;
}
// event handling functions for each of the drag and drop operations
function dragStartHandler(e) { console.log(“dragstart”); }
function dragHandler(e) { console.log(“drag”); }
function dragEndHandler(e) { console.log(“dragend”); }
function dragEnterHandler(e) { console.log(“dragenter”); }
function dragOverHandler(e) { console.log(“dragover”); }
function dragLeaveHandler(e) { console.log(“dragleave”); }
function dropHandler(e) { console.log(“drop”); }window.onload = init;`
该脚本将为每个拖放事件添加事件处理函数,并在事件发生时向控制台记录一条消息。如果您在 web 浏览器中打开它,并将文本“Draggable”拖动到“Drop Target”,您应该会在 JavaScript 控制台中看到一些事件消息。
根据您使用的浏览器,您可能看不到太多内容。例如,Google Chrome 只显示“dragstart”和“dragend ”,似乎忽略了所有其他事件!这是怎么回事?Chrome 希望设置一个叫做
dataTransfer
的对象,它本质上是一个数据有效载荷,在拖放操作中被(潜在地)传递给拖放目标。最简单的拖放操作包括以下内容:- 将
draggable
属性设置为true
(如果尚未设置) - 设置一个事件监听器函数来处理
dragstart
事件 - 在
dragstart
事件处理函数中设置dataTransfer
对象的有效载荷
好了,所以我们需要给
dragStartHandler()
函数添加一行代码,让这个脚本兼容谷歌 Chrome 等浏览器。修改script.js
中dragStartHandler()
函数的代码,如下所示:… function dragStartHandler(e) { console.log("dragstart"); e.dataTransfer.setData("text/plain" , "Payload Landed" ); } …
如您所见,
dataTransfer
对象有一个名为setData()
的方法,该方法设置与被拖动项一起发送的数据的类型和值。现在(如果您之前没有看到),您应该会看到 JavaScript 控制台记录了以下事件:dragstart drag dragenter dragover drag … dragleave dragend
在中间,当你拖动物品时,你会看到“拖拽”和“拖动”重复多次。
你可能会注意到少了一个事件,最重要的
drop
!原因是当你在拖放目标上时,dragover
事件有重置拖放操作的奇怪行为,其结果是drop
事件永远不会触发。dragenter
和dragover
事件可以用来处理拖放操作,但在大多数情况下,您会想要触发drop
事件。然后你需要做的是取消dragover
事件的默认行为。这是在事件对象上调用方法preventDefault()
的问题,该事件对象作为参数传递给dragOverHandler()
函数。此外,我们将检查默认行为是否已经被阻止,我们还将从函数返回 false,这是告诉浏览器忽略事件的默认行为的另一种方式。可以添加这两种方法,以确保浏览器对此指令提供最广泛的支持。编辑您的代码并添加以下内容:… function dragOverHandler(e) { console.log("dragover"); if (e.preventDefault) e.preventDefault(); return false; } …
现在,当您测试拖放操作时,您应该在 JavaScript 控制台中看到“drop”条目。对于最后一次编辑,我们将把“拖放目标”的文本分配给
dataTransfer
对象中有效载荷的文本。我们将使用innerHTML
属性来设置放置目标中的内容。编辑脚本以进行此更改:… function dropHandler(e) { console.log("drop"); droptarget.innerHTML = e.dataTransfer.getData("text/plain"); } …
getData()
方法用于检索在dragStartHandler()
函数中设置的有效负载的内容,然后将该值分配给放置目标的内容。再次测试该页面,您应该会看到当您将“可拖动”文本拖放到“拖放目标”文本上时,它会变为“有效负载已着陆”。好吧,让我们把它变得更人性化一点。我们将添加一个 CSS 类来创建一个黑色的虚线边框,当鼠标经过拖放目标时,这个边框就会出现和消失。我们还将添加一个默认的边框到背景中,这样当添加虚线边框时元素就不会移动。编辑
styles.css
,增加以下两条规则:h1 { border:1px solid #cccccc; } .over { border:1px dashed #000000; }
现在,我们将通过设置拖放目标上的
className
属性,在拖放目标上方时添加over
类。编辑script.js
中的dragOverHandler()
函数,添加类名:… function dragOverHandler(e) { console.log("dragover"); droptarget.className = "over"; if (e.preventDefault) e.preventDefault(); return false; } …
我们需要在
dragEndHandler()
和dragLeaveHandler()
函数中移除这个类,所以我们将这些函数的className
属性设置为null
,这将移除 CSS 类:… function dragEndHandler(e) { console.log("dragend"); droptarget.className = null; } … function dragLeaveHandler(e) { console.log("dragleave"); droptarget.className = null; } …
最后,我们需要防止
dropHandler()
函数的默认行为。有些浏览器会尝试打开已被丢弃的内容。例如,将链接拖放到浏览器窗口中可能会导致浏览器导航到该链接的 URL。为了防止这种情况,对script.js
进行如下编辑:… function dropHandler(e) { console.log("drop"); droptarget.innerHTML = e.dataTransfer.getData("text/plain"); if (e.preventDefault) e.preventDefault(); return false; } …
现在,如果您测试这个拖放操作,您应该看到当拖放操作完成时添加的文本有效载荷,并且边框应该在操作期间出现和消失(图 7-14 )。
图 7-14。正在进行的拖放操作(上图)和已完成的拖放操作(下图)
除了
draggable
属性,还有全局dropzone
属性。这个属性在理论上可以用来指定一个特定的拖放目标期望接收什么样的数据,以及正在进行什么样的拖放操作(规范列出了move
、link
和copy
作为其可能的值),在理论上可以用来替换在dragenter
和dragleave
事件中引入的任何逻辑。然而,在撰写本文时,您必须等待这一特性在主流 web 浏览器上实现。ADDEVENTLISTENER
例如,本书中处理事件的语法如下所示:
window.onload = init;
这是我使用的一种较老的语法,因为它得到了广泛的支持,但是您应该知道一种更新的、更灵活的语法,用于处理您可能遇到的事件。可以添加
addEventListener()
方法来将事件处理函数与事件相关联。前面的代码行可以重写如下:window.addEventListener( "load" , init , false );
这将
load
事件与函数init()
相关联,因此在该语法中,“on”前缀被从事件中去掉。这种语法的优点如下:- 前面代码片段中的
false
精确控制何时响应事件。简而言之,事件可以在两个阶段被触发:捕获阶段和冒泡阶段。在捕获阶段,在 DOM 中某个元素的所有父元素都触发了事件之后,该元素就会触发事件。起泡阶段是相反的;特定元素在其包含元素的父元素之前触发事件。值true
表示事件监听器在捕获阶段响应,值false
表示它在冒泡阶段响应。该值通常设置为false
。 - 通过添加对
addEventListener()
的额外调用,您可以将多个函数与单个事件相关联。
使用拖放操作对列表进行排序
让我们看看拖放操作的另一个例子,以用户可排序列表的形式。对于本例,您将创建一个有序列表,用户可以通过拖放操作重新排列列表项的顺序。
首先,复制前面的
jstemplate
目录,并将其重命名为dnd_list
。编辑index.html
文件,如下所示:`
HTML5 Apprentice - One
- Two
- Three
- Four
- Five
编辑
dnd_list/js
目录中名为script.js
的 JavaScript 文件。这个脚本将使用四个函数,init()
、dragStartHandler()
、dragOverHandler()
和dragEndHandler()
。首先创建全局变量并编辑init()
函数:// define global variables var dragging; // the list item being dragged var dir; // the direction (up or down) the drag is going in function init() { var li = document.getElementsByTagName("li"); // loop through list and make items draggable and set the event handler functions for ( var i = 0; i < li.length; i++ ) { li[i].draggable = true; li[i].ondragover = dragOverHandler; li[i].ondragstart = dragStartHandler; li[i].ondragend = dragEndHandler; } }
这个例子与前一个例子的不同之处在于,
draggable
属性值是从脚本中添加的。该脚本检索对页面上所有列表项元素的引用,并增加了交换这些元素的能力。继续脚本:
…
function dragStartHandler(e) { dragging = e.target; e.dataTransfer.setData('text/plain' , null); dragging.style.opacity = "0.5"; }
这里设置了
dataTransfer
对象,但是设置为值null
,因为我们没有使用有效载荷。它只需要被设置成允许dragover
事件在 Chrome 中触发。我们在这里也设置了 CSSopacity
属性,尽管建议像前面的例子那样添加和删除 CSS 类,以便将来可以轻松添加更多的样式。为了简洁起见,我们在这个例子中保持原样。在这个例子中,
dragOverHandler()
函数完成了真正的工作:`…
function dragOverHandler(e) {
// make sure the item being dragged isn’t the same as the one we’re dragging over
// and make sure it hasn’t been removed from the page
if (dragging != e.target && dragging.parentNode != null)
{
// determine whether the drag in going up or down
if ( e.target.previousElementSibling == dragging ) dir = “down”;
else dir = “up”;// remove the item being dragged from the page
dragging.parentNode.removeChild(dragging);
// add item being dragged above or below the item being dragged over
if (dir == “down”){
dragging = e.target.parentNode.appendChild(dragging , e.target);
}
else if (dir == “up”)
dragging = e.target.parentNode.insertBefore(dragging , e.target);
}
// prevent the default behavior
if (e.preventDefault) e.preventDefault();
return false;
}`这个函数包含了删除被拖动的项目并在不同的地方添加它的逻辑。
添加重置样式的最后一个函数,最后,确保脚本被设置为在页面完全加载时运行:
`…
function dragEndHandler(e) {
dragging.style.opacity = “1.0”;
}window.onload = init;`
就这样!你应该有一个有序的列表,你可以拖放项目来改变顺序(图 7-15 )。
图 7-15。拖放有序列表允许用户重新排序列表项目
总结
我希望本章向您展示了如何深入 HTML5 规范中的 JavaScript APIs。有了脚本,可能性和你的想象一样大。页面可以以多种方式改变,动态的和交互的。例如,一个最初不起眼的东西,比如一块空白画布,可以被构建成包含各种有趣的效果,以生成图像的实时变化。也可以使用拖放功能来构建各种独特的界面,这些界面可以在 web 表单、游戏或管理界面等应用中找到。结合使用 JavaScript、HTML 和 CSS,您可以为定制的 HTML5 视频播放器创建漂亮的皮肤(控件),在所有主流的 web 浏览器平台上看起来都一样。您也可以对
audio
元素采用同样的方法。学习 JavaScript APIs 如何工作的最好方法是使用它们;请记住,如果有些东西不起作用,后退一步,使用console.log()
中的输出来帮助您熟悉正在发生的事情以及您可以使用的东西。