第1章 基础知识

人类天生好奇,喜欢摆弄东西。那天在办公室收到了新的遥控四轴飞行器Parrot AR Drone,我们都没看说明书,就开始动手组装起来。我们喜欢自己琢磨,喜欢按照自己认为的事物运作方式来思考和解决问题。零件只要能拼上就行,除非拼不上了,或者事实跟我们的想法相背离,才不得已去翻翻安装指南。

学习层叠样式表(CSS,cascading style sheets)最好的方法也一样,就是不管三七二十一,直接上手写代码。事实上,可能很多人都是这么学习编程的。比如在某个博客上看到了一些建议,又比如通过开源代码库研究自己喜欢的设计师创造的某个特效的源代码。几乎没人是在完整地看了一遍规范全文之后才开始动手写代码的,因为那样的话,人也早睡着了。

自己动手写代码是最好的开始方式,只是如果不够细心,可能会误解某个重要概念,或者给以后写代码埋下隐患。我们对此深有体会,因为自己给自己挖坑的事儿已经干了不是一回两回了。因此,这一章就来回顾一些基础但容易误解的概念,讲一讲怎么让HTML和CSS保持组织分明且结构良好。

本章内容:

 可维护性的重要意义

 HTML和CSS的不同版本

 未来友好的代码与向后兼容的代码

 使用新的HTML5元素为HTML赋予意义

 在HTML中为添加样式设置恰当的接入点

 通过ARIA、微格式、微数据扩展HTML的语义

 浏览器引擎模式与验证

1.1 组织代码

人们通常不会注意到建筑物的基础。可是,没有坚实的基础,建筑物的主体就不可能稳固。虽然本书讲的是CSS的技术和概念,但这些内容的讲解与实现必须以结构良好且有效的HTML文档为前提。

本节会介绍结构良好且有意义的HTML文档对基于标准的Web开发的重要性,以及如何让文档更有意义、更灵活,从而减轻开发者的工作负担。但首先我们要讲一个对任何语言都同样重要的概念。

1.1.1 可维护性

可维护性可以说是所有优秀代码最重要的特点。如果你的代码已经看不出结构,变得难以阅读,那么很多问题就会接踵而至。开发新功能、修复bug、提升性能,所有这些操作都会因为代码可读性差以及代码脆弱而变得复杂,而且结果难料。这最终会导致开发人员一点代码也不敢改,因为每次只要改一点,就会出问题。于是,没人愿意再维护这个网站,更糟糕的情况是只能走严格的变更控制流程,每周发布一次,甚至每月才能发布一次!

如果你开发的网站最终要交付给客户或者另一个开发团队,那么可维护性就更重要了。这时候,代码是否容易看懂,是否意图明确,是否为将来的修改做过优化,都至关重要。哪个项目没有持续不断的变更需求?哪个项目不需要一直开发新功能?哪个项目不需要不断修复bug?因此,“唯一不变的就是变化”。

相对来说,CSS是随着代码量增加而最难保持可维护性的语言之一。即使网站规模不大,样式表也会很快变得难以控制。现代编程语言都有内置的变量、函数和命名空间等特性,这些特性都有利于保持代码的结构和模块化。这些特性CSS都没有,所以要按照使用这种语言和组织代码的特殊方式来管理它。本书后面在讨论各种主题时,大都会涉及可维护性。

1.1.2 HTML简史

Web的威力源自其普适性。即使残障用户也能使用是题中应有之义。

——Tim Berners-Lee

Tim Berners-Lee在1990年发明了HTML,当时是为了规范科研文档的格式。HTML是一种简单的标记语言,为文本赋予了基本的结构和意义,比如标题、列表、定义等。这些文档通常没有什么装饰性的元素,可以方便地通过计算机来检索,而人类可以使用文本终端、Web浏览器,或者必要时使用屏幕阅读器来阅读它们。

然而,人类是视觉发达的生物。随着万维网被越来越多的人所接受,HTML也逐渐增加了对展示效果的支持。除了用标题元素标记文档标题,还可以使用粗体标签和不同的字体来创建特殊的视觉效果。本来用于展示数据的表格(table),却成了页面布局的手段;块引用(blockquote)也经常被用来缩进文本,而不是只用来标记引文。HTML很快就偏离了为内容赋予结构和意义的初衷,变成了一堆字体和表格标签。Web设计者给这种标记起了个名字,叫“标签汤”(见图1-1)。

图像说明文字

正当Web变得一团糟之际,CSS作为解决方案面世了。CSS的初衷是把跟HTML混在一起的表现性标记提取出来,使其自成体系,达到结构与表现分离的目的。这就让有意义的标签或者说语义悄悄返回了HTML文档。font之类的表现性标签可以不用了,而表格布局也可以被逐步取代。对大多数网站而言,CSS都能提升其可访问性和加载速度。不仅如此,CSS还给Web设计和开发人员带来了更多好处:

 一种专用于控制视觉样式和布局的语言;

 在同一网站中更易于重用的样式;

 通过关注点分离得到了良好的代码结构。

关注点分离

关注点分离(separation of concerns)是软件开发行业的一个常见概念。对Web开发而言,关注点分离不仅适用于标记和样式,同样也适用于编写样式的方式。事实上,关注点分离也是确保代码可维护性的一种主要方法。

Unix开发社区有一句话很好地诠释了关注点分离的思想,即“分成小块,松散结合”(small pieces, loosely joined)。其中,每一“小块”都是一个模块,专注于做好一件事。而且,因为这个模块跟其他组件是“松散结合”的,所以它可以方便地在系统的其他部分中重用。Unix中的一“小块”可能是一个字数统计函数,可以应用于传入的任何文本片段。而在Web开发中,这一“小块”可能就是一个商品列表组件,如果能做到“松散结合”,就可以在一个网站的多个页面或者在同一布局的不同区块中重用。

可以把代码中的这些“小块”想象成积木。每一块积木都很简单,但把很多块积木以不同方式组装起来,就可以创造出无比复杂的东西。第12章还会讨论这个话题,届时会讲一讲怎样以结构化的方式应用这个策略。

1. HTML和CSS的版本

CSS有很多版本,或者“级别”。了解这些版本产生的背景,有助于了解应该或不应该使用哪些CSS特性。万维网联盟(W3C,World Wide Web Consortium)是制定Web技术标准的组织,该组织制定的每一个规范要经历几个阶段之后才能成为W3C推荐标准。CSS1是在1996年底成为W3C推荐标准的,当时只包含字体、颜色和外边距等基本的属性。CSS2在1998年成为推荐标准,增加了浮动和定位等高级特性,此外还有子选择符、相邻选择符和通用选择符等新选择符。

相比之下,CSS3则采用了完全不同的模式。实际上不存在所谓的CSS3规范,因为CSS3指的是一系列级别独立的模块。如果规范模块是对之前CSS概念的改进,那就从3级(level 3)开始命名。如果不是改进,而是一种全新的技术,那就从1级(level 1)开始命名。而我们所提到的CSS3,则是指所有足够新的CSS规范模块,比如CSS Backgrounds and Borders Level 3、 Selectors Level 4和CSS Grid Layout Level 1。这种模块化的方式可以让不同的规范有自己的演进速度。有些3级规范,比如CSS Color Level 3,已经成为推荐标准。而另外一些可能还处于候选推荐阶段,很多甚至还处于工作草案阶段。

虽然CSS3的制定工作在CSS2发布后就开始了,但这些新规范一开始的制定速度很缓慢。为此,W3C在2002年发布了CSS2 Revision 1。CSS 2.1修正了CSS2中的一些错误,删掉了支持度不高或者并非所有浏览器都实现了的一些特性,总体来说就是把CSS规范做了一番清理,好为浏览器实现提供更精准的蓝图。CSS 2.1在2011年6月成为推荐标准,此时距离CSS3启动已经有10多年了。由此可见,标准制定主体和浏览器开发商为了确保相应的特性得以原原本本地实现,需要花多么长的时间。不过,浏览器开发商经常会在标准还处于草案阶段时,就发布一些实验性的实现。这样,等到了候选推荐阶段,相应的实现就已经非常稳定了。换句话说,很多CSS特性早在相应模块成为推荐标准前就可以使用了。

HTML的历史也很复杂。HTML 4.01在1999年成为推荐标准,与此同时W3C也把注意力转向了XHTML 1.0。本来接着要发布XHTML 1.1,但其严格程度在实践中暴露了无法落地的问题,最终被Web开发社区抛弃。于是,这个Web主要语言的发展停滞了。

2004年,有几家公司共同组建了Web超文本应用技术工作组(WHATWG,Web Hypertext Application Technology Working Group),并致力于开发新的规范。2006年,W3C肯定了它们工作的必要性,并欣然加入该工作组。2009年,W3C完全放弃XHTML,正式接纳WHATWG制定的新标准,这就是后来的HTML5。起初,WHATWG和W3C都基于标准调整自己的工作,但后来它们的关系又变得复杂起来。今天,它们分别在编辑两份标准。WHATWG那份就叫HTML,而W3C那份则称为HTML5。没错,这种分裂确实不好。但万幸的是,这两份标准的内容相当接近,因此只讲HTML5是没有问题的。

2. 应该使用哪个版本

设计者和开发者经常问的一个问题就是应该使用HTML或CSS的哪个版本。这个问题不好回答。虽然规范反映了标准和Web技术开发的进度和焦点,但它其实跟设计者和开发者日常的工作关系不大。真正重要的是知道HTML和CSS的哪些部分已经在浏览器中实现了,以及这些实现是否稳健,有没有bug。比如,浏览器提供的这些特性是不是实验性特性,使用时需谨慎?或者,这些特性到底靠不靠谱,是不是已经得到了多数浏览器的支持?

今天,使用CSS和HTML就要了解浏览器对其中特性的支持程度。有时候我们会觉得技术发展太快,必须拼命追赶才不会落伍;有时候我们又会觉得技术发展太慢。本书会随时提示不同HTML和CSS特性的浏览器支持情况,还会给出在什么情况下可以使用它们的建议。不过显而易见的是,印在纸上的东西注定会过时,所以你得学会自己更新这方面的信息。

要了解浏览器支持情况,推荐几个不错的地方。对于CSS属性,可以访问“Can I use”网站(https://caniuse.com)。这个网站可以搜索属性或属性组,结果配有统计信息,显示支持它们的浏览器百分比,包括桌面浏览器和移动浏览器。另一个非常有想法的项目是https://webplatform. github.io/,是W3C和几家浏览器厂商及行业巨头合作搞出来的,目标是收集合并它们所有关于CSS、HTML、JavaScript API等支持情况的文档。不过,就跟很多大型项目一样,最终要完成那么庞大的Web技术文档的聚合,需要花很长时间。此外,Mozilla的开发者文档,即MDN,也是一个非常好的参考。

说到浏览器支持,关键要明白,并非所有浏览器都一样,实际上从来就没有完全一样的浏览器。某些CSS3特性只得到了少数浏览器的支持。比如,Internet Explorer 11和Safari 6.1之前的版本都没有正确支持Flexbox(Flexible Box Layout)。不过,就算需要支持老版本的浏览器,也不意味着不能使用Flexbox。核心布局上可以不用Flexbox,但对于某些特定的组件,Flexbox可能就非常合适。只要为不支持它的浏览器准备好可以接受的后备代码就行了。判断一个人是不是CSS大师,很大程度上要看他能否游刃有余地处理向后兼容的代码与未来友好的代码。

1.1.3 渐进增强

平衡向后兼容性与最新的HTML和CSS特性,涉及一种叫作渐进增强(progressive enhancement)的策略。所谓渐进增强,大意就是“首先为最小公分母准备可用的内容,然后再为支持新特性的浏览器添加更多交互优化”。使用渐进增强策略,意味着代码要分层,每一层增强代码都只会在相应特性被支持或被认为适当的情况下应用。听起来有点复杂,而实际上HTML和CSS的实现已经部分内置了这一策略。

对HTML而言,这意味着浏览器在遇到未知元素或属性时并不会报错,而且也不会对页面产生什么影响。比如,可以在页面里使用HTML5定义的新input元素。假设表单中有一个电子邮件字段的标记如下:

<input type="text" id="field-email" name="field-email">

要使用新的input元素,应该把type属性改成这样:

<input type="email" id="field-email" name="field-email">

尚未实现这个新字段类型的浏览器碰到它只会想:“这是啥意思呀?不明白。”然后回退为默认的text类型,结果和上面的第一行代码一样。而实现了这个类型的新浏览器则知道email想让用户在这里填写什么样的数据。而在很多移动设备中,相应的软键盘还会针对输入电子邮件地址调整界面布局。假如你还在这里使用了内置的表单验证,那么支持它的新浏览器也会帮你做验证。这样,我们既渐进增强了页面,也不会对旧版本浏览器产生不好的影响。

另外一个简单的变化就是,HTML5把文档类型声明更新为新的简短形式。所谓文档类型,就是位于HTML文档第一行的代码,供机器识别当前文档使用的标记语言版本。以往的HTML和XHTML版本中,这行代码很长很复杂,但在HTML5中,它已经简化成了下面这样:

<!DOCTYPE html>

今后,只要这样声明HTML文档类型就好了,因为这个HTML5语法的doctype是向后兼容的。后面几节我们会再介绍一些HTML5中出现的新元素,但如果要更深入地学习如何使用HTML5标记,建议看一看Jeremy Keith的HTML5 for Web Designers。

CSS中的渐进增强同样也反映在浏览器如何对待新属性上。任何浏览器无法识别的属性或值都会导致浏览器丢弃相应的声明。因此,只要同时提供合理的后备声明,使用新属性就不会带来不良后果。

举个例子,很多现代浏览器支持以rgba函数方式表示的颜色值。这种方式可以分别传入红、绿、蓝通道,以及阿尔法(alpha,即透明度)通道的值。我们可以这样使用它:

.overlay { 
  background-color: #000;
  background-color: rgba(0, 0, 0, 0.8);
}

这条规则定义了类名为overlay的元素背景为黑色,但随后又用rgba声明背景色应稍微透明。如果浏览器不支持rgba,那么相应元素的背景色就是不透明的黑色。如果浏览器支持rgba,那么第二条声明就会覆盖第一条。也就是说,即使并非所有浏览器都支持rgba,我们也可以使用它,只是要先为它声明合适的后备代码。

1. 厂商前缀

浏览器厂商也基于相同的原理为自家浏览器引入实验性特性。实验性特性的标准名称前面会加上一个特殊字符串,这样他们自己的浏览器就能识别该特性,而其他浏览器则会忽略该特性。有了这个方案,浏览器厂商就可以添加规范中没有或者尚不成熟的新特性,样式表作者也可以安心地试用这些新属性,不用担心浏览器因不认识它们而破坏页面。比如:

.myThing { 
  -webkit-transform: translate(0, 10px);
  -moz-transform: translate(0, 10px);
  -ms-transform: translate(0, 10px);
  transform: translate(0, 10px);
}

这里使用了几个不同的前缀,给相应的元素应用了变换(第10章会介绍)。以-webkit-开头的适用于基于WebKit的浏览器,如Safari。Chrome和Opera都基于Blink引擎,而Blink最初也是基于WebKit开发的,所以-webkit-前缀通常也适用于这3个浏览器。-moz-前缀适用于基于Mozilla的浏览器,如Firefox。-ms-前缀则适用于微软的Internet Explorer。

最后我们又加了一条不带前缀的声明,这样那些支持标准属性名称的浏览器就不会漏网了。过去经常出现开发人员漏加不带前缀的标准声明的情况。为此,有些浏览器厂商也开始支持竞争对手引擎特定的前缀,以便让流行的网站能在自己的浏览器上打开。但这样做也造成了混乱,于是多数浏览器厂商抛弃了厂商前缀。那实验性特性呢?有的厂商选择把它们隐藏在chrome://flags中,有的选择只在特定的预览版中提供。

本书中绝大多数示例都使用不带前缀的标准属性名称,因此建议大家经常查一查http://caniuse. com,以了解相应的支持情况。

2. 条件规则与检测脚本

如果希望根据浏览器是否支持某个CSS特性来提供完全不同的样式,那么可以选择@supports块。这个特殊的代码块称为条件规则,它会检测括号中的声明,并且仅在浏览器支持该声明的情况下,才会应用块中的规则:

@supports (display: grid) { 
  /* 在支持网格布局的浏览器中要应用的规则 */
}

条件规则的问题是其自身也很新,只能将它应用于新的浏览器中,因为旧版本浏览器不支持(比如第7章要介绍的网格布局)。此外,还可以通过JavaScript来检测支持情况,比如使用Modernizr这个库。Modernizr的原理是为HTML添加支持提示信息,然后可以依据这些信息来编写CSS。

随后几章还会更详细地介绍类似的策略和工具,这里关键是要知道:渐进增强可以让我们放下对版本号和规范的很多担忧。只要加点小心,就可以在适当的时候使用一些簇新的特性,同时又不会丢掉使用旧版浏览器的用户。

1.2 创建结构化、语义化富HTML

语义化标记是优秀HTML文档的基础。语义就是以系统方式表示的含义。对于根据一个形式符号的集合人工创造出的语言(比如HTML语言,及其元素和属性)来说,语义指的就是通过使用某个符号想要表示的含义。简而言之,语义化标记意味着在正确的地方使用正确的元素,从而得到有意义的文档。

有意义的文档可以确保尽可能多的人都能够使用,无论他们用的是最新版本的Chrome,还是Lynx这样只能处理文本的浏览器,甚至是屏幕阅读器或盲文点触设备之类的辅助技术。无论将来项目中会增加多么花哨的图形或交互,文档的基础语义都应该永远——而且必须永远——不打折扣。

结构良好的标记也意味着内容更对机器的胃口。机器?对,特别是Googlebot这种搜索引擎爬虫,对它胃口的内容可以让你的页面在Google搜索结果中排名更靠前。这是因为,Googlebot从你的页面中获得的相关数据越多,它对你的页面的索引和排名可能就越准确。于是,你的页面在搜索结果中出现的位置就可能更靠前。

对于CSS来说更重要的是,有意义的标记本身为添加样式提供了方便。这些标记不仅描述了文档的结构,而且还为我们继续装扮它提供了底层的框架。

实际上,编写CSS的最新实践都建议先给网站一组“基础”样式。图1-2是Paul Lloyd的样式指南,包括他个人博客中可能用到的所有元素的样式说明。每个元素都有使用的方式和情境。他的样式表可以确保,无论以后他给页面添加什么元素,都无须再另写样式。

图像说明文字

Paul的样式指南包含所有语义明确的元素,比如:

 h1、h2等

 p、ul、ol和dl

 strong和em

 blockquote和cite

 pre和code

 time、figcaption和caption

其中还包括表单、表格及其相关元素的基础样式,比如:

 fieldset、legend和label

 caption、thead、tbody和tfoot

设立这么一套基础样式的价值非常之大。虽然实际设计和开发中,它们很快会被继承和覆盖,但有了这么一套基础样式,将来的工作就会有条不紊。这套样式也可以作为校准样式来使用。在不断修改CSS的过程中,可以时不时对照一下样式指南中的组件,检查自己是否无意中覆盖了某些不该覆盖的样式。

1.2.1 ID和class属性

有意义的元素提供了不错的基础,却没有提供应用视觉效果所必需的全部“接入点”。我们几乎总是要根据上下文来调整基础元素的样式。除了元素本身,我们还需要一种方式把样式“接入”到文档上,这就是ID和class属性。

为元素添加ID和class属性不一定能给文档增加含义或结构。这两个属性只是一种让其他因素来操作与解析文档的通用手段,CSS也可以利用这一手段。我们可以设置这些属性的值,即为其起名字。

给属性起名字听起来简单,但在写代码时却是极其重要的(常常也是最难的)。“名不正则言不顺”,起什么名字意味着它是什么,或者应该怎么使用它。我们知道,写代码的时候,清晰和明确都是至关重要的原则。下面就以一个链接列表为例,看看怎么给它的class属性一个既容易辨识又好用的值:

<ul class="product-list"> 
  <li><a href="/product/1">Product 1</a></li>
  <li><a href="/product/2">Product 2</a></li>
  <li><a href="/product/3">Product 3</a></li>
</ul>

我们先利用class属性在文档中创造一个product-list模块。在CSS里,我们用类名来定义一类事物。这里的product-list就意味着它可以是任何商品列表。换句话说,为product-list写好样式后,不仅可以用在这里,还可以用在网站的其他地方,就像蓝图或者模板一样可以重用。

给元素添加类名时,即使类名明确用于样式,也不要体现出其视觉效果(第12章会详细讨论这一点,包括什么情况下类名可以体现视觉效果)。正确的做法是让类名表示组件的类型。比如,这里的类名是product-list,而非泛泛的large-centered-list。

前面的例子只给元素添加了class属性,并没有添加ID属性。对于添加样式而言,ID与class属性有一些重要的区别,但针对这个例子而言,最主要的区别是一个ID只能应用到页面中的一个元素。也就是说,不能像product-list那样使用ID把页面中的模块定义为可重用的“模板”。如果使用了ID,那么相应的product-list在每个页面中只能出现一次。

本书提倡使用ID来标识特定模块的特定实例。比如,下面就是product-list模块的一个特定实例:

<ul id="primary-product-list" class="product-list">
  <li><a href="/product/1">Product 1</a></li> 
  <li><a href="/product/2">Product 2</a></li>
  <li><a href="/product/3">Product 3</a></li>
</ul>

这是product-list的另外一个实例,它因为有同样的class属性而获得了相应的样式。但在这里,这个实例也被ID定义为primary-product-list。每个页面通常只能有一个主要商品的列表,因此这个ID值还是比较恰当的。利用这个ID,可以为这个模块实例添加额外的样式,可以增加一些JavaScript交互,还可以作为页内导航的目标。

实际开发中,一般不建议把ID属性作为CSS的“接入点”。利用类来添加样式往往能够让代码更简单也更容易维护。ID可以用于在文档中标识元素,但通常不用于添加样式。第12章将详细介绍这方面内容。

1.2.2 结构化元素

HTML5新增了一批结构化元素:

 section

 header

 footer

 nav

 article

 aside

 main

增加这些新元素是为了在HTML文档中创建逻辑性区块。它们可以用于包含独立内容(article)、导航组件(nav)、特定区块的头部(header),等等。其中,main元素是最新增加的,用于高亮页面中包含主要内容的区域。关于如何正确使用这些新元素,建议看看这个网站:http://html5doctor.com。

除了main之外,所有其他新元素都可以在一个文档中多次出现,以便让机器和人更好地理解文档。在HTML5引入这些新元素以前,我们经常能够看到带有类似类名的div元素,比如下面这篇博客文章的标记:

<div class="article"> 
  <div class="header">
    <h1>How I became a CSS Master</h1>
  </div>
  <p>Ten-thousand hours.</p>
</div>

其中的div元素对文档而言并没有语义价值,只是借助类名作为添加样式的“接入点”而已。这段代码中只有h1和p是有含义的。现在有了HTML5的新元素,这段标记可以改写成这样:

<article>
  <header>
    <h1>How I became a CSS Master</h1>
  </header>
  <p>Ten-thousand hours.</p>
</article>

经过修改,这段HTML的语义得到了增强,但同时也产生了意外的副作用。此时,我们只能通过article和header元素来添加样式了。添加样式的CSS选择符可能会是这样:

article {
  /* 样式 */
}
article header {
  /* 其他样式 */
}

但article和header都可能在同一个页面中多次出现,不一定是展示博文内容。如果确实存在这种重用的情况,而我们又通过元素选择符直接把样式绑定到了元素上,那么本来给博客文章应用的样式也会被应用到其他相同的元素上,不管合不合适。此时,更灵活也更有远见的做法是把这两个例子结合起来:

<article class="post">
  <header class="post-header">
    <h1>How I became a CSS Master</h1>
  </header>
  <p>Ten-thousand hours.</p>
</article>

相应的CSS规则就可以使用类名为这段标记应用样式了:

.post {
  /* 样式 */
}
.post-header {
  /* 其他样式 */
}

以上变化反映出了一个非常重要的概念,那就是,我们已经解耦了文档的语义与为文档添加样式的方式,从而让文档更便于移植、更具有目的性,因此也更容易维护。假如我们有一天觉得article并不是包含内容的最合适元素,或者由于CMS系统的固有限制,必须把它替换成div元素,那么只要改这一处就行了。因为样式是通过类名来应用的,所以无论出于什么原因修改了标签,都不会影响样式。

旧版本IE与新元素

在多数浏览器中使用新元素都没问题,只是IE8及更早的IE不会给自己不认识的元素应用样式。不过好在我们可以使用一个JavaScript“垫片”或“腻子”脚本来解决这个问题。

这里给大家推荐一个这样的脚本:https://github.com/aFarkas/html5shiv。

这个脚本其实也包含在前面推荐过的Modernizr库里面,后面几章也会用到这个库。

假如你有很多用户在使用旧版本的浏览器,那么在使用这些新元素的时候务必小心。这是因为,要确保一切正常,有可能需要引入上述JavaScript依赖才行。

1.2.3 div和span

既然有了新语义元素,那么为我们效力多年的div元素是否就多余了呢?不是的。在没有合适的语义元素的情况下,div仍然是给内容分组的一个不错的选择。有时候,我们会纯粹出于添加样式的目的而在文档中添加一个元素。比如,为实现居中布局而在整个页面外部包装一个元素。

如果有更具语义的结构化元素,那么务必使用它们,需要添加样式时再给它们一个适当的类名。但是,如果你只需要一个无语义的元素作为额外的样式接入点,那就使用div。以前有一个说法,叫作“div麻疹”,意思是有些人写的HTML里几乎全部都是div,也不管用得合不合适。因此,请确保只在额外提供样式接入的情况下才使用div,但也不要因为用了几个div就感到难为情。在后面介绍的几个具体的例子中,我们会看到,额外添加的无语义div元素对保证代码的清晰和可维护性非常重要。

与div元素类似的还有span。同样,在无须表示语义、仅需添加样式的情况中,可以使用span。与div不同,span是文本级元素,可以用于在文本流中建立结构。不过在使用无语义的span之前,也一样要确保真的不需要使用任何语义元素。比如,使用time标记时间和日期,使用q标记引用,使用em标记需要强调的内容,使用strong标记需要重点强调的内容:

<p>At <time datetime="20:07">7 minutes past eight</time> Harry shouted, <q>Can we just end
this, now!</q> He was <strong>very</strong> angry.</p>

1.2.4 重新定义的表现性文本元素

时至今日,<b><i>可以算是幸存的表现性标记了,它们以前分别用于将文本标记为粗体(bold)和斜体(italic)。你是不是以为新的HTML5规范会把它们剔除掉?没有,它们还在。这是因为,在旧有Web内容中,或者通过低水平WYSIWYG编辑器创建的内容中,这两个元素随处可见。HTML5的编辑最终决定保留它们,但改变了它们的含义。

今天,<i>元素用于标识与周围内容不一样的内容,一般在排版上会显示为斜体。HTML5规范中给出的例子包括另一种语言中的习语,以及一艘船的名字。

<b>元素的含义和<i>几乎一样,只是针对习惯上标记为粗体的内容。相关的例子包括商品或品类名。

这样的定义确实不够明确,但关键是要知道,这两个元素与<em><strong>的区别在于,它们没有任何强调自己所包含内容的意味。多数情况下,应该选择<em><strong>,因为它们是用来强调及重点强调内容的语义正确的选择。

1.2.5 扩展HTML语义

长时间以来,Web开发者一直在探索给HTML有限的词汇表添加新的语义和结构的方式。为内容添加更丰富的语义,对Web和基于其构建的工具而言意义重大。虽然建设语义Web王国任重而道远,但不管怎么说,还是有了实质性的进步。利用这些成果,HTML编写者可以为自己的文档添加更细粒度、更具表达性的语义。

1. ARIA的role属性

很多新的HTML5元素都考虑到了无障碍访问的场景。比如,如果屏幕阅读器能够理解页面中的nav元素,那么它就可以利用这个元素帮助用户定位到相应的内容,或者在必要时返回导航。

另一种实现这个目标的方式是利用无障碍富因特网应用(ARIA,accessible rich Internet application),它是对HTML规范的补充。ARIA为我们提供了针对辅助访问设备添加更多语义的手段,方式就是为文档中的不同元素指定其包含什么内容,或者说它们提供什么功能。比如,role="navigation"这个“地标角色”属性用于声明一个元素具有导航的角色。其他角色有:

 banner

 form

 main

 search

 complementary

 contentinfo

 application

完整的ARIA角色及其定义,参见ARIA规范。

我们再推荐一个关于如何使用无障碍角色的简要分析,Using WAI-ARIA Landmarks – 2013,作者是来自Paciello Group的Steve Faulkner。

ARIA还支持让开发人员指定更复杂的内容片段和界面元素。例如,在使用HTML创建一个音量滑动条部件时,应该包含值为slider的role属性:

<div id="volume-label">Volume</div> 
<div class="volume-rail">
  <a href="#" class="volume-handle" role="slider" aria-labelledby="volume-label"
  aria-valuemin="1" aria-valuemax="100" aria-valuenow="67" ></a>
</div>

属性aria-labelledby、aria-valuemin、aria-valuemax和aria-valuenow也分别提供了额外的信息,辅助阅读技术可以利用它们帮助残障用户使用这个滑动部件。

为HTML页面中的不同组件添加这些额外的语义属性,同样有助于为元素添加脚本和样式,是典型的多赢策略。

2. 微格式

目前最广泛采用的扩展HTML语义的方式是微格式。微格式是一组标准的命名约定和标记模式,可用于表示特定的数据类型。微格式的命名约定是基于vCard和iCalendar等已有的数据格式制定的。比如下面的联系人信息就是以hCard格式标记的:

<section class="h-card">
  <p><a class="u-url p-name" href="http://andybudd.com/">Andy Budd</a>
    <span class="p-org">Clearleft Ltd</span>
    <a class="u-email" href="mailto:info@andybudd.com">info@andybudd.com</a>
  </p>

  <p class="p-adr">
    <span class="p-locality">Brighton</span>,
    <span class="p-country-name">England</span>
  </p>
</section>

以微格式标记的联系人信息便于开发人员编写工具从中提取数据。比如可以编写一个浏览器插件,从你浏览的页面中发现微格式,然后让你把联系人信息下载到通讯录,或者把活动信息添加到你的日历应用。目前微格式支持的数据类型包括联系人、活动、菜谱、博文、简历,等等。微格式也可以用于表示关系,比如一段内容与链接到该内容的另一个URL之间的关系。

微格式得以流行的原因之一是容易实现,迄今已经被Yahoo!和Facebook等内容平台采用,而且已经直接添加到了WordPress和Drupal等内容发布工具中。2012年一项关于结构化数据实现的研究(http://microformats.org/2012/06/25/microformats-org-at-7)发现,微格式在Web上的应用最为广泛。不过最近微数据异军突起,也不容小觑。

3. 微数据

微数据是跟HTML5一起,作为给HTML添加结构化数据的另一种方式而推出的。它的目标与微格式非常相近,但在把微数据嵌入内容方面则有所不同。下面看一看用微数据标记同样的联系人信息会是什么样:

<section itemscope itemtype="http://schema.org/Person"> 
  <p><a itemprop="name" href="http://thatemil.com/">Emil Bjöklund</a></p>
    <span itemprop="affiliation" itemscope
    itemtype="http://schema.org/Organization">
      <span itemprop="name">inUse Experience AB</span>
    </span>
    <a itemprop="email" href="mailto:emil@thatemil.com">emil@thatemil.com</a>
  </p>
  <p itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
    <span class="addressLocality">Malm?</span>,
    <span class="addressCountry">Sweden</span>
  </p>
</section>

通过这个例子可以看出,微数据的语法比微格式要烦琐一些,不过这是有原因的。由于微数据设计的时候考虑到了可扩展性,它可以用来表示任意类型的数据。微数据只定义一些语法来表示数据结构,但自身并未定义任何词汇表。相反,微格式则定义了具体的结构化数据,比如hCard和hCalendar。

微数据把定义特定格式的事交给了使用者或第三方。上述例子中使用的格式就是由Bing、Google以及Yahoo!等搜索引擎共同创建的https://schema.org中的一个词汇表。这几家搜索引擎使用它来辅助索引和排名页面,当然搜索爬虫也会使用这些词汇表,从你的内容中更高效地提取丰富的信息。

1.2.6 验证

即使经过深思熟虑,你的标记已经非常语义化了,其中也仍然存在输入或者格式错误的风险。这些隐患会带来无法预料的麻烦。这时候就要使用验证了。

现实中的多数HTML文档并不是真正有效的HTML。用规范编写者的话说,就叫作“未遵行”(nonconformant)。这些文档中存在的问题有元素嵌套不对、包含未经编码的和号(&),以及缺少必要的属性等。浏览器对这类错误非常宽容,总会尝试猜测作者的意图。事实上,HTML规范中也包含了如何处理无效HTML的规定,以确保浏览器厂商以一致的方式处理错误。

总体来说,浏览器如此大度地帮我们处理错误是个好事,但不代表我们可以因此而放弃自己的职守。我们都应该尽力写出有效的HTML文档,这样有利于更快地查找问题,避免错误泛滥。假如你碰到一个渲染或布局上的bug,一时又找不出问题所在,最好先验证一下HTML,以保证你的样式应用到了格式正确的文档上。

验证HTML的工具有很多。比如,可以使用W3C网站上的HTML验证器(http://validator. w3.org/),或者与之相关的插件。其中的Web Developer扩展,Firefox、Opera和Chrome都支持。此外,如果你的项目有自动构建或测试环节,最好在其中加上HTML验证。

CSS也是可以验证的。W3C的CSS验证器地址是http://jigsaw.w3.org/css-validator/。你可能认为验证CSS没有验证HTML那么重要,毕竟CSS中的错误一般不会导致JavaScript出错,或者导致屏幕阅读器无法打开页面。但是,我们还是建议你重视CSS,保证其中没有什么低级错误,比如忘了写度量单位之类的。

根据你的CSS验证器设置而定,验证结果中可能包含很多关于厂商前缀的警告或错误。这些属性或值是浏览器厂商在实验性地支持某些CSS特性时使用的一种临时命名约定。比如,-webkit-flex这个display属性的值,就是标准flex属性值在WebKit浏览器上的实验性版本。这些地方虽然会被验证器标记为警告甚至错误,但你的文件依然能正常使用。总之,只要明白验证器给出的这些标记的真正含义就行了。

验证并不是最终裁决,很多本身很好的页面也会验证失败,这是由于使用了来自第三方或低水准CMS的内容,或者使用了试验性CSS特性。此外,验证器本身也可能会跟不上标准更新和浏览器实现的步伐。因此不要过于激进,只要把验证当作事先帮我们发现一些低级错误的手段即可。

1.3 小结

本章介绍了一些必要的基础知识,包括HTML和CSS的一些重要概念。我们回顾了HTML和CSS的一些历史,知道了如何跟进它们的发展进度,以及如何做到既向后兼容又对未来友好。经过本章的学习,相信大家已经明白了编写可维护代码的重要性,也掌握了构建HTML结构以便一致地应用CSS的方法。

下一章,我们会重温关于CSS选择符的基础知识,然后再介绍3级和4级规范中的高级选择符。相关知识点还包括特殊性、继承和层叠,以及如何在样式表中有效使用这些特性。

目录

  • 前言
  • 第1章 基础知识
  • 第2章 添加样式
  • 第3章 可见格式化模型
  • 第4章 网页排版
  • 第5章 漂亮的盒子
  • 第6章 内容布局
  • 第7章 页面布局与网格
  • 第8章 响应式Web设计与CSS
  • 第9章 表单与数据表
  • 第10章 变换、过渡与动画
  • 第11章 高级特效
  • 第12章 品控与流程