精通-HTML5-语义标准和排版-全-
精通 HTML5 语义标准和排版(全)
零、简介
除非你度过了一个没有数字的假期,否则你会听到很多关于 HTML5 的讨论。作为一名 web 专业人员,很明显,理解 HTML5 不仅有益,而且非常重要。在过去的几年中,Web 的未来已经从根本上改变了方向,从 XHTML 到 HTML5,这意味着有新的概念要学习,但也有过时的编码实践要忘记。
有时 HTML5 在它所包含的内容上看起来神秘而模糊,为 HTML5 给出一个定义可以把你引向许多方向。这本书将全面涵盖 HTML5 的状态,并为您提供它所涵盖的技术的坚实基础。在深入细节之前,先简要介绍一些基本概念,重点是为 HTML 打下坚实的基础。在 web 表单、多媒体和改进的网站语义等领域,有许多新的元素和属性要介绍。还有一些现在已经过时的元素和属性,作为一名专业的 web 开发人员,你绝对不会再使用它们了(对吗?).当前 HTML 元素的正确用法,新的和旧的,在清晰易懂的摘要中显示出来。CSS 和 JavaScript 是桌面上的下一个主题,两者都通过它们与 HTML 的关系进行了讨论和探索。
此外,为了给你理论和实践知识,使你的网络实践更上一层楼,相关的基础主题,如数字颜色,媒体格式,响应设计,甚至三角学,都将涉及到 HTML5。
虽然 HTML5 将在未来几年内成型,但您会发现它已经为今天要构建的许多下一代 web 应用奠定了基础——这些 web 应用可能就是您自己构建的!
这本书是给谁的?
HTML5 精通面向任何熟悉 HTML、CSS 和 JavaScript 的人,他们有兴趣更深入地理解定义这些语言的规范。HTML5 规范非常庞大。通过这本书的页面来接近它将会很快让你跟上速度,并且作为一个跳板来把你的 HTML 知识带到一个新的高度。
HTML、CSS 和 JavaScript 可能对你来说很熟悉和容易,但是你一定会发现让你惊讶的隐藏的宝石。了解这些微妙之处将对你成为 HTML5 大师大有帮助。你将带着基础知识和必要的手段去寻找更高层次的概念,以便跟随现代网络发展的趋势。
这本书的结构是怎样的?
这本书首先讨论了导致 HTML5 成为当今最热门的 web 开发趋势的令人惊讶的错综复杂的历史。对于那些 HTML 新手来说,在浏览 HTML5 的亮点和自 HTML 4.01 以来的变化之前,先介绍一些基础术语。在这个 HTML5 状态的高级视图之后,后续章节深入到感兴趣的特定领域,提供了 HTML 元素和相关技术的全面概述。
本书的前三分之一将为您提供 HTML5 中可用内容的坚实基础,并帮助您为当今的互联网组织和构建更好的网页。在第二章中,您将了解所有元素上的全局属性,以及 HTML5 的新内容模型类别,这些类别将每个 HTML 元素组织成一组重叠的组。接下来的两章将深入探讨这些元素。特别注意页面的语义结构。在对 HTML 中的所有元素有了广泛的理解之后,本书的后三分之一将深入探讨 web 表单的元素,接下来是将多媒体嵌入 web 页面的元素。
本书的后三分之一深入到与 HTML 紧密相关的概念中,但与用于构建页面的 HTML 元素是分开的。从第六章开始,讨论 HTML 和 CSS3 的关系。还展示了基本的 CSS 概念,并演示了 CSS3 样式的关键新功能的示例。接下来,介绍 HTML5 JavaScript APIs 的概念。构建了一个在 web 浏览器中探索 JavaScript 的小模板,并通过具体的实例进行了演示。这本书以面向移动设备的 web 开发的未来之路作为结尾,并以即将到来的和正在发展的 HTML5 技术的总结作为结尾,最后,涵盖了不属于 HTML5 但通常使用并与之相关联的技术(如地理定位 API)。
这本书可以从头到尾读一遍,也可以放在你的电脑上作为特定元素和概念的参考。为了从本书中获得最大收益,我建议您遵循(WHATWG) HTML 规范的智慧,在讨论如何阅读文档以获得最大收益时,它是这样表述的:
“[它]应该从头到尾,多次阅读。然后,至少要倒着读一遍。那么应该通过从目录中随机选择部分并遵循所有的相互参照来阅读”
最后,这本书在 http://html5mastery.com 有一个附带的网站,在那里你可以找到相关的信息和链接。
本书中使用的约定
这本书使用了几个毫无价值的约定:
-
除非另有说明, HTML5 和 HTML 指的是 HTML 语言的最新实现。
-
现代浏览器被认为是谷歌 Chrome 11 或更新、Mozilla Firefox 4 或更新、Safari 5 或更新、Opera 11 或更新、微软 Internet Explorer 9 或更新。
-
单个 HTML 元素可以用“元素”或不用“元素”来引用,例如,
pre
和pre
元素都引用 HTML<pre>
。 -
除非显示或暗示,否则假定本书中的所有 HTML 示例都嵌套在有效 HTML5 文档的
body
中。 -
除非另有说明,否则任何 CSS 和 JavaScript 代码都被假定分别位于外部样式表和外部脚本文件中。
-
包含在一大段代码中的代码片段可以包括省略号字符
<body> <p>The head area and the rest of the body aren't shown, but they should be there if you wrote this code yourself.</p> ...`(...)
,其用于表示在特定代码片段之前和/或之后有未示出的代码。这里有一个例子:`... -
Lastly, it should be noted that the JavaScript examples shown have variables and functions that are created in a global scope for the sake of brevity and clarity. This likely will be fine for most uses, but in a truly professional best practice environment, the scope of a particular set of JavaScript variables and functions would likely be placed inside a custom object to prevent naming conflicts between different scripts running on the same page.
随着手续的方式,让我们开始掌握 HTML5 的道路。
一、入门:过渡到 HTML5
HTML5 是十多年来对 HTML 规范的第一次重大更新。十年了!这是多么大的更新啊!令人兴奋的新特性,如多媒体支持、交互性、更智能的表单和更好的语义标记都出现了,但这并不是从零开始的。你所熟悉和喜爱的 HTML 仍然在那里等着你去使用,XHTML 也是如此。使用 HTML5,您可以(几乎)以任何您熟悉的方式对页面进行编码,但是对您的技能的掌握来自于对您所编码的内容的历史、约定和语义(含义)的理解,以及来自于创建驱动您的创作风格的明智决策。
这一章将解开 HTML5 的基础,这样你就能明白它是从哪里来的;接下来是基本 HTML 术语和概念的概述。接下来,总结 HTML5 的主要变化,并说明 XHTML 的状态。最后,总结了目前使用 HTML5 特性的一些工具,并列出其他 web 开发人员工具。
HTML5 = HTML
HTML5
HTML 代表什么?"超文本标记语言."这对你来说可能并不陌生。那么,HTML5 代表什么呢?“超文本标记语言,第 5 版”听起来很合理。这确实是一个合理的假设,但是 HTML5 有一段复杂的历史,这使得这个术语不像第一眼看上去那么清晰。对某些人来说,它可能意味着 HTML 规范的最新草案,对其他人来说,它可能意味着一个更大规范的稳定快照,或者一个标签来描述一整套新的和不太新的技术,这些技术旨在使 Web 成为一个更丰富、更吸引人的交互场所。
在 HTML5 之前
还记得 Web 2.0 吗?这个在 2000 年代中期变得突出的术语成了从对网络的只读心态到允许积极参与其内容的转变的同义词:读/写网络。随着这个词在越来越多的会议和其他地方出现,最终成为主流媒体的流行语,它的确切含义变得模糊不清。YouTube 等公司似乎拥有这种能力,但毫无疑问,全世界的网站开发者都面临着向困惑的客户解释他们网站的陈旧 HTML 无法被 Web 2.0 取代的难题。这个词在很大程度上象征着什么是可能的,什么是时尚的,什么是新的。实际上,它包含了以新方式重新包装的旧技术,比如用 JavaScript 和 XML(后来被称为 Ajax)异步加载内容。事实上,在读/写环境中与网站交互的能力已经存在多年了。
或许最重要的是,这个时期象征着给网络带来新生命的渴望。自 1999 年 HTML 4.01 发布以来,万维网联盟(W3C)一直没有发布 HTML 规范的推荐标准。几年后,W3C 忙于 XHTML 1.0 和 XHTML 2.0 的工作,这是一种重新设计的基于 XML 的 HTML 风格,旨在实现更严格、更一致的编码实践。由于 XHTML 是基于 XML 的,网页作者需要严格遵守规范;否则,页面在无效时不会加载。希望世界上的网站作者会采用这个新标准,清除网络上的畸形标记。但是有一个问题。世界没有改变。
为什么 XHTML 2.0 死亡而 HTML5 兴盛
当 Web 2.0 被创造出来的时候,对使用 XHTML 的批评越来越多。为了适应不支持 XHTML 的浏览器,web 页面作者编写 XHTML 标记,但继续使用 Internet MIME 类型“text/html”而不是正确的“application/xhtml+xml”从他们的 web 服务器提供页面,这将告诉浏览器它正在查看 xml。作者会构建他们认为有效的 XHTML 页面,但不会以 XML 的形式提交页面。他们不会看到任何编码错误在他们构建的浏览器中具体化。这一点变得没有意义了。如果没有被这样检查,XHTML 语法并不重要。2004 年,一个名为 Web 超文本应用工作组(WHATWG)的组织成立了,它的目标是发展优秀的旧 HTML,而不是像 W3C 当时那样专注于 XHTML。WHATWG 开始开发一个名为“Web 应用 1.0”的规范,最终成为 HTML5!
WHATWG 理念
WHATWG 在开发 HTML 规范时采用了不同于 W3C 的方法。WHATWG 的目标不是推动一些人认为的对 web 标准的严厉改革,而是逐步发展 HTML,保持与以前版本 HTML 的向后兼容性。这是有意义的,因为 web 浏览器并不使用版本化的方法来呈现 HTML 他们试图呈现扔给他们的任何 HTML,而不考虑网页作者试图遵循的规范版本(HTML 3.2、HTML 4.01 等等)。WHATWG 开发了一个规范,该规范主要由实际使用的内容驱动——哪些 web 浏览器供应商正在实现,哪些 web 页面作者正在使用。2007 年,三家网络浏览器制造商,Mozilla 基金会、苹果公司和 Opera 软件公司,要求 W3C 采用 WHATWG 的成果作为进一步开发 HTML 的起点。不久之后,W3C 采纳了这个建议,在 HTML 休眠了近十年之后,下一个版本 HTML 5(带空格)正在进行中。2009 年,在八个工作草案和没有发布候选版本之后,W3C 决定结束 XHTML 2,专注于 HTML5(最终简称为 HTML 5)。(参见图 1-1 了解这段复杂历史的图表。)此外,XHTML 以 XHTML5 的形式存在,它遵循 XML 语法规则,而不是 HTML 规则。在 HTML 语法中,XML 语法的一部分是允许的(例如,空元素上的尾随斜线,比如<br />
);然而,这些都不是真正的 XHTML 文档,除非它们是使用 MIME 类型“application/xhtml+xml”或“application/xml”从服务器显式传递的(稍后将详细介绍)。
图 1-1。HTML 错综复杂的演变。注意,HTML2 规范出现在 W3C 形成之前。
html 5 的当前状态
“规范永远不会完整,因为它在不断发展。”
WHATWG 常见问题
HTML5 做好了吗?不要。可以用了吗?没错。WHATWG 和 W3C 继续联合开发 HTML5 规范;然而,WHATWG 不再把它的规范称为 HTML5(你只是认为你在用你的脑袋去思考历史!).除了专注于编纂已经在实践中的东西之外,WHATWG 和 W3C HTML 工作组之间的另一个哲学差异是,WHATWG 将不再开发 HTML 的某个版本,该版本将在某个时候关闭以供进一步修订。W3C 将每个版本视为当前开发状态的“快照”,而 WHATWG 的目标是为 HTML 制定一个规范,并根据需要进行更新。这反映了开发依赖于特定 HTML 版本的特性的 web 应用的趋势,而是直接依赖于检查对特性的支持,而不管所使用的 HTML 规范的“版本”。
在 WHATWG 看来,W3C HTML5 规范([
w3.org/TR/html5/](http://w3.org/TR/html5/)
)是 WHATWG 监管的“活规范”中最稳定特性的快照。这个规范简称为 HTML ( http://whatwg.org/html/
)。HTML 规范被进一步嵌套为 web 应用 1.0 的子集(图 1-2 ),它包括与 HTML 分离的 Web 开发相关的规范,如 Web Workers(并发 JavaScript 线程)、Web Storage(用于在 Web 应用中存储数据)等。你可以在[www.whatwg.org/specs/web-apps/current-work/complete.html](http://www.whatwg.org/specs/web-apps/current-work/complete.html)
查看完整的网络应用规范。
图 1-2。HTML/HTML 5 如何融合在一起
这让我回到了 HTML 历史之旅的起点。HTML5 是什么?根据上下文:
- It is the latest version of HTML specification.
- It is a stable snapshot of the earlier version of the HTML specification.
- This is a label used to describe the contemporary state of open web technology.
就本书中提到的 HTML5 的含义而言,这里所涵盖的本质上是当前规范的快照。这意味着它可能领先于 W3C 的 HTML5 规范,但在本书付印时可能落后于 WHATWG 的“活规范”。这就是网络的本质。它在不断进化。对于本书所涵盖的内容,前面提到的第三点可能是最好的。这本书着眼于网络发展的当代状态。HTML5 是网络的新潮流,就像之前的 Web 2.0 一样。在适当的地方,相关的 API 和技术也包括在内,不管它们来自于什么样的规范,但是它们工作的总体框架是 HTML 的下一个版本——HTML 5!(或者你想怎么称呼它都行。)
html 5 文档的剖析
既然您已经充分掌握了通向 HTML5 的道路,那么让我们来看一个简单的文档,这样您就可以看到事情是如何变化的。打开您喜欢的代码编辑器,创建一个新的 HTML 文件,将其另存为index.html
,并键入以下内容:
`
Hello World
`惊喜!你会注意到这个文档不仅熟悉,而且比你以前见过的 HTML 更简单。很干净紧凑,是吧?好的,请随意在您的首选浏览器中打开它,看看文本 Hello World 是否真的会显示在您的页面上。如果你能说出前面代码中的每一个术语,请随意跳过下一节;否则,请继续阅读,复习基本的 HTML 术语和概念。
HTML 术语和概念
为了避免混淆本文中所指的内容以及您可能在其他地方读到或听到的内容,了解一些基本术语和概念非常重要。使用正确的术语很重要,既可以避免混淆,也有助于自己和他人的理解。
HTML 文档由三个基本构件组成:元素、属性和文本(内容)。考虑以下用于创建链接的 HTML 代码片段:
<a href="about.html">About Us</a>
元素是a
(代表锚)元素,它生成指向另一个 HTML 页面或其他资源的可点击链接。元素由两个标签组成:开始标签 ( <a>
)和结束标签 ( </a>
),也称为开始和结束标签或开始和结束标签。属性是作为名称/值对出现在开始标记内的文本。最后,文本内容(在浏览器中查看该代码时出现在网页中)出现在开始和结束标签之间(参见图 1-3 )。
图 1-3。显示元素、属性、内容和标签的典型 HTML 片段的基本组件。
元素
元素是 HTML 中的M;它们是指示 web 浏览器如何处理某些内容的标记。每个元素都有一个关键字,比如body
、p
、a
、img
等等,这些关键字定义了它是什么(分别是主体元素、段落元素、锚元素和图像元素)。不同的元素定义了不同类型的行为,比如创建链接、嵌入图像等等。您可能听说过元素与标记同义,但是元素和标记的含义略有不同。标签是要素的部分,如图图 1-5 所示。大多数元素由开始标签、一些内容和结束标签组成,但是根据标签的不同,这三个组件中的一个或多个可能会缺失。许多元素可以包含嵌套在其中的任意数量的其他元素,这些元素又由标记和内容组成。下面的例子显示了两个元素:p
元素,它是从第一个开始尖括号(<
)到最后一个结束尖括号(>
)的所有内容,以及em
元素,它包含开始<em>
标记、结束</em>
标记以及它们之间的内容。
<p>Here is some text, some of which is <em>emphasized</em></p>
请注意,<em></em>
元素完全包含在<p></p>
元素中;以任何其他方式嵌套它们都不是正确的语法,如下所示:
<p>Here is some text, some of which is <em>emphasized</p></em>
内容内部没有明确的层次结构,当 web 浏览器决定如何显示这些文本时,这是一个问题。web 浏览器仍将分析并尝试显示此代码,但它不是符合 HTML 规范的代码。
空元素
并非所有元素都包含文本内容。例如,img
、br
和hr
元素分别将图像、新行和水平线插入页面。除了占用空间之外,他们不会修改页面上的某些内容。这样的元素不是容器元素——也就是说,你不用写<hr>some content</hr>
或<br>some content</br>
。相反,任何内容或格式都是通过属性值来处理的(将在下一节中解释)。在 HTML 中,空元素(也称为 void 元素)被简单地写成<img>
、<br>
、<meta>
或<hr>
,没有结束标记。在 HTML、XHTML 的 XML 形式中,一个空元素需要一个空格和一个尾随斜杠,像<img />
、 1 <br />
、<meta />
1 或者<hr />
;这些被称为自动关闭标签。由于 HTML5 灵活的语法,这两种形式都可以使用。我倾向于 XHTML 语法,因为斜杠的存在使标记是合并在一起的开始和结束标记变得更清楚。
属性
属性出现在元素的开始标记中。一个特定的元素通常包含该元素特有的属性,以及对许多不同元素都有效的属性。它们用于在某些方面修改元素的行为。它们可以被认为是键/值配对,就像key="value"
一样,其中一个特定的元素将有许多已定义的属性(键),这些属性可以被设置为某个值。例如:
实际上,img 和 meta 元素也有属性。
<a href="contact.html">Here is some text that links to a contact page.</p>
属性href
出现在开始标记中,并被设置为一个定制值,该值改变 HTML 元素的行为。根据属性的不同,它可能包含多个空格分隔的值。您可能已经遇到过的其他属性可能包括alt
、src
和title
,但是还有更多属性。与元素一样,HTML5 在编写属性时同时支持 HTML 和 XHTML 语法;在 HTML 语法中,它们不需要用引号括起来,所以<a href=contact.html>
是可以接受的,但是,和元素一样,我相信 XHTML 语法更清楚,因为引号让你知道这个值是一个自定义值,就像你说过的话的引号。XHTML 语法清晰性的一个例外可能是它处理某些称为布尔属性的属性的方式。布尔属性仅根据它们在元素中的存在与否来提供效果。在 XHTML 中,要求每个属性都有一个值,它们的值要么留空,要么设置为与属性名相同的文本字符串。例如,video
元素包含一个名为autoplay
的属性;在 XHTML 语法中,这看起来像是<video autoplay="">
或<video autoplay="autoplay">
。然而,在更宽容的 HTML 语法中,这可以写成<video autoplay>
。在这种情况下,HTML 格式更加清晰。既然 HTML5 支持这两种语法,那么用哪种方式写就看你自己了!
家
安全地描述完元素、标签和属性之后,让我们把注意力转向另一个您应该知道的与 HTML 相关的概念:DOM。文档对象模型 (DOM)是一个特别在讨论 JavaScript 与页面的关系时突然出现的术语,作为一名网页设计者/开发者,这是一个需要注意的重要术语。这是什么?这是将文档(在本例中是 HTML 页面)表示为由连接节点组成的树状数据结构的标准方式。节点表示页面中的元素、属性和文本内容。通过其分支的树状结构,DOM 描述了节点是如何相互嵌套的。
DOM 及其包含的节点在 JavaScript 中被表示为对象,这些对象描述了特定节点包含的内容和能够做的事情。使用 JavaScript,可以使用点符号遍历树结构来访问页面上的各个组件。如果您不熟悉点标记法,它只是意味着 DOM 树中嵌套在另一个节点中的一个节点可以通过其包含的节点(在此上下文中称为对象)来访问,方法是提供用句点分隔的节点(对象)名称。例如,HTML 页面在 DOM 中被表示为一个名为document
的对象,它包含实际的 HTML 页面内容。由于 HTML 页面包含头部和主体,因此在document
对象中有一个head
和一个body
对象。因此,要访问 HTML 页面的body
元素,应该用 JavaScript 编写以下代码:document.body
。这实际上不会做任何事情;从 JavaScript 中访问了body
元素,但是没有以任何方式对其进行处理。重点是页面的结构由嵌套的对象表示,每个对象都可以用一个点(句点)来访问。
一旦深入到特定的 HTML 元素,命令就会变得更加通用,因为事先不知道特定页面上有哪些元素。JavaScript 包含许多命令,可用于访问 HTML 页面主体内的内容、动态更新内容、响应事件等等。例如,要访问页面上显示的第一个 HTML 元素,可以使用document.body.firstElementChild
。要访问该元素的第一个属性,可以使用document.body.firstElementChild.attributes[0]
。括号中的零只是指要访问的属性的编号;0 表示 HTML 元素中的第一个属性,1 表示第二个属性,依此类推。最后,要访问这个元素的内容,可以使用下面的代码:document.body.firstElementChild.firstChild
。
图 1-4 显示了一个简单网页的结构,只有一个链接在主体区域。看这个图,你会注意到顶部有一个额外的对象,叫做window
。虽然可以通过document
对象访问 DOM,但是document
实际上包含在这个window
对象中,它表示包含页面内容的 web 浏览器窗口。从技术上讲,访问 DOM 首先要通过 window
对象完成,它是“根”对象,通过它可以从 JavaScript 访问网页的所有其他方面。例如,访问页面的主体可以用window.document.body
或document.body
来完成(在这种情况下,引用开头的window
是隐含的)。
图 1-4。通过
HTML5 有什么新功能?
此时,您可能会问自己,“HTML5 到底有什么新功能?”首先,值得注意的是,HTML 最初被认为是一种表示文本文档的标记语言,而不是一个应用开发平台。然而,随着时间的推移,越来越多的功能被挤进了网络浏览器。在过去的十年中,HTML5 首先试图整合、记录和添加到语言中的特性。以下部分描述了一些主要变化。
向后兼容
HTML5 在大多数情况下与以前的 HTML 语法和标记形式兼容。这怎么是新的?多年来,一种“基于标准”的 web 创作方法一直强调从 HTML 语法向 XHTML 语法的过渡。正如在历史部分中所讨论的,HTML5 已经将的重点从语法上“纯粹的”基于 XML 的方法移开,取而代之的是将重点转向已经在使用的实践的更好的文档化。
错误处理
虽然网页作者可以用他们熟悉的任何方式编写文档,但是 HTML5 的一个主要变化是针对用户代理(网页浏览器制造商),而不是作者。Web 浏览器试图呈现 HTML 代码,不管它看起来像什么。由于 HTML 在实践中的灵活性,代码的结构有时会不明确。在过去,web 浏览器制造商以不同的方式处理不明确的代码,并实现不同的算法来处理不明确的 HTML(称为标签汤)。这导致不同浏览器的外观不一致。解决这个问题的一个显而易见的方法是使语言更加严格,这样网页作者就被迫以某种方式组织他们的页面。这是 XHTML1 背后的思想和努力。x;然而,另一种方法是在浏览器实现者端标准化错误的处理方式。这就是 HTML5 寻求做的事情。它试图最终记录语法中的变化应该如何处理。Web 浏览器在处理解析错误时有两种选择:要么实现 HTML5 规范中指定的规则,要么在出现第一个错误时中止处理文档。这个想法是,不同的浏览器将一致地处理相同的错误,那些没有实现错误处理行为的浏览器将停止解析 HTML,从而通知作者他们的语法有问题。
显然,对于 web 开发人员来说,如果页面没有呈现出来,就很明显有问题,但是如果页面呈现得很好,即使浏览器正在处理一个错误,也不那么明显。在这种情况下,页面被处理,但是它被认为不符合 HTML5 规范。这就是为什么 web 开发人员熟悉 HTML 中的变化以及他们不应该使用的元素和属性是非常重要的(参见本章后面的“过时功能”一节)。然而,如果有疑问,可以使用一致性检查服务,比如[
html5.validator.nu](http://html5.validator.nu)
或[
validator.w3.org](http://validator.w3.org)
,根据 HTML5 规范检查提供的 HTML 代码。
简化的文档类型
HTML 通常以 doctype 声明开始。在过去,这看起来像这样:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
或者
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
doctype 声明提供了一个指示,表明您将根据什么样的文档类型定义(DTD)来编写标记。DTD 基本上是一个详细描述标记的规则和语法的页面。所以,前面列出的两行代码的区别在于,它们为不同版本的(X)HTML 指定了 DTD,一个是 HTML 4.01,另一个是 XHTML 1.0。等一下!这预示着 HTML 的世界观将它分成不同的版本。因为我们讨论的是 HTML5,它与规范的以前版本向后兼容,所有需要做的就是说页面正在显示 HTML。因此,doctype 已简化为以下形式:
<!DOCTYPE html>
这对于文档类型的简化来说怎么样?这真是再简单不过了。嗯,实际上是可能的。它可能不存在,如果它被省略,您的 HTML 页面仍然会加载,但是不要从您的 web 页面中删除这一行!
HTML 中的文档类型有两个重要目的。首先,它们通知用户代理和验证器文档是针对什么 DTD 编写的。这个动作是被动的——也就是说,每次页面加载时,您的浏览器不会去下载 DTD 来检查您的标记是否有效;只有当您手动验证页面时,它才会生效。
第二个也是最重要的实际目的是,文档类型通知浏览器使用哪种解析算法来读取文档。Web 浏览器通常有三种解析 HTML 文档的方法:
- -No quirks (or "standard") mode
- -There are quirky patterns.
- -Limited quirk (or "almost standard") pattern
为了以一种模式呈现文档,浏览器依赖于 doctype 字符串的存在、不存在或值。这就是所谓的文档类型切换,它包含在浏览器中,作为一种决定如何呈现文档的方式。假设一个作者已经包含了一个 doctype,那么这个作者知道他或她在做什么,浏览器试图以一种严格的方式(换句话说,标准模式)解释严格的标记。缺少 doctype 会触发 quirks 模式,以旧的和不正确的方式呈现标记;这里的假设是,如果作者没有包含 doctype,那么他或她可能没有编写标准标记,因此标记将被视为过去为 buggier 浏览器编写的。触发无怪癖模式还是有限怪癖模式比较微妙,取决于所选择的文档类型以及查看文档的浏览器。
提示您想让自己相信浏览器会根据文档类型的存在与否来切换解析模式吗?如果你在 Mozilla Firefox 4 中打开一个页面,并选择 Tools
Page Info,在 General 选项卡下你会看到一个呈现模式列表,显示当前用来查看页面的模式。如果您在 web 页面中添加和删除 doctype 声明,并在每个状态下检查页面信息,您将会看到浏览器正在触发不同的解析模式。或者,如果您熟悉 JavaScript,可以将以下脚本插入 HTML 页面的 head 部分:
``
加载页面时,会弹出一个“CSS1Compat”或“BackCompat”的窗口前者意味着模式被设置为无怪癖模式;后者意味着它被设置为怪癖模式。
关于 doctype 的最后一个注意事项:为了与生成 HTML 代码并因此要求 doctype 语法看起来更像以前的遗留系统兼容,下面的 HTML5 替代 doctype 声明是可接受的:
<!DOCTYPE html SYSTEM "about:legacy-compat">
这仅提供给产生 HTML 的系统,所以作为网页作者,你不太可能使用这种声明,除非你想给你的手指一个额外的锻炼,或者向你的朋友显示你是 HTML5 的最小细微差别的大师。
简体字编码
你输入的 HTML 是文本,对吗?对你来说是的,但是对计算机来说它是以一系列的位来存储的:1 和 0。因此,一个特定的字符实际上是作为一个特定的二进制数存储的。阅读文本文档的计算机程序(如 web 浏览器)需要从根本上了解两件事:
- It should read text.
- The bits it reads are used to map to the representation of specific characters in the text.
这第二点被称为文档的字符编码。你可以把它想象成老式的电报交流系统,信息以莫尔斯电码的形式发送,然后被翻译成字母和单词。为了成功地使用莫尔斯电码传输文本,发送者和接收者都需要知道发送的点击是如何映射到特定的字母的。字符编码告诉计算机如何将它读取的位和字节翻译成字母,用于显示或其他目的。
HTML5 规范强烈建议所有 HTML 文档都有一个字符编码集。推荐的方法是让服务器在 HTTP Content-Type 头中将其作为响应头的一部分发送,但是如果这是不可能的,那么可以在 HTML 文档的头部分使用meta
HTML 元素。网络上使用最广泛的字符编码系统是 UTF-8,它可以编码超过 100 万个字符,涵盖了世界上使用的大多数书面语言文字。
在 HTML 4.01 中,meta
元素看起来像这样:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
为了向后兼容,HTML5 中仍然支持这一点,但是首选的语法更短,并且包括一个新的属性charset
:
<meta charset="UTF-8">
啊,这更简洁了!请记住,如果您的服务器将字符编码作为其 HTTP 响应头的一部分发送,您根本不需要这样做。
新增内容模型类别
一个内容模型用于指定特定 HTML 元素预期包含的内容种类。可以包含相同类型内容的不同 HTML 元素可以按类别分组。传统上,HTML 元素分为两类:块和内联。在 HTML5 中,这些已经显著扩展为七个主要类别:
- Metadata content
- Mobile content
- Sectional content
- Title content
- Wording content
- Embedded content
- Interactive content
block 类别大致对应于“流内容”,而 inline 类别对应于“语法内容”,以区分这个类别和 CSS 中使用的display:inline;
属性。这些将在下一章进一步探讨。
新元素
HTML5 引入了大量的新元素来帮助赋予网页结构更大的意义(语义)。像header
、nav
和footer
这样的新元素分别描述了页面标题和徽标出现的位置、主导航菜单出现的位置以及版权和法律信息出现的位置。这标准化了使用元素(比如带有id
属性的div
元素)创建网页区域的常见实践。例如,以前页脚部分可能是这样创建的:
<div id="footer">copyright 2011</div>
使用新的 section 标记,可以重写如下:
<footer>copyright 2011</footer>
使用这些新的结构化标签更加清晰,并且标准化了这个标签的标识符,因为一个id
属性可以由不同的作者写成“page-footer”、“thefooter”等等。“语义网”旨在提供机器可读的明确定义的内容,以便更好地进行数据挖掘/搜索。旧的格式使机器无法在几个不同的网页上一致地挑选出页脚,而 HTML5 语法使它完全可以预测——当然,假设作者实际上在他们网页的适当部分使用了页脚标签。
除了新的结构元素之外,web 表单中可用的元素类型也进行了重大升级,引入了用于输入日期、URL、电子邮件地址、电话号码等的新输入类型。还引入了许多用于嵌入式和交互式内容的新元素,比如video
、audio
和canvas
(一个可脚本化的绘图表面)。现有元素也有变化,例如重新定义了b
、i
和small
元素的含义(语义),使它们在本质上不再是表示性的。新元素和对现有元素的更改将在接下来的章节中更详细地探讨。
微数据
这种新的添加基于注释 HTML 元素的思想,目的是将元数据添加到页面内容中,以便外部应用、聚合器和搜索引擎可以更容易地以标准化的方式处理这些内容。这个想法并不新鲜。微格式和 RDFa 是用于注释 HTML 的两种格式,但是 HTML5 引入了第三种格式:微数据(本身基于 RDFa)。微数据使用一组全局属性,这些属性可用于向页面上的内容添加额外的语义结构。
嵌入式 MathML 和 SVG
数学标记语言(MathML)和可缩放矢量图形(SVG)都是基于 XML 的标记语言,其描述规范不同于 HTML。顾名思义,MathML 用于使用正确的数学符号描述和表示数学方程。SVG 用于描述交互式和静态(非交互式)矢量图形。这两种语言都不是新的,但是由于 HTML5 可能包含 XML 样式的语法,所以这两种语言都可以嵌入到常规的 HTML 页面中。这些将在附录 a 中进一步讨论。
API
本着为 web 应用开发创建平台的精神,HTML5 引入了许多脚本应用编程接口(API)。这些包括 JavaScript API 的附加功能,允许通过它们的类来选择元素。例如,使用数组语法,class
属性设置为aClass
的页面上的第一个元素可以通过 JavaScript 检索,如下所示:
document.getElementsByClassName("aClass")[0]
还添加了与新元素相关的 API,比如在新的video
和audio
元素中控制视频和音频播放的方法。添加的其他功能包括处理拖放用户交互、访问 web 浏览器历史状态,以及将网页数据存储在缓存中以供以后在脱机状态下检索。还有许多与 HTML5 一起工作的相关 API,但实际上属于不同的规范。这一类别中值得注意的一个是地理位置 API,它提供了一种在 web 上下文中处理位置数据的方法。后面的章节将更详细地探讨这些 API。
不再符合 SGML(再次!)
坦白地说,你根本不可能注意到这种变化。最纯粹的 HTML 最初是从标准通用标记语言(SGML)发展而来的,SGML 是一种更古老的标记语言。然而,web 浏览器实现的 HTML 并不完全符合 SGML 规范,HTML5 只是将这一事实编成了法典。HTML5 的语法元素借鉴了 SGML、HTML 和 XHTML1。使它成为一种混合的语言,有自己独特的权利。
过时的功能
HTML5 中有一个“第 22 条军规”,它必须与 HTML 的旧特性保持兼容,同时不鼓励使用某些不再被认为可以接受的元素。例如,HTML 包含某些本质上是表示性的标记,这意味着它对其内容的影响是以某种方式使其外观风格化(例如,font
元素)。表示性标记早已被级联样式表(CSS)所取代,所以这些特性中的大部分已经被弃用。作者不应该再使用这些元素,即使它们仍然出现在 HTML 规范中。这些元素不是简单地从规范中删除,以便用户代理(web 浏览器)在遇到它们时知道如何处理它们(例如在较旧的网页中);然而,这样的页面被认为是不符合要求的。web 浏览器将呈现它们,但是它们不符合当前的 HTML 规范。表 1-1 显示了过时元素及其替代品的清单。
除了不推荐使用的元素之外,许多属性也被归档在 obsolete 下,许多属性本质上是表示性的,很容易使用 CSS 进行模拟。见表 1-2 中 HTML5 中已被标记为过时的属性列表。它们不应该被使用。
XHTML 没了吗?
简单回答:不是。它现在被称为 XHTML5。然而,HTML5 规范规定,XHTML 不能再像 XHTML 1 那样使用 MIME 类型“text/html”。 x 。这成为现实的一个主要原因是 Internet Explorer 不会解析作为 XML 的页面,而是试图将页面下载到磁盘上,而不是显示它。然而,这不是唯一的原因。XML 的语法非常严格,最小的验证错误都会导致网页崩溃,变得不可用,整个世界都可以看到错误。因此,经常使用 XHTML 语法,但作为一种预防措施,还是以 HTML 的形式提供。
关于哑剧类型的所有这些噪音是什么?
多用途互联网邮件扩展(MIME)类型,也称为媒体类型,告诉网页它正在接收哪种数据。显然,web 浏览器想要以非常不同的方式处理图像和文本文档,所以有办法告诉它发送的是什么类型的数据是很重要的。由于 XHTML 和 HTML 看起来非常相似,所以需要告诉 web 浏览器它正在处理哪一个。如果是 XHTML,就需要使用符合 XML 规范的 XML 解析器进行解析;如果是 HTML,它需要使用符合 HTML 规范的 HTML 解析器来解析它。
在我继续之前,让我澄清一下 XHTML 和 HTML 之间的主要区别。尽管 XHTML 和 HTML 有共同的词汇表,但它们在理论上有几个优于 HTML 的地方,包括:
- Ill-formed XHTML will be found immediately, because the browser will refuse to display the page, but will display an error instead.
- XHTML provides a well-formed 2 document.
但是,除非页面使用 MIME 类型为“application/xhtml+xml”或“application/xml”的 XHTML,否则上述两点都不成立。如果您的 web 服务器为您的 web 页面提供 MIME 类型的“text/html”,那么您就不能充分利用 XHTML。
在 HTML 和 XHTML 之间抉择
那么,应该用 HTML5 还是 XHTML5 呢?这取决于您的语法偏好,以及文档中格式良好的保证对您有多重要。XML 风格的语法仍然可以在 HTML 中使用,但是不要期望它有 XHTML 的含义,除非它是这样服务的。最终,这是一个完全取决于你自身情况的判断。只是不要错误地认为,通过将页面作为 XHTML,您已经完成了创建专业的、结构良好的、语义有意义的文档所需的所有工作。
网络浏览器支持
无论你使用哪种语法,让我们来看看你如何看待你的劳动成果。随着 HTML5 的特性变得稳定,在完美的世界中,它们有望出现在您首选的 web 浏览器的最新版本中。但是,您如何知道哪些特性实际上是受支持的,哪些是不受支持的呢?在你开发的浏览器中测试代码是最可靠的选择,但是也有一些网站,比如[
caniuse.com](http://caniuse.com)
可以让你知道你的首选浏览器支持什么。另一个站点[
html5test.com](http://html5test.com)
,检测用于访问该站点的浏览器中某些功能是否可用(图 1-5 )。使用不同的浏览器,您可能会看到不同的分数和摘要,突出显示并非所有功能都适用于所有主流浏览器。
我应该指出“格式良好”并不意味着“有效”例如,具有属性 mymadeupattribute="true "的标记是格式良好的,但仍然无效。
图 1-5。 [
html5test.com](http://html5test.com)
谷歌 Chrome 11 的结果
由于当前的 web 浏览器并不支持 HTML5 的所有特性,所以检测对任何不会正常失败的特性的支持是一个好主意。Modernizr ( [
modernizr.com](http://modernizr.com)
)是一个值得研究的 JavaScript 库。该库检测 HTML5(和 CSS3)特性的可用性,这些特性的存在与否作为布尔值存储在 Modernizr 库创建的 JavaScript 对象中。 3 这些值可以使用有条件的 JavaScript 代码来检查,如果特性存在就添加功能,否则,如果不存在就处理页面。例如,要检测是否支持audio
元素,您可以在页面的 JavaScript 代码中编写以下内容:
// Check Modernizr object for audio Boolean value of true if (Modernizr.audio) { // Enable functionality on page for audio controls } else { // Handle lack of audio element support on the page }
如果你想了解更多关于创建检测 HTML5 特性的自定义方法,请查看马克·皮尔格拉姆在 http://diveintohtml5.org/detect.html的精彩总结。
注 Paul Irish (
[
paulirish.com](http://paulirish.com)
)是 Modernizr 项目的首席开发人员,同时也是一个名为 HTML5 样板([
html5boilerplate.com](http://html5boilerplate.com)
)的项目的首席开发人员,这个项目也值得一试。样板文件为 HTML5 web 项目提供了一个默认的基本模板。它包括 Modernizr 和 jQuery JavaScript 库,以及一个默认的网页结构和附加的 CSS 样式表。
网络浏览器开发工具
每个网络浏览器都使用被称为布局引擎的软件来解析 HTML 和 CSS,并将其呈现在屏幕上。不出所料,每种浏览器的功能通常都不同(尽管谷歌 Chrome 和苹果的 Safari 都使用 WebKit),这解释了对 HTML5 功能支持的差异,即使是在不同浏览器的最新版本中。因为它们是不同的,所以最好有一个可靠的浏览器内开发工具来探索和操作 HTML/CSS。在这方面,每个浏览器都有一套帮助 web 开发的工具。通常,这些工具在启用时允许右键单击页面上的内容,并且上下文菜单中会出现“检查元素”或类似的选项。检查 HTML 元素将揭示它的结构和 CSS 样式,可以在浏览器中浏览和操作(非永久性的)。这些工具还具有某种“控制台”的特性,JavaScript 代码可以使用代码console.log("message");
或类似代码记录命令。主要浏览器各自的布局引擎和开发工具列表见表 1-3 。
访问 http://getfirebug.com 的 ?? 下载这个火狐扩展。
5 通过“开发”菜单启用,该菜单通过勾选首选项… 高级中的复选框启用。
总结
这是一条曲折的道路——经过多年的酝酿——现在我们迎来了 HTML5。越来越多的新功能正在 web 浏览器中出现,当然今天大部分功能都在这里供我们使用!现在,您已经了解了 HTML5 如何融入更大的画面,并探索了哪些发生了变化,哪些保持不变,是时候深入了解如何最有效地使用新功能的细节了。你只是触及了表面!
二、为正确的工作使用正确的标签
有了 HTML5,web 开发人员就有了更多的 HTML 元素来为他们的页面添加含义。引入了许多新元素来更精确地标记、定义和组织内容。理解何时何地使用 HTML 中的许多元素对于创建逻辑结构化、符合规范、语义丰富的网页是必不可少的。
本章将概述 HTML5 中可用的元素,包括新添加的和从 HTML 4.01 继承的元素。你可能会想,这可能是一项艰巨的任务!毕竟,有一个 800 多页的规范涵盖了所有这些内容(完整 WHATWG HTML 规范的当前长度,截至 2011 年 7 月)。为了更容易地处理这个长长的元素列表,我们将研究集合中的元素,并用单独的章节来进一步研究特别感兴趣的元素组。HTML 中的元素可以松散地分为以下几组:
- Root element:
html
element is independent because it is the only element that contains all other elements in the document.- Document metadata and script elements:
head
elements contain metadata of documents, CSS styles and JavaScript in many cases.- Document subsection element: body element encapsulates the content of the page, and the new semantic subsection element defines the header, footer, article and other chapters on the page. These will be discussed in detail in Chapter 3.
- Content grouping elements: The content of a specific part of a webpage can be organized into paragraphs, lists, block references, charts, etc.
- Text-level semantic elements: A single text string can be marked with words or sentences to provide fine-grained control over the meaning of content, such as separating time, abbreviation and superscript from adjacent content, or providing emphasis, importance or other differences from ordinary prose. These will be discussed in detail in Chapter 3.
- Table data elements: Data suitable for display in the spreadsheet may be marked with rows and columns of
table
and related elements.- Form elements: These elements include text fields, drop-down menus, check boxes and other elements for collecting user input in web forms. These will be discussed in detail in Chapter 4.
- Embedded content elements: These include external media embedded in web pages, such as video, audio, bitmap drawing surface or third-party plug-ins, such as Adobe Flash content. These will be discussed in detail in of Chapter 5.
- Interactive elements: These include inherent interactive elements that do not require further scripts.
在深入这些组之前,我们将讨论适用于所有元素的两个方面:适用于所有 HTML 元素的全局属性和“内容模型类别”,这是 HTML 规范用来对元素进行相互分类的经常重叠的分组。我们开始吧!
全局属性
熟悉所有 HTML 元素上的属性(表 2-1 )为利用 HTML 中可用的核心功能提供了强大的工具,例如与 CSS 和 JavaScript 的集成,以及新的编辑和拖放 API。
语言代码列表见 www.iana.org/assignments/language-subtag-registry;显示的“子标签”是您将使用的标签。
注意当使用微数据时,有一组额外的全局属性适用,这允许向页面上的元素添加额外的标记,以使搜索引擎算法等机器更容易解析和使用内容。微数据将在本书后面讨论。
无障碍
accesskey
和tabindex
属性提供了使用键盘访问页面上的元素,这对于行动不便的用户可能很有帮助。当在一个元素上设置时,accesskey
属性允许在键盘上按下一个(或多个)键来激活该元素。例如,它可以用于使用键盘跟踪超链接。不幸的是,这种可访问性的优势也有不利的一面。通常需要键盘组合来激活访问键,例如同时按下 Ctrl 或 Alt 键和 set 访问键。具体使用哪种键盘组合因浏览器和操作系统而异,降低了该功能的实用性。此外,所使用的键盘组合可能会干扰现有的键盘快捷键,最严重的是屏幕阅读器为视力受损的 web 用户使用的快捷键。最后,在一个元素上设置多个访问键(如果从两种类型的设备上查看网站,这可能会有所帮助)并没有得到广泛的浏览器支持。所有这些因素使得使用访问键相当令人沮丧。
在我们继续之前,最后一点需要注意的是——Opera Software 的 Opera 网络浏览器在访问键方面提供了一个很好的特性。通过同时按下 Shift 键和 Esc 键,在 web 浏览器窗口上弹出一个菜单,显示页面上所有可用的访问键以及它们链接到的位置(如果在锚元素上设置了属性;否则显示“空”)。参见图 2-1 的示例。
图 2-1。 Opera 网络浏览器显示访问密钥和公司主页上的相关链接
下一个属性tabindex
,可能更有用。在网页上重复按下键盘上的 Tab 键,您会看到不同的页面元素变得突出显示。这是使用键盘访问网页元素的一种常见方式,在填写 web 表单时可能特别熟悉。当在许多不同的元素上设置tabindex
属性时(从 1 开始并在每个元素上递增 1),该属性确定按 Tab 键在不同元素间移动的顺序,首先访问较小的数字,例如:
`
`当按下 Tab 键时,列表中的第一项将被激活,接着是最后一项,最后是中间项。显然,在这样的列表中,让它们以与列表相同的顺序激活会更有可用性,但是这演示了如何使用tabindex
定制顺序。
元数据
属性为一个元素提供咨询信息。当鼠标悬停在链接、表单输入字段、缩写或首字母缩略词上时,通常会以工具提示的形式出现。这里有一个例子:
<a href="http://w3.org" title="World Wide Web Consortium">W3C</a>
当鼠标悬停在链接文本“W3C”上时,将显示一个显示“万维网联盟”的工具提示(图 2-2 )。
图 2-2。链接上设置的title
属性的工具提示效果
下一个属性lang
指定元素中的文本用什么语言编写。一些元素使用它来根据语言脚本的不同呈现不同的文本,例如使用的引号类型。例如,网页顶部的以下代码片段将指定整个 HTML 页面是用英语编写的:
<html lang="en">
最后,contextmenu
属性是在 HTML5 中添加的,但是在撰写本文时还没有在任何发布的主流浏览器中实现。其思想是通过将元素的contextmenu
设置为菜单的id
属性,可以将menu
元素与特定元素相关联并显示。要进一步了解它是如何工作的,您必须等待浏览器跟上规范的步伐!
识别
id
属性用于为页面上的特定元素分配一个惟一的标识符,随后可以使用 CSS 或 JavaScript 进行访问,以用于样式或脚本目的。 2 这是一个惟一的名称,可以分配给 HTML 元素之一,然后可以用它来引用该元素。属性的使用方式类似;然而,与id
属性不同,同一个类值可以应用于页面上的多个元素,目的是指定它们有一些共同点(比如特定的 CSS 样式)。总而言之,id
和class
属性之间的主要区别如下:
id 属性也可以作为超链接的目标。在第三章的“超链接”一节中讨论了如何使用 id 属性。
id
- 该值可能出现一次,并且每页仅出现一次和,以便标识单个元素。
class
- 该值可能每页出现多次。
- 一个特定的
class
属性值可能包含多个类名,每个类名之间用空格隔开。
为了说明这些差异,请考虑下面的代码片段:
<p id="about-text" class="homepage popup">
该代码包含about-text
的唯一id
属性和两个类:homepage
和popup
。about-text
的id
属性不能在其他地方使用,而homepage
和popup
都可以在其他元素上使用。注意,元素不需要同时被分配一个id
属性和一个class
属性;他们可以有其中之一,或者都没有。此外,为了避免问题,不要以数字开始id
或class
属性,并且绝对不要在名称中包含空格(使用破折号或下划线代替)。
合理的 ID 和类值
基于您希望元素看起来如何而不是它是什么来赋值可能很有诱惑力,但是最好避免这样做。例如,代替这样的值:
<div id="topBox"> <strong class="redText"> <p class="big">
您应该使用如下值:
<div id="notificationBox"> <strong class="important"> <p class="introduction">
为什么呢?仅仅是因为有一天你可能会发现你需要那个strong
元素是蓝色的而不是红色的,或者你可能想把这个内容框从页面的顶部移到底部——当这种情况发生时,你的id
或class
值将会产生误导。
这两个属性在 web 开发中被广泛使用,所以您必须熟悉它们。我们将在适当的地方使用 CSS 和 JavaScript 来说明 HTML5 的特性,但是简单地说,如果您想从 CSS 中引用这些属性值并设置样式,您可以键入值,然后在 id 前面加上一个散列符号(#
)和一个句点(.
),就像这样:
#about-text { background: blue; } .homepage { color: white; }
为了从 JavaScript 中引用这些属性值,您通常会在 JavaScript 中使用命令document.getElementById("about-text")
,其中about-text
被替换为您想要访问的元素的实际 ID。类似的命令可用于检索具有特定类值的所有元素:document.getElementsByClassName("homepage")
(其中homepage
被替换为您所引用的类值)。
可编辑性
在新的编辑 API 中使用了contenteditable
属性,这和 HTML 标准中的一样新。编辑 API 允许对页面内容进行页面内编辑,自从 2000 年 Internet Explorer 引入以来,它就有了属性contenteditable
,但直到现在才把该属性纳入 HTML 规范。这个想法是允许编辑 HTML 的元素,以便可以更容易地创建用于 web 表单等的富文本编辑器(例如,使用 web 应用接口为博客条目创建标记文本) 3 。
拼写检查
顾名思义,spellcheck
属性用于指定一个元素是否应该进行拼写检查。Web 浏览器通常具有拼写检查功能,例如,当用户在 web 表单的输入字段中键入内容时,该功能会检查拼写和语法错误。有时,最好在页面的某些元素上禁用这种拼写检查功能。例如,您可能不想检查输入到 web 表单中的电子邮件地址的拼写错误,因此可以使用spellcheck
属性通知浏览器忽略电子邮件地址表单输入控件。spellcheck
属性的值是true
或false
(或者一个空的文本字符串""
,它映射到 true 状态)。自然,这非常适合可以编辑的内容。
隐藏元素
hidden
属性,用于隐藏和显示 HTML 元素。这相当于使用 CSS 属性和值display:none
。虽然使用 CSS 隐藏元素是一种常见的做法,但实际上 HTML5 规范不鼓励使用hidden
属性。hidden
属性为受影响的元素添加了额外的语义。它的存在意味着该元素此时不相关。这可以被屏幕阅读器使用,例如,它可以忽略通过 CSS 显示规则应用的任何隐藏。
下面的 HTML 和 JavaScript 代码片段展示了如何使用hidden
属性来显示和隐藏内容。HTML 部分显示了一个定义列表,其中包含一只狐狸的通用名和它的拉丁名:
`
- Red Fox
- Vulpes vulpes
JavaScript 部分添加了在滚动或关闭红狐的拉丁名称时显示和隐藏其拉丁名称的功能。因为hidden
属性是一个布尔属性,所以可以使用 JavaScript 将其设置为true
或false
,这将导致显示和隐藏相关元素:
function toggleLatin(){ var entry = document.getElementById("fox");
var latin = document.getElementById("latin"); entry.onmouseover = function(){ latin.hidden = false; }; entry.onmouseout = function(){ latin.hidden = true; }; } window.onload = toggleLatin;
关于使用 contenteditable 的示例,请参考附录 A 中的“网络存储”部分。
拖放
与文档相反,draggable
和dropzone
属性是真正体现 HTML 向构建应用方向发展的两个属性。这些需要一些设置和 JavaScript 集成(我们将在第七章中讨论),但当功能正常时,这些属性允许 HTML 元素从页面的一部分拖放到另一个元素上(尽管正如你将看到的,你可能只会使用draggable
来实现这一点,因为dropzone
还没有获得太多的浏览器支持)。接下来发生的事情由 JavaScript 事件控制。
风格
属性提供了一种将 CSS 样式直接应用于元素的方法。以这种方式应用的任何样式都将覆盖元素上其他位置设置的样式。尽管这可以提供一种将样式应用于元素的快速方法,但是最好完全避免该属性,并在外部样式表中为您的页面提供样式。在你的网站中清晰的区分 CSS 和 HTML 代码提供了组织和灵活性的优势,因为当 CSS 规则包含在一个 CSS 文件中时,查找和禁用 CSS 规则要容易得多,而不是分散在 CSS 和 HTML 之间。
文本方向性
dir
属性控制文本的流向。文本通常从左向右流动,但是如果使用一种向相反方向流动的语言,例如阿拉伯语,文本也需要向那个方向流动。dir
属性提供了这种能力。如果整个块使用从右到左语言,则在容器元素上使用“rtl”值表示从右到左语言,使用“ltr”值表示从左到右语言,这将翻转文本和标点符号的对齐方式:
`
When rendered by a browser, this paragraph will appear aligned to the right.
While this paragraph will not because it lacks a dir
attribute.
也可以给定值“auto ”,它试图根据包含的文本自动确定文本方向。
注意CSS
direction
属性提供了相同的功能,并接受值inherit
、ltr
和rtl
。然而,这个 CSS 应该而不是被使用,因为这个信息应该被嵌入到页面本身,这样即使相关的样式表被禁用,文本内容的方向性仍然可以被确定。
自定义数据
data-*
属性有点奇怪。它是属性的总称,允许添加任何属性名称,以便在 HTML 元素中存储自定义数据,供 JavaScript 应用等使用。属性名中的*
不是实际完成的属性;可以用任何文本替换它来创建自定义属性。例如,可以添加city
来创建属性data-city
。或者可以添加score
来创建data-score
,它可能被用在一个网页上列出一个游戏的高分。以下面的 HTML 代码片段为例:
`
- Top Blaster Tom
- Middle Range Merv
- Last Pop Louis
然后可以编写一个 JavaScript 函数,在单击按钮时用获胜分数替换用户名:
function showScore(){ document.getElementById("show-score-button").onclick = function(){ var entries = document.getElementsByTagName("li"); entries[0].innerHTML = entries[0].dataset["score"]; entries[1].innerHTML = entries[1].dataset["score"]; entries[2].innerHTML = entries[2].dataset["score"]; } } window.onload = showScore;
注意前面代码中的
dataset
属性允许以键/值对的形式访问data-*
属性。但是,只需要属性的自定义部分,所以在数据集中查找属性score
,而不是data-score
。
这允许在单个元素中存储的内容有很大的灵活性,但同时又受到特定规范的支持。多么矛盾!这样做的目的是,这些属性只在特定的站点内使用,而不会被网站域外的软件所利用。在这种情况下,请谨慎使用这些方法,并且只有在绝对没有其他解决方案可行的情况下才使用!
内容模型类别
如第一章所述,最初,HTML 中的元素大致可以分为两类:块内和行内。一个类别或另一个类别的内容模型定义了什么样的内容可以放在特定的元素中。例如,行内元素不能包含块元素。随着 HTML5 的发展,这两种类型的内容不足以描述可用 HTML 元素的行为。WHATWG 取代了 block 和 inline,加入了一个更大的类别集, 4 ,这些类别根据相似的特征对元素进行分组。每个类别中的元素都有相似的内容模型,并且必须遵守定义内容类型的规则。现在有七个主要类别,列于表 2-2 。
即使这七个类别也不能完全涵盖所有情况下的所有要素。例如,一个元素可能不属于这些类别中的任何一个(例如html
元素),或者可能被称为透明的,这意味着它继承了其父元素的类别。许多元素属于不止一个类别,在这种情况下,它们被称为具有混合内容模型。
参见 http://blog.whatwg.org/content-model-overhaul 了解这一变化背后的动力。
注意实际上还有另外七个小类别,它们将特定上下文场景中的元素进行分组,比如当它们是表单的一部分时。要查看其他类别,请在
[
dev.w3.org/html5/spec-author-view/index.html#element-content-categories](http://dev.w3.org/html5/spec-author-view/index.html#element-content-categories)
或[
whatwg.org/specs/web-apps/current-work/#element-content-categories](http://whatwg.org/specs/web-apps/current-work/#element-content-categories)
浏览 HTML 规范中的类别参考。
根元素
要开始研究 HTML 中的元素,从开始看是有意义的——嗯,差不多是从开始看。我们在前一章讨论了 doctype 声明,它是 HTML 文档中出现的第一个元素。然而,跟在 doctype 声明后面的是html
元素,它作为包含文档中所有其他 HTML 元素的元素,也称为根元素。
根元素是标签可选的少数元素之一(见表 2-3 中包含可选标签的其他元素列表)。虽然标签可能是可选的,但元素不会被省略。如果省略了可选元素,它们将被隐含(元素body
是一个例外,如果没有提供页面内容,它将被省略)。例如,下面是一个完全有效的 HTML5 文档:
<!DOCTYPE html><title>Tiny HTML5</title><p>This is a valid HTML5 page!
在这种情况下,解析文档时会添加缺少的html
、head
、body
和p
元素的标签。如果不包含 doctype 声明,它甚至可以更短,但这将在 web 浏览器中触发 quirks 模式(关于 quirks 模式的讨论,请参考第一章中的 doctype 部分)。
注意要根据当前的 HTML5 规范检查文档的有效性,请访问
[
html5.validator.nu](http://html5.validator.nu)
或[
validator.w3.org](http://validator.w3.org)
,这两个网站都提供基于网络的服务,可以验证提供的网站 URL、上传的 HTML 文件或直接输入 HTML。对于 W3C 中的验证器,请确保将验证器下拉菜单中的文档类型设置为“HTML5(实验)”以确保根据最新的规范进行检查。
尽管前面的代码片段很容易激怒那些热衷于 XHTML 的朋友,但是为了清楚起见,包含可选的标记仍然是一个好主意。虽然 HTML5 可能看起来像是从 20 世纪带回了编码实践,但重要的是要记住,它记录的是实践中使用的内容,而不一定是最佳实践。
html 元素的属性
除了全局属性之外,html
元素还有一个新属性,manifest
,用于创建离线应用的新 API 中。当您利用这个属性时,web 应用的基本功能组件(例如 JavaScript)可以缓存在浏览器中,这样当用户离线并重新加载页面时,页面仍然可以运行。
注意
html
元素还有一个属性xmlns
,用于声明 XML 名称空间,它描述了正在使用的标记语言。这个属性应该出现在有效的 XHTML 文档中。它只有一个支持的值,文本[
www.w3.org/1999/xhtml](http://www.w3.org/1999/xhtml)
。它只适用于 XHTML,在 HTML 中没有任何意义,除了在可能跨越 HTML 和 XHTML 的文档中使两者之间的转换更容易。
记录元数据和脚本元素
在开始根标签html
之后,出现head
元素的开始标签,它包含文档元数据元素,这些元数据元素指定诸如文档在浏览器窗口中出现时的标题、页面上元素的默认 URL 地址、字符编码信息以及链接和嵌入的 CSS 样式表(如果使用的话)之类的页面属性。表 2-4 列出了相关元素及其内容模型类别。
5 仅当 itemprop 属性存在时才适合流内容类别,这是 HTML5 中可用的微数据规范的一部分。
6 像meta
元素一样,只有当itemprop
属性存在时,link 元素才适合流和短语内容类别。
网页信息:标题和元数据
如前面展示的最小 HTML5 示例所示,title
是 head 部分中唯一的强制元素。开始和结束title
标签之间的文本将显示在浏览器窗口标题栏中。文档标题是文档中一个经常被忽视的区域;你肯定见过标题为“无标题文档”的页面。这是不幸的,因为如果给予适当的关注,文档标题可以为您和您的用户提供许多好处:
- Better search engine ranking. Search engines will consider the page title when determining the relevance of your webpage to a specific topic and when determining the content displayed on the search results page.
更好的方便。如果你的用户给页面添加书签,默认情况下,页面标题将被用作书签名称。 Better usability. Identifying websites and pages in the same location helps to identify the association between specific pages and specific websites. For example, "Semantic Web W3C" is the title of the main semantic web page on the website of the World Wide Web Consortium, which clearly identifies the theme of the web page and the website to which it belongs.*
** 注意以网页中有意义的标题的重要性为例,试着打开几个“无标题文档”窗口,然后在最小化它们后在它们之间切换——在切换之前你能分辨出哪个是哪个吗?或者尝试在单个 web 浏览器窗口中打开与选项卡相同的页面。你能一眼看出哪一页是哪一页吗?
还要注意,如果网站名称包含在标题中,网站名称应该出现在页面主题标题之后。当公司或网站名称放在实际页面标题之前时,也会出现类似的问题,因为如果标题太长而无法在最小化时或在选项卡中显示,则可能会从右向左裁剪,导致实际页面主题标题从视图中被裁剪。
只有当scoped
属性存在时, 7 才符合流内容类别。
元元素
前一章介绍了常用于设置页面字符编码的meta
元素,但它也常用于设置一系列元数据信息和指令的键/值对。例如,它可以用于定义网页的概要描述,或者用于为页面上的内容定义一组关键字。在键/值配对中创建“键”时,meta
元素使用两个属性之一,http-equiv
或name
,而“值”部分使用content
属性来设置。这里有一个例子:
`
`
注历史上,搜索引擎使用关键词,但今天许多人不使用它们,因为它很容易误导依赖于这一特征的搜索引擎,以发送垃圾搜索结果。但是,如果使用得当,包含它们并没有什么害处,尽管更新关键字以反映频繁编辑的文档中的内容变化是很重要的,这可能会产生额外的维护步骤,而没有实质性的好处。选择权在你!
http-equiv
属性的一些用途(称为 pragma 指令)提供了设置 cookies、指定字符编码等任务,这些任务对于现有的更好的方法来说是多余的。因此,HTML5 规范中不推荐几个值。具体来说,content-language
和set-cookie
指令是不符合标准的,不应该使用(lang
属性应该用于设置内容的语言,真正的 HTTP 头应该用于设置 cookies)。HTML5 规范中的其他官方 pragma 指令是content-type
,它是charset
属性的另一种形式;default-style
,用于设置页面的默认 CSS 样式表(在第六章中解释);和refresh
,它在几秒钟后刷新或重定向页面,如前面的代码示例所示。
注意为了在暂停后重定向页面,在使用
refresh
pragma 指令时,URL 被添加到content
属性中。经过指定的秒数后,页面被重定向到 URL。重定向看起来像这样:
<meta http-equiv="refresh" content="3; URL=homepage.html">
.
为http-equiv
和name
属性建议的值的完整列表很大,所以您最好参考一些资源,比如分别位于[
wiki.whatwg.org/wiki/PragmaExtensions](http://wiki.whatwg.org/wiki/PragmaExtensions)
和[
wiki.whatwg.org/wiki/MetaExtensions](http://wiki.whatwg.org/wiki/MetaExtensions)
的 WHATWG PragmaExtensions 和 MetaExtensions wikis。除了这些网址上列出的值之外,HTML5 规范还包含了application-name
作为name
属性的值。该值允许为 web 应用(不是标准的 web 页面,而是添加了脚本功能的实际 web 应用)指定一个标题,该标题可以被视为独立于页面标题或对页面标题的补充(使用title
元素设置)。这是因为在使用应用来反映正在发生的操作(如加载附加内容等)时,页面标题可能会发生变化。
注意可能更有用的是元名关键字
robots
,它指导网络爬虫(谷歌和其他公司用来索引网页的自动程序)如何索引页面。关于这个名称值和其他值的信息可以在前面提到的 WHATWG 元扩展 wiki 的[
wiki.whatwg.org/wiki/MetaExtensions](http://wiki.whatwg.org/wiki/MetaExtensions)
上找到。
链接、样式和资源:基础、链接和样式
每个文档只能使用一个base
元素。它应该出现在任何其他属性中包含 URL 的元素之前。这个想法是,base
元素可以防止在整个页面中添加链接时重复输入相同的根 URL。它的两个属性,href
和target
,指定了要使用的 URL 和默认目标(如果适用)。target
告诉,例如,一个链接是在一个新窗口还是在同一个窗口打开。例如,如果这出现在 head 部分中:
<base href="http://example.com/portfolio/" target="_blank" />
这出现在正文部分:
<a href="photographs.html">Photography</a>
然后单击该链接将打开一个新的浏览器窗口,并将用户连接到 URL [
example.com/portfolio/photographs.html](http://example.com/portfolio/photographs.html)
。
链接元素
link
元素具有属性href
、rel
、media
、hreflang
、type
和sizes
,以及全局属性。其中,至少href
和rel
是共同设定的。href
指定链接资源的地址(URL ),而rel
指定资源的类型。以下代码是您最有可能使用该元素的方式,因为它通常用于将样式表链接到您的页面:
<link rel="stylesheet" href="main.css" type="text/css" />
然而,rel
属性可以用来提供关于页面的附加相关信息。这个属性的本质可能有点难以理解,所以让我们考虑一个例子。除了样式表之外,当使用link
元素引用网页头部的 RSS 提要时,您可能已经遇到过rel
,就像这样:
<link rel="alternate" href="http://example.com/feed/" type="application/rss+xml" />
前面的代码意味着“这个文档的另一个版本存在于[
example.com/feed/](http://example.com/feed/)
”,用户代理可以发现并找到 RSS 提要——大多数现代浏览器都会在浏览器地址栏中显示一个提要图标,允许用户选择、查看和订阅提要。
如果替代版本是翻译版本,那么alternate
值也可以与hreflang
属性一起使用。如果替代版本是为不同的媒体设计的,它也可以和media
属性一起使用。 8 它也可以和type
属性一起使用,表示链接的文档是不同文件格式的页面版本,比如页面的 PDF 版本。例如,一个链接引用了一个可打印的 PDF 版本的文档,该文档也是法语的,可能如下所示:
<link rel="alternate" href="alt-fr.pdf" type="application/pdf"?hreflang="fr" media="print" title="French version PDF for printing" />
HTML 规范为rel
属性列出了十几个预定义的类型(表 2-5 )。
8 媒体属性有screen, tty, tv, projection, handheld, print, braille, aural,
和all
等多个有效值,分别针对电脑屏幕、终端、电视、投影仪、手持设备、打印页面、盲文触觉反馈设备、语音合成器以及以上所有设备。其中,您最有可能使用屏幕和打印,也可能使用手持设备。Opera 在全屏模式下使用投影,所以如果您已经为主样式表指定了一种屏幕类型,您可能需要考虑包含投影:<link media="screen, projection" ... />
。
属性指定了被链接资源的 MIME 类型。它纯粹是对浏览器的建议(例如,用户代理不会仅仅依赖这里指定的值来确定资源类型),并且可以在样式表中省略,因为它们是在link
元素中最常见的文件类型。
样式元素
style
元素允许将 CSS 样式规则直接嵌入到 HTML 文档中,这可能是在创建页面时添加样式规则的一种快捷方便的方式,但是与全局style
属性一样,文档的 CSS 样式最好通过使用link
元素将外部样式表链接到页面来提供。一个新的属性是scoped
属性,它(理论上)允许将一个通用的 CSS 应用于一个特定的 HTML 块。然而,在撰写本文时,主流 web 浏览器的当前版本不支持该属性。
添加行为和回退:脚本和 noscript
script
元素类似于style
和link
元素的组合,因为它支持直接在 HTML 文档中编写嵌入式客户端 JavaScript 代码或允许将脚本作为外部文件加载。与 CSS 一样,除非不可避免地要在 HTML 中嵌入代码,否则最好将 JavaScript 放在加载的外部文件中,如下所示:
<script type="text/javascript" src="js/menu.js"></script>
前面的代码片段显示了链接到外部 JavaScript 文件时script
元素的一般外观(在本例中,该文件名为menu.js
,位于名为js
的目录中)。请注意,在外部脚本中链接时,元素的开始和结束标记之间不应包含任何内容。
HTML5 定义了一个名为async
的新属性,它与另一个属性defer
结合使用,可以用来控制何时解析外部加载的脚本。这两个属性都是布尔属性,因此它们的存在和缺失会以各种方式影响外部脚本的加载(表 2-6 )。
脚本:在顶部还是在底部?
需要注意的是,script
和style
元素不需要出现在 HTML 文档的头部;事实上,雅虎!Developer Network 公布了 35 个提高性能的最佳实践,指出script
元素应该放在网页的底部(见[
developer.yahoo.com/performance/rules.html](http://developer.yahoo.com/performance/rules.html)
)。(相反,style
元素应该出现在顶部的头部区域。)此外,第一章中提到的 HTML5 样板文件([
html5boilerplate.com](http://html5boilerplate.com)
)将所有脚本(除了 Modernizr,因为它更早就需要)放在页面底部,在结束的</body>
标签之前。可以说,这种做法虽然提高了性能,但存在语义问题,因为它将关于页面的元数据移到了正文区域,而不是它所属的头部。理想情况下,随着async
和defer
属性获得更广泛的支持,script
元素在页面中的位置将成为一个争论点。
最后,noscript
元素用于显示周围的内容,只有在浏览器禁用或不支持脚本时才会显示。如果noscript
元素放在文档的头部,那么它只能包含link
、style
和meta
元素;否则,如果它在主体中,它可以包含所有会出现在body
元素中的正常元素,除了另一个noscript
元素。还值得注意的是,noscript
元素的语法只适用于 HTML 文档,对 XHTML 文档没有影响。noscript
是 W3C 推动 XHTML 时前途未卜的元素之一,它保留了一点处理禁用脚本的粗糙和肮脏的名声。更好的方法是编写一个脚本,将页面内容从静态动态转换,因为即使启用了脚本,脚本也可能失败,在这种情况下,noscript
中的内容不会显示出来。例如,考虑下面的代码(为了简洁起见,它嵌入在 HTML 中):
<div> <p id="fall-back">Script disabled</p> <script type="text/javascript"> var fallBack = document.getElementById('fall-back'); var fallBackParent = fallBack.parentNode; fallBackParent.removeChild(fallBack); fallBackParent.innerHTML = "<button id='alert-button'>Show Alert!</button>" var showAlert = document.getElementById('alert-button');
` showAlert.onclick = function(){alert( "script worked!" );}
一个段落(其id
被设置为fall-back
)具有一些默认的静态内容。接下来是一个script
元素,它删除了默认的 HTML,并用一个按钮来代替,这个按钮具有点击时显示警告框的功能。这样做的好处是,不仅为将静态页面转换成动态页面提供了更细粒度的控制,而且这也适用于 XHTML。
记录切片元素
在结束的head
标签之后是开始的body
标签,它可以包含任何非头部特定的标记,通常是预期出现在 web 浏览器窗口内容区域的内容:段落、列表、图像等等。body
标签传统上有几个表示属性:background
、text
、link
、vlink
和alink
,分别用于设置文档的背景颜色、文本颜色、链接颜色、已访问链接颜色和活动链接颜色。所有这些属性在 HTML 4.01 中都被贬低了,在 HTML5 中已经被标记为过时。它们的效果应该用 CSS 来创建。CSS background-color
、color
、a:link
、a:visited
和a:active
属性和伪类(在第六章中讨论)是适当的。
结束body
标签后面紧跟着结束html
标签。这是一个完整的 HTML 文档,尽管没有任何内容。我们将在本章和后续章节中看到的其余元素将主要是内容元素——出现在正文中以各种方式标记和组织内容的元素。
语义分段元素
可能出现在主体中的第一组元素是那些涉及到将内容组织到不同逻辑部分的元素。这些显示在表 2-7 中,其中还列出了每个内容模型的类别。你会注意到几乎所有的 HTML5 都是新的。您还会注意到,它们的名称暗示了网页的常见组件,如页眉、页脚、导航栏等。这些元素的语义细微差别值得他们自己密切关注,所以暂时只需注意 HTML5 中有新的元素来赋予内容比传统的div
和span
元素更细粒度的含义。我们将在第三章的中深入探讨这些新的切片元素。
内容分组元素
在页面上内容的特定部分内,该部分的各种组件被分成段落、列表、图表等。段落等元素将内容分组为比节更小的单元。除了 HTML5 中新增的figure
和figcaption
之外,这个组中的元素对您来说可能都非常熟悉。这些元素的内容模型类别和描述见表 2-8 。
必然段:p
也许你在编写网页时最常用的标记是p
。关于p
没有太多要说的:它只是用来标记一个段落。然而,这个不起眼的元素经常被 WYSIWYG(所见即所得)web 创作软件滥用,作为在内容之间添加填充的快速而肮脏的间隔。您可能见过如下所示的标记,其中作者在他们的编辑器中按了几次 Enter 键:
`
`
这是(X)HTML 被吸收到表现方式中的一个典型例子。我们在这里发现多个无意义的段落,里面有一个不间断的空格实体(因为有些浏览器不显示空元素),但这种效果确实应该用 CSS 来实现。在内容下方添加一些空间的快速方法是向相关内容添加一个类:
<p class="section">Your content here.</p>
然后使用 CSS 样式化该类,在顶部或底部添加填充:
.section { padding-bottom: 3em; }
注意EMS 的使用传统上是专业 web 开发人员的首选度量单位,因为它是一个相对的长度单位,如果用户(或开发人员)放大或缩小页面,它将按比例调整大小。然而,近年来,随着 web 浏览器在处理绝对单位(如像素)的大小调整方面变得更好,对 ems 的偏好变得不那么明显了。关于尺寸是用 ems 还是像素有相当大的争论。em 使用起来可能不太直观,而像素则不能适应层叠式的大小变化。选择一个或另一个通常不会是一场灾难,所以选择你觉得更舒服的。总之,在一天结束时,你应该在各种场景下测试你的页面,所以问题应该会很快显露出来。
打破思维:hr
hr
元素,或者说水平标尺,在历史上一直是一个表示元素,但是它被重新定义为代表一个内容和另一个内容之间的主题分隔。hr
元素有几个属性——size
、width
、noshade
和align
——但是在 HTML5 中所有的属性都被声明为过时,所以 CSS 必须用于水平标尺的样式,这是应该的。
保留格式:前
那么,pre
呢?由pre
元素引起的视觉效果是保留标记中的空白(即制表符、空格和换行符),所以如果空白对于理解内容很重要,比如在计算机代码示例中,那么就使用pre
。或者,可以用 CSS white-space:pre
属性和值复制的效果,在可以使用另一个元素更好地描述相关内容的情况下,这可能是一个更可行的选择。例如,在标记计算机代码的情况下,code
元素和white-space
CSS 属性可以用来保存格式,并在格式可能丢失的情况下传达内容的含义(例如,如果它被屏幕阅读软件读取)。
报价文本:批量报价
blockquote
是一个元素,历史上一直用于其呈现效果,而不是其语义或结构相关性,但它应被视为包含从另一个来源引用的内容,无论它将如何呈现。blockquote
元素有一个cite
属性,允许作者引用引用的来源(通常以 URL 的形式,但也可以是任何形式的引用,比如另一个作者的名字或电影的名称)。
注意例如,对于段落中出现的行内引用,还存在
q
元素,它在第三章的文本级语义部分列出。
列表
在当前的 HTML 规范中有三种列表类型:无序列表(ul
)、有序列表(ol
)和描述列表(dl
)。
列表类型之间的差异非常小且简单明了:
- When your content (as you expected) does not have any particular order, you should use a unordered list .
- When your content is arranged in a certain order, you should use a ordered list : alphabetically, numerically and so on.
- Description list is used to associate names or terms with values or other data-any items directly related to each other, such as glossary.
无序列表和有序列表:ul、ol 和 li
无序列表和有序列表分别由开始的ul
或ol
标记组成,后面是任意数量的列表项—li
—元素,最后是结束的</ul>
或</ol>
标记。开始和结束标记只能包含列表项,但是列表项可以包含任何流元素,包括段落、div
s、标题以及更多列表。只要它们都包含在一个列表项中,它就是完全有效且格式良好的标记。至于 HTML/XHTML 之间的区别,在 HTML 中你不必用</li>
关闭列表项,但是在 XHTML 中你必须这样做。
除了全局属性之外,无序列表没有任何属性。另一方面,有序列表有三个附加属性:reversed
、start
和type
。第一个是reversed
,决定了列表的排序方向。它是一个布尔属性,意味着它的存在与否决定了它的行为。如果这个属性存在,列表将是降序的(9,8,7,6,5…);否则,将是升序(1,2,3,4,5…)。不幸的是,在撰写本文时,浏览器对该属性的支持很差。
在这种情况下,您可能会发现 HTML 语法更清晰。例如,以下两种方法都可行:
`
- item 1
- item 2
- item 1
- item 2
接下来,start
属性允许作者从一个非 1 的数字开始对有序列表进行编号,这在您需要中断有序列表时非常有用,比如在搜索结果列表被分成几页的情况下。有趣的是,这个属性在 HTML 4.01 中被弃用了,但在 HTML5 中又恢复了良好的地位。
最后,type
属性可用于将列表开头的标记从十进制数字(默认)更改为字母列表或罗马数字。然而,强烈建议使用 CSS list-style-type
属性,它提供了更多的选项,并且将这种表示上的改变转移到 CSS 而不是 HTML 中。
当li
元素是无序列表的一部分时,它只有全局属性,但是当它在有序列表中时,它又多了一个属性:value
。此属性允许作者为特定列表项指定一个无序编号。
属性的问题已经解决了,让我们来看一个实现列表的例子。令人欣慰的是,虽然标记可能并不令人兴奋,但它至少是灵活的,使用 CSS 可以以多种方式显示列表:水平、垂直、扩展/折叠等等。例如,采用以下导航菜单:
`
`我们可以使用下面的 CSS 规则很容易地将它变成一个水平菜单:
li { float: left; }
简单!但是在这个阶段,它显然很难看,而且有点不可用,所以让我们稍微整理一下:
li { border: 1px solid; float: left; list-style-type: none; padding: 0.2em 1em; }
通过添加边框,删除列表项目符号,并添加少量填充,我们得到了如图 2-3 所示的列表。
图 2-3。一个无序列表,列表项向左浮动,加边框,填充
如你所见,我们已经有了一个非常实用的水平菜单。我们可以用一些锚标签的样式使它更有趣,给它们一个background-color
,用display:block
让它们填充整个列表项区域,用伪类:hover
改变它们的背景,等等。
为了帮助减轻创建链接列表的痛苦,也值得尝试一下 Accessify 的 List-O-Matic (
[
accessify.com/tools-and-wizards/developer-tools/list-o-matic](http://accessify.com/tools-and-wizards/developer-tools/list-o-matic)
),这是一个在线列表生成器,可以让你从各种预建的样式中进行选择。
因此,您已经可以看到一个简单的列表可以用不同于默认样式的方式显示。使用 CSS 来创建列表的动态行为是可能的。例如,正如 Eric Meyer ( [
meyerweb.com/eric/css/edge/menus/demo.html](http://meyerweb.com/eric/css/edge/menus/demo.html)
)所述,:hover
伪类可用于将嵌套列表显示为弹出菜单。实现这一点的 CSS 非常简单:
li ul {display: none;} li:hover > ul {display: block;}
这意味着任何作为li
的派生元素的ul
——即嵌套列表——都不应该显示。第二行表示任何被悬停的li
的子元素ul
都应该正常显示。在兼容的浏览器中,最终结果(加上一些更多的表示风格)看起来像图 2-4 。
图 2-4。一个纯 CSS 嵌套菜单
一切都很整洁。Patrick Griffiths 的 Suckerfish Dropdowns 脚本([www.htmldog.com/articles/suckerfish/dropdowns](http://www.htmldog.com/articles/suckerfish/dropdowns)
)提供了一个 CSS 解决方案和一个 JavaScript 解决方案,这两个解决方案都非常健壮(满足了多个嵌套菜单的需要)并且非常容易实现,只需要包含一个小脚本和向 CSS 文件添加一个类选择器。
描述列表:dl、dt 和 dd
描述列表由一个开始的dl
,接着是一个术语(dt
,然后是任意数量的描述(dd
)。典型的描述列表如下所示:
`
- Bottle
- A receptacle having a narrow neck, usually no handles, and a mouth that can be
plugged, corked, or capped. - To hold in; restrain: "bottled up my emotions."
- Rocket
- A vehicle or device propelled by one or more rocket engines, especially such a
vehicle designed to travel through space.
大多数浏览器会以类似于图 2-5 所示的方式显示前面的代码。
图 2-5。一个描述列表,左边是术语,描述缩进
如上所述,描述列表相当灵活。只要术语和描述之间有直接关系,就可以使用这个列表来表示许多结构。例如,作为术语的照片可以具有包括关于摄影师和照相机的信息的描述。此外,描述列表可用于显示会议上一系列演示的时间表,演示的标题为术语,描述包括演示作者的详细信息以及日期和时间。在线购物应用中也可以使用描述列表来描述产品细节等。
尽管描述列表使用起来很灵活,但你应该记住以下警告:定义术语只能包含短语内容,不能包含流内容,因此不能包含段落、标题或列表——这意味着术语不能像标题那样被赋予不同的重要性级别(h1
、h2
等等)。然而,列表中的描述元素(dd
)可以包含任何流元素或一系列元素,只要它们格式良好。
图表、照片、插图:数字和图片说明
元素背后的思想是提供图表、图像、代码等等,可选地带有标题或图例(figcaption
元素),作为与文档主要内容相关的独立单元。该图形应该能够在不破坏文档整体布局的情况下移动或删除。
例如,下图显示了两幅并排的图像,标题如下(见图 2-6 ):
`


图 2-6。示例从figure
和figcaption
元件输出
创建分部:div
一个div
(“division”的缩写)用于标出一个内容块。它没有给它所围绕的内容添加任何特殊的含义,只是用来添加额外的 CSS 样式或 JavaScript。传统上,div
是帮助页面布局的常用元素,但在许多情况下已经被语义更具体的元素所取代(在第三章中有更深入的讨论)。
文本级语义元素
我们现在开始进行最细粒度的控制:文本级的元素吸收了有意义的单个单词和句子。这些元素的正确语义用法有细微差别,最好在下一章中与文档分段元素一起讨论,所以现在先熟悉一下表 2-9 中的大量这些元素,我们将在第三章中进一步讨论它们。大多数已经存在了一段时间,但是有几个如i
和b
在 HTML5 中有了新的含义。
仅当它包含专有措辞内容时。
表格数据元素
几年前,使用表格来生成像素级精确的网站布局是非常普遍的做法。在当时,对于外行人来说,表格的这种使用提供了网页设计的一个飞跃,因为设计可以在 Adobe Photoshop 中创建,切割成一个网格,然后在表格中重新组合,以创建网页中的原始设计。然而,这种设计方法的不灵活性和可访问性很快就被 CSS 所取代,当吹捧的“无表格布局”成为一个流行语,意思是没有表格的像素完美布局。哎哟。桌子哪里出问题了?在标准世界中,当它们被用于一个它们原本不打算用于的目的——表示——时,它们就出错了。表格是用来显示数据的。洛杉矶电子表格,仅此而已。
如表 2-10 所示,表元素是适合流内容模型类别的单个单元。所有其他与表格相关的元素都在一个table
元素中工作,以定义其结构,因此它们不适合任何主要类别。与body
元素一样,大量本质上是表示性的属性被扔进了过时的垃圾箱;例如,在tr
中,除了全局属性之外的所有属性都被去掉了。
10 可以放在语法内容中,如果它只包含语法内容。
表格基础知识
手工编码标记时,创建一个简单的表相当简单。单个表格的基本要素包括一个开始的table
标记,后面至少有一个表格行(一个tr
),后面至少有一个表格单元格(一个td
,意思是“表格数据”)。这里有一个例子:
`
Some data |
对于创建表来说,这已经是最简单的了,但是创建一个只有一项数据的表是不太可能的,所以让我们把事情变得更有趣一些。下面的标记是针对一个包含两行数据的两列表格(嵌入样式是提供一个border
作为视觉辅助,以更好地区分表格的布局;理想情况下,它的效果应该放在生产环境中的外部 CSS 文件中):
<style type="text/css">
` td { border: 1px solid #ff0000; }
Name | Place of residence |
Anselm Bradford | Auckland |
Paul Haine | Oxford |
这将创建一个基本的表格,但是让我们进一步使用表格标题。
添加表头
您可以通过在表格顶部标记标题来指示列,从而使该表格更加清晰易读。虽然您可以通过向每个表格单元格添加一个类名,然后用 CSS 对其进行样式化来做到这一点,但是更好的方法是将最上面的表格单元格变成真正的表头,用th
元素代替td
。
`
...
Name | Place of residence |
---|
前面的标记呈现如图图 2-7 所示。
图 2-7。标题单元格使用th
的基本表格
这种方法有几个好处。首先,它对可访问性有很大的帮助。虽然屏幕阅读设备在有能力的用户手中可能会将第一个表示例读取为“姓名,居住地,安瑟姆·布拉德福德,奥克兰,保罗·海雷,牛津”,并且有可用的表头,但它可以理解表头如何与数据相关,并将其读取为“姓名,安瑟姆·布拉德福德,居住地,奥克兰,姓名,保罗·海雷,居住地,牛津……”11当然,在这个简单的示例中,推断表的结构非常容易。不难看出“姓名,居住地,安瑟姆·布拉德福德,奥克兰,保罗·海雷,牛津”是一个人的名字后面跟着一个地名,但是当表格变得更复杂时(因为有更多的行和列),这就成了一个大问题。
注意你也可以使用
speak-header
CSS 属性来控制在听觉样式表中是读出once
还是always
。
除了让屏幕阅读器的用户更容易访问表格之外,使用适当的表格标题还为视力正常的用户提供了关于表格结构的有用的视觉提示,并使 web 作者的生活稍微轻松一些,他们不必为每个标题都包含一个额外的类名。此外,这给了设计者一个额外的 CSS 和脚本钩子。
添加图例:标题
现在您已经打开了表格,您可以通过以caption
元素的形式包含表格标题来使事情变得更好。这个元素需要直接放在开始的table
标签之后:
`
…
Name | Place of residence |
---|
大多数用户代理会呈现标题,如图 2-8 所示。
图 2-8。一个基本表使用一个caption
11W3C 在[www.w3.org/WAI/References/Tablin](http://www.w3.org/WAI/References/Tablin)
提供了一个工具来帮助理解你的表格如何被辅助设备读取。
添加结构:thead、tfoot、tbody
如果你的表格看起来有点长,有点笨拙,你可以用thead
、tfoot
和tbody
添加一些进一步的结构来帮助你的浏览器理解事情。这些元素允许您将行分组为标题部分、脚注部分和正文部分。很像th
元素,如果需要的话,这三个元素给你 CSS 和脚本的另一个钩子,而不必添加额外的类或 id。像caption
一样,这些元素必须按照特定的顺序和位置放在表格标记中。第一,如果包含三者中的任何一个,就必须包含thead
。这个元素可以放在任何你想放的地方,但是最好直接放在开始的table
标签下面——除非你已经包含了一个caption
,在这种情况下thead
元素必须直接放在它的下面。如果你愿意,你可以把它放在你的tfoot
和tbody
下面,它仍然是有效的标记,但是只有当你几个月后回到你的标记上,想知道你到底在想什么的时候,你才需要这样做。
然而,tfoot
元素、必须在tbody
元素之前出现。为什么页脚在正文之前?这样,用户代理就可以在从中间开始绘制之前先绘制表格的顶部和底部,如果您计划让表格主体滚动并且有许多行,这就很有用。
最后,添加tbody
元素。不管怎样,这个标签实际上隐含在你的表中。例如,尝试将tbody {font-style: italic}
添加到您的 CSS 中,并将其应用到一个基本的表格中,您会看到它将表格中的文本设置为斜体。即使它的存在是隐含的,如果你使用thead
和tfoot
,你必须明确包含tbody
标签。因此,一旦添加了这些元素,您的标记应该看起来有点像这样:
`
Name | Place of residence |
---|---|
Name | Place of residence |
Anselm Bradford | Auckland |
Paul Haine | Oxford |
除了现在在表的底部重复出现的标题之外,具有这些元素的表和没有这些元素的表在视觉上没有区别,但是包含它们是好的,因为它们提供了关于表的结构的额外的、有用的信息,在打印或在屏幕上查看时可以利用这些信息。
使用tfoot
元素时要小心。因为该元素可能会在几页中重复出现,所以最好将其用作thead
元素内容的副本(如前面的示例所示),而不是长表的字面结论,例如价格列下的最终总计(如果它出现在表完成之前,则没有什么意义)。
添加更多的结构:colgroup 和 col
如果您需要一个表格单元格跨越多行或多列,您可以使用rowspan
和colspan
属性来实现这种效果,这两个属性都采用一个数值来表示一个特定的单元格应该跨越多少个单元格。这一切都很简单。举个例子,让我们想象一下,除了居住在新西兰的奥克兰,我在佛蒙特州还有第二处住所。将这些数据添加到表中需要一个额外的行,但是我不会在新的居住地旁边留下一个空的表格单元格,我将插入一个rowspan
属性,这样包含我的名字的单元格就可以与两个居住地配对:
`…
Anselm Bradford Auckland Vermont Paul Haine …`该表现在呈现如图图 2-9 所示。
图 2-9。使用rowspan
属性的基本表格
如果需要,表格单元格可以跨越行和列。你只需要确保你的细胞和跨度相加。例如,如果您的表格有两行,一行包含五个td
元素,那么第二行最多只能跨越五个单元格——超过这一行,表格将无效,并且会不可预测地呈现,少于这一行,剩余的单元格将占用剩余的空间。
我听说过去有人建议rowspan
和colspan
是表象性的,应该避免,但这是不正确的;您正在使用属性来定义结构,而不是表示,因此您应该将该信息保存在标记中。
到目前为止,您可能已经注意到,大多数表格标记只与行和这些行中的单个单元格相关——没有tc
元素。相反,我们有两个元素可以定义列和列组,并且都是可选的:col
和colgroup
。
colgroup
元素允许您使用一个span
属性和一个数值来指定表中将存在多少组列(因此每组列有一个colgroup
,一个组只能包含一列)以及每组中包含多少列。这个元素直接放在开始的table
标签之后,除了可选的col
标签之外,它不包含任何标记,稍后会介绍。
例如,考虑图 2-10 中所示的表格。
图 2-10。多列表:这里有三个列组,分别是“姓名”、“居住地”、“出生日期”
沿着最上面的标题阅读,您可以看到这个表格有三组列,最后一列横跨三个单元格的宽度。使用colgroup
,您可以在表的开始定义该结构,如下所示:
<table> <colgroup></colgroup> <colgroup></colgroup> <colgroup span="3"></colgroup> <tr> …
有了这个标记,您就说这个表包含三组列,前两组包含一个列(暗示一个列;这种情况下不需要添加span="1"
属性),第三组包含三列。
如表 2-10 所示,有一个col
元素,这是一个自结束元素,也有一个span
属性,用于指定colgroup
中是否存在列。在功能和语义上,它实际上与colgroup
相同,但不幸的是 HTML 规范不允许嵌套colgroup
元素,所以您必须使用col
来代替。使用前面的示例,您可以用两种不同的方式指定最终的三列集合,或者每列一个col
,如下所示:
<table> <colgroup></colgroup> <colgroup></colgroup> <colgroup> <col /> <col /> <col /> </colgroup> <tr> …
或者使用单个col
和一个span
属性,如下所示:
<table> <colgroup></colgroup> <colgroup></colgroup> <colgroup><col span="3"></colgroup> <tr> …
这开始看起来像是大量的工作——为什么有人会为此费心呢?的确,乍一看,您似乎提供了多余的信息,但是这种标记确实有它的用途。有一些附带的好处,但是colgroup
和col
存在的主要原因是允许浏览器呈现表格,即使所有的表格行数据还没有到达。如果没有这两个标签提供的信息,浏览器必须首先解析整个表格,找到包含最大数量的单元格的行。接下来,浏览器必须计算该行的宽度,只有这样它才能知道表格的宽度,并允许它被呈现。当您让浏览器预先知道表的列结构时,浏览器可以在数据到达时呈现数据。
表格元素
就其本质而言,web 表单是一个很大的主题——在 HTML5 中,随着许多新元素的加入,这个主题变得更大。表单元素通常需要对属性进行仔细的编码,以使元素组能够正确地协同工作,并使表单成功地将其数据提交给服务器。表单将在第四章中深入讨论,但作为尝试,我们将在这里看一个基本示例:
`
`当在网络浏览器中渲染时,这将看起来像图 2-11 。
图 2-11。一个简单 web 表单的出现
这只是一个显示表单结构的简单示例。表单通常遵循以下格式:
- Include the form component in a
form
element, which defines the file for processing the form data when submitting the form data.- Some controls for collecting input are defined, such as text fields, drop-down lists, radio buttons and so on.
- Provide a button to submit the form to retrieve and process the input data.
如果您对表单非常熟悉,您可能会注意到上一个示例中的一个不熟悉的属性— placeholder
,它在textarea
元素中添加了灰色文本,提供了应该输入什么的提示。placeholder
属性是 HTML5 中的一个新属性,但这仅仅是冰山一角!在最新的规范中,web 表单发生了巨大的变化。除了在表 2-11 中列出的新的和更新的元素之外,还可以使用input
元素创建大量新的输入类型,例如急需的电子邮件地址、电话号码、日期和时间等表单输入!你将不得不等到第四章更深入的讨论。同时,让自己熟悉与表单相关的基本元素,然后让我们暂时了解一下嵌入式内容元素的概况。
如果类型属性未设置为隐藏,则 12 符合交互类别。
嵌入内容元素
多年来,除了格式化文档之外,HTML 不擅长做任何事情。静态图像是可以嵌入页面的复杂内容的缩影,而动态 gif 则是动画中的精华。这些缺点有助于 Adobe Flash Player 等技术的传播,这些技术解决了网页上动画、交互性、视频和音频解决方案的缺乏。随着 HTML5 的出现,这种语言已经发生了重大转变,成为开发 web 应用的平台,而不仅仅是格式化 web 文档。新的video
元素也许比其他任何元素都更能体现 HTML5 希望表现的东西——使用 HTML/CSS/JavaScript 开放标准的丰富多媒体体验。在video
元素被引入之前,没有 Adobe Flash 的视频似乎是不可能的。当然,其他解决方案也存在,比如苹果的 QuickTime 插件,但多年来,Flash 视频一直在 YouTube.com 等流行的视频分享网站上占据主导地位。
2010 年春天,苹果公司(Apple Inc .)的史蒂夫·乔布斯(Steve Jobs)发布了一封公开信,抨击 Adobe Flash 是一项有缺陷的过时技术,随着 HTML5 视频的传播,这项技术将被搁置一旁。HTML5 作为网络的未来被推向公众意识。就目前而言,未来还在前方;Flash 视频仍然得到广泛使用,HTML5 视频在应该使用哪种视频格式上存在分歧。但慢慢地,HTML5 技术家族正在朝着标准化功能取得健康的进展,以前只有 Adobe Flash 和其他第三方插件才有可能实现这一点——不仅仅是在视频方面。新的canvas
元素提供了一个位图绘制表面,可以使用 JavaScript 来创建复杂的渲染和体验 13 类似于以前使用 Adobe Flash 制作的丰富的多媒体体验。
嵌入式内容元素是如此丰富多样的一组元素,我们将在第五章中深入探讨它们。与本章中涉及的其他元素集一样,表 2-12 显示了新的和更新的元素以及它们所属的内容模型类别。熟悉这些,我们将在几章后回到它们。现在,前进!
13 请访问 ro.me
上的罗马项目,了解嵌入式元素的功能。
14 如果设置了 usemap 属性,则可以放在交互类别中。
与 img 元素一样,如果设置了 usemap 属性,则可以将其放在交互类别中。
16 可以放在语法内容中,如果它只包含语法内容。
互动元素
最后一组元素是一个很小的集合,它包含了通常通过点击进行交互的元素,而不需要额外的脚本或其他创建交互性的机制。这组四个元素中的三个,details
、summary
和command
是新的,而menu
在 HTML 4.01 中被贬低后,在 HTML5 中被重新定义。这些要素的通常汇总见表 2-13 。
显示更多信息:摘要和详细信息
summary
和details
元素一起提供了一个“小部件”,可以显示和隐藏额外的文本,而不需要使用 JavaScript 或 CSS。summary
元素作为一个标题(或摘要,如元素名所示),提供了一个切换按钮,单击该按钮可以显示和隐藏details
标签之间的文本。默认情况下,details
内的文本将被隐藏,直到该开关被点击;然而,布尔属性open
可以被添加到details
元素,以便默认显示文本。
不幸的是,在撰写本文时,details
和summary
元素还没有得到很好的支持,只有谷歌的 Chrome 浏览器支持这一功能。 18 如果你使用的浏览器不支持这些元素,那么细节区内的所有内容都会显示出来,不会有任何形式的切换。
一个基本示例可能是这样的:
`
Legal Notice
All content copyright 2011 Anselm Bradford
这将显示一个开关和文本“法律声明”,单击它可以显示和隐藏版权声明。details
元素可以包含任何流元素,这意味着细节可能相当复杂。例如,下一个例子展示了一些可能出现在剧院网站上的 HTML。这个例子使用了summary
和details
元素来显示和隐藏剧院当前正在上演的戏剧的附加信息:
`
A Midsummer Night's Dream
Duration: 1hr 42m
Showtimes:
- Tuesday, 8pm
- Wednesday, 8pm
- Thursday, 8pm
- Friday, 6pm and 8pm
如果类型属性设置为工具栏,则 17 适合交互类别。
18 访问 http://caniuse.com网站,在搜索栏中输入详情,查看浏览器支持的当前状态。
与上一个只切换一行文本的例子不同,这个例子显示和隐藏了两个段落和一个无序列表。
与元素的交互将看起来像图 2-12 。
图 2-12。点击摘要开关后显示隐藏和显示状态的细节元素
注意
summary
元素是可选的;如果省略,浏览器将为details
元素创建自己的标签,比如文本“Details”
例如,使用 JavaScript,details
元素可以用来在 web 应用中设置用户首选项面板。在本例中,summary 是用户的昵称,用户可以打开并更改它。保存他们的更改会更新summary
元素中的值,这将改变细节标签的外观。
`
Mac
图 2-13 显示了打开详细信息、更改名称并点击保存…的过程,然后(使用 JavaScript)更新摘要文本。显然,这将需要永久保存真正有用的更改,要么在后端数据库中,要么可能在客户端存储中(所谓的 web 存储在相关的 API 中可用,在附录 A 中讨论)。
图 2-13。一个使用 JavaScript 动态更新细节摘要的细节元素
提供工具栏:菜单和命令
放置在menu
元素中的单个command
元素可以用来创建一个类似桌面应用中的菜单栏。不仅如此,它还可以用来创建上下文菜单或其他交互式菜单选项列表。还记得使用了这两个元素的contextmenu
全局属性吗?不幸的是,像contextmenu
属性一样,command
属性还没有在任何主流浏览器中实现,所以这些菜单仍然停留在 HTML5 规范的理论空间中。
总结
恭喜你!这是一个非常密集的章节,充满了大量的信息,但是我希望您已经了解了 HTML5 中存在哪些新元素,并且可能复习了其他一直存在的元素。现在是时候转向我们在本章中忽略的细节,看看如何实际使用这些丰富的新 HTML 元素了。**
三、识别语义
好了,好了,我们已经研究了可用的元素,这很好,但是你可能想知道如何从了解 HTML 元素到掌握 HTML 元素。好了,在知道存在什么元素之后,下一步就是知道什么时候使用一个而不是另一个。本章将引导你走上这条道路,重点放在定义网页结构和语义的元素上。我们将编写一个简单的 web 页面大纲来学习结构语义,然后深入一个案例研究来探索文本级语义元素的细微差别。
什么是语义,我为什么要关心语义?
“HTML 中的元素、属性和属性值(由该规范)定义为具有特定的含义(语义)。例如,ol 元素表示有序列表,lang 属性表示内容的语言。
作者不得将元素、属性或属性值用于其适当的预期语义目的之外的目的。”
WHATWG HTML 规范
单词 semantic 在 web 开发社区中经常可以在诸如语义网、好语义网、语义丰富的 HTML 等短语中听到,但它感觉有点像是一个被抛来抛去的流行语,让演讲者看起来对当代 Web 开发很时髦。到底是什么意思?这个问题正是语义学想要回答的。语义是某事物的意义,尤其是关于语言的组成部分。语义 HTML 是关于描述它们所包含的内容类型的 HTML 元素。它关注的是将内容封装在一个 HTML 元素和另一个元素中的含义。它也是关于按照 HTML 规范中的指导方针,为特定的内容使用适当的元素。
有时候一个元素将包含什么样的内容是非常清楚的。例如,看到time
可能会让你快速找到这个元素的内容。另一方面,看到s
可能需要仔细查看 HTML 规范中的相关段落。但是让我们再来看看time
元素。它将包含的内容是否像最初看起来的那样清楚?例如,我可以这样写吗:
<p>Innovations of the <time>21st century</time>.</p>
或以下内容:
<p>The machine ran <time>infinitely</time>.</p>
嗯,没有;每个元素都有约束条件,虽然这两部分内容都与时间相关(一个时间段和一个时间属性),但它们不是time
元素想要描述的与时间相关的内容类型。稍后我们将更多地讨论time
,但简而言之,规范告诉我们它是公历中精确的日期,可选地带有一个时间。所以,并没有我们最初想的那么清楚!为什么这个元素不叫date
而叫datetime
?好吧,撇开元素名称不谈,关键是每一个都有微妙的差别,不管这个名称使它的目的看起来有多清晰。掌握这些细微差别将有助于创建结构合理、易于维护、对数据挖掘服务(例如,搜索引擎)更友好的页面。所以,把你的假设留在门口,让我们深入 HTML 元素的细微差别!我们将从结构元素开始。
勾勒思维
看看你是否能理解这个场景。你有一个新的网站项目要做。在仔细考虑了网站的总体概念后,你启动你最喜欢的图形编辑器,开始移动颜色块,摆弄纹理和线条。你扔进一个标题和标志,并把标题拖来拖去,直到它看起来合适。这引起共鸣了吗?当着手一个新的网站项目时,立即投入进去并开始设计网站的外观是很诱人的,但是如果你发现自己也在这样做,那就值得停下来看看大图。最终,一个设计良好的网站是一个提供有效的交付和访问其内容的网站。你的设计应该旨在创建一个信息层次结构——页面的各个部分应该首先吸引浏览者的注意力,然后是第二,以此类推。如果你事先没有把所有的内容组织成一个层次结构,你可能会发现自己一次又一次地放弃你的设计,因为你发现这个或那个部分需要在页面上或多或少地突出。
所以,暂停一下,把设计放一会儿,拿起纸和笔,写下你网站的主要内容。为每一部分考虑一个标签,以及它相对于页面内容的位置。暂时忘记细节,而是专注于创建可以位于主要内容部分之上的标签。例如,对于一个简单的主页,你可能会想出类似于图 3-1 的东西。
图 3-1。一个简单的主页轮廓
把这个列表想象成你的网站内容的目录或站点地图,什么在什么之前,什么是一个更大部分的一个子部分。在这个例子中,网站的标题(可能是一个公司名称)是所有其他内容所属的信息。下面是到其他页面的主导航(记住这是大图,所以在这个例子中没有显示单独的链接),接下来是主页上的特色内容,包括一篇特色文章,其中有对文章内容的简要总结。最后,主页上的其他文章被放在“其他新闻”部分。
开发一个这样的列表可以帮助你组织和可视化你网站上的信息层次,但这不仅仅是一个崇高的练习,在开发你的网站时可能有帮助,也可能没有帮助 HTML5 规范实际上提供了一个具体的算法来建立一个基于大纲的网站结构,例如图 3-1 中的大纲。不仅如此,该算法是学习如何正确组织网页的一个很好的指南。
HTML5 轮廓算法
HTML5 规范的这一部分被称为 HTML5 大纲算法,它提供了将内容分成嵌套列表的规则,每个列表都有一个标题。提供理解和解析网页结构的标准算法意味着每个适当构造的网页自动获得一个“目录”,该目录可以被例如屏幕阅读器(对于视力受损的用户)使用,或者在内容的联合部分中使用。
注意一个部分 1 只是一组内容——把它想象成页面上某个内容周围的一个盒子——而一个标题就像是打在盒子上的标签,说明它包含的内容。
简单来说,outline 算法使用以下规则解析文档:
- Specify the
body
element as the root section under which all other page sections are grouped.- Specify the first title content element as the title of
body
.- For each subsequent slice content element found, define new slices and sub-slices and add them to the outline.
- Specify the first title content element found in each subsequent chapter as the title of that chapter.
在前面的步骤中,请注意提到的两个内容模型类别,标题内容和小节内容。这两个类别中的元素位于第二章中的“语义分段元素”一节。两者都包含一小组元素;标题内容包括h1
、h2
、h3
、h4
、h5
、h6
和hgroup
元素,而article
、aside
、nav
和section
元素属于小节内容。 2
1HTML 中有一个 section 元素,但在此上下文中,“section”是一个通用术语,涵盖了 sectioning 内容模型类别中的 HTML 元素。
2 请注意(可能令人惊讶的是)div 不包括在分段内容中。
使用标题内容隐式创建大纲
之前的网站轮廓(图 3-1 )可以使用h1
、h2
、h3
和h4
元素来建立顺序和层次,如下所示:
`
Site Title
Main Navigation
Featured Content
An Article
Article Synopsis
Other News
Another Article
`坚持住。如果您仔细阅读大纲算法解析规则,您可能会注意到这里的一个问题。从h1
到h4
元素是标题内容元素,而不是小节内容元素,那么除了由主体创建的根节之外,这个页面还会有其他节吗?答案是肯定的。如果算法遇到预期的情况,可以隐式地创建部分。
从h1
到h6
标题元素据说有一个等级,这是它们名字中给出的数字(例如,h1
的等级为 1,h2
的等级为 2,依此类推)。较低等级的标题元素,当被放置在较高等级的元素之下时,将被嵌套在较高等级的标题元素的部分内。然而,如果标题元素与它的前身具有相同或更高的等级,它将隐式地创建一个新的部分,如果还没有定义的话(图 3-2 )。请注意,这里没有添加任何元素;这就是 HTML5 outline 算法如何解释页面上的内容是如何分组在一起的。
图 3-2。相邻标题元素的隐式部分创建。具有相同等级的相邻标题元素将隐式创建新节,而等级较低的标题元素将隐式创建嵌套节。
在早期,使用div
元素中的h1
到h6
元素来构建页面的各个部分是一种常见的做法。这样做的问题是div
元素不是元素的分段内容组的一部分,所以它不会根据 HTML5 outline 算法将页面分成几个部分。隐式创建节提供了与这种旧编码风格的向后兼容性,因为节可以基于在div
元素中使用的标题元素自动创建。然而,这并不是构建页面各部分的首选方式。相反,在查看源代码时,显式包含内容元素将使嵌套结构更加明显,这是我们接下来要做的。
使用切片内容创建轮廓
在四个分区内容元素中,section
元素 3 是最通用的,提供了(理想情况下)主题相关的内容分组。通过将section
元素添加到代码中,我们可以明确地显示这些部分将出现在哪里,这可能会更容易地看到页面是如何组织的(如果以下示例中的元素嵌套没有意义,请参考图 3-2 ):
`…
<body> <h1>Site Title</h1> <section><h2>Main Navigation</h2></section> <section><h2>Featured Content</h2> <section><h3>An Article</h3> <section><h4>Article Synopsis</h4></section> </section> </section>` ` <section><h2>Other News</h2> <section><h3>Another Article</h3></section> </section> </body> …`其他的是文章、旁白和导航。
注意第一个
h1
不需要节,因为body
元素就是服务于这个角色的。然而,您可能已经注意到了,body
元素不包含在 sectioning content 类别中。主体属于称为截面根的特殊元素类别,这些元素内部可能定义了轮廓,但是如果它们包含在另一个轮廓中,它将被隐藏。例如,blockquote
元素是 sectioning 根类别的成员,并且可以包含形成轮廓的 sectioning 元素,但是这在页面的轮廓中是不可见的,因为它被封装在 blockquote 中。这个类别的其他成员是details
、fieldset
、figure
和td
元素。
通过包含分节元素,实际上不再需要标题元素的不同排序。在这个问题上,规范陈述如下:
“…强烈建议作者要么只使用 h1 元素,要么对节的嵌套级别使用适当级别的元素。”
所以,将代码中的所有标题元素转换成h1
元素可能是明智的;否则,页面对结构变化的适应性会降低。例如,如果嵌套结构发生了变化,那么标题等级需要更新以反映嵌套深度的变化(不这样做不会破坏任何东西,但在语义上是不允许的)。
注意也就是说,目前搜索引擎优化(SEO)只关注标题的
h1
元素。例如,微软的必应搜索引擎发布了一个网站管理员常见问题([www.bing.com/toolbox/webmaster/](http://www.bing.com/toolbox/webmaster/)
),指出一个页面每页不应该有一个以上的h1
元素。这一指示预示着早期的网络,所以理想情况下,这最终会被删除,但与此同时,如果搜索引擎优化对你的网络项目至关重要,执行尽职调查,你的目标搜索引擎是否会有多个h1
元素的问题,或严格使用标题排名适合他们的嵌套层次。
请注意,将所有标题元素转换为h1
意味着页面上的标题在默认情况下可能具有相同的大小,而不管这些部分是如何嵌套的。例如,截至本文撰写之时,最新版本的 Google Chrome 和 Firefox 足够智能,可以减少嵌套在其他部分中的标题元素的大小,而 Safari 和 Opera 则不行。然而,后两个是否实现这个行为是一个争论点,因为页面的所有表现质量(包括标题大小)都应该使用 CSS 而不是 HTML 来处理。
改善大纲语义
区段是一个很好的通用分组元素,但是使用更具体的区段元素可以更好地定义此页面上的许多内容。让我们看看 sectioning 内容组中剩余的三个元素,看看哪一个比section
元素更合适。使用图 3-3 中的流程图,我们将逐步完成内容。
图 3-3。选择特定切片内容元素的流程图
首先,导航可以包装在nav
中,因为这个部分将定义网页的主要导航链接(在这个简化的例子中,链接被省略了,但通常不会被省略)。接下来是“特色内容”区域。嗯,这是一个有趣的问题,因为它包含的内容(一篇文章)如果从网站的其他部分分离出来,就可以自己理解。因此,它可以被包装在一个article
中。但是等一下;那真的是最合适的选择吗?为了使你的标记在语义上正确,你必须关注页面上一段内容的目的;内容的特定部分包含什么,以及它在将来会包含什么?例如,article
元素是为完全自包含的内容设计的,可以在 RSS 提要中整合。在这个例子中,特色内容区域用于为了在站点上突出显示而从其余内容中分离出来的内容。因此,可以说它不是自包含的,因为它包含的内容是在站点的一个更大的内容集中强调的。因此,如果没有周边内容,指定“特色内容”是没有意义的。此外,特色内容区域可能包含一篇以上的文章或其他内容,它们可能根本不相关。该区域用于特色内容,不一定是相关内容。如果这个领域有更多的内容,那么将所有的内容(相关的和不相关的)捆绑在一起并联合起来还有意义吗?我不这么认为。所以,这给我们留下了一组内容,这些内容在主题上是一致的(因为它们都是有特色的),但又不够独立,不能被认为是一篇文章。因此,该内容最好保留在section
中。
注意html 5 规范旨在尽可能清晰地表达元素的语义用法,但仍然有大量内容具有不同的含义和解释。虽然像图 3-3 这样的流程图有助于缩小一个元素比另一个元素更合适的范围,但是你很可能会遇到使用哪个元素的问题的答案变得模糊不清的情况。在这些情况下,最好是选择一个更通用的元素,这个元素肯定适合给定的内容,而不是应用一个更具体的元素,这个元素可能合适,也可能不合适。
接下来,“其他新闻”部分可以被视为“特色内容”它按主题将内容分组在一起(所有未被精选的内容),但它不够独立,不足以保证使用article
,所以它也有一个section
。所示的两篇文章是独立的,它们本身是有意义的,所以它们被包装在article
元素中。
接下来,“文章简介”本身没有意义,它与嵌套在里面的文章相关,所以最好放在aside
中。最后,尽管担心使用多个h1
标签,我们还是会将所有标题转换成h1
,这样你就可以看到是什么样子了。
最终结果如下所示:
`…
<body> <h1>Site Title</h1> <nav><h1>Main Navigation</h1></nav> <section><h1>Featured Content</h1> <article><h1>An Article</h1> <aside><h1>Article Synopsis</h1></aside> </article>` ` </section> <section><h1>Other News</h1> <article><h1>Another Article</h1></article> </section> </body> …`好多了!这种标记现在不那么通用了。导航很容易识别;我们看到页面上有一篇特色文章,边上有一个简短的提要,页面的另一部分有另一篇文章。目前,除了单独部分中的标题之外,没有其他内容(段落、图表等等),但是很容易就有了!这只是建立文件的大纲。
注意在这个例子中,
aside
已经嵌套在一个article
中,而后者又进一步嵌套在一个section
中。如果旁白直接出现在body
元素中,它应该与整个页面的内容相关,而不仅仅是一部分。对于一个内容很少的网页来说,这可能是可以的,但是对于一个内容丰富的网页来说,分组和/或嵌套aside
以及它们的相关内容很可能是必要的。
页眉和页脚
每个部分可以选择性地有一个header
和一个footer
元素。一个header
4 用于将一节的介绍性内容或导航帮助 5 组合在一起,因此它可能包含该节的标题元素以及其他介绍性内容(目录、相关徽标等)。一个footer
包含与部分或页面整体相关的内容,例如作者是谁、版权信息、联系地址和其他(一般)小片段信息。有时页面主导航菜单的相关链接或重复链接可能会出现在页脚,但一般来说,如果它们是无关的或多余的链接,并且对网站的主导航不重要,就不需要放入nav
元素中。
注意需要注意的是,整个页面可以有页眉和页脚,也可以有页面上的节,所以一个页面可以很容易地有多个页眉和页脚。
我们是否应该在现有代码中添加一个header
?当然,但是实际上现在没有必要。一个header
用于将介绍性内容分组在一起。目前,我们每个部分都有一个标题元素,所以没有什么可以组合在一起。还是有?主导航是一个导航辅助工具,所以它可以放在页面的标题下。另外,让我们在“站点标题”下添加一个副标题,名为“站点口号!”—现在我们有三个介绍性元素要分组!将这些包裹在header
中会给我们带来以下结果:
4 不要与“标题”混淆,后者指的是 h1 至 h6 元素。header 元素甚至不是标题内容类别的一部分!
例如,导航工具可以是主菜单或搜索栏,这在许多网站的右上角都可以找到。
`…
<body> <header> <h1>Site Title</h1> <p>Site Slogan!</p> <nav><h1>Main Navigation</h1></nav> </header> <section><h1>Featured Content</h1> …`注意“网站口号”在段落元素中。如果使用了标题元素,标题和口号就会被隐含地分成两个部分,这似乎是不对的。但是,有一段好像也不太对。标题用一个h1
和口号用一个h2
似乎更合适,因为这两者之间有明显的重要性等级,然而它们是同一个介绍性文本的一部分。这就是标题内容类别中最后一个元素派上用场的地方:输入hgroup
!
使用 hgroup
hgroup
元素允许将一组标题元素放在一起,并且只将其中排名最高的一个元素暴露给 HTML5 outline 算法,这对于您可能希望在一个部分中有两个标题的情况非常有用,例如一篇文章有一个标题和副标题。通过将标题包装在一个h1
中,将副标题包装在一个h2 a
中,然后用一个hgroup
将它们组合在一起,h2
不会创建新的部分,因为它不会被轮廓算法看到。这样做的另一个优点是标题元素出现的顺序可以改变,因为出于结构目的,较低等级的标题被有效地忽略了。对于我们一直在使用的示例代码,我们可以将标语移动到标题上方,而不会产生结构上的影响,从而得到如下代码外观:
`…
<body> <header> <hgroup> <h2>Site Slogan!</h2> <h1>Site Title</h1> </hgroup> <nav><h1>Main Navigation</h1></nav> </header> <section><h1>Featured Content</h1> …`用地址格式化页脚
在footer
中包含的一个常见元素是联系文章作者或网站所有者的方式。为此,在页脚中可以找到的一个典型元素是address
元素。此元素不用于邮政信箱或类似地址,因为这不是联系作者的最直接方式(尽管这些信息也可能出现在页脚)。而是为最近的文章或整个页面提供直接的联系信息(电子邮件地址、电话号码等等)。
注意除了
article
或文档正文之外,不适合在分节元素中使用address
元素。因此,section
元素可能有一个页脚,但它不会包含address
元素。如果在这种情况下包含了一个address
元素,那么该地址将在语义上应用于遇到的包含该部分的第一个冠词,或者应用于主体,无论哪个先遇到。
让我们在现有代码中添加一个页脚,为整个页面提供一个电子邮件地址联系人:
`…
<body> <header> <hgroup> <h2>Site Slogan!</h2> <h1>Site Title</h1> </hgroup> <nav><h1>Main Navigation</h1></nav> </header> <section><h1>Featured Content</h1> <article><h1>An Article</h1> <aside><h1>Article Synopsis</h1></aside> </article> </section> <section><h1>Other News</h1> <article><h1>Another Article</h1></article> </section> <footer> <address>Contact: <a href="mailto:wm@example.com">Webmaster</a></address> </footer> </body> …`确定页眉和页脚内容
参见图 3-4 的流程图,该流程图总结了确定页眉和页脚内容的过程。
图 3-4。确定页眉和页脚元素的流程图
查看 HTML5 轮廓
现在我们已经有了一个简单站点的框架,让我们来看看它的轮廓吧!Geoffrey Sneddon ( [
gsnedders.com](http://gsnedders.com)
)开发了一个很棒的工具来解析 HTML5 标记并返回一个大纲。前往[
gsnedders.html5.org/outliner/](http://gsnedders.html5.org/outliner/)
,在那里你可以上传 HTML,输入一个 URL 进行解析,或者将 HTML 直接输入到一个 web 表单中。打完“大纲这个!”按钮,您将看到您提供的 HTML 文档中各部分的概要。尝试输入我们一直在构建的代码。你应该会看到类似图 3-5 的轮廓。试用此工具是学习切片内容嵌套结构的好方法。
如果你喜欢在浏览器中内置一个工具,Chrome 网上商店有一个谷歌 Chrome 扩展。前往[
chrome.google.com/extensions/](https://chrome.google.com/extensions/)
并搜索 HTML5 大纲视图。扩展安装在地址栏中。当访问一个 HTML5 网页时,可以点击扩展来显示页面的轮廓(图 3-6 )。不幸的是,这不适用于您在本地查看的网页。
Opera 在[
addons.opera.com/addons/extensions/details/html5-outliner/](https://addons.opera.com/addons/extensions/details/html5-outliner/)
有一个等价的扩展。
此外,如果你在[
code.google.com/p/h5o/](http://code.google.com/p/h5o/)
访问 HTML5 Outliner 项目,你会发现一个 Firefox 的实验性插件和一个已经在 Firefox 和 Opera 中测试过的 bookmarklet(那里也有一个 Chrome 扩展的链接,因为这是开发该扩展的项目)。
图 3-5。Geoffrey Sneddon 的 HTML5 Outliner 输出示例
图 3-6。Chrome html 5 Outliner 扩展出现在地址栏右侧。该图像显示了[
w3.org](http://w3.org)
地点的部分轮廓。
div(和跨度)过时了吗?
所有这些关于在页面上划分内容的讨论几乎没有提到div
s!时代变了。在 HTML5 之前,div
遍布网页;它们是不可避免的脚手架,用于创建页眉、页脚、列和页面的所有其他部分。有了这些新的切片元素,div
会重蹈blink
的覆辙吗?不,div
s 仍然非常存在;在某些情况下,它们已经被更多语义定义的元素所扩充和取代,但是它们仍然是有效的元素,并且有它们的位置。因为div
元素本身没有任何语义,所以它们可以适当地用于分组相关内容、无关内容、整个页面或一个句子。 6 从语义的角度来看,这些都是相等的,div
并没有给这些分组增加更多的含义。它们的多功能性在于它们没有额外的语义包袱。因此,它们对于应用 CSS 规则或从 JavaScript 访问一段内容仍然很有用。当没有其他元素适合时,一个div
是一个伟大的包罗万象的元素!
同样,span
在 HTML5 中仍然存在。它与div
有相似之处;当div
用于流内容时,span
可以用于短语内容。
如果您有一段内容似乎不适合放在另一个元素下,下面是两个很好的经验法则来确定您是否应该使用div
(或者span
,如果您正在处理文本级的语法内容):
您是纯粹将标记作为挂钩来应用 CSS 样式还是从 JavaScript 访问内容?* Can only one
class
orid
attribute be used to describe the meaning of the content? Thediv
andspan
elements do not introduce any semantics of their own, but if there are no other suitable elements, you can add meanings through theclass
andid
attributes.
案例研究:城市出版社
既然您已经习惯了使用 HTML5 sectioning 元素来组织 web 页面的结构,我们将把注意力转向细节,页面的文本级语义。这些是包含语法内容的元素——段落中的文本等等。我们将以虚拟报纸城市出版社 ( 图 3-7 )的主页为例,探讨这些元素。出于演示目的,城市新闻页面使用了大量不同的元素进行编码。
6 但是,需要注意的是 div 不在分节内容类别中,用于代替节的 div 不会向文档大纲添加新的节。
图 3-7。虚构的报纸主页被用作使用语义丰富的 HTML 元素的案例研究
在我们深入研究文本级语义之前,让我们先来看看这个页面的轮廓,看看它是如何构成的(图 3-8 )。
图 3-8。城市新闻网页的轮廓
查看大纲,您可以看到文本“The City Press”是主体的标题,它包含两个嵌套的部分,“Main Menu”(nav
元素)和“Today:2011 年 10 月 10 日,星期一” ( section
元素)。在“Today”部分,有一个article
元素(“更多阳光预报……”)和两个aside
元素,一个是天气预报,一个是“城市之声”,其中包含文章故事的一段引文。
添加文本级语义
大量的元素可用于标记段落和其他内容组中的文本。这些元素用于定义哪些单词是超链接,哪些应该被强调,哪些应该被标记为重要,哪些应该与文本的其他部分区分开来,等等。我们将逐一探究这些元素。
超链接:a
链接最有可能出现在段落旁边,作为你学习的 HTML 的第一部分,但是我们仍然可以探测到模糊的深度,并发现一些潜在有用的行为。严格来说,a
元素不是链接;这是一个超文本锚,它可以链接到一个新文件,通过它的id
属性指向任何元素,或者,如果它缺少href
属性,就充当一个占位符,否则链接可能会被放置在那里。
与以前的 HTML 版本相比,a
元素中一个令人兴奋的新变化是,在链接中可以放置更多的灵活内容。任何流内容都可以嵌套在其中,只要它不是来自交互式内容类别(按钮、链接等),因此在过去,链接可能是这样的:
<a href="newpage.html">link text that appears on web page</a>
它现在看起来像这样:
<a href="newpage.html"> <section> <h1>Wow an entire section in a link!</h1> </section> </a>
作为快速复习,href
属性决定了链接的位置,而标签之间的文本是在网页中查看时变得可交互的内容。当文本被点击时,它将连接到在href
属性中指定的任何东西,通常是一个文件(最常见的是一个网页)。然而,它可能连接到所谓的片段标识符,这是链接文档中元素的id
属性中的值。如果href
属性包含一个片段标识符,用户不仅会被引导到链接的文档,还会被带到与片段标识符具有相同 ID 的任何元素。
为了链接到片段标识符,在 URL 的末尾添加一个散列符号和值,如下所示:
<a href="newpage.html#parttwo">link</a>
如果您想要链接到同一页面上的片段标识符元素,则不需要包括文档文件名:
<a href="#parttwo">link</a>
这将链接到带有“parttwo
”的id
的元素,可能如下所示:
<h3 id="parttwo">Part Two</h3>
链接到同一页面上的某个已标识元素可以有多种用途,例如用于一篇长文档的目录,有时还可以用作“返回页面顶部”链接:
<a href="#top">back to top</a><sup>7</sup>
注意另一个常见的用途是创建跳过链接,这些链接允许人们跳过很长的导航链接以获取内容。对于使用键盘、移动设备或屏幕阅读设备导航的用户来说,通常会包含跳转链接,但有时它们也会以可视方式呈现给那些放大图片但可能不喜欢滚动的用户。总结跳转链接的一篇有用的文章是 Jim Thatcher 的“跳转导航”(
[www.jimthatcher.com/skipnav.htm](http://www.jimthatcher.com/skipnav.htm)
)。
拥有一个不包含href
属性的锚元素是完全可以接受的。例如, City Press 网页上的菜单包括一个围绕“主页”的链接,该链接不链接到任何地方:
`
`让超链接链接到用户当前所在的页面是没有意义的,但同时,出于语义或样式的目的,从所讨论的菜单项中删除锚元素也是没有意义的。在城市新闻网页的情况下,href
属性被移除,而class="current"
被动态地添加到服务器端,这提供了一个 CSS“钩子”,可以很容易地将主页链接的样式与菜单的其他部分不同(图 3-9 )。
显式包含一个 ID 为“top”的锚或标识元素是一个好主意——一些浏览器会推断出这样的位置。
图 3-9。城市新闻菜单,残疾人主页链接显示通过一个类应用的单独样式
HTML5 在a
元素中增加了三个新的属性,media
、type
和download
,分别指定链接是否为在特定媒体/设备上显示而优化;链接资源的 MIME 类型(image/jpeg
、text/html
等等);以及最后,该链接是否指定了要下载的资源。如果资源是要下载的,那么download
属性中的值就是给下载文件的默认文件名。在撰写本文时,这个属性才刚刚出现在 Google Chrome 的最新版本中。
锚元素上剩下的属性是target
、hreflang,
和rel
。定义链接打开位置的target
属性将在第四章和第五章中进一步讨论,但它通常具有下列值之一:_blank
、_self
、_parent
或_top
,这些值决定链接是在新窗口、当前窗口、包含窗口(例如,如果使用了内嵌框架)还是最顶层的包含窗口(如果多个框架嵌套在一起)中打开。属性定义了链接资源的语言;然而,它纯粹是建议性的,实际上没有任何权力设置链接页面的语言(无论如何,它可能在其html
元素上设置了一个lang
属性)。最后,rel
属性定义了链接的类型。它有许多可能的值,如表 3-1 所示。
注意
area
元素(在第五章的中讨论)使用了与表 3-1 中所示相同的值作为其rel
属性。此外,这些值中的许多也在link
元素中使用(参见第二章中与表 2-5 的重叠部分)。
注意WHATWG HTML 规范为链接定义了一个
ping
属性(a
元素和area
元素——在第五章中有介绍),用于定义链接被访问时应该通知的 URL。然而,对它的包含存在争议(担心它可能被滥用),并且 HTML5 规范的 W3C 版本不包含该属性。在这个问题解决之前,你最好暂时不要去管这个属性。
重点和重要性:em 和 strong
虽然 web 浏览器通常会显示分别带有斜体和粗体文本的em
和strong
,但是这些元素不应该被视为粗体或斜体文本的手段,因为表示功能应该留给 CSS 样式表;更确切地说,em
元素应该用来表示的强调,而strong
元素应该用来表示重要性。嵌套这些元素中的任何一个都传达了对所包含内容的越来越多的强调和/或重要性,例如:
`CAUTION
WARNING
DANGER
I am worried!
I am very worried!
`你可能不希望你的重点以这种方式显示出来。请记住,您可以随时重新设计em
和strong
元素的样式,以您喜欢的方式显示,同时仍然保留它们的含义。例如,如果你的文档是日文表意文本,那么你就不太可能需要斜体来强调。改变背景颜色可能更合适。
注在 Molly E. Holzschlag 的文章“世界变小:全球网络的开放标准”(
[www.alistapart.com/articles/worldgrowssmall](http://www.alistapart.com/articles/worldgrowssmall)
)中更详细地讨论了国际化的上述问题。
对于城市新闻网页,strong
在天气预报中被用于给出预报值相对于周围标签的重要性(例如,“高”和“低”):
`
`从语义的角度来看,实际的天气内容是这一部分中最重要的,因此strong
元素被恰当地应用于天气值,而不是周围的标签。
注图 3-7 中城市压力截图显示的划掉的温度将在后面的“不准确文本”部分添加。
正文分开设置:I 和 b
i
和b
元素在历史上用于呈现粗体和斜体文本,在实践中,如果没有额外的样式,在 web 浏览器中看起来与em
和strong
元素在视觉上无法区分。那么,为什么在 HTML5 中它们没有被扔进过时的垃圾箱呢?每一个实际上都有细微的、语义(意思)上的不同,这些都被编入了 HTML5。虽然em
元素是为了强调,但是i
元素是为了将文本与其他文本进行偏移,以便在阅读时,它可以以不同的声音或语气出现。这是什么意思?好吧,就拿下面这段文字摘录来说:
西蒙傻笑道:“是的,我很乐意把垃圾拿出去。”“唉,我真的不想!”他一边拿起垃圾袋一边想。
第二段讲话是西蒙的内部对话,所以它会以不同的声音读出来,而不是实际所说的。该文本的 HTML 可能如下所示:
<p>Simon smirked, “Yes, I'm happy to take the garbage out.” “<i>Ugh, I <em>really</em> don't want to!</i>,” he thought as he picked up the garbage bag.</p>
注意这里已经抛出了一个em
,这样您就可以看到这两个元素在含义上的不同。
当内容是一个技术术语或分类名称或者是一个外语短语时,也可以使用i
元素。城市出版社的网页包含法语短语“Oh là là”,这是i
元素的一个很好的候选词!HTML 看起来像这样:
<i lang="fr">Oh là là!</i>
注意使用了lang
属性来指定这个文本是法语。使用lang
属性对浏览器如何处理文本有影响,因为不同的语言可以对引用的文本使用不同的字形。
b
元素用于分隔文本,而不传达任何额外的重要性(与strong
元素相反)或可选的声音或语气(与i
元素相反)。例如,可以使用b
将产品评论中的产品名称与附带的文本分开。或者它可以用于食谱中的配料:
<p>After bringing <b>water</b> to a boil, add <b>potatoes</b> and <b>carrots</b></p>
另一种用法是在文章或故事的开头。这是吸引读者的第一段。它并不比文本的其余部分更重要,但它应该突出,以便抓住注意力。城市新闻网页包括以下内容:
<p><b class="lede">Meteorologists predict more sunshine and scorching temperatures for the upcoming week, prompting area farmers to install irrigation systems.</b></p>
在这种情况下,b
元素的用途是使用一个class
属性专门定义的。
旁注:小
与i
和b
一样,small
传统上被用作一种表现元素。它让文本——你猜对了——变小了!这是一个更适合 CSS font-size
属性的任务。在 HTML5 中,small
获得了真正的语义。它现在意味着小字、小字、旁注、所有可能包含法律免责声明、许可协议、版权等的地方。它也可用于指示一件作品的属性,如文本或图像。城市新闻中的记者姓名放在小元素中。例如,在主照片标题的末尾,HTML 显示如下:
<small>Chris Elhorn | The City Press</small>
不准确的文本
另一个在 HTML5 中改变了含义的元素,s
元素在历史上意味着删除线文本,本质上是表示性的,被指定为过时的 bin。然而,在 HTML5 中,s
被重新定义为不再准确或相关的文本。例如,城市新闻网页在天气侧边栏显示温度。这些值最近已经被更新,所以旧值使用s
元素被标记为不准确,而新值使用strong
元素被赋予重要性(如前面的“强调和重要性”一节所示):
`
- High:
82°F96°F - Low:
70°F79°F
注意在某些情况下,可能有必要将文本标记为已删除,例如在维基(维基百科等用户编辑网站的更通用术语)中查看页面变化时。虽然对标记为 deleted 的文本使用
s
元素可能很诱人,但是有一个del
元素可以做到这一点。
突出显示文本:标记
mark
是一个新元素,可用于突出显示某些文本以供参考。你可以把它想象成一个荧光笔,用来标记书中的一些文字。它也可以用来强调原报价中没有强调的部分。在城市出版社网页中,右下角的blockquote
中使用了mark
元素来为开始的文本添加粗体(默认情况下,大多数浏览器会将mark
中的内容呈现为黄色背景,但是您可以使用 CSS 来改变这一点):
`
`
We're all hoping it rains soon, some farmers have installed
irrigation systems, at considerable expense
注意注意在
blockquote
的cite
属性中使用了一个片段标识符。这将它链接回页面上的原始源文章,其属性为id="article1"
。
内联报价:问
元素的作用是给文本块添加行内引号。它对于组织内容就像blockquote
对于流动内容一样。这个元素已经存在一段时间了,但是在过去遭受了不一致的浏览器支持,所以不推荐使用它。然而,情况已经有所好转,?? 元素在现代网络浏览器中获得了更广泛的支持。浏览器应该在引用的开头和结尾自动包含印刷正确的引号,这意味着你,作者,应该而不是包含它们。此外,通过明智地使用lang
属性,这些引号应该以适合于指定语言的样式显示(例如,一些欧洲语言将使用 v 形符号或 guillemets : and,而不是“and”)。如果由于浏览器支持不佳而无法实现这一点,可以使用 CSS 通过 ASCII 代码和quotes
属性添加适当的引号。例如,要将正确的引号添加到一个<q lang="fr">
元素中,请使用:
q:lang(fr){ quotes: "\AB" "\BB" "\2018" "\2019"; }
但是,Safari 不支持这种功能。
此外,浏览器应该显示嵌套引号的意识(在英语中,如果引号以双引号字符开始,那么引号中的引号应该使用单引号字符,反之亦然)。例如,下面这段 HTML:
<p><q>This is a quote that has a <q>nested quote</q> as part of it.</q></p>
应该显示如下:
This is a quote that has a 'nested quote' as part of it.
虽然这听起来非常令人兴奋,但现实却更加平淡无奇。例如,Safari 也不支持这一点,但大多数其他浏览器都支持。虽然支持变得更好,但您可能仍然会发现最好暂时不要使用q
,即使它确实提供了一点独特的语义。
处理条款:dfn 和 abbr
dfn
(definition)元素在标记将在整个文档中重复使用的术语的定义实例时非常有用。它通常与abbr
(缩写)元素结合使用,以定义第一次出现的缩写或首字母缩略词,供以后参考。在使用了dfn
元素之后,术语的定义需要出现在与dfn
元素相同的段落、描述列表(dl
元素)或部分中。通常,这相当于让结尾的dfn
标签后面的文本定义所讨论的术语。在 City Press 网页中,一个虚构的组织——国家农场协会(NFA)被包装在一个abbr
元素中,并且在文本中第一次出现一个dfn
元素:
<dfn id="nfa"><abbr title="National Farm Association">NFA</abbr></dfn> (National Farm Association)
强烈建议您将title
属性与abbr
元素一起使用,以便在悬停在元素上时提供缩写的全文扩展。此外,dfn
元素被赋予了一个id
属性,该属性可用于将未来出现的缩写链接回其定义实例:
<a href="#nfa"><abbr title="National Farm Association">NFA</abbr></a>
该链接使用片段标识符指向前面的dfn
元素,因此用户可以跳回到文本中定义“NFA”含义的地方(在dfn
后面的括号中)(图 3-10 )。
图 3-10。缩写可以链接回第一个使用dfn
元素定义它们的地方。
下标和上标:sub 和 sup
科学界有一个笑话,两个人在餐馆里,女服务员问他们想喝点什么。第一个人回答说“H 2 O。”女服务员看着第二个顾客,后者想了一会儿说,“我也要 H 2 O!”
过了一会儿,女服务员端着饮料回来了。第一个人喝了一大口。第二个人喝了一口,倒在地板上,口吐白沫。“白痴,”第一个喊道。
有趣的是,第二个人并没有像第一个人那样显得聪明,而是无意间点了“H 2 O 2 ”(过氧化氢)而不是 H 2 O(水)。
虽然sup
和sub
(上标和下标)本质上看起来是表象性的,但正如这个笑话所展示的,它们传达了它们周围文本的重要意义。
考虑以下两个等式:
- e = MC 2
虽然它们看起来很像,但前面的方程中只有一个是爱因斯坦的;拼写出来,前一个等式是“e 等于 m 乘以 c 的平方”,而后一个是“e 等于 m 乘以 c 乘以 2。”
注意对于比这更复杂的方程,你应该使用数学标记语言(MathML),它在一个独立于描述 HTML 的规范中定义,但可以嵌入在 HTML 文档中。
或者这个怎么样:
2
第一个是水的化学方程式——两个氢原子和一个氧原子——第二个只是字母 H 后跟数字 2,然后是字母 O ,没有意义。因此,2 的放置和样式很重要,如果你把它的样式和位置去掉,放在一个样式表中,一些浏览器可能会失去意义。
W3C 还指出,一些语言(除了英语)要求使用下标和上标文本。这里有一个法语例子:M lle Dupont。
在文体上,上标和下标也出现在英语中。您最有可能在日期中看到它们,或者指出脚注/尾注的存在:
九月十四日日日日* The report of the committee stated that the minister's actions were sincere.
在 City Press 的例子中,一个上标出现在页面顶部的日期中,在time
元素内,这将在下面讨论:
<time datetime="2011-10-10">Monday, October 10<sup>th</sup>, 2011</time>
具体日期:时间
元素是对 HTML 调色板的一个很好的补充,特别是对于联合内容。正如本章开头提到的,它定义了公历中的一个特定日期。它可能包括一个时间和一个时区偏移量,但它不需要这样做。最好使用datetime
属性精确地指定日期/时间,因为实际内容可能相当松散,例如:
<time datetime="2011-05-08">Mother's Day</time>
datetime
属性遵循格式 YYYY-MM-DDThh:mm:ssTZD (即 2011-10-23T10:23:44+05:00)。时间和时区部分是可选的。
唯一的另一个属性是布尔属性pubdate
,它指示日期/时间是最近的文章元素的发布日期,或者如果没有找到文章,则是整个文档的发布日期。城市出版社的网页在主要文章的页脚使用了这个(通常可以找到出版日期):
<small>Posted <time datetime="2011-10-10T12:16:20Z" pubdate>October 10, 2011</time></small>
注时区偏移量中的 Z 代表世界协调时(UTC),与+00:00 相同。
换行:br 和 wbr
换行符元素br
(呈现为<br>
或<br />
,因为它是自结束的)通常用在新行是内容格式的预期部分的内容中,比如邮政地址或诗歌。城市出版社在邮政地址的页脚部分使用这个:
`
The City Press
123 General Street
Springfield, OH 45501
另一个换行元素,wbr
(或<wbr />
),是 HTML5 中新增的。它定义了如果空间不允许整个单词,在单词的什么地方可以换行。在城市出版社的网页中,提到了一家名为irrigination direct的公司。这是wbr
元素的完美候选,因为如果需要的话,HTML 可以指示浏览器在中间断开名称,并且名称仍然有意义。代码如下:
<i>Irrigation<wbr />Direct</i>
其他文本级元素
这一部分涵盖了一些没有在城市出版社网页中使用的落伍者——无论如何,让我们简要地浏览一下他们!
作品名称:引用
cite
元素表示作品的标题,如书籍、歌曲、电影、电视节目、绘画等等。我们已经遇到了在blockquote
元素中使用的cite
属性,用于将来源归属于引用,但是还有一个cite
元素,用于包含与任何特定元素或其他材料的引用无关的独立引用。大多数用户代理会用斜体显示引文,这是印刷世界中常见的印刷惯例。
格式化计算机 I/O:代码、变量、样本、kbd
code
、var
、samp
和kbd
元素与之前的 HTML 规范没有任何变化。它们分别定义了计算机代码片段、变量、计算系统的样本输出和用户输入。既然这里没有什么新情况要报告,我们就继续吧。
标记文本编辑:ins 和 del
有时,能够指示文档中发生的编辑是有用的,例如,指示添加了什么内容以及删除了什么内容。例如,博客上的管理区将有一个创建和编辑帖子的区域。标记和查看博客帖子的修订版,指出与原始版本相比有什么变化,这可能很有用。HTML5 正好有这个用途的元素!元素用于标记一段文本的插入,而元素用于标记一段文本的删除。这里重要的一点是,它将内容标记为已编辑,但内容应该仍然存在(对于用del
标记为已删除的内容)。
JavaScript 大师 John Resig 有一个示例脚本,它使用ins
和del
来显示对一段编辑过的文本的更改。你可以在[
ejohn.org/projects/javascript-diff-algorithm/](http://ejohn.org/projects/javascript-diff-algorithm/)
查看例子。
注意在没有进一步样式化的情况下,
ins
文本的默认外观通常带有下划线,而del
则带有删除线。这意味着默认情况下del
看起来会像s
;然而,它们肯定是不可互换的。s
用于标记不准确的内容,而del
用于标记被删除的内容,不管它是否准确。
ins
和del
有两个属性,cite
和datetime
。cite
属性用于指定解释编辑的文档的网址,而datetime
属性用于指示编辑发生的日期和可选时间。有关该值的格式,请参考本章前面的 time 元素,但它看起来类似于:
<p> <ins datetime="2011-10-26T12:00Z" cite="edit10262011.html">new text</ins> is added, while
`old text is removed.
处理外国剧本
有几个元素用于显示来自世界各地的书写脚本的不同格式要求和语义。虽然拉丁字母(英语中使用的)已经在 Web 上占据主导地位很多年了,但是作为一种全球现象,Web 需要能够正确地处理不同的字母。下面的元素将帮助格式化 ruby 符号和双向文本。
Ruby 符号:ruby、rt 和 rp
拼音指的是与另一个文本片段有关联的一个文本片段,称为基础文本—它最常用于提供基础文本的简短注释或发音指南,并在东亚排版中使用。通常情况下,它会在基本文本上方显示红宝石文本,但浏览器支持会有所不同,如图 3-11 中的所示。
图 3-11。在(从左到右)、Safari、Chrome、Firefox 中呈现的红宝石文本
图 3-11 中的文本有以下 HTML 来源:
<ruby> This is ruby base <rp>(</rp><rt>This is ruby text</rt><rp>)</rp> </ruby>
如您所见,顶部文本是在rt
元素中指定的。元素用于为不支持 ruby 的浏览器定义备份内容。在这种情况下,它们用于设置在 Firefox 上显示的括号(图 3-11 )。
注关于 ruby 符号的更多信息,请参考位于
[www.w3.org/TR/ruby/](http://www.w3.org/TR/ruby/)
的 W3C Ruby 符号规范。
文本方向:bdo 和 bdi
如果您用外语标记文本,那么您可能会发现使用了bdo
元素(即“双向覆盖”)和dir
属性(这是该元素所必需的)。bdo
分别控制文本片段和文本块的方向,最适合用于标记从右向左而不是从左向右书写的阿拉伯语和其他语言。当单词或短语的方向性不同于包含元素中设置的方向性时,使用bdo
。默认情况下,包含元素的方向是从左到右。让我们来看一个使用英语的例子(尽管这是一种从左到右的语言,您将能够看到bdo
元素的效果):
<p>When rendered by a browser, <bdo dir="rtl">these words</bdo> will appear as 'sdrow eseht'</p>
该标记呈现如图图 3-12 所示。
图 3-12。单词“这些单词”已被bdo
元素反转。
有关如何创建使用从右向左脚本的页面的详细概述,请参考下面的 W3C 国际化教程:[www.w3.org/International/tutorials/bidi-xhtml/](http://www.w3.org/International/tutorials/bidi-xhtml/)
。
注意有一个叫做
bdi
的新元素,它是用来隔离一段文本的,这样它就可以被格式化为向左或向右流动,完全不受周围环境的方向性影响。这个元素还没有在任何主流浏览器中实现,所以您必须等待使用它,但是这个想法是,当一段特定文本的方向性未知时,可以使用它。例如,博客访问者留下的多语言评论可能包括用从左到右和从右到左语言写的文本,但你事先不知道文本的方向。通常,一个文本块将从其父元素继承从左到右或从右到左的方向,但出现在bdi
中的文本将忽略任何继承的方向设置,并将默认将其dir
属性设置为auto
,这将根据其字符集自动检测文本的方向。
总结
结构化标记的目标是尽可能恰当地使用可用元素。有时选择的元素会比其他时候更接近地描述它们的内容。有时,使用什么样的元素是不明确的——在这种情况下,需要仔细研究规范并做出谨慎的判断——但是 HTML5 提供了比该语言以前的版本更多的选择和更好的规则。关键是停止视觉上的思考。如果您正在使用 Adobe Photoshop 或 Fireworks 进行设计,那么在构建网站时,您可能会发现这有助于忘记样式,直接在文档中键入所有内容(或者占位符文本,如果您当时没有所有内容([
lipsum.com](http://lipsum.com)
是填充文本的好来源)。所谓“内容”,我指的是一切——不仅仅是正文,还包括任何导航、页眉、页脚等等。如果某个东西最后可能会以一个形象结束(比如一个企业 logo),那么在这个阶段就不要担心它。只是在它的位置包含一些文本;如果需要,您可以随时用内嵌图像替换它。
当你这样做的时候,想想你正在键入的内容,想想你将如何向其他人描述它,想想有哪些元素符合你的描述。忘记现在会是什么样子——不要认为h1
是“大、丑、粗”的,因为那只是默认情况下的样子。把它想象成网页内容轮廓中的标题——一个要点。就外观而言,一切都可以用 CSS 重新设计。
四、掌握表单
在第二章中,我们看了一下 web 表单的新特性。现在是时候更深入地关注它们了。好吧,说实话,如果在许多圈子里,甚至是与 web 相关的圈子里,人们兴奋地这么说,很可能会对 web 表单兴奋的荒谬性嗤之以鼻。在过去,表单一直被归入 web 开发中令人不快的角落,是那些对单选按钮组、文本输入宽度和充满选项的下拉菜单的挑剔感兴趣的人的领域。性感的设计和丰富的互动性哪里去了?但也许更重要的是,应该有的表单输入类型都在哪里,而不是需要用 JavaScript 拼凑起来?表单标记已经伴随我们很多年了,至少早在 HTML 2 规范中就出现了,从那以后没有任何显著的变化。如果知道 HTML5 照亮了它们,它们已经成长为一套有能力处理输入的 HTML,那将是一种解脱。添加了一组新的表单输入类型、表单元素和属性,以及与 JavaScript 和 CSS 接口的新方法。有了 HTML5,许多以前必须通过其他方式构建的功能现在都免费提供了。这太令人兴奋了!
本章开始时,我研究了可用的表单标记,分析了 HTML5 中添加到表单的新特性(有很多),如何最好地使用不同类型的表单控件,以及如何用简单的结构化标记增强可用性和可访问性。我们将剖析一个完整的表单,然后以关于 web 表单可用性和相关问题的一些大的想法结束。
表单标记刷新程序
本质上,表单非常简单——它们允许用户在一端输入一些数据,然后将数据发送到另一端,由表单处理程序处理。一个表单处理程序通常意味着一个处理输入表单数据的脚本,它是用服务器端编程语言或框架如 PHP、Python 或 ASP.NET 编写的。可以把表单想象成 HTML 中的数据中介,位于您和网页服务器之间。
表单由一个form
元素组成,该元素围绕任意数量的“表单控件”(用于收集输入的表单 HTML 元素)以及任何其他正文标记(如段落和其他流内容)建立一个容器(但是,不允许将一个表单嵌套在另一个表单中)。在所有输入表单控件的下面,都有一个按钮,用于提交表单,将输入的数据发送到服务器。表单可以使用两种不同的方法发送数据:GET 和 POST。这些方法之间最明显的区别是,当使用 GET 方法时,来自表单提交的数据出现在网站的地址 URL 中。例如,使用 GET 方法提交的表单的 URL 可能如下所示:
handle_form.php?name=Anselm+Bradford&age=31.
这可能是提交图 4-1 中的表格的结果。
图 4-1。收集用户输入的简单网络表单
对于此表单,HTML 可能如下所示:
`
`这个例子使用 PHP 脚本来处理表单,但是也可以使用很多其他的服务器端语言,比如 ASP.NET、Python 等等。对于 PHP,有三个“超全局”变量允许它访问表单中提交的数据。变量$_GET
和$_POST
分别访问通过 get 和 POST 方法提交的表单数据,而$_REQUEST
充当所有提交数据的容器,不管提交的方法是什么。例如,为了回显(在 web 浏览器中显示)在前面的表单中提交的值,可以使用下面的 PHP 代码:
`
"; echo "Age: " . $_REQUEST["age"]; } ?> ` 注这只是一个简单的例子,用来显示提交的内容;在生产环境中,需要对表单输入进行筛选,以确保没有恶意代码作为表单字段数据的一部分提交。例如,对于更新数据库的表单,SQL 命令可以输入到表单中,如果没有编写服务器端脚本来过滤掉这类命令,恶意用户可能会删除数据库中的数据,甚至更糟!
剖析表单元素
暂时忽略其他元素,form
元素非常简单,只作为一个具有少量属性的容器存在。除了前面显示的method
和action
属性之外,form
元素还有以下属性(以及第二章中列出的全局属性):accept-charset
、autocomplete
、enctype
、name
、novalidate
和target
。在 HTML5 中,一个属性accept
被扔进了废弃的垃圾箱,而两个属性autocomplete
和novalidate
是新的。下面是一个完整构造的form
元素的例子:
<form action="handle_form.php" method="post" target="_blank" accept-charset="UTF-8" enctype="multipart/form-data" autocomplete="off" novalidate>
虽然这会使用所有属性(不包括全局属性),但没有必要输入所有或任何属性,因为它们都是可选的。通常,至少会指定action
和method
(如前面的例子)属性。在下一节中,我们将讨论这些属性的作用,这样您就可以自己决定需要哪些属性。
表格元素属性
以下部分详细描述了每个表单属性。
动作属性
action
属性告诉用户代理(web 浏览器)在提交表单时应该对表单内容做什么(使用 submit 按钮,这是一个表单控件元素,我们将在后面讨论)。和前面的例子一样,可以指定一个服务器端脚本来处理提交的表单数据。如果省略,表单将重定向到提交表单的同一个 URL。
方法属性
method
属性是表单被设置为使用 GET(默认)或 POST 的地方。一般的经验法则是,如果表单提交正在主动修改数据(比如以某种方式更新服务器端数据库)或者包含敏感信息(比如密码),您的表单应该使用 POST 。另一方面,如果表单提交是被动的,比如搜索引擎的数据库查询,那么使用 GET 。为什么?让数据在页面的 URL 中可见(使用 GET)将允许页面被书签标记,这对于搜索查询很有用,而不在 URL 中(使用 POST)则为敏感数据提供了更好的安全性。此外,GET 更适合于少量数据,因为它有大小限制,因为 URL 只能有这么长(实际长度因 web 浏览器而异)。
接受字符集属性
accept-charset
属性允许您指定服务器可以在提交的表单数据中处理哪些字符编码。如果没有指定(通常的情况),则使用页面的字符集(应该在 head 部分使用meta
元素设置和/或在服务器的 HTTP 响应头中发送)。如果可以接受多个字符集,则该属性可以是以空格分隔的值列表;但是,为了避免字符显示不正确的问题,在处理编码时,请始终使用 UTF-8。UTF 8 支持世界上所有的主要语言。
enctype 属性
enctype
属性用于指定发送表单数据时如何编码。它有三个可能的值:application/x-www-form-urlencoded
、multipart/form-data
和text/plain
。在大多数情况下,可以省略它,因为它将默认为提到的第一个值,这将以 URL 安全的方式对数据进行编码。这通常是区分不明确的表单数据和其他信息所必需的。例如,如果某些表单数据中有一个正斜杠(实线),那么如果它以未编码状态放在网页的 URL 中,就会出现问题,因为正斜杠意味着移动到 web 服务器上的新目录。相反,正斜杠将被作为%2F
发送,这是在 UTF-8 字符集中分配给正斜杠的十六进制值。即使使用 POST 方法发送数据,这种格式也有助于从发送到 web 服务器的其他信息中挑选出表单数据。
更改该属性的时机是在使用文件输入元素(用于上传文件,这将在后面描述)时,在这种情况下,该属性应该包含一个值multipart/form-data
,它允许表单处理二进制数据。最后一个值text/plain
,发送未编码的表单数据。通常不应该使用这个值,因为它很难挑选出表单数据。它主要是由于历史原因而可用的。
目标属性
target
属性的工作方式类似于链接上的target
属性(a
元素),它告诉浏览器在哪里打开在action
属性中指定的 URL。值_blank
将在新窗口中打开表单提交结果,_self
将在同一窗口中打开表单提交结果,_parent
将在父浏览上下文中打开表单提交结果。例如,iframe
元素可以用来将一个带有表单的 HTML 页面嵌入到另一个 HTML 页面中,该页面被认为是嵌套的 HTML 页面的父浏览上下文。最后,如果表单嵌套了几页深(例如,使用多个iframe
),那么_top
的值将加载最顶层的页面。
姓名属性
下一个属性name
,用于向脚本标识表单。它本质上是赋予表单的唯一 ID,用于在页面上使用的所有表单中识别它。在 JavaScript 中,可以通过document.forms
属性访问表单,该属性包含对页面上所有表单的引用。例如,对于具有属性name="contactform"
的表单,可以使用 JavaScript document.forms.contactform
来访问它。如果您不太熟悉 JavaScript,只需知道您也可以使用id
(甚至class
)属性来访问表单,这可能是从 JavaScript 与表单交互的更好途径。
自动完成和更新属性
最后,最后两个属性autocomplete
和novalidate
将在后面详细讨论,但简单地说,它们分别告诉浏览器是否自动填充表单中记住的值以及是否验证表单的输入。
收集输入
form
元素只是数据收集元素的容器,称为表单控件,它们是input
、select
和textarea
元素。这些工作的细节将在适当的时候进行说明,但是首先要注意它们都使用的一个属性:?? 属性。与出现在form
元素上的name
属性不同,这个属性在这个上下文中具有更重要的作用。所有向表单处理程序传递数据的表单控件都必须有一个name
属性;否则,当提交表单时,它们不会传递它们的值。除了表单控件被组合在一起的情况,例如单选按钮的情况,name
属性值应该是唯一的,这样就可以从其余的表单输入值中挑选出一个特定的值。例如,下面的表单片段显示了两个输入字段:
… <input name="firstname" type="text" value="Anselm" /> <input name="lastname" type="text" value="Bradford" /> …
这里的名称/值对是名字/安瑟伦和姓氏/布拉德福德(这些值是使用value
属性的默认设置,但是可以通过用户输入的数据进行更改)。如前所述,如果表单是使用 GET 方法提交的(并且 web 页面文件名是handle_form.php
),那么页面的 URL 应该是这样的:
handle_form.php?firstname=Anselm&lastname=Bradford
这显示了 URL 中的name
属性及其相关值。
一般来说,一个表单的真正核心是变形元素input
。我称之为变形,因为input
元素在 HTML 元素中相当独特,它可以呈现许多不同的外观和行为,这取决于它的type
属性中的值。type
属性将一组关键字中的一个作为它的值。例如,前面的示例将带有type="text"
的两个表单控件的输入类型都设置为文本输入。HTML5 中的可用类型列表大幅增加,如表 4-1 所示。每种输入类型的外观将由 web 浏览器决定(可能会因浏览器而异),但可以用 CSS 进行样式化。
注意如果在
input
元素上没有指定type
属性,或者如果使用了您的首选 web 浏览器尚不支持的类型,输入类型将变成单行文本输入字段。
原始输入类型
传统上,input
有一小组可能的类型:text
、password
、file
、checkbox
、radio
、hidden
、button
、reset
、submit
和image
。为了让您熟悉不同的类型,我们将简要地浏览一下最初的类型,然后更深入地介绍新的类型。
注意当我在接下来的章节中介绍输入类型时,我提供了许多表单输入字段的截屏。如果您在这里看到的与您自己首选的 web 浏览器中显示的不完全匹配,请不要感到惊讶。有些字段可能看起来不同,有些功能可能略有不同,或者有些可能根本不支持。浏览器对 HTML5 规范中新的表单元素和其他特性的支持正在迅速改善。然而,这里的目标并不是向您展示使用哪种浏览器来查找特定的表单输入字段;更确切地说,它是向你展示最新的网络浏览器有什么可能,以及我们希望在仍在追赶的浏览器的未来迭代中有什么可期待的。这是未来的路,无论是通过 Safari、Chrome、Opera、Firefox 还是 Internet Explorer 来展示。
文本输入
不出所料,使用了一个text
输入来收集输入的文本。这是input
使用的默认类型,也可能是您遇到的最常见的输入。这是一个单行控件,通常以带有内嵌边框的矩形框的形式出现,如图图 4-2 所示。
图 4-2。显示输入到输入中的一些内容的文本输入
输入字符串的允许长度可以通过添加一个maxlength
属性来指定,该属性采用一个等于允许的最大字符数的数值。当用户试图插入超过允许数量的字符时,没有提供直接的反馈机制——表单控件只是停止接受额外的字符,并且如果这样的字符串被粘贴到控件中,将截断过长的字符串。如果您想提醒用户空间已经用完,您需要使用 JavaScript。
注意
maxlength
属性不是万无一失的;例如,可以通过 JavaScript 将更长的文本添加到文本输入中,这些文本将被提交给服务器而不会被截断。为了限制完全确定提交的文本长度,也需要在服务器上检查长度。
您还可以包含一个value
属性来预设文本控件的内容:
<input type="text" name="data" value="Default text" />
页面加载时,value
属性中输入的文本会出现在文本字段中,如图 4-2 中的所示。
注意使用
value
属性是一种包含默认数据的方法,如果用户在提交表单之前没有更新输入的值,就会提交默认数据。value
属性传统上也是提示用户在文本字段中输入内容的方式。这是一个非常普遍的需求,事实上 HTML5 已经为这种“占位符”文本添加了一个属性。但是现在不要担心这个特性,因为它将在本章的后面介绍!
密码输入
一个password
输入几乎等同于一个文本输入;功能上唯一的区别是字符输入在输入时被屏蔽,通常是被一系列的点屏蔽,如图图 4-3 所示。它与文本输入共享相同的可能属性;唯一的区别是type
属性被设置为type="password"
。
图 4-3。显示正在输入文本的密码输入
这种类型的输入不是很安全——如果您的表单使用了GET
方法,表单数据仍然会以纯文本的形式传输,并且会在 URL 中可见。这种视觉屏蔽实际上只是为了防止任何人越过你的肩膀看到输入内容(例如,当你在公共场所登录银行网站时)。
文件输入
一个file
输入通常采用某种浏览按钮的形式,以及一个显示已选择文件信息的区域。浏览器之间的外观差别很大,比其他常见的输入类型更大,如图 4-4 所示。
图 4-4。两种不同网络浏览器中文件输入控件的外观,谷歌 Chrome(上)和 Opera(下)
文件输入控件允许您在本地网络上浏览文件,以便将其上传到网站。一旦你选择了文件,文件就会以某种方式显示,这取决于网络浏览器,如图图 4-5 所示。
图 4-5。文件输入控件在两种不同网络浏览器中的外观:Google Chrome(上)显示文件名,Opera(下)显示文件路径。
如前所述,为了让表单成功发送二进制数据,比如上传文件时,将相关表单上的enctype
属性设置为enctype="multipart/form-data"
。这将正确格式化表单数据,以便服务器端脚本可以处理该文件。
注意在 PHP 中,有一个名为
$_FILES
的全局变量,可以用来访问上传的文件。你可以在[
php.net/manual/en/features.file-upload.php](http://php.net/manual/en/features.file-upload.php)
找到用 PHP 处理上传文件的信息。
默认情况下,一次只能上传一个文件;然而,通过向文件输入控件添加布尔multiple
属性,可以选择多个文件进行上传:
<input type="file" name="filedata" multiple />
如果没有该属性,在操作系统的文件浏览器中一次只能选择一个文件;但是,包含此属性后,可以按住 Command 键(Mac)或 Control 键(Windows)来选择多个文件。同样,文件输入控件的外观会因浏览器而异,但它可能看起来像图 4-6 。
图 4-6。选择多个文件后 Safari 中文件输入控件的外观
注意不幸的是,当代的 HTML 特性通常都是这种情况,在可预见的将来,不要指望 Internet Explorer 会支持
multiple
属性。但是,如果使用另一个主要的 web 浏览器,它在 Windows 上也可以工作。
file
输入包含一个accept
属性,理论上可以用来限制上传文件的类型;然而,在实践中,它纯粹是咨询性的,可能会被忽略。它接受一个逗号分隔的 MIME 类型列表,该列表与可接受的文件类型有关,例如看起来像accept="image/gif,image/jpeg,image/jpg"
。这将通知浏览器服务器只接受 GIF 和 JPEG 图像。即使浏览器注意到了这个属性,在生产环境中,您也需要使用服务器端过滤器来检查提交的文件实际上是正确的类型,因为这种客户端检查很容易被绕过。
检查框
这是一个我们熟悉的老复选框,我们喜欢用来收集或真或假的输入。除了文本输入,这是您可能遇到的最常见的输入类型之一。一个checkbox
输入通常采用一个方形框的形式,可以被选中或取消选中,但是使用 JavaScript,可以设置第三种“不确定”状态,看起来既不在真也不在假的条件下,如图 4-7 所示。
图 4-7。三个复选框:一个未选中,一个未选中,一个处于不确定状态
不确定状态是通过checkbox
元素上的布尔indeterminate
属性设置的,如下所示:
function init() { document.getElementById("option3").indeterminate = true; } window.onload = init;
这段代码将被放在一个脚本中(或者放在页面上的一个script
元素中,或者最好放在一个外部文件中),并将影响下面的 HTML 片段:
<input type="checkbox" name="option3" id="option3" />
要将复选框设置为选中状态,需要添加一个布尔属性checked
。只有被选中的复选框的数据才会与表单一起提交。value
属性可以用来设置提交表单时发送的值,但也可以省略,在这种情况下,默认值将是文本“on”图 4-7 中的复选框可能如下所示:
<input type="checkbox" name="option1" id="option1" /> <input type="checkbox" name="option2" id="option2" checked /> <input type="checkbox" name="option3" id="option3" />
当这些作为表单的一部分提交时,web 页面的 URL 将被附加上 querystring ?option2=on
(如果使用 GET 方法)。
单选按钮
像复选框一样,单选按钮可能你以前也遇到过。它们可能不太常见,但仍然在网络上广泛流行。一个radio
输入有两种状态,选择或不选择,如图图 4-8 所示。
图 4-8。两个单选按钮,一个选中,一个未选中
您使用单选按钮来指示只能从几个选项中选择一个选项,即一个单选按钮组。
为了向用户代理表明一个单选按钮是组的一部分,您为每个单选输入元素的name
属性赋予一个相同的值。当呈现表单时,如果输入与另一个输入共享一个名称值,用户代理将不允许选择多个单选输入。以下是前面示例的标记:
<input type="radio" name="example" value="first" /> <input type="radio" name="example" value="second" checked />
正如您在前面的代码中看到的,单选按钮的状态可以像复选框一样,用checked
属性进行预设。
注意现实世界中的表单也会包含表单标签,但是我们将在“用字段集和标签添加结构”一节中讨论这些
添加隐藏输入
hidden
输入类型用于将用户看不到的额外数据包含在表单中,但与表单数据的其余部分一起提交。这里有一个例子:
<input type="hidden" name="hiddenValue" value="42" />
当包含该输入的表单被提交时,值hiddenValue=42
将出现在 URL 中(如果使用GET
方法),并将与表单中的其他数据一起被传递到服务器端。
注意
hidden
输入有时与file
输入类型一起使用,告诉服务器可接受上传的最大文件大小。例如,一个表单可能包含以下输入,告诉服务器脚本文件上传应该限制在最大 20 千字节(大约)的文件大小:
<input type="hidden" name="MAX_FILE_SIZE" value="20000" />
.
按钮、提交、重置和图像输入
button
输入类型与button
元素有很多重叠(将在本章后面讨论),在这两者之间,button
元素可能是更好的选择。两者都创建了一个带有文本标签的可点击按钮,但是本质的区别在于button
元素可以在它的标签中使用 HTML 元素,而button
输入类型只能使用纯文本(参见本章后面的“使用按钮和图像提交表单”一节中的例子),这使得它不够灵活。submit
和reset
类型也创建按钮,具有提交表单(将数据发送到服务器)的额外能力,并在单击时将表单值重置为默认值。与button
输入一样,button
也可用于这些任务。简而言之,如果它看起来像一个按钮,请使用button
元素!
最后一种类型image
,用于使用图像代替提交按钮;这将在后面的“使用按钮和图像提交表单”一节中讨论。
新的输入类型
正如你在表 4-1 中看到的,input
元素有很多种新类型。有些是比较通用的,比如数字的输入,而有些是非常具体的,比如创建一个颜色选择器。
注意许多表单元素还没有在现代 web 浏览器中实现。Opera 和 Google Chrome 似乎领先一步,但对于那些不支持新类型的应用,输入字段将会退回到默认状态——基本的文本输入。与其他 HTML5 特性一样,您可以使用 Modernizr (
[
www.modernizr.com](http://www.modernizr.com)
)来检测对新输入类型的支持。要添加对旧 web 浏览器中缺少的功能的支持,可以使用 html5Widgets 之类的脚本,这是一个 JavaScript poly fill——这意味着它在不支持表单功能的浏览器中使用 JavaScript 来填充缺少的表单功能。下载 html5Widgets 脚本并在[
github.com/zoltan-dulac/html5Widgets](https://github.com/zoltan-dulac/html5Widgets)
找到实现细节。
颜色选择器
color
输入类型创建了一个颜色选择器(也称为颜色井),可用于选择颜色,如图图 4-9 所示。提交表单时,颜色值作为 URL 编码的十六进制值 1 发送。例如,黑色(默认)将作为%23000000
发送,其中“%23
是散列符号(#)的 URL 编码,这意味着%23000000
是颜色值#000000
(这是在例如 CSS 代码中遇到颜色的更常见方式)。
图 4-9。用颜色输入类型创建的弹出颜色选择器,如 Opera web 浏览器所示
这个特定的输入可能看起来相当深奥,您甚至可能会问自己什么时候真正需要它。这里有一个例子可以让你了解如何使用它:一个包含“按颜色搜索”功能的搜索引擎,可能是汽车经销商搜索表单的一部分,允许用户在搜索新车时包含首选颜色:
<p><label>Preferred color: <input type="color" name="carcolor" /></label></p><sup>2</sup>
虽然您可能不会经常使用这种输入,但是当您需要它时,拥有它无疑是非常宝贵的。在一天结束的时候,能够给一个表单添加一些颜色是多么酷啊,实际上,表单经常是一个平淡无奇的文本输入域的海洋。
日期和时间输入
日期/时间控件是需要收集特定日期和时间的输入的网站的常见要求,例如机票预订网站或酒店住宿预订系统。为了满足这一需求,HTML5 增加了相当多的与选择特定日期和时间值及范围相关的输入。date
、time
、datetime
、datetime-local
、month
和week
输入类型创建了更细粒度或更粗粒度的收集时间和日期信息的方法。这些表单控件通常具有一个数字步进器和/或看起来像标准下拉列表的东西(图 4-10 )。目前最复杂的实现是在 Opera 中,当点击选择想要的日期时会产生一个弹出日历控件(图 4-11 )。
1 URL 编码是指将特殊字符转换成代码,通过网站 URL 地址安全传输。
正如在前面的例子中提到的,我们将在后面讨论标签元素。
图 4-10。Opera 网络浏览器中显示的所有不同的日期和时间相关输入。从上到下:date
、time
、datetime
、datetime-local
、month
、week
。
图 4-11。弹出一个日期/时间控件,显示 Opera 中自带的日历控件
这些输入类型可能比任何其他控件都更能显示 web 表单的新增功能有多少可能是免费的。图 4-11 中的日历控件由 web 浏览器提供,只有一行 HTML,这通常需要大量的 JavaScript、CSS 和 HTML 来创建。在撰写本文时,Opera 是唯一一个本地提供日历控件的浏览器,所以 JavaScript/CSS/HTML 解决方案仍然是大多数当代 web 浏览器需要的途径。不幸的是,如果可能的话,利用浏览器本身提供的功能,一旦这些输入类型获得了更广泛的支持,您的页面就可以过渡到本身提供的界面。我们只能希望其他 web 浏览器能很快跟上,但与此同时,jQueryUI ( [
jqueryui.com](http://jqueryui.com)
)这样的项目可以与 Modernizr ( [
modernizr.com](http://modernizr.com)
)结合使用,以便在不支持日期/时间输入时提供后备。jQueryUI 包含了一个名为 datepicker ( [
jqueryui.com/demos/datepicker/](http://jqueryui.com/demos/datepicker/)
)的日历控件,它产生的日历控件与图 4-11 中的非常相似,但是由于它不是一个本地控件,如果控件是表单输入的一个关键部分,那么需要处理 JavaScript 被禁用的情况。
为了用日期/时间预设输入,可以为value
属性提供一个设置初始值的文本字符串。表 4-2 显示了每个日期/时间输入类型的文本字符串的格式。
注意表 4-2 中datetime
和datetime-local
的区别。datetime
中的 Z 表示输入的日期和时间被发送到服务器,并期望它们处于 UTC 时区(例如,在英国)。这为时间提供了一个公共时区,这可能更容易使用,但这意味着服务器、客户端或用户需要偏移这个时区才能到达他们自己的时区。另一种类型datetime-local
不包含时区值,所以数据应该是访问者提交表单时所在时区的数据。
表 4-2 中的相同文本字符串格式也可用于min
和max
属性,这些属性可用于创建具有最小和/或最大允许日期/时间值的日期范围。此外,可以添加属性step
来设置日期/时间字段可以移动的量。例如,对于涉及时间的输入类型(time
、datetime
和datetime-local
),有一个 60 秒的默认步长,这意味着每单击一次控件以增加值,将向前(或向后)移动 1 分钟。对于包含时间的输入类型,step
属性以秒表示,因此通过添加step="120"
,,默认值将变为 2 分钟(60 秒乘以 2)而不是 1 分钟。date
、week
和month
输入类型具有更大的测量单位,默认步长值分别为 1 天、1 周和 1 个月。例如,下面的代码片段将提供一个控件,该控件允许选择每隔一周(通过将步长设置为 2 周),并且将限于(北半球)夏季的日期:
<input type="week" name="event" step="2" min="2011-W25" max="2011-W38" />
数字输入:数字和范围
number
输入类型做了您所期望的事情——它处理数字输入。在支持的浏览器中,外观是类似于time
输入类型的步进控件(图 4-12 )。与日期/时间控件一样,min
和max
属性可用于将可能的数字范围限制在一个范围内。此外,step
属性可以用来增加或减少一定数量的值(默认为 1)。如果min
和/或step
属性设置为十进制值,则可以使用小数值。
图 4-12。正在输入数值的数字输入类型
在显示数字的实际值不是最重要的情况下,有range
输入类型(图 4-13 ),它显示一个在最小值和最大值之间的可拖动滑块(默认为 0 和 100)。也可以在该类型上设置min
、max
和step
属性。
图 4-13。默认情况下,范围不显示当前值。
搜索输入
search
输入类型是 HTML5 中新增的最简单的类型。根据浏览器的不同,它可能与常规的文本输入类型控件没有区别。那么,它为什么会在那里?这是主要出于语义原因而添加的。将表单输入类型设置为search
会将该字段与页面上的其他文本字段分开。在将来,这可以用于应用搜索字段所期望的功能或行为。例如,Safari 目前在搜索栏的右侧添加了一个 x 按钮,可以清除在栏中键入的任何文本(图 4-14 )。这与工具栏中内置的 web 搜索栏的行为是一致的。
图 4-14。 Safari 在搜索输入类型上默认提供了一个清除搜索的按钮。
个人详细信息:电子邮件、电话和网站 URL 输入
像search
输入类型一样,最后三种输入类型——email
、tel
和url
——只是附加了额外语义的文本字段。它们甚至看起来像普通的文本输入框(图 4-2 ),但是在表面之下,它们有一种特殊的品质,可以节省你在页面上摆弄脚本的大量时间。它们是展示 HTML5 中一个新特性——内置表单验证——的很好的例子!让我们来看下一个。
验证和提交表单
我在讨论form
元素时顺便提到,有一个名为novalidate
的属性可以添加到form
中,以绕过许多验证规则,否则这些规则将在输入中被检查。这就像把表格拨回到过去一样。如果你想这么做的话,我只是提到它的存在,但是让我们看看这些验证规则是关于什么的。
取email
输入类型;它提供了语义上的含义,即无论输入什么文本,都将采用有效的电子邮件地址的形式。这是提交表单时 web 浏览器可以检查的内容。如果输入的值不是一个格式正确的电子邮件地址,浏览器会抛出一个错误信息,告诉用户输入一个正确的值(图 4-15 )。
图 4-15。提交表单时,在email
输入类型字段中输入的无效文本会产生验证错误。
对于不支持新的 HTML5 客户端验证的浏览器,可以包含一个由 Weston Ruter 编写的名为webforms2.js
的 JavaScript 来提供支持。这个脚本可以在[
github.com/westonruter/webforms2](https://github.com/westonruter/webforms2)
下载。
注意在服务器端仔细检查从表单接收的输入也是一个很好的经验法则。这里显示的客户端验证行为主要是为了提高表单的可用性。它们为用户提供了一些即时反馈,告诉他们输入了错误的信息类型,而不需要您使用 JavaScript 或其他方式自己创建该功能。当在服务器端处理数据时,您会希望再次检查以确保提交了正确类型的信息。谁知道呢,也许用户从某个不支持客户端验证的不知名的 web 浏览器访问了您的表单,躲过了所有的浏览器嗅探检查,并在将电子邮件地址输入表单时设法破坏了它。这些事情会发生,所以在服务器上仔细检查提交的数据。
默认情况下,email
输入类型只支持输入一个电子邮件地址,如果在该字段中添加了多个电子邮件地址,验证将会失败。然而,如果布尔属性multiple
被添加到输入字段,这是可以改变的。然后,它将允许在单个字段中输入逗号分隔的电子邮件地址列表。
如果在字段中输入了错误的信息,输入类型url
也会抱怨。输入的任何格式错误的 URL 地址都会引发类似于图 4-15 的验证错误。此外,它会在需要时添加http://
来完成一个完整的 URL 地址。
tel
输入类型稍微宽松一些,因为它不寻找特定的格式,但是如果输入了非数字字符,它会报错。由于电话的号码格式在世界各地都不相同,所以对号码的格式不可能有严格的要求。为了细化这种输入类型,使能够验证特定的电话号码格式,有一个属性就是为了这个目的。这个属性称为pattern
属性,它将一个正则表达式(regex) 3 作为值,并根据它是否匹配 regex 来验证字段中的输入。正则表达式这一主题超出了本书的范围,但作为一个简单的例子,下面的代码片段将电话字段的输入限制为 NNN-NNN-NNNN 格式,其中第一个数字是 2 到 9 之间的数字,其余数字是 0 到 9 之间的数字 4 :
<input type="tel" name="usphone" pattern="^[2-9]\d{2}-\d{3}-\d{4}$" />
肯定是神秘的寻找!如果你想知道正则表达式是如何工作的,在网上快速搜索一下正则表达式,你会找到大量的资源。对于一些方便的、预构建的正则表达式模式来匹配从信用卡号到纬度和经度的一切,请查看[
html5pattern.com](http://html5pattern.com)
,它还包括一个测试平台,您可以在其中开发和测试自己的模式。
注意
pattern
属性不只是在电话输入类型上找到的;它存在于所有基于文本的输入类型中。那些是text
、search
、url
、tel
、email
和password
。
制作所需输入
一种更简单的验证形式(双关语)是要求在提交表单之前,至少给某些字段和表单控件赋予某种值。这就是布尔属性required
可以被利用的地方。通过将此属性添加到表单控件:
<p><input type="text" name="example" required /></p>
如果在该字段中没有输入任何内容,将出现一个错误弹出窗口(图 4-16 )。
图 4-16。显示必填字段未填写的验证错误
使用按钮和图像提交表单
提交按钮用于将所有的表单数据提交到表单的action
属性中指定的文件(如果没有指定action
属性,则提交到表单本身)。正如本章前面提到的,button
元素是一个比input
元素更灵活的创建按钮的选项。例如,比较下面的 HTML 代码片段中的两行代码,它产生了图 4-17 中的按钮:
正则表达式是一种非常简洁的语法,用于匹配符合特定模式的文本。
理想情况下,正则表达式在构建时应该考虑到某些方面的灵活性,但在需要的地方要严格。这个简单的例子并不完全理想,因为它需要数字之间的破折号。最好是一个正则表达式,只要有适当的数字序列,它就可以处理任何破折号或没有破折号的组合。
`
` 图 4-17。两个提交按钮,一个用input
元素创建(顶部),另一个用button
元素创建(底部)
由于button
元素不是一个自结束元素,它允许 HTML 被包含在创建它的标签的文本中,这提供了更好的标签样式的可能性。
如果type
属性被设置为type="reset"
,则创建一个重置按钮。重置按钮输入将同一表单中的所有表单控件重置为其初始值。包含重置按钮曾经是一种常见的做法,但后来变得不流行了,因为用户意外重置表单而不是提交表单的风险很高。如果没有任何撤消功能,重置按钮几乎没有什么用处,即使有也应该谨慎使用。
注意拜托,我们都做过这样的事情:到达一个表单的末尾,切换到我们认为是提交的按钮,按下回车键,绝望地看着所有的表单数据迅速消失。如果你曾经想过在一个表单上包含一个重置按钮,试着回忆一下过去你有多少次填写了一个表单,到达了末尾,然后想,“实际上,我想我会把它们都删除掉,不用麻烦了。”没错。
button
元素的提交类型不与input
元素重叠的一个用例是,如果您想使用一个图像作为提交按钮,在这种情况下,需要使用一个具有image
的type
属性和一个附加的src
属性的input
。虽然可以在按钮内部使用图像(如图 4-17 中的,但是使用图像输入控件会使整个图像成为按钮,而不需要浏览器添加任何额外的 UI 元素。src
属性指向镜像文件在服务器上的位置,就像img
一样。同样像img
一样,出于可访问性的原因,记得包含一个描述图像用途的alt
属性。
使用图像作为提交按钮也会将 x 和 y 坐标作为值发送(图像中您所单击位置的 x 和 y 坐标);这适用于图像提交与服务器端图像映射结合使用的情况。如果你的图像有一个name
属性,那么它也会和坐标一起被发送。例如,如果您的表单使用了GET
方法,图像提交按钮如下所示:
<input type="image" name="imagesubmit"/>
将像这样传递值:
handle_form.php?imagesubmit.x=10&imagesubmit.y=20
如果你没有用这些值做任何事情,不要担心它们。当它们与表单数据的其余部分一起提交时,它们不会造成任何损害。如果您真的想阻止它们在 URL 中出现,那么您可以将表单的属性从GET
更改为POST
,这将对用户隐藏所有的值。
其他常用输入元素属性
虽然不是表单控件功能的核心,但有几个属性值得注意。首先,有许多属性可以用来覆盖特定元素上的表单行为。表 4-3 列出了这些属性,从它们的名称可以看出,它们将覆盖父表单元素的属性中设置的特定值(如有必要,请参考表单元素属性的讨论)。
每个表单控件元素(button
、input
等等)还有一个名为form
的属性,它可以用来将一个表单控件元素与一个不同于它在 HTML 结构中所包含的表单关联起来。如果通过一些示例代码来查看,这一点可能会更清楚:
`
`在这种情况下,名为example
的input
元素的form
属性被设置为值form1 form2
,这意味着它被视为包含 form1 和 form2。当提交任一表单时,example
输入控件也将被提交。这样,form
属性可以用来模拟一个嵌套的表单,即使 HTML 中不允许这种结构。还可以在提交按钮上设置form
属性,这样一个提交按钮可以提交另一个表单。例如,如果您在页面的页眉和页脚中有一个搜索字段,并且希望两个搜索输入都提交到同一个表单,那么可以使用这个特性。页眉和页脚提交控件可以与同一个表单相关联,而不是将整个页面内容放在表单中(这将阻止在页面上创建其他表单)。
注同其他当代形式特征一样,歌剧在这一点上是领先群雄的。一定要在你的首选浏览器中测试这个特性,但是如果它还没有实现也不要惊讶!
提供占位符文本
可以对一个表单做一些小事情,使它变得更加完美和可用。这是一件这样的事情。您可以在表单中添加文本来提示它们的用途(地址、电话号码等),而不是在表单中设置空白文本字段。传统上,这是通过在input
元素上设置value
属性来完成的。这种方法的问题是,最初添加的文本可能会被删除,这对于实际上只是一个提示而不是一个可编辑的功能来说没有意义。这个问题已经在 HTML5 中用placeholder
属性解决了。当文本字段为空时,添加到该属性的文本将出现在文本字段中。图 4-18 显示了这个样子。
图 4-18。四个不同的基于文本的输入字段(search
、email
、tel
和url
,添加了占位符文本
将输入设为只读
readonly
属性可用于防止输入控件中的内容从其初始值被修改。如果通过 JavaScript 更新表单字段值而不允许用户更改该值,这可能会很有用,如果该值是以某种方式计算的结果,则可能会这样做。这是一个布尔属性,所以您可以简单地将它添加到元素中:
<input type="text" value="You can't delete this" readonly />
自动完成和自动对焦
自动完成是 web 浏览器的一项功能,用于帮助自动填写表单上的详细信息。如果有人在填写姓名、地址、电话号码等。,在不同的表单上重复(例如,在电子商务网站上创建帐户时),自动完成功能可以存储这些值,然后将它们填充到将来要填写的类似表单中。这是在 web 浏览器应用中设置的首选项。与这个特性相协调的是autocomplete
属性,它允许浏览器的自动完成特性对表单中的单个元素或整个表单关闭。对于接收敏感数据或其他表单永远不需要的字段,可以这样做。该属性的值为on
或off
,如下所示:
<form method="post" autocomplete="off">
这将关闭本示例表单中所有表单控件的自动完成功能。
autofocus
属性足够简单;它是一个布尔属性,当添加到一个表单控件中时,当页面加载时,焦点会立即跳转到指定的元素。这意味着用户可以立即开始输入,而不需要事先单击第一个(或任何一个)元素。它由button
、input
、keygen
、select
和textarea
支撑。只有当页面上没有其他用户想首先点击的输入时,添加这个属性才是一个好主意。
使用数据列表
大多数输入类型都包含一个名为list
5 的属性,它与一个新元素datalist
一起工作。此元素定义了在表单控件输入中输入数据时可用的选项列表。datalist
元素本身并不显示在网页上,而是向表单中其他元素的list
属性提供数据。举个例子,让我们从一个在线工作申请的表单中截取一个片段。可能会有一个文本输入字段,要求输入期望的工资范围,如下所示:
<p><input type="text" name="salary" placeholder="Enter desired salary" /></p>
用户可以输入任何金额,但提供一个常用值列表供用户选择会有所帮助。例如,可能会显示以下值:
40,000+
60,000+
80,000+
100,000+
120,000+
这些可以表示为数据列表,如下所示:
<datalist id="salaries"> <option value="40,000+" /> <option value="60,000+" /> <option value="80,000+" /> <option value="100,000+" /> <option value="120,000+" /> </datalist>
然后,文本输入字段可以通过将其list
属性设置为数据列表的 ID 来使用该列表:
<p><input type="text" name="salary" placeholder="Enter desired salary" list="salaries" /></p>
结果是当用户开始在字段中输入一个值时,数据列表中的相关匹配就会弹出,如图图 4-19 所示。
5 排除的有password
、checkbox
、radio
、file
、hidden
、image
、button
、submit
、reset
。
图 4-19。显示数据列表如何与文本输入字段相关联的三个面板。当将焦点放在文本输入字段中时,会出现关联的数据列表,并且随着字母的键入,列表会缩小到匹配的选项。
其他表单控件
毫无疑问,input
元素是一个大而通用的元素,但它不是唯一的表单控件。我们已经谈到了button
,但还有select
(和option
)和textarea
。然而,这些对 HTML5 来说并不陌生。增加的是datalist
,这在之前的章节中已经介绍过了;progress
;meter
(又称规);output
;还有keygen
。让我们从旧的到新的,看看每个还没有被覆盖的控件。
菜单
select
元素是一个容器元素,允许任意数量的option
和optgroup
元素。通常显示为下拉列表,如图图 4-20 所示。
图 4-20。 A select
菜单
这个元素有两个其他地方没有涉及的特定属性,disabled
和size
。 6 它也可能使用multiple
属性(在本章前面的file
输入部分首次提出)。可以添加布尔disabled
属性来禁用(灰显)用户与菜单的交互。size
和multiple
属性是相关的。如果添加了布尔multiple
属性,select
菜单通常会显示为一个可滚动的列表框,允许用户通过按住 Command 键(Mac)或 Control 键(Windows)并单击多个项目来进行多项选择。接受数值的size
属性决定显示多少行选项。图 4-21 显示了一个列表菜单的例子。
6 它还拥有第二章中涉及的全局属性以及本章前面涉及的name
、required
、autofocus
和form
属性。
图 4-21。一个列表菜单,用添加了multiple
属性的select
元素创建
select
中的每一行都包含在一个option
元素中,如下所示:
<select name="cheesemenu"> <option>Cheddar</option> <option>Stilton</option> <option>Brie</option> </select>
注意前面显示的
datalist
元素也使用了option
元素,但是用自结束标记对它们进行了格式化,并对列表数据使用了value
属性。
option
元素有四个特定的属性:disabled
、selected
、value
和label
。像select
上的disabled
属性一样,这是一个布尔属性,它阻止用户选择菜单中的特定项目。布尔selected
属性用于向用户代理指示最初应该选择特定的option
;没有它,浏览器可能什么都不显示(只是一个空白的选择框)或者显示它遇到的第一个option
。例如,默认选择第二个选项,添加selected
,如下所示:
<select name="cheesemenu"> <option>Cheddar</option> <option selected>Stilton</option> <option>Brie</option> </select>
多个option
元素可以设置selected
属性,但前提是select
添加了multiple
属性。
添加不同的值
value
属性用于允许提交不同于特定option
内容的值。如果省略了value
属性,那么内容被用作值。例如,给定以下菜单:
<select name="cheesemenu"> <option value="ch01">Cheddar</option> <option value="ch02">Stilton</option> <option>Brie</option> </select>
如果选择了第一个或第二个选项,它们将分别提交值ch01
和ch02
。如果选择第三个选项,它将使用内容“Brie”作为其值,因为没有指定的value
属性。当您向用户显示的内容与您要提交给服务器的内容不同时,这种行为非常有用。例如,如果您正在为一个电子商务网站构建一个表单,您可能会有一个产品下拉菜单。您可能希望向用户显示产品的名称,但是某种类型的产品编号对您来说在服务器端进行管理要容易得多。因此,在添加每个产品名称的同时,您可以将产品编号作为一个值添加到列表中的每个option
中。名称会显示给用户,但是在选择产品并提交表单后,产品编号会提交给服务器。
注意在实践中,所有的选项都应该一致地使用或者不使用
value
属性。
添加速记标签
最后,我们来看一下label
属性。该属性被设计为接受一个短值来代替显示一个option
的内容。label
属性可以用来提供一个可选的显示标签,同时仍然保留传递给服务器的值的原始内容。这方面的代码如下所示:
<select name="cheesemenu"> <option>Cheddar</option> <option>Stilton</option> <option>Brie</option> <option label="All">All of the cheeses in all of the worlds</option> </select>
添加菜单结构
为了帮助为你的菜单提供结构,你可以使用optgroup
元素来分组相似的option
元素。因此,不用下面的标记:
<select name="cheesemenu"> <option>- - - English cheeses - - -</option> <option value="cheddar">Cheddar</option> <option value="stilton">Stilton</option> <option>- - -French cheeses- - -</option> <option value="brie">Brie</option> </select>
你可以用这个:
<select name="cheesemenu"> <optgroup label="English cheeses"> <option value="cheddar">Cheddar</option> <option value="stilton">Stilton</option> </optgroup> <optgroup label="French cheeses"> <option value="brie">Brie</option> </optgroup> </select>
前面的标记将如图 4-22 所示。
图 4-22。一个由多个optgroup
元素组成的select
菜单
optgroup
元素是为正确的作业使用正确标签的一个明显例子。使用optgroup
元素划分option
元素的一个好处是optgroup
标签不能被选择,它的值也不能作为数据提交,而在前一个例子中,页面作者要么不得不忍受错误的提交,要么提供一个客户端或服务器端的验证器来确保这样的划分符没有被提交。另外,optgroup
元素有一个布尔disabled
属性,如果需要的话,可以用来禁用整组option
元素。
文本框
textarea
元素在某些方面类似于 text input
元素,但是它允许多行输入,而不是只有一行。它使用一对属性cols
和 r ows
来控制它的大小,而不是使用一个value
属性来预设任何文本内容,而是使用元素本身的内容。它是一个容器元素,而不是一个自结束的空元素。
下面的代码创建了一个 20 列宽 5 行高的textarea
(如果输入超出了可视区域,就会出现滚动条)。
<textarea cols="20" rows="5">Type your content here</textarea>
尽管textarea
元素是内容的容器,但是使用新的placeholder
属性并将元素中的内容留空会更好地格式化前面的代码,如下所示:
<textarea cols="20" rows="5" placeholder="Type your content here"></textarea>
图 4-23 显示了结果。
图 4-23。 A textarea
20 列宽 5 行高,添加了占位符文本
textarea
元素包含一个在别处没有提到的属性:wrap
。此属性用于指示是否应该在文本框的可用区域中文本换行的位置向提交的文本区域数据添加换行符。这个属性的值可以是hard
或soft
。第一个是hard
,意思是在提交的表单数据中,在文本区域中提交的文本换行的地方添加换行符。在这种状态下提交表单后,您会在 URL 中看到 URL 编码的换行符,看起来像%0D%0A
(如果使用GET
方法)。另一方面,soft
值意味着虽然文本可以在屏幕上换行,但提交的数据将在没有任何换行符的情况下发送,该换行符指示文本在文本区域中的换行位置。这是默认行为。
文本区域控件也可以使用maxlength
属性来限制可以输入的字符数量;这类似于maxlength
属性在单行文本输入控件上的工作方式,同样的注意事项也适用(也就是说,如果数据长度非常重要,请在服务器端仔细检查长度)。
显示进度
这是 HTML5 规范中的一个新元素,非常简洁!从根本上来说,这很简单:展示一个人在一个多阶段的过程中走了多远。例如,一个表单可能分布在多个页面上,这个元素用于指示用户在页面总数中的哪一页。这个元素叫什么?progress
!它是这样工作的:有两个属性,max
和value
。max
属性是任务中的最大步骤数,而value
是用户正在进行的当前步骤。
让我们看一个例子:
<progress max="3" value="1">Step 1 of 3</progress>
这定义了处于三个步骤的第一步。在支持该元素的浏览器中,显示可能类似于图 4-24 。
图 4-24。在支持的网络浏览器中呈现的progress
元素
注意对于那些不支持该元素的浏览器,在元素的标签之间放置一些有意义的文本是很重要的,因为这将在不支持进度元素的情况下显示(图 4-25 )。
图 4-25。在不支持progress
元素的 web 浏览器中,元素标签之间的文本内容将被显示。
显示仪表
meter
元素(图 4-26 )可能看起来与progress
元素(图 4-26 )相同,但是有一个重要的示意性区别。progress
元素用于显示任务中步骤的进度,而meter
元素用于显示一个量表——即一个已知范围内的特定值。它可以用来显示还有多少硬盘空间或还有多少库存。
图 4-26。谷歌浏览器中出现的meter
元素
min
和max
属性设置范围的最小值和最大值,而value
属性设置仪表在范围中的位置。该元素还具有许多属性,用于示意性目的,以确定特定值在设定范围内的含义。optimum
、low
和high
属性允许将范围分割成区域。这可被网络浏览器用来根据数值在范围内的位置显示不同的量规,如图 4-27 中的所示。
图 4-27。一个meter
元素通过高低范围减少数值
前面仪表的代码如下:
`
注意,和progress
元素一样,一些描述性的文本内容被放置在meter
元素的标签之间。此内容将在不支持该元素的 web 浏览器中显示。
显示计算的输出
output
元素用于显示计算结果。它在很大程度上是一个语义元素,因为输出看起来像页面上的纯文本。
除了全局属性和form
和name
属性(前面已经讨论过了),output
元素还有一个属性:for
。这个属性应该包含一个元素的 id 列表,这些元素将进入这个output
元素正在显示的计算中:
<input id="inputa" type="number"> + <input id="inputb" type="number"> = <output id="resultfld" name="result" for="inputa inputb"></output>
典型的用例是显示脚本生成的计算结果。下面的脚本将在每次两个数字输入改变时更新output
元素的值:
var inputa; var inputb; var resultfld; function init(){ inputa = document.getElementById("inputa"); inputb = document.getElementById("inputb"); resultfld = document.getElementById("resultfld"); inputa.oninput = updateResult; inputb.oninput = updateResult; } function updateResult(){ resultfld.value = Number(inputa.value)+Number(inputb.value); } window.onload = init;
密钥生成器
keygen
元素用于生成一个私有和公共密钥,其公共端在表单提交时被发送到服务器。元素本身看起来相当神秘,因为默认情况下它是一个数字下拉列表,仅此而已。这些数字是加密算法中使用的密钥的位数。数字越大,密钥越难破解。这个元素的预期用例是,服务器将生成一个证书并发送给客户机,以便在两者之间建立可信的安全通信。web 页面和 web 服务器之间加密通信的加密技术超出了本书的范围,除非您已经对加密技术有所了解,否则您不会使用这个元素。 7 即使你对密码学有所了解,也不要过于依赖当前形式的这种元素。它很有可能在未来改变方向(毕竟 HTML 规范仍处于草案阶段)。例如,微软的代表已经声明他们无意支持keygen
( keygen
最初是由 Internet Explorer 的竞争对手 Netscape 开发的,所以微软从不同的方向着手实现加密)。当前对元素进行标准化的方法使得所使用的实际算法是可选的,以便元素本身可以被支持(为了向后兼容),但是加密组件可以被省略。微软是否在这些条款下实现该元素还有待观察。除非你别无选择,否则你最好暂时不要管keygen
,直到尘埃落定。
这种的分歧导致了
video
元素中的视频编解码器没有标准化,正如你将在第五章中看到的,这意味着需要提供不止一个视频文件来兼容所有主流浏览器。
IBM 出版了一本关于公钥加密的初级读本,你可能想看一看以熟悉其中的概念:【www.ibm.com/developerworks/web/library/s-pki.html】??。
添加带有字段集和标签的结构
元素允许 web 作者将表单控件分成主题链接的部分,使用户更容易地处理表单,同时也增强了辅助设备的可访问性。大多数浏览器会显示一个带有简单边框的fieldset
。例如,下面的标记显示了如图图 4-28 所示的结果:
`
` 图 4-28。一个fieldset
的通常渲染的例子
要识别每个fieldset
,必须使用legend
元素:
`
`该表单现在看起来类似于图 4-29 。
图 4-29。一个fieldset
带一个legend
带一个
最后要提到的表单元素label
,也增加了可用性和可访问性。该元素用于在文本标签和表单控件之间建立关联。当标签和表单控件相互关联时,可以单击其中任何一个来与控件进行交互。例如,与复选框相关联的标签意味着可以单击该标签,然后它会选择该复选框。有两种方法可以在标记中创建这样的可点击标签。第一种也是更好的方法是将表单控件包含在一个label
中。这里有一个例子:
<label>Favorite cheese: <input type="text" name="ch" /></label>
在前面的例子中,单击标签中的“Favorite cheese:”文本会使嵌套的input
获得焦点,在这种情况下,这意味着用户可以开始在文本字段中键入内容。另一个解决方案是使用for
属性:
<label for="favcheese">Favorite cheese: </label> <input type="text" id="favcheese" name="ch" />
使用for
属性的好处是表单控件不需要出现在label
内部。属性for
中的值是关联表单控件的 ID,这意味着两者是关联的,即使它们位于标记中不同的位置。这可能是有用的;然而,由于这种属性的手动配对,使用for
属性可能会很费力。除非万不得已,否则我不建议使用它。一个用例是表单出现在表格中,标签出现在一列,表单控件出现在另一列。
注意切记表格仅用于按行和列排列表格数据,不应用于布局;这包括使用一张桌子的唯一目的是美观地布置一个形式!为此,请改用 CSS。
把所有这些放在一起
还记得市按吗?上一章虚构的报纸网站?让我们看一下收集新闻提示的站点表单,这样我们就可以看到所有这些表单控件集合在一起。
第 1 页,收集用户详细信息
该表格分为两页,第一页用于在网站上注册举报人,第二页用于记录他们的新举报。表单的首页看起来像图 4-30 。
图 4-30。第一张表格在市按“提交小费”表格
要创建这个表单,首先我们需要一个form
元素。我们将设置两个属性:action
,它将转到我们的第二个表单页面,以及method
。我们需要哪种方法?GET 还是 POST?嗯,用户将提交数据,这些数据很可能最终会存储在数据库中,所以这是一个主动修改数据的提交。所以,我们用 POST。此外,如果表单上出现密码输入字段,这应该是使用 POST 的一个很大的提示,它确实出现了。好,这是我们的开始形式:
<form action="form2.php" method="post">
</form>
接下来,虽然表单跨两个页面,但是有三个步骤来处理表单,因为在数据提交后会有一个确认页面,所以我们应该添加一个progress
元素来显示:
`
`请注意,value
属性被设置为零,max
被设置为二,但是替代内容文本是“1/3”这是因为我们将有三个步骤要完成:第一个表单、第二个表单和第二个表单提交后的确认页面。因此可选文本将会是 1/3,2/3,3/3,而value
属性将会是 0,1,2。
接下来,我们看到我们需要用户创建一个用户名和密码(假设在完成这个过程后,他们能够在其他地方登录),我们可以将它们分成自己的字段集。图例用于向字段集添加标题,文本和密码输入字段设置为必填。此外,占位符文本也被添加到两者中。一个正则表达式模式被添加到密码字段,以便它只接受六个字符或更长的密码。
`…
…`在此之下,我们为个人详细信息区域添加了另一个字段集。第一个字段是一个通用文本输入字段,用于收集举报人的姓名。添加占位符文本,提示要输入的信息的格式:
`…
`接下来,一个必需的颜色选择器输入被用作对自动垃圾邮件机器人的简单检查,自动垃圾邮件机器人可能无法为“雪”选择正确的颜色。遗憾的是,pattern
属性在color
输入类型上不可用,所以这需要在服务器端用 JavaScript 进行检查。我们假设正在服务器上检查它:
… <p><label>Spam check. What color is snow? (choose a color) <input type="color" name="captcha" required/></label></p> …
最后,在结束的form
标签之前添加一个提交按钮,完成这个表单:
`…
第 2 页,采集评论
在填写完表单并点击提交后,用户会在第二页上看到第二个表单,如图 4-31 所示。
图 4-31。第二张表单上的城市按“提交小费”表单
像第一个页面一样,form
元素是用action
和method
属性创建的。此外,因为表单上有一个文件输入字段,所以我们将enctype
属性设置为multipart/form-data
,这样就可以发送二进制数据。接下来,添加一个progress
元素,并在第一个表单上出现的元素的基础上递增:
`
`接下来,为提示详细信息区域创建一个字段集和图例。为新闻事件的当地日期和时间添加了日期/时间输入字段。一个textarea
元素用于收集提示细节,一个文件输入用于允许在必要时附加一个支持文件:
`…
…`使用 JavaScript,我们将使只读文本输入中的值在滑块移动时动态更新。我们将设置占位符文本,以指示滑块上的高数字表示“最紧急”,并在移动滑块时将文本输入更新为滑块的值。JavaScript 被添加到文档头的script
元素中:
`…
…`
注意记住,在生产环境中,你可能希望将你的脚本转移到一个外部文件中,因为这样可以更好地组织你的标记和脚本。
为关于提示的“通信首选项”创建一个新的字段集。提供复选框来指示是否可以联系用户。如果是这样,那么另一个字段集嵌套在第一个字段集中,其中两个单选按钮允许用户选择他们喜欢的联系方式。默认选择“电子邮件”(带有checked
属性):
`…
…`接下来,一个meter
给出了该报目前人员配备情况的一些指标,10 个员工中有 6 个在办公室。我们不会推测这个度量工具更新的频率,但是在这里使用它是一个合适的元素,因为员工数量是一个已知的量。
最后,像最后一个表单一样,提交按钮出现在最后,表单关闭:
`…
`
第 3 页,提供确认
提交第二个表单会将用户带到确认页面,在这里,progress
元素被更新到它的最终位置,并给出一条简短的感谢消息:
`
`结果页面看起来像图 4-32 。
图 4-32。第三页也是最后一页致谢
表单可用性
创建一个表单很容易,但是创建一个真正好的表单要难得多。 City Press 提示表单显示了创建简单表单时需要考虑的所有选项和输入变量。表单变得越复杂,就越像是一个应用而不是网页,所以你需要认真考虑可用性。即使是在城市媒体的提示表单中,也可以添加更多来使其更加有用。例如,可以将title
属性添加到所有输入字段中,以便在用户将光标悬停在这些字段上时(或使用屏幕阅读器)提示这些字段所期望的内容。然而,事情比这更严重。如果你的表单处于公众开始给你钱的时候,你需要严格地测试你的表单,观察人们使用它并记录他们的反应(即使你的观众只是办公室另一边的几个同事),并确保它尽可能地工作。
对表单可用性主题的详尽讨论已经超出了本书的范围,但是下面几节中概述的指南应该足以帮助你避免一些常见的表单可用性问题。除了这一章中的信息,我推荐你阅读史蒂夫·克鲁格的经典之作不要让我思考:网站可用性的常识方法(New Riders 出版社,2000)。
将正确的输入用于正确的工作
因此,您知道所有的输入类型,但是在给定的情况下,哪种输入类型更合适呢?其中一些是显而易见的——file
输入只有一个目的,没有其他类型的输入可以代替它——但是,例如,复选框和单选按钮呢?一个好的经验法则是,如果你有两个或更多选项的列表,用户必须选择一个而只能选择一个,使用单选按钮。选择一个单选按钮应该会取消选择属于同一命名组的任何其他单选按钮。但是如果列表包含许多选项,可以考虑使用一个select
菜单。您将无法选择多个选项,并且您将节省一些空间(以“可发现性”为代价)。
另一方面,当有几个选择时,就使用复选框,用户可以把它们都留为空白,也可以选择任意多个。选中一个复选框不会取消选择该组中的任何其他复选框。与一系列复选框相对应的菜单是一个带有multiple
属性的select
菜单,但是复选框通常更容易使用,因为它们不需要用户理解使用什么键盘/鼠标组合来选择/取消选择选项,所以您可能希望尽可能避免多选列表。
当只有一个用户可以打开或关闭的选项时,也应该使用复选框,比如同意订阅时事通讯。您不会在这里使用单选按钮,因为单选按钮只能通过选择另一个来取消选择。
还记得使用标签、字段集和图例来帮助提高可用性和可访问性。
保持简洁明了
只收集你需要的信息,仅此而已。你真的需要知道我是先生还是女士吗?你真的需要我的传真号码吗?我的职业?我的年薪?质疑表单中每个字段的存在,如果是必填字段,再次质疑它是否需要。你的营销人员可能喜欢收集大量网站访问者的个人数据,但是你的表单越长,越多不相关的用户开始感觉到它,他们放弃它的风险就越高。
不要让我思考,不要让我工作,也不要试图欺骗我
尽可能使您的表单易于完成。如果在任何时候,用户不得不暂停几秒钟,试图找出哪里出了问题,或者你的意思是什么,那么这几秒钟他可能只是想“哦,算了”,然后去做三明治。因此,例如,如果您的表单包含必填字段,请考虑将这些字段的样式设计得更突出(当然,还要添加required
属性,这样表单验证就会生效)。从一开始就明确必须填写的字段。如果这是大多数字段,请考虑在可选字段旁边添加文本“可选”。
如果您需要某种格式的数据,不要依赖用户以这种格式输入数据——这是服务器上的表单处理程序应该处理的事情。例如,如果用户需要输入信用卡号码,如果她愿意,让她填写 1234 5678 9012 3456(这是她信用卡上的格式),1234567890123456,或 1234-5678-9012-3456——在合理范围内对用户有效的任何数字。灵活使用pattern
属性可以允许表单输入的灵活性,但仍然可以验证关键信息和基本格式(比如检查输入的数字是否正确)。记住,计算机应该为用户节省时间。如果你愿意,可以提供一个首选格式的指南,但也要考虑到备选条目的可能性。
如果用户犯了一个服务器端代码无法解决的错误,那么用一个清晰、有意义、适当的错误消息让他知道——越快越好。使用内置的表单验证,并提供 JavaScript 和/或服务器生成的验证和错误消息作为备份。表单越复杂,出错的地方就越多,所以测试,测试,测试,确保没有无意义的错误信息。
请记住,互联网是全球性的
如果您的表单不针对任何一个国家,请尽量不要填写“州”和“邮政编码”,当然,如果您包含这些字段,也不要强制填写。同样与前一点相关的是,不要试图限制用户数据的格式,除非你完全确定它的格式。
注意与表单国际化相关的一个即将出现的属性是
dirname
属性,它是在textarea
、text
输入和search
输入元素上指定的。还记得表单数据是如何作为键/值对提交的吗?该属性创建一个发送到服务器的键,该键以输入到表单控件中的文本的方向性作为其值。这将显示为ltr
(从左到右)或rtl
(从右到左)。但是,不要指望这个属性今天还能工作,因为它还没有被主流浏览器实现。关于它的更多信息和代码示例可在[
dev.w3.org/html5/spec/common-input-element-attributes.html#attr-input-dirname](http://dev.w3.org/html5/spec/common-input-element-attributes.html#attr-input-dirname)
获得。
需要时提供后备
本章中的很多内容都是新的,可能并不适用于所有的浏览器。幸运的是,当不支持特定的输入类型时,输入类型通常会退化为常规的文本字段。但是,如果您设计了 HTML5 规范中所有丰富的控件,但没有考虑它在旧浏览器中的外观,这仍然会对表单的可用性产生严重影响。调查像 Modernizr ( [
modernizr.com](http://modernizr.com)
)、jQueryUI ( [
jqueryui.com](http://jqueryui.com)
)和 webforms2 ( [
code.google.com/p/webforms2/](http://code.google.com/p/webforms2/)
)这样的项目,因为这些项目将帮助您将“polyfills”实现到您的站点中,这意味着它们(通过 JavaScript)提供了您期望在浏览器中本机可用的功能。
总结
创建表单相当容易——只需插入几个input
标签,添加一点文本,点击最后的提交按钮,然后您就可以早点回家了——但是创建可用的、可访问的、符合逻辑的表单要困难得多。思考你的表单内容的意义。哪些原理图元素适合用作输入?这些都不是应该留到最后一刻的事情。你的表单可能是你的网站最重要的部分,特别是如果它是一个允许人们输入信用卡信息的表单,所以它应该是一个简单易用的模型。它不应该让人们心烦意乱,激怒他们,或者打消他们的念头——这么说似乎很奇怪,但我这辈子见过一些可怕的形式就是这样做的。最后,要准备好面对现实,为了创建最终可用的表单,您必须求助于 JavaScript 或服务器端代码(或者两者都用)来帮助您为用户提供现代 web 表单的预期特性。
五、多媒体:视频、音频和嵌入式媒体
网站只有带项目符号的文本列表,没有任何形式的图像的日子已经一去不复返了。但是如果你仔细想想,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
,用户也可以访问该文档。这里有一个例子:
``
如果在不支持iframe
的浏览器中查看之前的代码片段,链接将会显示;否则,embed.html
的内容将呈现在内嵌框架的边界内,这是一个 300 像素宽、150 像素高的框(在本例中,由width
和height
属性设置)。根据需要出现水平和垂直滚动条以适应内容,如图图 5-3 所示。
图 5-3。带有滚动条的内嵌框架的出现
因为它提供了一个固定的区域来显示内容,所以一个iframe
也可以用来在页面上的一个有限区域中显示大量的内容(通常是文本)。例如,也许你的网站有一个“条款和条件”声明,这通常是一个充满法律文本的长文档。通过页面底部页脚中的链接可以访问该文档,但是您也希望在注册表单中嵌入该文档,以便用户在您的站点上注册。你可以将现有的文档嵌入注册页面,如图 5-4 所示,而不是在你的网站上复制一个条款和条件页面——链接在页脚和用户注册表单上。
图 5-4。使用嵌入式框架在小区域嵌入现有的大量文本文档的示例
图 5-4 的源代码可能是这样的:
`…
…`
新的 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
属性被设置为特定内嵌框架的名称时,特定的内嵌框架可以被用作链接的目标。这里有一个例子:
` Next page`
在这种情况下,当点击“下一页”超链接文本时,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 文档,则只有一个样式表将被选择并在页面上使用。替代样式表的工作方式类似于首选样式表,还意味着它可以替代页面上指定的首选样式表。出于偏好或可访问性的原因,用户可以选择替代的样式表。
下面显示了一个附加了持久样式表、首选样式表和两个备选样式表的页面:
`…
<head> <meta charset="utf-8" /> <title>HTML5 and CSS together</title>
…`
在这个示例页面中,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 指令来设置首选样式表。例如,以下代码将标题为“蓝色主题”的样式表设置为默认样式表:
`…
<head> <meta charset="utf-8" /> <meta http-equiv="Default-Style" content="Blue Theme" /> <title>HTML5 and CSS together</title>
使用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>
`
通过添加以下代码,可以将列表中奇数行涂成灰色:
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
(R)值。请注意放大图像时出现的突出的像素化。
多重背景
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
的目录(在你的桌面上或者任何你方便访问的地方)。您将使用该目录作为本章示例的起始模板。启动您最喜欢的文本编辑器,并创建以下网页:
`
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
,如下所示:
`
该页面包含两个按钮和两个按钮之间的空白段落,我们将使用 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,如下所示:
`
正如您所看到的,这段代码包含了带有回退内容 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)和暂停(R)状态
脚本化 2D 画布 API
现在让我们来看看更具互动性的东西。正如你在第五章中看到的,canvas
元素没有太多可以用 HTML 来处理的东西,因为它只有两个属性,简单地指定了画布区域的宽度和高度。canvas
的真正力量来自于 JavaScript 对元素的操作。请记住,画布是位图画布,这意味着它本质上是一个空白图像,我们可以通过一组绘图命令来操纵它的像素。
要使用画布,需要从元素中检索特定的“上下文”,然后可以将它用作绘制操作的目标。上下文指定了我们正在处理什么样的图像。有两种选择:二维或三维图像。
让我们从寻找检索我们将要使用的上下文的方法开始。 4 首先从章节开始复制jstemplate
目录,并重命名为canvas
。修改index.html
中的 HTML,添加一个canvas
元素(和一些后备内容)来代替文本段落:
对于没有耐心的人来说,检索上下文的方法称为 getContext()。
`…
<body> <canvas id="canvas" width="300" height="300"> <p>The canvas element is not supported!</p> </canvas> </body> …`现在修改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(degreesMath.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;
invalidate(); // call invalidate to redraw the canvas
}
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
,如下所示:
`
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
文件,如下所示:
`
- 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 中触发。我们在这里也设置了 CSS opacity
属性,尽管建议像前面的例子那样添加和删除 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()
中的输出来帮助您熟悉正在发生的事情以及您可以使用的东西。
八、前方的路
HTML5 有许多边缘,这些领域仍在朝着标准的完美进行塑造和雕琢,但在很大程度上,未来的道路与 HTML5 关系不大,而与 HTML5 的应用领域关系更大。这是一个互联的世界,通过手机连接的人比通过台式电脑连接的人多。在未来的几年里,支持互联网的智能手机可能会普及到大众,就像几年前低端的“哑”手机一样,网络将会有移动的访问者,在拥挤的公共汽车上、在街角、在咖啡馆、在餐馆,甚至,我敢说,在电影院浏览网页。移动设备给你这个网页开发者/设计者带来了特殊的挑战。屏幕分辨率更复杂,屏幕空间更有限,但在 CSS3 的帮助下,HTML5 在当代智能手机上得到了很好的支持。您从前面章节中学到的东西在这里也适用,尽管速度可能会慢一点,屏幕区域也会小一点,但这些都是需要解决的设计挑战,它们被移动空间的独特可能性所抵消。本章将讨论这些挑战和可能性,并以讨论 HTML5 规范的一些最后的角落结束。
移动网络的挑战
在过去的几年里,许多新的支持互联网的手持设备已经变得广泛可用。虽然支持互联网的移动设备自 20 世纪 90 年代以来就已经存在,但可以说,随着 2007 年 iPhone 及其彩色大触摸屏的推出,它们首次被广泛视为浏览网页的可行平台(而不仅仅是用来收发电子邮件)。随之而来的是运行 iOS、谷歌 Android 操作系统、微软 Windows Phone 7 平台等的大型触摸屏设备的激增。网页设计突然变成了一个多方面的过程,比以前多了许多倍。在具有大量处理能力的台式计算机环境中,可以在大的宽屏显示器上浏览网页,但是在处理器受限并且显示器只有几英寸宽的小型手持移动电话上浏览网页。此外,还有介于两者之间的平板设备,如苹果的 iPad 或三星的 Galaxy Tab。然而,设计桌面显示器不仅仅是考虑屏幕尺寸和处理能力的差异。移动设备没有鼠标,点击可能要用手指来完成(如果设备比较老,可能要用手写笔),所以最好避免小按钮。然后是连通性的问题。当用户四处移动时,移动设备可能会断开连接。
1 要了解全球移动设备使用的爆炸性增长,看看世界银行收集的移动电话用户数据:【http://data.worldbank.org/indicator/IT. CEL.SETS.P2/countries/1W?显示=图形。
响应式设计
让我们从屏幕尺寸开始考虑,因为这是台式电脑和手持设备之间最明显的区别。手机设计有两种主要方法。首先是开发两个不同的网站,一个用于桌面环境,另一个用于移动设备。根据用户在哪个浏览器中查看站点,用户会被定向到其中一个。对于复杂的网站来说,这可能是一种可行的方法,因为复杂的网站可能需要大量的工作来适应移动查看体验,并且最好为移动用户提供一个简单版本的网站。这种方法的一个问题是涉及到潜在的高维护。如果为台式机、平板设备和移动电话创建一个不同的网站,如果另一个具有不同外形的设备变得流行,会发生什么?比如 Opera 有任天堂 Wii 游戏机的网页浏览器,运行 Android 操作系统的可上网手表甚至已经开发出来了!另一个选项是根据查看设备的屏幕大小更改正在使用的样式表。媒体查询(稍后讨论)可用于根据查看设备的物理质量更改 CSS,例如基于显示器尺寸和分辨率的更改。将这种方法与流体布局相结合,你会得到所谓的响应式设计、、 2 ,旨在适应各种屏幕尺寸。
固定的 960 像素宽的网格一直是使用 CSS 创建行和列的 web 设计标准。由此发展而来的是响应式网格设计,它根据宽度增加或减少页面上的列数,并将可见列扩展到浏览器可视区域的宽度。像 Skeleton ( [
getskeleton.com](http://getskeleton.com)
)这样的开发工具包已经出现,可以帮助开发这种灵活的布局。
注意自适应图像(
[
adaptive-images.com](http://adaptive-images.com)
)是一个与响应式设计相关的概念,用于处理页面上图像的缩放,以便它们在移动设备上缩小,在桌面显示器上放大。
视口
在桌面环境中,视窗是在网络浏览器中可见的网页区域。例如,如果您在桌面上打开一个网页,并将页面大小缩小到移动电话显示屏的实际大小,您看到的区域就是视窗。您可能会看到滚动条出现,以便您可以滚动到其余的内容。您还会注意到,如果文本没有使用 CSS 调整大小,它可能会自动换行以适应可视区域。
移动设备上的视区行为略有不同。与桌面环境不同,在移动环境中,web 浏览器覆盖一个固定的区域。视口定义了文本将被换行的区域等等,其可以延伸到可视区域之外。例如,iPhone 上的默认视窗是 980 像素宽(以适应网页设计中常用的 960 像素网页宽度),但页面可以缩小,以便完全适合 320 像素的可视区域(图 8-1 )。
参见 Ethan Marcotte 在 http://alistapart.com/articles/responsive-web-design/发表的关于响应式网页设计的开创性文章。
图 8-1。典型智能手机上的可视区域为 320 像素,而页面所在的视区可能要大得多,这要求用户在以实际大小查看时需要平移才能看到页面上的所有内容。
理想情况下,网页的宽度应该与可视区域相对应,这就是为移动查看优化网页所能做到的。首先,有一个meta
元素值告诉移动设备将布局视口的宽度设置为可视区域的宽度。为此,将在网页的 head 部分添加以下内容:
<meta name="viewport" content="width=device-width" />
注意有一个 CSS 规则
@viewport
,它正在开发中,作为meta
元素视窗规则的替代。有关实施细节,请参见 Opera 网站上的页面:[www.opera.com/docs/specs/presto28/css/viewportdeviceadaptation/](http://www.opera.com/docs/specs/presto28/css/viewportdeviceadaptation/)
。
接下来,可以添加一个额外的选项来防止页面不必要的缩放。将initial-scale=1.0
添加到meta
元素:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
viewport
meta
元素值是最近才添加的;它是苹果公司为在 iPhone 上使用而添加的,但后来被整合到其他当代移动操作系统中。然而,为了适应旧的移动浏览器,添加两个额外的meta
元素是个好主意:
<meta name="HandheldFriendly" content="true" /> <meta name="MobileOptimized" content="320" />
这两个价值预示着移动网络的早期,所以最终它们将不再需要包含,但现在包含它们是一个好主意。
现在,如果页面布局针对 320 像素宽的查看区域进行了优化,它将正好适合大多数现代移动电话的显示区域,但要实际达到这一宽度并允许网站在桌面环境中仍然可以舒适地查看,您很可能会使用媒体查询,我们接下来将讨论这一点。
注第一章中提到的 HTML5 样板工程在
[
html5boilerplate.com/mobile](http://html5boilerplate.com/mobile)
有一个为移动设备量身定制的版本,其中包含了这些meta
元素值。
媒体查询
媒体查询是 CSS3 中的一项新功能,它允许 CSS 检查查看设备的物理特性,并相应地更改 CSS 规则。媒体查询产生于为不同的媒体类型定制 CSS 的需要,例如为印刷或屏幕。典型的媒体查询可能如下所示:
@media only screen and (min-width:480px) and (max-width:767px) { body { width:560px; } }
这出现在链接到页面的外部 CSS 文件中。它指定了包含的 CSS 规则将应用到的媒体类型(在这种情况下,它用于在屏幕上显示)以及包含的规则将应用到的查看屏幕的最小和最大宽度。没有必要同时指定最小和最大宽度。也可以选择许多其他值,比如精确的宽度、高度、方向、颜色、分辨率等等。3
注自媒体类型(
screen
、print
等)。)在较老的浏览器中受支持,因此不一定会忽略@media
规则。在查询的开头添加了only
关键字,以便对旧浏览器隐藏包含的样式规则,因为旧浏览器不会识别它,这将导致它们跳过媒体查询声明的其余部分。
在分层配置中使用媒体查询有助于适应各种屏幕尺寸,每个尺寸都将内容放入越来越小的屏幕区域:
@media only screen and (min-width:768) and (max-width:959px) { body { width:768px; } } @media only screen and (min-width:480) and (max-width:767px) { body { width:480px; } } @media only screen and (max-width:479px) { body { width:340px; } }
这三个规则通过三个屏幕尺寸变化来约束页面的宽度。这个例子只是显示了对宽度的限制,但是您可能希望为移动查看设置全方位的内容样式,这可能意味着更大的文本和更大的链接区域。例如,第四章中使用的城市媒体新闻提示表单可能会使用媒体查询进行修改,以出现在移动浏览器中,如图图 8-2 所示。为了使内容适合移动环境,使用 CSS 将无关信息放置在视图之外,并添加了样式以使菜单更加突出。
3 完整列表和详细信息见[www.w3.org/TR/css3-mediaqueries/](http://www.w3.org/TR/css3-mediaqueries/)
。
图 8-2。城市出版社手机优化新闻提示提交表单页面
测试手机网页
在实际的移动设备上测试网页是了解移动页面实际上针对移动设备进行了优化的最佳方式,但是也可以在运行于桌面计算机上的模拟设备上非常有效地测试网页。每个主要平台都有自己的运行在台式计算机上的模拟设备。这对于检查布局和基本功能非常有用,但对于测试性能却不太有用,因为模拟设备使用的是计算机的处理器,它比移动设备中的处理器强大许多倍。无论如何,以下是一些在台式计算机上测试移动网页的有用资源:
- Android:Android 模拟器可以在
[
developer.android.com](http://developer.android.com)
作为 Android 软件开发工具包(SDK)的一部分下载,可以模拟和仿真各种硬件细节。Android 的一个注意事项是,当访问本地 web 服务器上的页面时,通常的[
localhost/](http://localhost/)
地址会寻找运行在模拟器本身上的 web 服务器。相反,需要使用特殊的 URLhttp://10.0.2.2/
来访问运行在主机上的网络服务器。 - iOS :苹果公司在
[
developer.apple.com/xcode/](http://developer.apple.com/xcode/)
提供了一个带有 Xcode 软件包的模拟器。该模拟器可以模拟带有和不带有 Retina 显示屏的 iPad 和 iPhone。 - Windows Phone 7:Windows Phone SDK 包括一个模拟器,用于测试 Windows Phone 7 平台上的内容。这可以从
[
create.msdn.com](http://create.msdn.com)
下载。 - Opera : Opera 不开发硬件,但有一个桌面网络浏览器的移动版本,它有一个可以从
[
opera.com/developer/tools/mobile](http://opera.com/developer/tools/mobile)
下载的模拟器。
除此之外,Adobe Device Central 也是创建各种配置和屏幕尺寸的通用虚拟移动设备的一个选项。可以在虚拟设备中的文件 打开 URL…下打开网页 URL。
也可以在标准的网络浏览器中测试响应式设计。如果在 Safari 的高级偏好设置中打开了开发菜单,你会发现在开发用户代理下有一个选项,让浏览器伪装成手机浏览器(甚至是另一家厂商的浏览器!).打开一个网页并调整窗口大小,它就像在 Safari 的移动版本上被浏览一样!
离线应用缓存
脱机应用缓存是一种确保关键文件存储在客户端上的方法,这样,如果连接丢失并且页面被重新加载,缓存的文件仍将被加载。如果所有必要的文件都被缓存,即使用户离线,网站也可以正常导航。这在互联网连接可能中断的移动网络环境中可能特别有用。
应用缓存使用根html
元素上的manifest
属性将 web 页面与缓存清单文件相关联,该文件列出了要缓存的文件(在某些情况下,不是缓存!).缓存清单文件只是一个文件扩展名为.manifest
的文本文件。必须使用text/cache-manifest
MIME 类型从 web 服务器提供服务。它使用相对或绝对 URL,但通常如下所示:
`
…`sitecache.manifest
文件(取决于您的配置;你可以用不同的方式命名)必须以关键字CACHE MANIFEST
开始,然后可选地跟随三个部分。这些是:
CACHE
:要缓存的文件。NETWORK
:总是应该从网上检索的文件。FALLBACK
:缓存中未找到的文件的回退内容。首先给出要查找的文件,然后是回退文件的位置。
缓存清单还可以包含注释,这些注释会被忽略。注释以井号(#)开头。缓存清单文件可能如下所示:
CACHE MANIFEST # list of files to cache CACHE: index.html css/styles.css js/script.js # files that require a network connection NETWORK: userlogin.php # files to use in place of other files that were not cached FALLBACK: img/logo.png img/offline-logo.png contact.php contact.html
这需要出现在任何使用缓存的页面上。在CACHE
部分列出的文件将存储在客户端机器上,并在必要时检索以浏览网站。出现在NETWORK
部分下的任何文件将总是从服务器中获取。FALLBACK
部分将成对的文件映射在一起,这样如果第一个文件找不到,第二个文件将代替它在页面上的位置。使用回退,您可以向用户提供视觉线索,告诉他们正在浏览页面的缓存版本。例如,您可以提供标记为“脱机”的替代图像,这些图像只有在用户查看缓存图像时才会显示。
其他 HTML5 技术
HTML5 家族中有许多技术要么不够成熟,要么过于宽泛,无法在本书的范围内解决。然而,这并不是你不应该意识到他们的存在的理由!以下是这些技术的高级概述。
微数据
微数据是机器的 HTML5。它适用于搜索引擎和其他外部应用,这些应用可能希望从您的内容中获取意义,但需要更多信息来理解它。要做到这一点,可以用属性对现有的 HTML 元素进行注释,这些属性定义页面上的数据类型,比现有的语义 HTML 元素更精细。
在最基本的层面上,微数据使用三个属性:itemscope
、itemtype
和itemprop
。例如,考虑一下关于这本书的一段话:
`
HTML5 Mastery: Semantics, Standards, and Styling by Anselm Bradford and Paul Haine.
为了使用微数据进行标记,首先添加了itemscope
属性来表示该段落包含相关的数据集合,在本例中是一本书和作者:
`
HTML5 Mastery: Semantics, Standards, and Styling by Anselm Bradford and Paul Haine.
这个注释意味着段落中的任何元素都是相互关联的。然后使用itemtype
属性给出一个微数据词汇表的 URL,这是一组为不同类型的公共数据定义的属性值,比如人、地点、事件和事物。网站[
schema.org](http://schema.org)
(由微软、谷歌和雅虎推出!)包含许多用于分类数据的词汇表。我们将用他们关于Book
的文献来注释这一段。itemtype
被添加到词汇表定义的 URL 中:
`
HTML5 Mastery: Semantics, Standards, and Styling by Anselm Bradford and Paul Haine.
访问词汇地址,您会看到有大量的属性可以使用,比我们在这个简单的例子中需要的要多得多。我们将使用itemprop
属性用文档中的属性来注释内容。为了将标题与作者分开,我们还需要添加span
元素,以便将注释附加到:
`
<span itemprop="name"<HTML5 Mastery: Semantics, Standards, and Styling>/span> by >span itemprop="author">Anselm Bradford>/span> and >span itemprop="author">Paul Haine>/span>.
首先是书名,然后是作者。作者有点特殊,因为他们是微数据宇宙中的另一个对象,即Person
对象,这意味着他们可以拥有自己的itemscope
属性,并有关于他们的附加信息,如出生日期、地址、电话号码、国籍等。
搜索引擎或其他应用可以解析这一段落,并能够找出书名的开始和结束以及作者是谁。这只是一个尝试,但是您现在应该对微数据有所了解了。这个概念相当简单:键和值用于为页面上的内容提供详细的元数据。
关于微数据有一些争议,因为它与之前存在的另外两种格式重叠,微格式和 RDFa 4 (属性中的资源描述框架)。此外,由于[
schema.org](http://schema.org)
是由几家在搜索引擎技术上有既得利益的大公司推出的,这个网站所提倡的语法的中立性受到了质疑。但是,这是熟悉注释页面内容的概念的一个很好的起点。
4 微数据其实是从 RDFa 进化而来的。
撤销管理器 API
撤消管理器 API 是一个新的接口,允许网页访问 web 浏览器中的撤消/重做事务管理器。这是什么意思?嗯,想想当你在网页浏览器的搜索栏中输入一些文本时;您可以选择编辑撤销来撤销您输入的文本。这是浏览器的原生撤销管理器,允许你撤销已经完成的操作。但是,如果您有一个交互式 web 应用,比如基于
canvas
的富文本编辑器或绘图应用,它是由 JavaScript 控制的,浏览器的本地撤销管理器不会获取脚本执行的修改。这就是撤销管理器 API 的用武之地,因为它提供了一种方法来将通过 JavaScript 执行的动作添加到浏览器的撤销/重做管理器中。正如历史 API 通过将 URL 添加到历史“堆栈”来操作一样,撤消管理器允许将页面的一组特定更改(统称为事务)添加到浏览器的本机撤消堆栈中。由于这一特性仍在开发中,所以不要指望它能在今天的浏览器中工作,但请记住,在不久的将来,它很可能会成为浏览器实现中的一项功能。
即将到来的 CSS 技术
如你所知,这并不是 HTML5 技术,但它与 HTML 携手并进。由于 HTML5 中有很多开发,所以 CSS3 中也有很多。一个有趣的领域与页面布局有关,这个领域的规范还没有完全合并成一个主导方法。最终,预计该领域将有三个主要规范:多栏布局(在第六章中讨论);灵活的框布局,或 FlexBox,它们提供了元素在其父容器中更好的对齐和定位;和网格/模板/区域布局。目前,网格/模板/区域布局被分为四个不同的规范,但这些规范在未来可能会合并。网格定位和网格布局用于处理行和列,并指定如何在这种布局中定位元素(多列布局可以被认为是无行网格布局)。模板布局也使用网格,但定义了一个模板,如“abc”或“bca”,其中每个字母对应一个元素,然后可以通过改变模板字母顺序来重新排列。最后,区域布局提出了一种方法,当内容溢出边界时,内容可以通过这种方法从一个元素“流向”另一个元素。CSS 中的定位一直是一个令人头痛的问题,特别是对于新手设计师来说,所以这些规范的新建议可能会像 HTML5 为 HTML 编码人员提供爆炸式增长的可能性一样,增强 CSS 编码人员的能力(很可能这两个人是同一个人,所以创造性的可能性可能会大大扩展!).
最后,值得注意的是选择器第 4 级规范中定义的一些新的选择器。 6 见表 8-1 中即将出现的新选择器列表及其用法说明。不要期望这些在不久的将来会起作用,但是将来这些很可能会得到你的首选浏览器的支持。
5 见[www.w3.org/TR/css3-flexbox/](http://www.w3.org/TR/css3-flexbox/)
。
6 见[
dev.w3.org/csswg/selectors4/](http://dev.w3.org/csswg/selectors4/)
。
总结
如果你假设事情的发展方向是错误的,并着手定义自己的新道路,那么预测未来是很容易的。这似乎是 HTML5 的发展方向。对 XHTML 的局限不再抱幻想的 Web 开发人员转向 HTML5,希望它能为他们提供新一代 web 应用所需的平台。既然它已经铺好了自己的前进道路,重塑网络的势头带来了全新的可能性。语法的灵活性,新的元素,多媒体和丰富的 JavaScript 和 CSS 特性。我们已经走了很多路,还有更多的路要走,但是现在我们已经走到了这段旅程的终点。让这成为你探索的开始。从这里你可以走很多路,比如深入研究移动开发、CSS3 或者广泛的 HTML5 APIs。规范的细节无疑将会发展,因为这是 Web 的本质,但是您在这里学到的基础知识和 HTML5 组件背后的概念将在相同的知识基础下继续,即使将来语法有所变化。是时候尝试这些技术了,看看在开放标准的世界里有什么是可能的。继续制作语义合理、内容丰富的页面。继续前进,让网络成为一个更好的地方。
九、相关技术
除了 CSS3,许多技术都与 HTML5 相关联,但实际上是在它们自己的规范中定义的。有些曾经是 HTML5 的一部分,但是随着它们的发展,已经被剥离到它们自己的文档中,而另一些则直接用于 HTML5,但是从来不是 HTML5 规范的一部分。本附录提供了这些通常与 HTML5 相关的不同技术的高级概述。此外,你还可以在本章末尾找到使用 HTML5 的有用网站资源列表。
地理位置
地理定位 API 1 定义了网络浏览器如何使用用户的 IP 或 WiFi 地址来确定用户的地理位置,如果用户在移动设备上,则使用设备上的全球定位系统(GPS)。位置以经纬度坐标的形式给出,根据检索位置时使用的方法,这可能更准确或更不准确。例如,用用户的 IP 地址来确定他们的位置远不如用基于卫星的 GPS 来确定他们的位置准确。
检索用户位置的含义非常有趣,因为它使得基于位置的服务能够通过 web 浏览器提供。这对于移动设备来说可能是最有用的,因为信息和广告可以通过移动设备上的网络浏览器传送。
检索用户位置的 JavaScript 方法保存在window.navigator.geolocation
内部的Geolocation
对象中。该位置可以检索一次,也可以连续更新。API 定义了三种方法:
getCurrentPosition()
:检索当前位置一次watchPosition()
:当当前位置改变时,检索并更新当前位置clearWatch()
:停止更新观察位置
当试图检索用户的位置时,网络浏览器通常会提示是否允许使用地理定位。
1 见【www.w3.org/TR/geolocation-API/】的。
检索当前位置
当成功获得位置时(如果使用 GPS,这可能需要几分钟),为函数调用的getCurrentPosition()
方法提供了一个参数。这里有一个例子:
function init() { // get the current position and call the “locatedSuccess" function when successful window.navigator.geolocation.getCurrentPosition(locatedSuccess); } function locatedSuccess(geo) { // log the returned Geoposition object console.log(geo); } window.onload = init;
注意根据所使用的网络浏览器,这段代码可能只能在一个活动的网络服务器上运行(在本地运行也可以)。如果不行,先检查页面的 URL 地址开头包含
http://
。
该脚本将尝试获取当前位置,并在获取后调用locatedSuccess()
函数。位置查询是异步完成的,以便其他流程可以继续在页面上运行。该函数被传递一个参数,该参数包含一个包含位置信息的Geoposition
对象。Geoposition
对象包含一个timestamp
属性和coords
属性,后者包含另一个对象,一个Coordinates
对象。Coordinate
对象包含以下属性,根据查看设备的硬件功能,这些属性可能包含null
值(例如,如果您的设备没有 GPS 功能,这些值将受到限制):
latitude
:地球上的南北位置longitude
:地球上的东西位置altitude
:位置的高度,如果观察装置具有测量高度的能力,则收集该高度accuracy
和altitudeAccuracy
:以米为单位测量的位置精度heading
:围绕一个圆以度数测量的行进方向speed
:在某一航向上行驶的速度,单位为米/秒
注意除了
timestamp
和coords
属性,Firefox 还包括一个address
属性,用于检索地址信息,如城市、国家,甚至街道信息!
更新前面的locatedSuccess()
函数,在屏幕上打印位置数据:
function locatedSuccess(geo) { var lat = geo.coords.latitude; var long = geo.coords.longitude; document.body.innerHTML = “<ul><li>lat:"+lat+"</li><li>long:"+long+"</li></ul>"; }
可以给getCurrentPosition()
方法一个额外的函数名,以指定在请求用户位置失败时运行的函数。编辑init()
代码并添加一个locatedFailed()
功能:
function init() { // get the current position //and call the “locatedSuccess" or “locatedFailed" if successful or not window.navigator.geolocation.getCurrentPosition (locatedSuccess, locatedFailed); } function locatedFailed(e) { // log the error code and message console.log(e.code , e.message); }
当无法获得位置时,locatedFailed()
功能将运行。传递给它的参数是一个包含错误代码和消息的PositionError
对象。以下是可能的错误:
- 错误代码 1,权限被拒绝:用户未授权使用地理定位。
- 错误代码 2,位置不可用:位置无法确定。
- 错误代码 3,位置检索超时:检索位置时间过长。
如果你想测试这个功能,最简单的方法就是拒绝浏览器的地理定位请求。根据浏览器以及您是否接受了之前的地理位置信息请求,浏览器会记住您的选择,不会再次询问您。例如,Google Chrome 将要求你点击地址栏右侧的“目标”图标,在那里你可以选择清除地理位置设置(需要重新加载页面以使设置生效)。图 A-1 显示了这个对话框的样子。对于 Firefox,地理定位权限位于工具页面信息下,这将打开一个对话框,显示当前正在查看的页面的信息。选择权限选项卡将允许您设置与特定页面共享位置信息的首选项。Safari 在浏览器偏好设置面板的“隐私”标签中设定了位置设置。如果您不确定在首选浏览器中的何处清除地理位置信息,请查看应用的偏好设置或地址栏的右侧,因为这些位置通常是地理位置权限设置的位置。
图 A-1。用于清除谷歌浏览器页面地理位置设置的对话框
最后,可以向getCurrentPosition()
方法传递一个自定义对象,该对象可用于设置检索位置时使用的各种选项。该对象可以用下列属性和值来设置:
enableHighAccuracy
:设定为true
或false
。如果启用,将使用精度最高的方法来确定位置,如 GPS。请注意,这将增加电池的使用和检索位置的时间长度。timeout
:检索位置时等待多长时间(以毫秒为单位),然后抛出位置不可用错误。maximumAge
:一个特定的位置应该被认为是当前位置多长时间(以毫秒为单位)。
这些选项可以使用速记对象创建符号添加到getCurrentPosition()
方法中,如下所示:
var options = { enableHighAccuracy: true, timeout: 120000, maximumAge: 1000 }; window.navigator.geolocation.getCurrentPosition(locatedSuccess, locatedFailed, options);
这将启用高精度定位(这取决于可用的硬件),将超时设置为两分钟,并将位置的最大年龄设置为一秒。
观察当前位置
对于固定设备(如台式计算机)来说,获得一次位置是可以的,但是对于移动设备来说,必须不断地检索位置才能准确。watchPosition()
方法用于不断轮询位置(这是最大年龄选项有用的地方)以更新位置信息。它采用与getCurrentPosition()
方法相同的参数,但是它应该被设置为一个变量,如果停止连续更新位置,这个变量可以在以后被引用并传递给clearWatch()
方法。这里有一个例子:
var geoWatchID = window.navigator.geolocation.watchPosition(locatedSuccess, locatedFailed, options);
在代码的后面,geoWatchID
可以作为参数传递给clearWatch()
以停止位置更新:
clearWatch(geoWatchID);
SVG 和 MathML
SVG 和 MathML 有两个完全不同的目的,但它们有一个共同点:它们都是基于 XML 的语言,可以嵌入到 HTML5 中。可缩放矢量图形(SVG)用于描述矢量形状,而数学标记语言(MathML)用于描述数学符号。
SVG 和canvas
是网络标准图像选项的一半。虽然canvas
很好地处理了位图,但是 SVG 很好地处理了矢量形状。它还具有内置的动画功能,这将需要在canvas
从头开始构建。
这两者的语法超出了本文的讨论范围,但是由于它们都是基于 XML 的,所以看起来非常像 HTML,只是元素集不同。例如,下面的代码显示了一个包含 MathML 和 SVG 的 HTML 页面,用于描述和绘制第七章中的三角函数。
*<!DOCTYPE html>* <html> <head> <meta charset="utf-8" /> <title>SVG and MathML Demo</title> </head> <body> <h1>SVG and MathML embedded in an HTML5 page</h1> <p> <math> <mi>x</mi> <mo>=</mo> <mrow> <msub><mi>x</mi><mn>1</mn></msub> <mo>+</mo> <mi>cos</mi> <mfenced><mi>θ</mi></mfenced> <mo>⁢</mo> <mi>c</mi> </mrow>
`
上述代码创建了图 A-2 中的符号和图表。
嵌入 HTML5 页面的 SVG 和 MathML
x = x【1】+cos(θ)c
y = y1+sin(μ)c
图 A-2。使用 HTML、MathML 和 SVG 从标记创建的图表
客户端存储
想象一下,一个 web 应用可以将用户处理过的数据保存在客户端数据库中,然后在用户在线连接时与基于服务器的数据库同步。这种离线功能对于改善应用的延迟非常有用,因为用户的数据不需要频繁地在网络上来回发送,而且在连接不稳定的情况下,例如在移动环境中,这种功能也很有帮助。
网络存储
Cookies 一直是在客户端浏览器上存储数据的方法。cookies 的一个问题是它们很小,每个只允许 4kb 的存储空间,这对于当今数据丰富的网页/应用来说是微不足道的。对此,新一代客户端存储解决方案应运而生。最稳定的解决方案,也是可以被视为 cookies 替代品的解决方案,是 Web 存储 API, 2 ,它允许高达 5 兆字节的存储空间。Web 存储实际上分为两个选项,localStorage
对象和sessionStorage
对象,它们都是window
对象的属性。两者的区别在于,存储在localStorage
中的数据是持久的,而存储在sessionStorage
中的数据在浏览器会话结束时(比如退出浏览器时)会丢失,但除此之外,它们的使用方式是相同的。每个只是一系列的键/值对,所以用一些数据设置一个键,然后用这个键在以后检索数据。
使用网络存储
使用网络存储非常简单。要向存储中添加数据,请使用以下语法之一:
window.localStorage.setItem("key","value"); window.localStorage["key"] = “value";
在这段代码中,“键”和“值”可以是任何文本字符串。要从存储中检索数据,请使用以下任一方法:
var val = window.localStorage.getItem("key"); var val = window.localStorage["key"];
要删除数据,请删除特定的密钥或清除整个存储:
window.localStorage.removeItem("key"); window.localStorage.clear();
网络存储示例
使用contenteditable
属性,您可以创建一个简单的文本编辑器来保存客户机上的更改。对于本例,创建一个名为edit.html
的新 HTML 文件,并用以下代码填充它:
<!DOCTYPE html>
`
`
2 见【http://dev.w3.org/html5/webstorage/】的。
现在创建一个名为script.js
的新 JavaScript 文件,并将它放在名为js
的目录中,该目录与edit.html
在同一个位置。用以下脚本填充它:
`var editable; // variable for editable area
// initialize the variables and add event handlers
function init()
{
editable = document.getElementById('editable');
var startEditBtn = document.getElementById('startEditBtn');
var stopEditBtn = document.getElementById('stopEditBtn');
var clearBtn = document.getElementById('clearBtn');
startEditBtn.onmousedown = startEdit;
stopEditBtn.onmousedown = stopEdit;
clearBtn.onmousedown = clear;
// update text with data in local storage
if (localStorage.getItem("savedtext")) editable.innerHTML =
localStorage.getItem("savedtext");
}
function startEdit()
{
// add the contenteditable attribute
editable.setAttribute("contenteditable", true);
}
function stopEdit()
{
// disable the contenteditable attribute
editable.setAttribute("contenteditable", false);
// save the text
localStorage.setItem("savedtext", editable.innerHTML);
}
function clear()
{
// clear the local storage
localStorage.clear();
// reload the page
window.location.href = “";
}
window.onload = init;`
在 web 浏览器中打开 HTML 页面,您将能够打开编辑功能(这将添加contenteditable
属性),保存编辑内容,并查看这些编辑内容,因为它们将存储在本地存储中(图 A-3 )。
图 A-3。使用本地存储的简单应用
其他存储选项
本地存储很容易使用,但这种方便也带来了它的功能限制。它确实无法与后端 web 服务器上的数据库相比,后者可能描述了存储数据之间的关系,并提供了确保数据完整性的方法。由于 web 技术正朝着支持创建 web 应用的方向发展,所以在客户端拥有一个功能完备的数据库是一个理想的选择。一个这样的选项是 Web SQL,它本质上将一个 SQLite 3 数据库嵌入到 Web 浏览器中。这意味着结构化查询语言 (SQL)命令可以直接从 JavaScript 中使用。相当酷!不幸的是,Web SQL 的未来变得相当暗淡,因为关于将 SQLite 用作嵌入式数据库的标准化的分歧导致 W3C 放弃了对该倡议的支持。正因为如此,Mozilla 已经表示将放弃对 Firefox 的支持,这意味着这种支持是不稳定的,不可靠的。太糟糕了。
另一个选项是索引数据库 API, 4 ,也称为 IndexedDB,目前仅在 Firefox 中受支持,但其他主流 web 浏览器也计划提供支持。这种数据库解决方案存储键/值对,就像 web 存储一样,但包括更复杂的功能,例如用于确保数据成功提交到数据库的事务,这有助于保证数据完整性。IndexedDB 不像 web SQL 那样复杂(它不是关系数据库),但它比 Web 存储更强大,并且看起来它将成为未来处理比 Web 存储所能容纳的更复杂的客户端数据存储的选项。
网络工作者
网络工作者正在让网络上的计算密集型任务变得不那么痛苦。JavaScript 是一种单线程语言,这意味着一个占用大量处理能力的脚本可能会完全瘫痪任何可能正在运行的用户交互脚本。使用 web worker,可以生成一个新的线程来运行脚本,而不会中断主脚本中 UI 交互或其他事件的处理。Web 工作者分为两种类型:专用工作者和共享工作者。共享工作者比专用工作者更强大,因为他们可以与多个脚本通信,而专用工作者只对最初产生它的脚本做出响应。
3 见【http://sqlite.org】的。
4 见【www.w3.org/TR/IndexedDB/】的。
网络套接字 API
网络套接字 API 5 是定义用于提供与远程主机的双向通信的协议的规范。网络的根源传统上基本上是单向的。服务器向客户端 web 浏览器发送一个页面,然后两者之间什么也不发生,直到用户单击一个链接并请求另一个页面。web 套接字提供的是一个开放的连接,在页面加载后,可以随时通过这个连接将数据从客户端发送到服务器,反之亦然。例如,这可以用于创建多人在线游戏或应用,因为数据可以从一个客户端发送到服务器,并分发到连接到同一服务器的所有其他客户端。
视频会议和点对点通信
一个为两种浏览器之间的视频会议创建规范的项目正在进行中。这是 W3C HTML5 和 WHATWG HTML 规范之间的主要区别,因为它包含在 WHATWG 版本中,但在 W3C 规范中被省略了。相反,W3C 有一个单独的规范,名为“WebRTC 1.0:浏览器间的 Web 实时通信”由于这两个规范都处于草案状态,不难想象,包含在 WHATWG HTML 草案中的版本很可能在未来被分离出来成为一个单独的规范,就像 W3C 所发生的那样。
无论如何,撇开管理问题不谈,实现视频会议的实际技术需要两个独立的网络浏览器收集视频和音频,并通过对等连接相互传输。具体来说,需要执行以下步骤:
- 访问网络摄像头或其他视频/音频输入设备。
- 在本地录制视频/音频,以便将其流式传输到远程 web 浏览器。
- 连接并将视频/音频发送到远程 web 浏览器。
- 在本地和远程 web 浏览器的
video
或audio
元素中显示视频/音频流。
一个名为 Stream API 的 API 定义了一个名为MediaStream
的接口,它将与 JavaScript 一起使用来处理流媒体的解析和显示。就发送媒体流而言,将使用另一个 API,称为对等连接 API。这个 API 描述了一个PeerConnection
JavaScript 接口,它定义了连接和发送媒体流到远程对等点的方法。
5 见【http://dev.w3.org/html5/websockets/】的。
6 见【http://dev.w3.org/2011/webrtc/editor/webrtc.html.】??
围空
WAI-ARIA, 7 《可访问的富互联网应用规范》(WAI 代表 Web Accessibility Initiative),旨在提供一种语法,使残疾人可以访问现代动态 Web 应用。WAI-ARIA 使用属性来标记页面内容的用户交互,并描述页面元素之间的相互关系。WAI-ARIA 定义了一个role
属性,它有一大组值,用于描述一个 web 特性是如何呈现的,以及页面是如何构造的。还有大量的“aria-
*”前缀属性用于描述网页特征的状态。这些属性可用于注释,例如,菜单是否有子菜单,或者被拖动的对象放在目标上时可以执行什么操作。WAI-ARIA 规范本身就是一个很大的规范;欲了解更多信息,请访问 WAI 的 WAI-ARIA 概况页面:[www.w3.org/WAI/intro/aria](http://www.w3.org/WAI/intro/aria)
。
文件 API
有三个正在开发中的规范与客户端机器的文件系统上的文件的读取、浏览和写入相关。主要的一个是文件 API, 8 ,它包括名为File
、FileList
、FileReader
和FileError
的接口,这些接口定义了一些方法,例如,可以用来读取文件或文件组的名称和最后修改日期。该规范还定义了一个与原始二进制数据接口的Blob
,可以对其大小和类型进行检查,并将其分割成块。文件 API 通过 web 表单的文件输入类型,甚至通过将文件从用户系统拖放到 web 浏览器窗口来处理可能出现在 web 浏览器中的文件。扩展文件 API 的是目录和系统 9 和 Writer10API。目录和系统描述了与用户的本地文件系统直接交互的方法。显然,这涉及到安全问题,所以公开的文件系统是沙箱化的,这样 web 应用就不能不受限制地入侵用户的计算机。编写器 API 完成了您所期望的工作;它定义了如何将文件或原始数据块写入文件系统。它还定义了一个FileWriterSync
接口,用于与 Web Workers API 一起编写文件。
有用的网络资源
使用 HTML5 进行开发时,您可能会发现以下网站资源非常有用:
- W3C 的 HTML5 规范工作草案 :
[
w3.org/TR/html5/](http://w3.org/TR/html5/)
- WHATWG“live”HTML 规范 :
[www.whatwg.org/specs/web-apps/current-work/](http://www.whatwg.org/specs/web-apps/current-work/)
- Html5.org:包括一个 HTML5 验证器和一个跟踪 WHATWG 规范变化的跟踪器:
[
html5.org](http://html5.org)
- Html5rocks.com:包括一个完全用 HTML5 技术制作的在线代码编辑器游乐场和幻灯片演示:
[
html5rocks.com](http://html5rocks.com)
7 见【www.w3.org/TR/wai-aria/】的。
8 见【www.w3.org/TR/FileAPI/】??。
9 见【www.w3.org/TR/file-system-api/】的。
10 见【www.w3.org/TR/file-writer-api/】的。
- Html5doctor.com:包含关于 HTML5 的翔实文章以及全面的元素参考;
[
html5doctor.com](http://html5doctor.com)
** Caniuse.com:html 5、CSS3 及相关技术的兼容表:[
caniuse.com](http://caniuse.com)
* Html5test.com:html 5 及相关功能支持的浏览器评分:[
html5test.com](http://html5test.com)
* CSS3 选择器测试:支持多种 CSS 选择器的浏览器测试:[
www.css3.info/selectors-test/](http://www.css3.info/selectors-test/)
* Mobilehtml5.org:手机和平板浏览器的 HTML5 特性兼容表:[
mobilehtml5.org](http://mobilehtml5.org)
* HTML5boilerplate.com:html 5 页面的起始模板:[
html5boilerplate.com](http://html5boilerplate.com)
* Modernizr :测试浏览器对 HTML5、CSS3 及相关特性支持的 JavaScript 库:[
modernizr.com/](http://modernizr.com/)
* 谷歌 Chrome 框架:在旧浏览器中启用现代网络技术功能的方法:[
code.google.com/chrome/chromeframe/](http://code.google.com/chrome/chromeframe/)
* Html5pattern.com:web 表单中客户端验证的正则表达式模式:[
html5pattern.com](http://html5pattern.com)
* Mozilla Developer Network(MDN):关于 HTML5 和其他 web 技术的优秀且易于掌握的资源:[
developer.mozilla.org/en/HTML/HTML5/](https://developer.mozilla.org/en/HTML/HTML5/)
* Html5gallery.com:使用 HTML5 技术的网站展示:[
html5gallery.com](http://html5gallery.com)
* Mediaqueri.es :使用媒体查询的网站展示:[
mediaqueri.es](http://mediaqueri.es)
*
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战