精彩绝伦的CSS(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-16 10:14:53

点击下载

作者:(美)Eric A. Meyer

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

精彩绝伦的CSS

精彩绝伦的CSS试读:

前言

CSS已经发展得非常成功了,甚至像HTML一样成功!尽管有时候它的确令人难以掌控。现在无论是在浏览器还是应用商店,甚至聊天客户端,CSS都无处不在并且没有任何消退的迹象。随着这门语言的应用愈加广泛,其能力也在不断增强。

本书涵盖了近100个使用CSS制作优秀网站的窍门、技术、工具以及一些技巧,其中每个部分都是相对独立的。你可以随便翻开一页就读,而不用担心会错过前面章节中的某些重要内容。本书假定你至少已经熟悉一点儿CSS和CSS的使用,这种假定的熟练程度可以描述为“进阶的初学者到中级的使用者”。所以,如果你一点儿也不了解CSS,或者知道的比那些撰写规范的人都多,那本书恐怕不适合你。如果你不是这两类人,本书还是有很多值得学习和享受的内容的。在本书的第一部分,我们对一些常用的工具和基本技术进行了介绍,其中还包括一些不那么好理解的CSS选择器。第二部分展示了CSS可以实现的各种各样的效果,其中包括一些好玩的特效、对同一目标的不同实现方式以及布局等。第三部分介绍的是前沿技术,这些技术或许你现在的项目里还用不到,但是随着时间的推移,它们一定会在你的工作中变得越来越重要。

请访问本书的网站www.wiley.com/go/smashingcss下载代码示例。

大约十年以前,你可能还以为CSS大限将至了。但是到了2010年,它却变得更加充满活力且比以往更加引人注目了。我希望你能享受这前后封皮之间的内容,就如同我写作此书时同样地享受。致谢

感谢Chris Webb拉我“入伙”并且耐心地忍受着我每一次的拖延和耽搁。有那么几次,项目看起来都要停掉了,Chris却总能以他良好的幽默感和无限的淡定督促项目前进。我非常尊敬他,而且我们还计划把某一年的暑假安排在一起,这样我们就可以坐在游泳池边一起举杯喝鸡尾酒了。

感谢Debbye Butler和Brian Herrmann在编辑、审稿期间对书稿的润色提升,并且详细指出了行文中的瑕疵,以及我含糊的解释。感谢我忠实的读者们,无论是实体书的读者还是网络上的读者,我都真心地感谢你们,非常感谢。

感谢我的妻子和女儿,对你们的谢意我无以言表。Eric A. Meyer2010年8月13日,俄亥俄州克利夫兰高地Part 1 第一部分 基本原理第1章 工具

无论做什么事情,工具都能起到很大的辅助作用,创建网页或者应用亦如此。对于CSS来说,既有可以帮助我们书写CSS的工具,也有使用CSS构建的工具来辅助我们进行开发。甚至有的工具可以使我们的浏览器支持更多原生并不支持的CSS特性,你不仅是开发者,还是个能工巧匠!相信本章介绍的这些工具绝对会撑爆你的工具箱。1.1 Firebug

Firebug(见图1-1)是任何网页开发人员的工具箱中不可或缺的两个工具之一(关于另外一个,请参考1.2节)。它是火狐浏览器(Firefox)中一个完全免费的扩展,如果用的是其他浏览器,也可以接着往下看,因为你一样可以使用Firebug!图1-1 Firebug主页

要想安装Firebug,可以在火狐浏览器中访问getfirebug.com,然后单击Install(安装)按钮(写作本书时,这个按钮就在网页的右上方)开始安装,安装完成后重启火狐浏览器即可。现在准备开始你的神奇之旅吧!

我没法在这么短的篇幅中讲解Firebug的全部功能。实际上,即使整整一章的篇幅都未必够用,我这里只讲一些重点。

图1-2中所示的HTML选项卡左侧展示的是文档结构,单击箭头可以展开或收缩文档的子结构。注意在该选项卡中,当把鼠标悬停在某个元素名上时,该元素会在页面中突出显示。最神奇的是,它还可以显示彩色区域并利用不同色彩展示元素的内边距(padding)和外边距(margin)。例如,本例中的内容区域为浅蓝色,内边距是淡紫色,外边距是浅黄色。具体是什么颜色其实不重要,关键是在页面上可以很直观地看到效果。图1-2 通过Firebug查看元素的布局(另见彩插图1-2)

在HTML选项卡的右侧,可以通过单击Style(样式)选项卡查看应用在当前元素上的CSS(见图1-3)。这里不仅包含你自己写的样式,还包含浏览器自身的内建样式。例如,你可以看一下html.css和quirk.css这两个文件的内容,这些就是内建样式(这些样式称为“用户代理样式”,可以通过点击Style选项卡,在弹出的菜单中选择是否显示用户代理样式)。

有一点需要注意,Firebug有时候会显示一些像-moz-background-clip这种未曾声明过的属性,如果确定没有明确声明那些属性,基本就可以将其忽略掉。另外,如果你使用的是简写形式的属性,它也会自动扩展成独立的属性,也就是说像这样的代码:

font: 1em "Andale Mono", "Courier New", Courier, monospace;图1-3 在独立的Firebug窗口中Style选项卡全部展开

在Firebug中就会变成这样:font-family: "Andale Mono","Courier New",Courier, monospace;font-size: 1em;font-size-adjust: none;font-stretch: normal;font-style: normal;font-variant: normal;font-weight: normal;line-height: normal;

尽管刚开始可能有点儿令人困惑,但是这种呈现方式并不赖,因为它可以提醒你在这些简写的背后还隐藏着一些东西(更多的简写属性见第2章。)

另外需要注意的是,Style选项卡中列出的规则是按特殊性(specificity)降序排列的,也就是说,第一个是应用在当前元素上最特殊的规则(优先级最高),第二个是特殊性稍低一点的规则,依次类推。(更多关于特殊性的内容,详见2.3节。)

通过随时单击鼠标右键并在弹出的菜单中选择Inspect Element(审查元素)选项可以查看任何元素(如图1-4所示),你也可以单击Firebug图标旁边带箭头的方框图标切换到审查模式。此时,随着鼠标在页面上的移动,当前元素会显现出轮廓,单击元素即可进行查看。

在Firebug中单击任何声明左侧的空白处都可以禁用该声明,这种禁用声明的方式对于测试各个属性之间的相互作用非常有用。在图1-5中还可以看到,鼠标悬停在颜色值上会出现一个小色块,它用来展示该值对应的颜色。图1-4 右键菜单中的Inspect Element选项图1-5 禁用的样式和悬浮的颜色框(另见彩插图1-5)

如图1-6所示,在Firebug的Style选项卡中也可以查看元素的计算样式。这就意味着,无论你是否曾经声明过,它都会把所有已知的应用在该元素上的CSS属性展示出来。记住,所有的CSS属性都有默认值。在该视图下可以查看全部的默认值,例如当你想知道浏览器应用在标题上的行高(line-height)的确切像素值时,该视图就变得非常有用了。图1-6 计算样式

通过查看元素的盒模型(box model)部分,可以精确地查看元素的尺寸大小,如元素的宽、高、内边距和外边距等(如图1-7所示),这些都是用像素表示的。更酷的是当鼠标悬停在该面板中的框上时,页面上就会出现沿着元素外框的上边缘和左边缘放置的像素尺。

在图1-8中我们可以很明显地看到,Firebug还有许多其他功能,诸如可以编辑元素的属性值(比如class)或者元素本身的内容、添加或编辑CSS属性和值等。通过在Firebug界面中随时单击鼠标左键或者右键可以自行探索Firebug的功能,看看你还能做点儿什么?图1-7 Layout(布局)选项卡(另见彩插图1-7)图1-8 在Style选项卡下动态编辑CSS

需要注意,当在Style选项卡下查看元素的CSS时,无法看到任何伪元素(pseudo-element)影响元素的相关规则。作为例子,如果我们使用了选择器p:first-letter,那么当查看p元素时就无法看到这条规则,伪类(pseudo-class)是可以看到的,但是伪元素不能。这对于包含生成内容的清除浮动(clearfix)解决方案,可能是个问题(见4.5节)。

如果你没有使用Firefox进行开发,但却想一睹Firebug的风采,那么访问页面getfirebug.com/lite.html(如图1-9所示)并且按照页面上的指引进行安装,就可以使Firebug运行在Internet Explorer、Opera或者Safari等浏览器中,以此来适应你的开发环境。你可以把Firebug精简版链接到测试页面进行测试,或者把它保存到书签栏存成一个书签小程序(bookmarklet)——这也是我推荐的方式。图1-9 Firebug精简版在IE浏览器上运行

这个版本的Firebug并不像火狐扩展版的功能那么多,因此才有“精简版”一说,但是它仍然很好很强大。1.2 Web Developer Toolbar

除了Firebug,另外一个网页开发人员必不可少的工具就是WDT(Web Developer Toolbar)了,它也是火狐浏览器的一个完全免费的扩展。

访问chrispederick.com/work/web-developer可以获得此扩展。你也可以访问addons.mozilla.org,然后搜索“Web Developer Toolbar”,在WDT的安装页面进行安装(如图1-10所示)。图1-10 addons.mozilla.org网站上的Web Developer Toolbar页面

跟Firebug一样,我也没法把WDT的所有功能都讲一遍,那样的话一整章也不够用。这里只挑选了其中几个菜单进行重点讲解,当然,当你装完WDT之后应该花点儿时间看看所有的菜单和选项。

有时候需要频繁更新一些小的改动,而浏览器缓存可能变得很烦人,这时候禁用浏览器缓存就很有用了。如图1-11所示,还可以通过WDT禁用JavaScript,它可以让你看到当全部脚本都停止工作或者JavaScript框架没有载入时会发生什么。

如图1-12所示,CSS菜单中的某些功能在Firebug中也有,但是WDT有个非常棒的功能——可以只禁用嵌入样式(embedded style)、链接样式表(linked style)或者只禁用行内样式(inline style),当然你不应该使用行内样式(inline style)!如果你想看到一个畸形的页面,甚至可以用WDT关掉大部分的浏览器内建样式。

图1-13中的Information(网页信息)菜单中包含了大量好玩的小报告,包括显示文档中的类(class)和ID信息、显示div的顺序、网页中的颜色信息汇总,等等。你还可以启用显示元素信息模式,当单击任何元素时都可以看到该元素各属性和值的汇总信息,诸如元素在页面中的位置、字体、元素的祖先和后代元素,等等。Information菜单展示的内容跟XRAY很像,本章后面会介绍更多关于XRAY的内容。图1-11 Disable(禁用)菜单图1-12 通过CSS菜单禁用链接样式表图1-13 通过Information菜单显示类和ID的值

通过图1-14中所示的Outline(标示)菜单可以标示出各种元素,包括全部块(block)元素、全部行内(inline)元素、全部链接、全部定位元素、全部表格单元格等,还可以自定义需要标示的元素以及标示颜色。当标示元素时,你也可以选择是否在页面上显示元素的名字。这个菜单可比它看上去要强大得多,我把它当做一个Layout Diagnostic(布局诊断)菜单来用,因为我可以快速标示出不同的元素集合并且快速查看它们之间的叠放关系,快速找出可能出错的地方。

Tools(工具)菜单提供了一些验证器、错误检查器和调试控制台的快捷访问方式,而其中最好的一个特性就是可以验证本机的HTML和本机的CSS(如图1-15所示,分别对应“ValidateLocal HTML”和“Validate Local CSS”项),在这两种情况下,当前浏览的网页都会被打包成序列化的字符串发送到相关的验证器。因此,如果选择了Validate Local HTML,页面的标记就会被发送到HTML验证器,然后你会看到一个报告。这对于验证那些受防火墙保护的页面和公网无法访问(因此验证器服务也无法访问)的页面来说非常实用,有了本机验证功能,这些都不再是问题了。图1-14 使用Outline菜单标示出块元素图1-15 Tools菜单

正如我开始所说的,对于WDT的全部功能来说这些只是管中窥豹,所以花点儿时间仔细研究一下这个工具吧,它会让你的生活变得更轻松!1.3 IE 开发者工具栏

如果你主要用Internet Explorer 7完成开发工作,那就不能安装Web Developer Toolbar了,但你可以安装IEDT(Internet Explorer Developer Toolbar,IE开发者工具栏)来作为替代品。

IEDT的URL地址是那种非常典型的谁也记不住的微软URL,所以打开你最喜欢的搜索引擎(使用Google会有那么一点儿讽刺),然后输入“Internet Explorer Developer Toolbar”,应该第一个搜索结果就是了,如果是IE7的话可以继续单击进行安装。IEDT不支持IE8,一会儿我们再看看IE8都提供了什么。

完成安装后,你就可以找到右上角的Tools菜单(如图1-16所示,就在Pages(页面)菜单附近),注意不是左侧Favorites(收藏)和Help(帮助)菜单之间的那个Tools菜单。在该菜单下选择Toolbars(工具栏),然后选择Explorer Bar(浏览器栏),最后再选择“IE Developer Toolbar”即可。图1-16 IE开发者工具栏在IE7右上角

单击IE Developer Toolbar后,你就会看到在浏览器窗口底部打开了一个像Firebug一样的面板,如图1-17所示,在面板顶部也有一些跟Web Developer Toolbar很像的菜单。单击面板右上角关闭按钮附近的层叠窗口小图标,可以使面板扩展为一个独立的窗口,通过这种方式扩展面板对于一些低分辨率的上网本和高射投影仪来说特别有用。图1-17 运行中的IE开发者工具栏

这个工具栏有个很好的功能,就是通过Show Default Style Values(显示默认样式值)复选框可以很容易地在计算样式和声明样式之间进行切换(如图1-18所示)。同样,通过Show Read-onlyProperties(显示只读属性)复选框可以查看当前元素的DOM(Document Object Model,文档对象模型)属性的每个细枝末节。如果你不熟悉JavaScript和DOM脚本编程,这些可能还不适合你(当然,它也不适合我)。

IE开发者工具栏包含了Web Developer Toolbar的部分功能,但把大部分最有用的功能放在了第一层菜单中,诸如标示元素和验证本机的HTML和CSS。查看菜单中还有一个很小巧的功能,即CSS选择器匹配(如图1-19所示),它会弹出一个窗口展示CSS中的每条规则及其匹配了文档中的多少元素,如果有的规则显示0匹配,则说明该规则没有匹配页面上的任何元素,可以考虑删掉这条规则。图1-18 未勾选Show Default Style Values时的IE开发者工具栏图1-19 选择器匹配报告

IE8内置了一个开发人员工具,所以就不用额外安装了。帮助文档是在线的,截至撰写本书之时,该文档仍放在一个比IEDT的URL强不了多少的URL地址上。所以还是打开你最喜爱的搜索引擎,然后输入“Discovering Internet Explorer Developer Tools”,第一个搜索结果应该就是该文档了。

想打开这个工具的话,在IE的Tools菜单中选择Developer Tools(开发人员工具,如图1-20所示)或者按键盘上的F12健即可。可以看到它跟IE Developer Toolbar的界面很像,可以说是Firebug和Web Developer Toolbar混合的产物。它的菜单大部分和IE7的开发者工具栏很像,但是跟IE7的开发者工具栏相比,那些选项卡却更像Firebug。图1-20 IE8的开发人员工具

对于开发人员工具的Style(样式)选项卡来说有一件事让我感到很困惑,就是样式的排列顺序对我来说没有任何意义,它不是按照特殊性顺序排列的。尽管这个列表确实显示了哪些声明被其他声明覆盖了(这也挺好的),但是不像Firebug那样有明确的顺序,不太好用。

尽管如此,即便开发者工具栏和开发人员工具不能涵盖Firebug或Web Developer Toolbar的全部功能,它也还是很有用的。任何网页开发人员都应该把它装在Internet Explorer上,它对于查看布局的源代码和追踪IE的各种怪异错误非常好用。1.4 Dragonfly(Opera 浏览器)

如果主要用Opera浏览器进行开发,那么你会用到Dragonfly(如图1-21所示),它是一个Opera 9.5及以后版本内置的开发环境,访问opera.com/dragonfly可以获得更多信息。图1-21 Dragonfly的主页

打开Dragonfly的默认方式是单击Tools菜单,然后在Advanced(高级)选项下选择DeveloperTools(开发者工具),你还可以安装一个Debug(调试)菜单,访问opera.com/dragonfly可以找到安装链接,一旦安装完成,就有了一些访问Dragonfly和其他功能的快捷方式。当然,你也可以使用键盘快捷键打开Dragonfly,Mac快捷键是Option+Command+I,Windows是Option+Control+I。但奇怪的是,这里的键盘快捷键是不能进行切换的。如果已经打开了Dragonfly,就不能使用键盘快捷键关掉它了。这种情况下就需要使用鼠标或者Command+W(Control+W)关掉它,这在Dragonfly作为独立的窗口打开时很管用,但是当Dragonfly停靠在浏览器窗口时则仅当单击Dragonfly使其获取焦点时Command+W才能关掉它,否则会关掉整个窗口。

Debuy菜单有个很好的特性,它包含了到HTML、CSS和其他规范的链接。还有个好玩的地方是它的Layout(布局)子菜单,可以将Opera设置成像Emulate Text Browser(模拟文本浏览器)和Show Structural Elements(显示结构元素)那样的布局模式,甚至还有一种Nostalgia(怀旧)布局模式(如图1-22所示),它可以温暖任何一个80年代计算机老兵的心。

虽然Dragonfly的界面布局跟Firebug的非常相似,但也有一些显著的不同。首先,右侧的Style选项卡可以显示计算样式和声明的样式(如图1-23所示),每个分组都可以扩展和收缩。跟Firebug一样,你看到的也不完全是声明的属性,因为简写的属性也同样被扩展成了独立的属性。Dragonfly有个很好的体验,就是如果你想查看简写属性,那么至少在计算样式里可以看到。图1-22 Nostalgia视图下的Dragonfly页图1-23 计算样式分组展开时的Dragonfly

另外一个与Firebug的差异(不太受欢迎)是,任何被其他声明覆盖的声明都会灰掉,而且有个橘黄色的[overwritten]文字跟在后面(如图1-24所示),这会显得很乱,而且很难看清被覆盖的声明的值。图1-24 Styles选项卡中展示被覆盖的样式(另见彩插图1-24)

图1-25展示了Layout(布局)选项卡,它可以显示当前被检查元素的布局框,这里不仅可以显示布局框的大小,还能显示许多属性的像素值,比如offsetTop和scrollLeft等。图1-25 Dragonfly详尽的Layout选项卡1.5 Web 检查器(Safari 浏览器)

如果你主要用Safari浏览器进行开发,那么就会用到Web检查器(Web Inspector)。

要想使用Web检查器,打开Safari的Preferences(偏好设置)选择Advanced(高级),然后将Show Develop menu in menu bar(在菜单栏中显示开发菜单)前面的复选框勾选上,如图1-26所示。当完成这些之后,你就可以通过单击Develop(开发)菜单下的Show Web Inspector(显示Web检查器)打开Web检查器了,或者通过键盘快捷键Option+Command+I开启。跟Dragonfly一样,Web检查器的键盘快捷键也不能进行切换,如果Web检查器已经打开的话就不能用快捷键关闭它了。这种情况下需要使用鼠标,而Command+W只有在Web检查器作为独立窗口打开时才起作用,如果Web检查器停靠在主窗口,那么使用Command+W的话就会关掉整个窗口。图1-26 开启Develop菜单

尽管Web检查器的界面布局跟Firebug的非常像,但也有一些显著的不同。比如右侧的面板将Computed Style(已计算样式)作为一个分组,如图1-27所示。跟Firebug一样,这里看到的也不全是声明过的属性,因为简写的属性也会被扩展成独立的属性,并且如果选择了Show Inherited(显示继承的样式)就会得到一个很长的属性列表。

在它的正下方,当前被检查元素的每条规则都以独立的分组显示。可以分别展开或收起每个分组,而这些分组的下方是Metrics(版式)子面板,它展示了当前被检查元素的布局框的尺寸(如图1-28所示)。图1-27 已计算样式图1-28 常态下的样式和布局分组1.6 XRAY

如果你正想找一个轻量级的、跨浏览器的元素检查器,那么图1-29中所示的XRAY一定“很合你的胃口”。它的功能非常有限,但如果你不需要过多的功能,那么对于功能的专注其实正是它的强项。图1-29 XRAY的页面

打开westciv.com/xray,直接把那个大大的XRAY按钮拖放到你的书签栏(或者拖到菜单栏,如果你想把它隐藏的话)。然后,任何时候当你打开一个页面并且想检查某个元素时,打开XRAY,然后选择感兴趣的元素即可。

选择元素之后,元素会突出显示且其各边就会出现尺寸信息,你还会看到XRAY框,如图1-30所示。XRAY框里面会提供一些额外的信息,诸如元素在文档树中的位置、ID或类的值、核心的CSS属性值等。如果选择了继承结构(inheritance hierarchy)的任何元素,XRAY会继续检查该元素。单击XRAY框右上角的关闭图标即可退出XRAY,直到下次需要的时候再打开。

还有一个类似但用途不同的工具,即MRI(westciv.com/mri),它允许你输入选择器,然后就可以在页面上显示都有哪些元素被选中了。图1-30 运行中的XRAY1.7 SelectORacle

如图1-31所示,SelectORacle这个名字听起来像是某个数据库产品的广告,但它真不是!相反,它是一个可以将有效的选择器转换成类似常规英语的在线工具。这个名字源自于“选择器”(selector)和“预言”(oracle)的组合。

打开gallery.theopalgroup.com/selectoracle并且输入一个或多个有效的CSS选择器,随便多复杂都行。选择英语或者切换到西班牙语,然后单击Explain This!(解释)按钮就会得到每个选择器的完整解释。例如:ul li:nth-child(2n+3):not(:last-child)

会被解释成:

选择ul元素的后代元素中,从第三个子元素开始的索引为奇数的全部li元素,且不包含最后一个li元素。图1-31 SelectORacle页面(另见彩插图1-31)

OK,或许这乍一看有点儿令人困惑,但是如果你慢慢读的话,大多数情况下都能读明白。当然,截至撰写本书之时,世界上还没有任何一种浏览器能够完整支持这一选择器,所以不必太在意。你只需要知道,当遇到一个可能表意不清的选择器时,SelectORacle或许能帮上忙。

你曾想过当浏览器载入了过时的页面,或者某些元素出了问题(比如font元素)时,浏览器可以喊你一下吗?现在诊断样式表(diagnostic style sheet)就可以做到,当然,只是视觉上的提醒。1.8 诊断样式表

当页面上的标记出了问题时,诊断样式表可以快速给出视觉提示。在meyerweb.com/eric/tools/css/diagnostic可以找到一个使用诊断样式表的例子(有CSS3和IE7友好的版本),还有个类似的资源在accessites.org/site/2006/07/ big-red-angry-text。

那么它有什么用呢?例如,meyerweb(meyerweb.com)的诊断样式表中有这么一行:

*[style], font, center {outline: 5px solid red;}

它会把任何含有Style属性的元素及任何字体(font)元素和居中(center)元素用一条红色的粗实线圈起来。你还可以给它再加点儿“佐料”,比如background-color: lime,以进一步突显它。这样做是为了捕获那些有问题的标记出现的地方,无论这些标记是从CMS(ContentManagement System,内容管理系统)还是其他什么地方引入的。

你或许以为验证可以捕获任何标记问题,但其实不是。当然,当你使用了font元素时它会显示警告,但你可能会遇到验证器无法捕获的一些问题,我们来看一个普通的JavaScript链接示例:<a href="#" onclick="javascript:nextPage();">Next</a>

这段代码在验证器下一切正常,因为标记是正确的。问题是,对于不使用JavaScript的用户,这个链接什么也不能做。这里应该有一个无脚本(non-JS)时的替代方案,并且应该提供一个可用的链接地址。因此,meyerweb的诊断样式表还有这样一行:a[href="#"] {background: lime;}

这行代码会找出任何缺少无脚本后备链接地址的链接元素。(该代码使用了属性选择器,关于属性选择器详见2.11节。)

那么如何使用它诊断CSS呢?可以将它引入到你正在开发的网站的CSS中并在网站上线时删除,也可以把它设置成浏览器中的用户样式表,这样就可以在你访问的任何页面中使用了。

这里有一个完整的诊断样式表,它可以找出没有内容的空元素、没有alt或title属性或属性值为空的图像元素、找出没有摘要(summary)属性和表头含有无效范围(Scope)值的表元素、含有有问题的或空的title和href属性值的链接元素等。注意,由于含有属性选择器,该版本无法在IE7中使用,而由于含有:not()和:empty()伪类,该版本也无法在IE8中使用。图1-32中展示了该诊断CSS的一个测试页。图1-32 诊断CSS的一个测试页div:empty, span:empty,li:empty, p:empty,td:empty, th:empty {padding: 0.5em; background: yellow;}*[style], font, center {outline: 5px solid red;}*[class=""], *[id=""] {outline: 5px dotted red;}img[alt=""] {border: 3px dotted red;}img:not([alt]) {border: 5px solid red;}img[title=""] {outline: 3px dotted fuchsia;}img:not([title]) {outline: 5px solid fuchsia;}table:not([summary]) {outline: 5px solid red;}table[summary=""] {outline: 3px dotted red;}th {border: 2px solid red;}th[scope="col"], th[scope="row"] {border: none;}a[href]:not([title]) {border: 5px solid red;}a[title=""] {outline: 3px dotted red;}a[href="#"] {background: lime;}a[href=""] {background: fuchsia;}1.9 重启样式表

有一件关于CSS的事情你可能没有考虑过,那就是即便你创建了HTML文档后一行CSS也不写,CSS也总是对文档起作用。实际上,有大量的CSS被应用在“未应用样式”的文档上(如图1-33所示),它们全部来自于浏览器本身。标题元素的默认大小和字体粗细、不同元素和不同行之间的间距、列表项前面的项目符号,甚至块元素和行内元素之间的区别全部是默认样式集决定的。图1-33 实际上应用了许多样式的“未应用样式的”文档

当然,不同浏览器的默认样式略有不同,这不能怪浏览器厂商,因为还没有规范确切地说明文档应该具有什么样的默认样式。鉴于这种情况,大部分浏览器都尽量模仿Mosaic浏览器显示文档的效果。是的,确实是Mosaic!因为它曾经是Netscape 1.0和IE3等浏览器试图模仿的对象。如果深入了解浏览器的默认样式,你就会发现那些从早期的Mosaic测试版浏览器复制过来的东西,甚至连像素值都是。

作为对策,很多人开发了重置样式(如图1-34所示),这意味着可以通过设置通用的属性尽量减少浏览器之间的不一致性,最简单的方式是:* {margin: 0; padding: 0;}图1-34 应用了基本的重置CSS的文档

很多人都在用这行代码,因为它很简单。但问题是该规则会应用在文档中的全部元素上,包括像文本输入框和下拉选择框这种表单元素。由于目前不同浏览器处理表单元素样式的方法不尽相同(而且有一些根本不会应用表单样式),这种“全部元素”的方法就意味着在减少浏览器差异时表单元素的表现会变得很不一致。

正因如此,才有了更复杂一点儿的重置样式,其中一个比较流行的位于meyerweb.com/eric/tools/css/reset,它的开始部分如下所示:http://en.wikipedia.org/wiki/Mosaic_(web_browser) 查看。html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, acronym, address, big, cite, code,del, dfn, em, font, img, ins, kbd, q, s, samp,small, strike, strong, sub, sup, tt, var,b, u, i, center,dl, dt, dd, ol, ul, li,fieldset, form, label, legend,table, caption, tbody, tfoot, thead, tr, th, td {margin: 0;padding: 0;border: 0;outline: 0;font-size: 100%;vertical-align: baseline;}body {line-height: 1;}

哇!元素可真够多的。之所以这么做是为了选择除表单元素(input、select、textarea)之外的所有元素并使它们表现一致。第一条规则明确地将外边距、内边距、边框和轮廓设置为0,并且强制将所有的元素设置成同样的字号和相同的文本垂直对齐方式。第二条规则为body元素设置了一个较小的行高,这个行高值会被body元素的所有后代元素继承。

在meyerweb的重置样式中还包含更多的规则,包括移除列表的项目符号和在blockquote(块引用)标记和q元素外侧自动生成的引号的规则等。再一次地,基本目的还是为了让我们在开始书写CSS之前,尽量使所有的浏览器表现一致些,同时也让页面好看一些。

此时你或许会想:“等等,这就意味着我得撤销之前的全部工作!我从来也没想让页面有个一倍的行高啊!那就太局促了!我也不想让垂直对齐把上下标搞的乱七八糟!”

这些担心都是正常的,而你需要做的只是把样式表按照你的偏好进行修改。假设你经常使用1.4倍的行高来使文字显得疏落有致,只需要修改重置样式表中的这一行:body {line-height: 1.4;}

既然已经找到这儿了,除了自己喜欢的字体,你可能还想为页面添加一个你喜欢的背景色和统一的文本颜色:body {font: smaller/1.4 Helvetica, sans-serif;background: #ABACAB;color: #444;}

如果不想改变列表的默认样式,可以将清除列表项目符号的规则删除(但是我没有这么做,因为我们都有不同的需求)。你还可以继续添加精确定义列表的缩进方式和缩进量的规则、段落和列表项之间的间距规则以及你喜欢的加粗样式等。

至此,你拥有的已经不是重置样式表了,而是一个重启样式表(reboot style sheet)。通过此样式表可以将浏览器重启成你首选的修饰文档的基准样式,在此自定义的基点上可以建立任何项目。有了这个重启器(rebooter),任何项目都不用重新开始了,你可以把它作为核心的样式并在其基础上编写最终的样式表。

可以很负责地说,你不仅可以使用CSS重启浏览器样式,还可以用JavaScript来改良一些浏览器。1.10 IE9.JS

Dean Edwards的IE9.js可以使IE5到IE8各版本的浏览器在处理CSS和HTML时更像IE9(截至撰写本书之时IE9还没有发布)。你可以在code.google.com/p/ie7-js找到它,注意这里ie7并没有写错(如图1-35所示),因为这个项目是从IE7.js开始的,然后是IE8和IE9,新版本是很必要的。图1-35 IE7.js页面

IE9.js是一个JavaScript例程集,如果浏览器是IE9以下的版本,它就会扫描页面的CSS和HTML,并且计算出哪部分是当前IE版本不支持的,然后它会悄悄地在后台执行一番花哨杂耍式的操作,让浏览器支持这些原来并不支持的部分。

举个例子,IE5和IE6不支持属性选择器,假设我们有下面这样一条规则:a[href] {text-decoration: none; color: red;}

IE5和IE6会完全忽略这条规则,而链接元素不会有任何变化,尽管这可能让Jakob Neilsen很高兴,但是该项目的设计者可能就没法给人留下深刻印象了。IE9.js花哨杂耍式的操作可以确保IE5和IE6能够将那些样式应用在链接元素上,使它可以正常工作。你需要做的只是将IE9.js链接到任何需要用到它的页面。

当然,如果禁用了JavaScript就什么效果都没有了,这意味着需要权衡一下,比如是否他们大部分还在使用IE6,以及他们有没有可能禁用JavaScript等。当然,这些是我们设计任何网站时都需要权衡的,也算是轻车熟路了。

通常的使用建议是将链接到JavaScript文件的script元素用一个条件注释包起来,像这样:<!--[if lt IE 9]><script src="/code/IE9.js" type="text/javascript"></script><![endif]-->

脚本本身会确保只在需要的时候才运行,所以可以省略条件注释,然而这么做的话就意味着不管浏览器是否需要运行该脚本,每位访问者都会把它完整地下载下来。而有了条件注释,就可以确保只有那些有可能运行该脚本的浏览器才会加载该脚本。

如前所述,对于这一脚本还有一些早期的版本,它们可以使之前的旧版IE提升到像IE7或IE8那样,如果你觉得IE9.js不能满足你的需求,那就试试之前的版本吧。第2章 选择器

我们能真切地感觉到,选择器是CSS的核心部分。如果没有它们的话,我们除了把属性嵌入到每个元素里,就没有其他办法能把样式应用在元素上了,那真是太糟糕了。通过选择器赋予的选择任何形式任何种类元素的能力,我们可以只用很少的几行CSS完成很大一部分样式设置工作。

本章我们将深入探讨如何巧妙地使用选择器,并且概述一下哪些类型的选择器被普遍支持且应用最为广泛。2.1 伪类与伪元素

在CSS中有两种“伪”字头的选择器:伪类(pseudo-class)和伪元素(pseudo-element)。CSS2.1中的伪类有:

□:link——未访问过的链接;

□:visited——访问过的链接;

□:hover——鼠标悬停的元素;

□:focus——获取焦点的元素;

□:active——激活的元素(例如一个被单击的链接元素);

□:first-child——作为其他元素第一个子元素的元素;

□:lang()——根据元素的lang属性确定的元素。

CSS2.1中的伪元素有:

□::first-line

□::first-letter

□::before

□::after

那么区别在哪儿呢?区别就在于这些伪选择器影响文档的方式不同。伪类的表现有点儿像给文档添加类,而伪元素的效果就好像有元素被插入到了文档中。

以::first-letter为例,假设你要把每个h1的第一个字母增大到其他字母的两倍半(如图2-1所示),很简单:h1::first-letter {font-size: 250%;}

这就仿佛CSS和标记被修改成了这样:h1 first-letter {font-size: 250%;}<h1><first-letter>H</first-letter>owdy, y’all!</h1>图2-1 放大h1的第一个字母

这真的是浏览器内部真实发生的情况吗?谁知道呢,反正结果确实像是发生了这种情况,因此才有了“伪元素”这个名字。

类似地,伪类的表现就像是它们使文档中的元素被添加了新的类。例如,想象一下若对于每一个作为其他元素第一个子元素的元素,浏览器给它们都附加了一个名为first-child的类,然后就可以像下面这样为它们应用样式了:

li.first-child {border-left: none;}

只需要简单地把点改成冒号就变成了li:first-child,就可以得到同样的最终效果,而不用费力把类添加到所有标记上。

有时还会看到伪元素使用双冒号的语法,这是在CSS2.1之后引入的。截至撰写本书之时,还没有哪个浏览器要求你必须在伪元素前面使用双冒号的,一个冒号就可以了。

另外提醒读者,CSS3增加了如下一些伪类,截至撰写本书之时,它们中的大部分还没有被广泛地支持:

□:target

□:root

□:nth-child()

□:nth-of-type()

□:nth-last-of-type()

□:first-of-type

□:last-of-type

□:only-of-type

□:only-child

□:last-child

□:empty

□:not()

□:enabled

□:disabled

□:checked2.2 为目标元素添加样式

当希望指向文档中的某个片段时,目标(target)会非常有用。什么,怎么实现?其实非常简单:<a href="http://example.com/law.html#sec2-7">Section 2.7</a>

任何人单击这个链接(如果浏览器处理正确的话)将不止跳转到目标页,而且还会跳到页面中文档片段标识符(地址中的#sec2-7部分)出现的地方。这有时是通过锚点(anchor)实现的,但是只用ID来实现会更好一些。例如有如下两种场景:<h3><a name="sec2-7">Exceptions</a></h3><h3 id="sec2-7">Exceptions</h3>

这两种情况下,当浏览器跳到了文档中的目标位置时,都不会有任何视觉提示告知你已经到达了目标位置,而使用:target伪类就可以给出视觉提示。例如,若想让作为某个文档片段标识符目标的h3拥有特定的提示效果(如图2-2所示),可以这样写:h3:target {color: maroon;background: #FFA;}图2-2 突出显示目标元素(另见彩插图2-2)

当然,你或许想把这个样式应用到任何目标元素上,而不管它是什么元素,那么只需要把h3换成一个通用选择器即可,就像这样:*:target {color: maroon;background: #FFA;}

从技术上讲,这种情况下通用选择器也是可选的,可以把这个选择器简单地写成:target。

如果想让目标样式多一点Web 2.0的感觉,还可以设置一个渐隐背景的效果。你懂的,就是那种“你已经完成了某项操作,所以页面上的一块背景会从黄色变到白色,让你知道已经完成了该项操作”的效果。通过:target和一个动画GIF可以很容易地实现这种效果,只需要创建一个从黄色渐变到白色(如果白色是你网站的背景色的话)的动画并且把它当做背景图像。

*:target {background: url(/pix/yellow-fade.gif);}2.3 特殊性

你很难快速地把特殊性(specificity)这个词读3遍,而若想彻底地掌握它甚至更难。但是,它却是理解CSS规则之间相互作用的关键。特殊性是一个选择器“特殊程度”的数字表示,有3样东西经常被用来确定选择器的特殊性:

□每个元素描述符贡献0,0,0,1;

□每个类、伪类或者属性描述符贡献0,0,1,0;

□每个ID描述符贡献0,1,0,0。

先不要抓狂,来看几个小例子。div ul ul li 0,0,0,4 4个元素描述符div.aside ul li 0,0,1,3 1个类描述符,3个元素描述符a:hover 0,0,1,1 1个伪类描述符,1个元素描述符div.navlinks a:hover 0,0,2,2 1个伪类描述符,1个类描述符,2个元素描述符#title em 0,1,0,1 1个ID描述符,1个元素描述符h1#title em 0,1,0,2 1个ID描述符,2个元素描述符

希望这些能够帮助你理解特殊性是如何计算的。那么,为什么是逗号呢?因为可以这么说,每个“级别”的特殊性的值都是相互独立的。因此,具有一个单独的类描述符的选择器会比由13个元素描述符组成的选择器拥有更高的特殊性。.aside /* 0,0,1,0 */div table tbody tr td div ul li ol li ul li pre /* 0,0,0,13 */

第一个选择器左数第三位的“1”胜过了第二个选择器同样位置的“0”,基于这样的事实,第二个选择器第四位的“13”(在这个非常有限的例子中)就毫无意义了。逗号可以使我们看得更清楚,否则选择器的特殊性可能被写成“10”和“13”,从而造成后者特殊性更高的假象(在CSS的早期还没有引入逗号分隔符时,这是经常出现的误解)。

还有另外一种常见的误解,就是特殊性的结构相近问题。例如,假设有如下两个选择器:ul li {font-style: normal;}html li {font-style: italic;}

它们当中哪个会赢呢?它们都有两个元素描述符,这意味着它们的特殊性都是0,0,0,2。其实,后写的会赢,与html元素相比ul元素在文档中的位置离li元素更近也不管用。特殊性只是单纯的数值,它不会以任何方式评估页面的结构。结果,列表元素将全部变成斜体,因为当特殊性相等时后声明的规则会胜出。

因为我说过有3样东西影响特殊性,所以你或许想知道特殊性标识符第一位的0是干嘛用的。其实,第一个0是用于行内样式(inline style)的,且仅用于行内样式。因此,如果有下面这样的样式和标记,则div的背景将会是蓝色。div#header {background: purple;} /* 0,1,0,0 */<div id="header" style="background: blue;"> <!-- 1,0,0,0 -->2.4 重要性

有一样东西是可以无视特殊性的,那就是!important。如果你是一名程序员的话,我现在就要打消你的错误想法——!important不代表 “不重要”。

可以使用!important把任何单独的声明标记为重要。下面是一个简单的例子:a:hover {color: red !important; text-decoration: none;}

在这个例子中color: red被标记为重要,但是text-decoration: none没有。任何你想标记为重要的声明都需要有自己的!important。

基本上,任何重要的声明都会覆盖非重要的声明。好了,就此打住吧。使用下面的代码,结果将会得到一个绿色的链接:div#gohome a#home {color: red;}div a {color: green !important;}<div id="gohome"><a id="home" href="/">Home</a></div>

第一个规则非常高的特殊性(0,2,0,2)对于解决颜色冲突没有任何作用,因为!important可以胜过它。

当然,如果我们为第一条规则也添加一个重要标志,那么情况就不同了。div#gohome a#home {color: red !important;}div a {color: green !important;}

由于两个颜色声明都是重要的,所以会采用正常的层叠规则来解决冲突,换句话说,特殊性又起作用了,所以链接会变成红色。

这提醒我们使用!important时要非常小心,因为一旦开始用它覆盖其他规则,很快你就会发现必须再用另外的!important覆盖这条重要的规则,因此就必须再引入一个!important声明,最终会把所有的声明都变成重要的,也就意味着最终没有一个规则是重要的。2.5 省略简写属性值的关键词时会发生什么

我们都知道CSS中有很多简写属性,其中background、border、font、margin和padding是几个最常用的,这是一次表达一堆信息的高效而简洁的方式。但是,当省略了属性值中的某些部分时会发生什么呢?考虑下面的代码:strong {font: bold italic small-caps medium/1.2 Verdana, sans-serif;}

如图2-3所示,这会对strong元素应用1.2倍行高且字体为粗斜体、中等字号、小型大写字母(small-capsmedium-size)的Verdana(或者其他sans-serif字体族的字体)样式。图2-3 疯狂的strong

假设我们缩减一下这个属性值:strong {font: medium Verdana, sans-serif;}

则结果是中等大小、正常粗细的Verdana字体(如果Verdana字体不可用的话就是其他的sans-serif字体),而粗体的效果消失了,如图2-4所示。图2-4 因疏忽而无加粗效果

原因是当省略了部分简写属性值的时候,缺失的部分就会使用该属性的默认值。因此,当省略了字体粗细(font-weight)、字体样式(font-style)和字体异体(font-variant)时,实际相当于这样:strong {font: normal normal normal small/normal Verdana, sans-serif;}

是的,即使行高line-height也有默认值,该默认值可以覆盖任何继承的行高值。

如果设置样式的时候不小心,就可能会出现类似的问题。考虑下面两条规则,第一条来自于全站的样式表,而第二条来自于某个页面的嵌入样式。body {background: #FCC url(/i/pagebg.gif) 10px 25% no-repeat fixed;}body {background: url(i/body-bg.gif);}

在这两条规则的作用下,这里讨论的页面会有一个新的从左上角开始的背景图像,且当页面滚动时会随着页面滚动。这是因为第二条规则实际上等价于:body {background: transparent url(i/body-bg.gif) 0 0 repeat scroll;}

现在,如果这确实是想要的效果,那么你可以这么做,但它的目的似乎更像是要把图片换成另外的图片。其实在这种情况下,你只需要设置特定的属性,像这样:body {background-image: url(i/body-bg.gif);}

总之,这是大部分简写属性的工作方式。不过,也有一些例外的,如margin、padding、border-style、border-width和border-color等,省略这些属性的值,结果就像是从已给出的值“复制”了一份。下面有几个功能相同的声明:margin: 1em; margin: 1em 1em 1em 1em;padding: 10px 25px; padding: 10px 25px 10px 25px;border-color: red green blue; border-color: red green blue green;

当然,这些值的顺序还是上右下左(Top-Right-Bottom-Left),可以简记为TRBL,这可以免除你“记不住”这一麻烦(TRouBLe)。2.6 选择性地覆盖简写属性

虽然简写属性会用默认值覆盖未声明的部分,但这并不意味着我们需要避免这种情况。实际上,你可以用简写属性声明80%,然后在另一处通过覆盖实现另外20%。

假设你想实现一个具有3 px的点线边框,其中3条边是黑色,而第四条边是红色(如图2-5所示),可以每次都写一个边,但是那样要多敲很多次键盘。所以可以像下面这样声明:border: 3px dotted black;border-left-color: red;

这样,就只需要调整那一小部分跟其他部分不同的地方。你甚至可以用同样的规则实现一些更好的效果。

再举一个常见的选择性覆盖简写属性的例子。注意,标题元素可能除了字号以外大部分属性都相同。如果你对浏览器默认的字号很满意的话,就可以直接这么写了:图2-5 把一侧的边变成红色(另见彩插图2-5)h1, h2, h3, h4, h5, h6 {font-weight: normal;font-style: italic;font-family: Helvetica, sans-serif;line-height: 1.5;}

另一方面,如果想设置自己的标题字号,如图2-6所示,则需要换个思路:h1, h2, h3, h4, h5, h6 {font: italic 100%/1.5 Helvetica, sans-serif;}h1 {font-size: 225%;}h2 {font-size: 185%;}h3 {font-size: 140%;}/* …… */图2-6 通过选择性覆盖快速设置标题字号

当使用这种选择性覆盖时,最好确保用来覆盖简写属性的属性出现在简写属性之后。只有通过这种方式,当选择器具有相同的特殊性时(这也是经常出现的情况),用来覆盖简写属性的属性才能胜过相应的简写属性。2.7 通用选择

本节我将介绍一下选择器中星号(*)的使用,先别太兴奋,它并不是你想象中的万能牌。下面是一个简单的例子:

* {color: blue;}

这个星号称为通用选择器,该选择器的作用是选择文档中的全部元素并对其应用样式。

这看起来像是一个通配符,而且在某种情况下确实是,因为你可以用它选择一大堆元素而无需给它们命名。假设我想选择这个div中的全部元素:<div><h1>Hey-ho!</h1><p>I’m a <em>paragraph</em>.</p><ol><li>Uno</li><li>Deux</li><li>Drei</li></ol></div>

就这么简单:div * {border: 1px solid red;}

结果跟这样写的效果是一样的:div h1, div p, div em, div ol, div li {border: 1px solid red;}

好吧,应该说是几乎一模一样。如图2-7所示,视觉效果是一样的,但是还有一个非常轻微的不同,那就是它们的特殊性。你可以看到,通用选择器的特殊性贡献为0,0,0,0,这意味着div *的特殊性为0,0,0,1,而div h1(还有这一组里的其他选择器)的特殊性为0,0,0,2。除了这个,结果是一样的。图2-7 把div的所有后代加上红框(另见彩插图2-7)

你或许以为可以用h*来代替h1、h2、h3、h4、h5、h6,但是对不起,不行。就像前面展示过的,只能把它作为一个元素通配符,这就是它的全部功能了。2.8 ID 还是类

任何一个有抱负的网页设计师遇到的第一个大难题就是:我该用类(class)还是ID呢?

就像生活中的许多事情一样,这个问题有一个简单的答案,然而也有一个非常复杂的答案。简单的答案是:对于任何在页面中可能出现不止一次的“标签”使用类,对于任何只出现一次的使用ID。这里所谓的“标签”,指的是一个附加到元素上的描述性词汇。实际上,类和ID有99.44%的可能性被用在描述元素上。

使用ID值的两个经典例子是页头(header)和页脚(footer),前提是任何给定页面都只有一个页头和一个页脚。类值的应用就比较分散了,比如可以用more表示“详细信息”链接,用tabs表示一组导航选项卡,或者用odd表示奇数表格行。

至于那个更复杂的答案就不仅要权衡标签是否在页面中唯一了,还要考虑到ID和类对特殊性的影响。由于包含ID的选择器比那些只包含类的选择器具有更高的特殊性,因此你可能会无法覆盖某个特定规则。

这里有个简单的例子,假设你已经在全站样式表中写过如下规则:#header {background: black;}#header a {color: white;}

然后你又决定让联系信息页面不要那么严肃,所以想把页头设置成浅灰色,并让所有的导航链接有一个舒缓的暗绿色样式。由于该联系信息页面有几组不同的导航链接,所以你会这么写:#header {background: #BBB;}.navlinks a {color: #257000;}

很遗憾,如图2-8所示,因为#header a具有更高的特殊性,所以页头的导航链接仍然是白色。图2-8 页头毫无吸引力的链接

对于这个问题,可以这样来解决:#header a, .navlinks a {color: #257000;}

甚至这样:#header .navlinks a, .navlinks a {color: #257000;}

这两种方式都可以,不过看起来有点儿笨拙,不是吗?(虽然不如往.navlinks a上砸个!important那么笨,但还是有点儿笨。)另外一个处理这种情况的方法是将包含ID的页头标签里面的id换成类,即:<div class="header">

此处的class="header"原来是id="header"。这样就不用太担心ID弄出来的那些很难搞的特殊性冲突问题了。也就是说,在你的全站样式表中这么写:.header {background: black;}.header a {color: white;}

然后在联系信息页面的样式表中这么写:.header {background: #BBB;}.navlinks a {color: #257000;}

最终结果就是漂亮的绿色文字链接,如图2-9所示。图2-9 页头引人注目的链接(另见彩插图2-9)

这就是全部需要做的工作了,所以,这是为大部分或者全部标签都应用类的一个好理由。

另外一个理由是,你没法确定什么时候标签会从一个变成多个。页头(header)就是个最好的例子,因为一个页面中可能有多个“页头”。如果觉得奇怪的话,想想新闻网站或者其他门户网站,每个部分和侧栏框都会有自己的“页头”,并且页脚也一样。因此一贯地使用类就很合理了。

现在,你或许会争辩说那些并不是一个页面上真正的页头和页脚,它们只是标题和其他附加信息或者诸如此类的内容。其实这是个语义问题,是没法明确解决的,比如你称为首行的,我可能叫它头部。关键是你用于网页的唯一标签有朝一日可能变得不再唯一。避免这种情况发生的最好办法就是一开始就全部使用类。

那么有没有什么用得着ID的地方呢?当然可以有。总会有一些情况下,你可以确定某个元素在页面中是唯一的,并且永远不会重复。还有一些情况下,你希望用ID给选择器赋予更高的特殊性,以便轻松地使它胜过其他选择器。并且ID对于脚本编程、链接目标和其他CSS之外的东西都至关重要,你只需要在书写CSS时小心一点儿就是了。

还有另外一种可以使用ID且不用担心特殊性问题的方法,我们在2.13节介绍。2.9 ID 与类共用

偶尔可能会有这样的情况,有一个唯一的元素并且它还是一大类元素中的一分子。例如,假设站点侧边栏中有一堆小面板,每个面板都有一个框并且具有特定的颜色和字体,但是每个面板都有独特的地方,比如每个都有不同的背景图等。

在这种情况下可以给元素同时应用类和ID,就像这样:<div class="panel" id="weather"><div class="panel" id="stocks"><div class="panel" id="latest">

然后,在CSS里就可以按需处理了。.panel {border: 1px solid silver;background: #EEE top left no-repeat;color: #333;font: x-small sans-serif;}#weather {background-image: url(/pix/panel-weather.jpg);}#stocks {background-image: url(/pix/panel-stocks.jpg);}#latest {background-image: url(/pix/panel-latest.jpg);}

你甚至可以把其中两个组合为一个选择器,像这样:.panel#weather {font-weight: bold;}#latest.panel {color: #511;}

没错,它们的书写顺序无关紧要,不必跟它们在HTML中出现的顺序一致。2.10 多类

元素的class属性有个经常被忽略的能力,就是你可以用任意多的词组成一个由空格分隔的列表,将其作为该属性的值。换句话说,就是你可以给元素应用多个类。

作为例子,我们继续使用之前的标记并把它改成不使用id属性的状态:<div class="panel weather"><div class="panel stocks"><div class="panel latest">

然后只需要调整CSS使其能够应对用类代替ID的情况:.panel {border: 1px solid silver;background: #EEE top left no-repeat;color: #333;font: x-small sans-serif;}.weather {background-image: url(/pix/panel-weather.jpg);}.stocks {background-image: url(/pix/panel-stocks.jpg);}.latest {background-image: url(/pix/panel-latest.jpg);}.panel.weather {font-weight: bold;}.latest.panel {color: #511;}

类在HTML源代码中的书写顺序与在样式表中的书写顺序无关,即.panel.weather的效果跟.weather.panel的效果是一样的,并且无论这两个类在HTML源代码中的书写顺序如何,特殊性也都是一样的。它们在源代码中是否被其他类名分隔开也是无关紧要的,比如:<div class="weather alert tornado watch panel">

此时通过.panel.weather和.weather.panel都可以选中该元素。

有个正在慢慢被忽略的注意事项:IE6及早期版本不识别样式表中多个类的写法,当使用.panel.weather时,IE6只会识别成.weather。尽管在HTML中仍然可以使用多个类,并且可以在CSS中定位这些类,但是每次只能使用一个类。因此,.weather和.panel都可以在IE6下很好地匹配之前例子中的标记,只是IE6会假定.weather.panel给元素应用了一个包含panel这个词的类,这可能并不是你想要的结果。2.11 简单的属性选择

属性选择器是CSS2中引入的并且在CSS3中得到了扩展,截至撰写本书之时已经得到所有主流浏览器的支持(IE6除外,如果这对你来说是个问题的话,参见1.10节)。

最基本的思路就是可以通过元素已有的属性选择元素,或者基于元素属性值的某个方面进行选择。因此,你可以选择所有确实是链接的a元素,像这样:a[href]

该选择器会选择所有含有href属性的a元素,不会选择没有href属性的a元素,命名锚点(例如<a name="top">)就是最明显的例子。它基本上是a:link, a:visited的一个简化版。例如应用:a[href] {color: green;}

则页面将会如图2-10所示。

至于href的属性值是什么一点都不重要,实际上甚至连属性值是否为合法的URI(UniformResource Identifier,统一资源标识符)或其他资源都无关紧要。选择<a href="#">和选择<a href="http://w3.org/">的方式完全相同。图2-10 通过属性选择器选择链接元素

现在,如果想选择指向某个特定地址的全部超链接该怎么做呢?若想筛选出一个特定的URI,只需要这么做(如图2-11所示):a[href="http://w3.org/"] {font-style: italic;}图2-11 通过属性选择器选择具有特定URL的链接元素

该选择器只会选择href属性值中包含http://w3.org/的链接元素,注意到我的措辞了吗?我没有说“指向W3C网站的超链接元素”,因为事实并非如此。这里面的原则是必须做到精确匹配,每个字母都要匹配。如果有这样一个链接<a href="http://www.w3.org/">,则该选择器不会选择这个链接,因为必须做到精确匹配才行。

或许这对于超链接来说用处不太大,但是它可以帮助筛选特定的图像(以便为其添加样式),比如公司的标志。如果你的CMS总是为页顶的标志生成这样的标记:<img src="/img/2010/mainlogo.png" alt="ConHugeCo Inc." />

则总是可以用这样的方式选择该图像:img[src="/img/2010/mainlogo.png"]

你不需要给它设置类或者ID或者其他什么东西,而只需要基于src的值给它设置样式。前提是,正如我所说,你应该确定它总是会保持那个特定的值,而不会变成其他值。(对于非精确匹配,请参见2.14节。)

有一点需要注意,按照CSS规范,“选择器中属性名和属性值是否区分大小写取决于文档语言”(参见www.w3.org/TR/CSS2/ selector.html#matching-attrs)。换句话说,有些标记语言可能会区分属性名的大小写,而另外一些语言则不会。XHTML就区分大小写,所以你最好假定属性名和属性值都是区分大小写的。2.12 类的属性选择

如果读过前面一节,你可能会想:“嘿,我可以用属性选择器改造一下.class符号啊!”是的,确实可以。只不过并不是我之前展示过的某种方法。

通过属性选择器我们可以实现跟div.panel一样的效果:div[class~="panel"]

看到波浪号(~)了吗?就在等号的前面,在这种情况下它绝对是个关键部分。它的存在意味着该属性选择器会选择“以空格分隔的类名列表中包含该词的元素”,这就是小小波浪号的巨大作用。

为了加深理解,我来展示一下没有波浪号的话会发生什么。去掉波浪号后的选择器为:div[class="panel"]

这会选择任何class属性的值为panel且只能为panel的div元素,如果某个元素的class属性值为panel weather,则这个选择器无法匹配该元素,因为panel和panel weather不是精确匹配。而另一方面,div.panel则可以很好地匹配<div class="panel weather">。

通过使用波浪号可以获得与“点类”(dot-class)语法完全相同的效果,因此下面两个规则除了输入的字母不同之外,其他方面完全相同:div[class~="panel"]div.panel

这时候你或许会想,“嘿,真棒!我一直想知道如何才能选择一个又长又复杂的类呢。”嗯,但是记住,属性选择器并不只局限于我们上面使用过的这两个属性——类和id。你可以选择任何属性值为可以被空格分隔的一串单词的元素,而说到“单词”,我指的是“字符构成的字符串”。

下面是使用属性选择器的其他几个例子:img[alt~="figure"] 任何alt属性文本中包含“figure”的图像元素table[summary~="data"] 任何summary属性文本中包含“data”的表格元素*[title~="2009"] 任何title属性文本中包含“2009”的元素2.13 ID 还是属性选择器

不仅可以把属性选择器当做类选择器的加长替代版,还可以用它代替ID选择器。下面两条规则会选择同样的元素:p#lead-in {font-weight: bold;}p[id="lead-in"] {font-weight: normal; font-style: italic;}

很好,不过先花点儿时间考虑一下这两条规则的视觉效果:如图2-12所示,lead-in段落会同时拥有加粗和斜体的效果。图2-12 根据不同的特殊性合并样式

这是因为属性选择器的特殊性贡献为0,0,1,0,与类和伪类的贡献相同。因此第一条规则的特殊性为0,1,0,1,而第二条规则的特殊性为0,0,1,1。在字体粗细的斗争中,第一条规则因其较高的特殊性胜出了。

这是特殊性的一个有趣的小技巧,通过它可以引入崭新的创作模式。例如,你或许还记得之前在2.8节讨论过的ID会轻松地胜过类,并且你或许已经考虑用类来选择所有的标签了。如果用户群都在使用支持属性选择器的浏览器,那么你就又可以把ID和类混着用了,然后在需要通过ID引用元素时使用属性选择器即可。通过这种方式,就不用担心#ID选择器的特殊性会胜过你写的其他任何东西了。2.14 部分属性值选择

当CSS2完成之后,下个版本的CSS制定工作就开始了,即我们称为CSS3,尽管已经不再是一个规范了(这个说来话长)。其中一个最受关注的领域就是选择器,而属性选择器也不例外。规范的制定者们挑选出了一系列子串匹配模式,它们全都非常有用。

最基本的一个是子串匹配器。要想看看它有多大用处的话,考虑一个之前的例子:a[href="http://w3.org/"]

该选择器会选择全部指向这个精确URL的链接元素,这没有任何问题。然而,假设有很多个链接到W3C网站,而不只是主页的链接,并且你还想对它们应用同样的样式。解决这个问题的一个好办法就是只选择URL中的w3.org部分,如图2-13所示。下面是具体做法:a[href*="w3.org"] {font-weight: bold;}图2-13 选择所有URL中包含w3.org的链接元素

是的,只需要在等号前面加个星号就行了。注意这不是通用选择器,也不能把星号放到值的前面来创建UNIX或者grep式的通配符。你只能把它放到等号的前面,意思是“属性值中将包含该字符序列”。

一如既往,这个选择器也可以用在任何元素的任何属性上。回顾之前选择公司唯一标志图像的例子,你还可以这样写:img[src*="mainlogo.png"]

它会选择任何指向mainlogo.png文件的图像(img)元素,或者是src属性值中包含mainlogo.png这些字符的图像元素。因此,它将同时选择:<img src="/img/2010/mainlogo.png.gif" alt="ConHugeCo Inc." /><img src="/img/2010/mainlogo.pngdir/big.png" alt="ConHugeCo Inc." />

然而,你或许不应该这样命名文件和目录。当然,这只是建议。

有许多创造性的方式可以使用这种特殊的能力,通过选择图像元素URL中对应的部分可以选择来自于特定目录中的图像。因此,根据链接元素的href属性值,你就可以对链接到你网站某个特定区域的链接应用样式了。a[href*="/contact"] {color: maroon;}a[href*="/news"] {font-weight: bold;}

请牢记,应该区分属性值的大小写,因为这样可以把事情变得简单。因而下面的3个例子中头两个可以匹配到,而第三个则不能。img[alt*="Figure"] {border: 1px solid gray;}<img src="fig1.gif" alt="Figure 1. The larch." /><img src="fig2.gif" alt="Figure 2. Mayor Quimby, a political figure of some note." /><img src="digg.gif" alt="Several men trying to figure out how to dig a hole." />

第三个图像之所以没有匹配是因为“figure”与“Figure”是不同的。当然,在这种情况下或许是个好事,因为(根据alt的文本)第三个图像并不是作为常规的插图(Figure)出现的,它只是恰好在alt属性值中含有“figure”这个词而已。这也没关系,但是要知道下面的情况也会被匹配:<img src="lost.gif" alt="Lost again. Figures, don’t it?" />

是的,是一样的“Figure”,所以会匹配。

如果知道大小写只差一个字母,那就很容易突破这种限制了。因此,如果想确保选择全部包含“Figure”和“figure”的实例,那么可以让选择器变成这样:img[alt*="igure"] {border: 1px solid gray;}

当然它也会匹配诸如“configure”、“disfigure”和“oliguresis”等(仅举几例)这样的实例。

然而,子串选择并不止于此。具体将在下一节详细解释。2.15 更多部分属性值选择

尽管任意属性值的子串匹配已经很好了(见2.14节),但有时你可能想限制只查找属性值的开头或结尾部分。幸运的是,确实有几个办法可以实现。

如果想根据属性值的开始部分的子串进行选择,可以使用这种模式:a[href^="http"]

由于有了脱字符(^),该规则会选取任何href属性值是以http开头的链接元素。用这种方式很容易选择全部的站外链接。假设全部站内链接都是相对于页面或者站点的,并且在文件系统里不使用http字符串,则你可以简单地这样写:a[href^="http"] {font-weight: bold;}

或者更复杂一点儿,像这样:a[href^="http"] {padding-right: 18px;background: url(/pix/external.png) 100% 50% no-repeat;}

结果如图2-14所示。图2-14 为http开头的链接添加图标

要想根据属性值的结尾子串选择元素,可以使用这个模式:a[href$=".pdf"]

由于有了美元符号($),该规则会选择href属性值是以.pdf结尾的链接元素。这是使PDF文件下载链接变得显眼一些的简单办法,如图2-15所示。例如:a[href$=".pdf"] {padding-right: 18px;background: url(/pix/pdf.png) 100% 50% no-repeat;}图2-15 PDF文档链接的PDF图标

这真是太棒了!下面还有其他一些使用属性选择器对链接应用样式的例子。a[href^="https"] 安全服务器链接a[href^="mailto"] 电子邮件联系链接a[href^="aim"] AOL即时通信服务链接a[href$=".doc"] 微软Word文档a[href$=".xls"] 微软Excel文档a[href$=".zip"] 压缩文档

一如既往,记住这里并不局限于超链接。如果回想一下2.14节里“Figure”的那个例子,你会很快意识到许多问题都可以用一个简单的脱字符解决:img[alt^="Figure"] {border: 1px solid gray;}

现在我们选择的图像元素的alt文本是精确地以“Figure”开始的,并且不用担心“Figure”出现在alt文本的中间或者其他地方的情况,这些情况都会被跳过。2.16 选择后代元素

基于元素在文档结构中的位置选择元素也很常见,这通常是使用后代选择器实现的,就像这样:

div#header a {color: #DEFACE;}

该规则会选择id为header的div的后代元素(包含在div中)中的全部锚点元素(a)。

大多数情况下,这确实是我们想要的效果:选择头部的链接元素而不用管它们在头部的位置,也不用考虑两个元素之间会不会有其他元素。

然而,有时候你或许想选择作为其他元素子元素的那些元素,而不是任意的后代元素。想象一下只选择作为有序列表(ol)元素子元素(而不是后代元素)的列表项(li),如图2-16所示。这样,如果有任何无序列表包含在该有序列表中,则它们的列表项不会被选中。我们需要的只是一个子选择符。ol > li {list-style-type: upper-alpha;}图2-16 只选择作为有序列表子元素的列表项

这个大于号限制了只能选择有序列表的子元素。如果把它去掉的话,该规则就会应用在作为有序列表后代的任何列表项上,包括嵌套的列表项(参见图2-17)。

是的,确实会发生这种情况,我没骗你。图2-17中展示的是含有有序列表标志的无序列表,发生这种情况只是因为我去掉了大于号。图2-17 无序列表项被应用了编号样式2.17 模拟部分子选择

如果不得不支持像IE6这种不支持子选择符的古董浏览器,并且也不想靠JavaScript添加对这些浏览器的支持(见1.10节),那你可以通过通用选择器模拟子选择。

假设我们希望为id为main的div中的全部div添加一个边框(如图2-18所示),使用子选择符的方式为:

div#main > div {border: 1px solid gray;}图2-18 伪造子选择好了,那么我们怎么模拟这个效果呢?这样:div#main div {border: 1px solid gray;}

div#main * div {border: 0;}

第二条规则选择了id为main的div的所有后代的后代div元素。实际上,它撤销了第一条规则的效果。两条规则都应用到div#main的孙子div上,并且都设置了边框,因而它们是冲突的。但因为它们具有相同的特殊性,所以最后一个规则胜出。至于div#main中的div元素则只能被两条规则中的第一个选中,所以边框仍然还在。

有一点是需要牢记的,这个“伪造”子选择技术只能用于非继承的属性。对于可继承的属性,你可以创建一些意想不到的效果。举个例子,假设你写了如下规则:ol li {font-weight: bold;}ol * li {font-weight: normal;}

假设你希望加粗具有某个特定类名的有序列表下的无序列表的字体(如图2-19所示):ol.urgent ul {font-weight: bold;}图2-19 继承的样式被直接指定的样式覆盖了

加上这条规则后,这些无序列表中的列表项会怎样?不加粗!为什么?这是因为之前展示的ol * li规则会直接作用到这些列表项上,因此,font-weight: normal会覆盖它们从ol.urgentli规则继承来的加粗(bold)的值。

如果使用非继承的属性就不会发生这种情况了,比如background(背景)、border(边框)、display(显示)、margin(外边距)、padding(内边距)等。如果你还不清楚哪些属性是继承属性,可以看这里w3.org/TR/CSS2/propidx.html或者看CSS规范中关于属性的描述部分。2.18 兄弟选择

除了能够依据父子关系和祖先后代关系选择元素之外,基于元素已有的兄弟元素(即它们共同拥有一个父元素)来进行选择也是可能的。在图2-20中可以看到,突出显示的即为兄弟元素。图2-20 突出显示的兄弟元素

其实列表项就是很典型的兄弟元素,任何拥有同一个父元素的元素都是兄弟元素。图2-21中选择了跟在二级标题后的段落元素。图2-21 选择紧跟在二级标题后的段落元素

CSS定义了一个选择符,它允许基于元素之前的兄弟元素来选择元素。例如,如果想移除任何紧跟在二级标题(h2)元素后的段落(p)元素的上外边距,则可以这样写:h2 {margin-bottom: 0;}h2 + p {margin-top: 0;}

兄弟选择很适合用于选择某些特定的元素组合并为其设置样式,比如为紧跟在div后面的表格或者标题元素之后的列表元素增加间距等。

还有一个非常类似的选择符,它允许选择后续的兄弟元素,但不包含直接相邻的兄弟元素。它使用一个波浪号作为选择符:h1 ~ ul {list-style-type: lower-alpha;}

对下面的标记来说,这段代码会选择位于h1元素后面且与之共享父元素的ul元素,除第一个ul之外。<body><ul>…</ul><h1>Planning</h1><p>This is an abstract.</p><ul>...</ul><ul>...</ul><h2>Introduction</h2><p>We have some thoughts here.</p><ul>...</ul></body>

由于这些元素共享父元素(body元素),因而它们都是兄弟元素。因为第一个列表元素没有跟随在一级标题h1后,所以它没有被h1 ~ ul选中,而其他列表元素都被选中了,虽然它们中间还有其他元素。2.19 生成内容

CSS提供了一种可以生成内容并将其插入到文档中的方法,这使通常意义上的内容与表现的界限变模糊了。该方法是通过伪元素:before和:after以及它们的content属性实现的。

下面是个插入内容的例子(图2-22中亦有展示),在全部列表项的文本之前插入一个短字符串:li:before {content: "Item: "; border-bottom: 1px solid gray;}图2-22 在列表项前面加点儿内容

注意在content属性值中的空白,它是作为属性值字符串的一部分插入的。如果没有这个空白,也就没有右侧的内边距,元素就会和生成的内容贴得太近。(当然,我们完全可以给生成的内容设置右侧内边距,只是这里没这么做而已。)

要知道,只能插入文本而不能插入结构。如果试图把标记插入到content属性值中,则标记会被转换成纯文本。li:before {content: "<em>Item:</em> "; border-bottom: 1px solid gray;}

如图2-23所示,悲剧了。图2-23 标记被转换成了纯文本

另一方面,你也可以插入浏览器所支持的任何字符或图像字符(如图2-24所示),只需要知道它们的十六进制字符编码,并且在前面加一个反斜杠(也称作转义符)即可。li:before {content: "\BB ";}图2-24 通过转义编码插入字符

理论上讲,直接把Unicode字符写进CSS,把样式表全部改成Unicode编码也是可行的。然而,如果服务器配置为只能使用ASCII编码发送CSS,那可能会出现问题。如果你可以更改服务器配置的话,就不必转义十六进制字符,直接把字符写进样式表即可。不过,经过全面的测试,较老的浏览器可能无法恰当地处理Unicode字符。

:before和:after是伪元素,因为它们就好像在元素中插入了一个封闭的元素。这种伪元素可能放到元素内容的前面或着后面,这取决于使用了哪个伪元素。你可以像对待span那样对伪元素应用样式。

使用生成内容可以做很多有趣的事情,不过也要谨慎。设想如果CSS没有载入或者不被支持,页面会发生什么情况(例如在一些移动设备上)?如果使用生成内容插入一些对理解页面至关重要的内容,那么当没有生成内容时就会很麻烦。因此,强烈推荐仅将生成内容用于渐进增强(progressive enhancement)的特性,以便当页面不支持这些特性时也可以正常展现内容。

向页面的打印副本中插入超链接URL就是个很好的渐进增强示例,如图2-25所示。为了实现这种效果,把下面的规则添加到打印媒体样式表中:a[href]:after {content: " [" attr(href) "]"; font-size: smaller;}

这算是渐进增强,因为当浏览器不支持该特性时,打印页面会像往常一样只显示链接,而不显示生成的URL。当浏览器支持该特性时,打印页面就明显地被增强了。(关于该技术的更多内容,请见“Going To Print”,地址为http://alistapart.com/articles/goingtoprint。)

生成内容已经得到了广泛支持,但IE8之前的版本不支持生成内容。要让IE8之前的版本支持生成内容,可以使用第1章介绍的IE9.js。图2-25 向打印样式表中插入链接URLPart 2 第二部分 核心技术第3章 提示

一些恰当的提示可以让人受益终生,而我最喜欢的两个就是“永远偏爱漂亮街道上的小房子而不是糟糕街道上的大房子”和“不要吃铅笔铅”。同理,在CSS中一些巧妙的只言片语也能立刻让你如虎添翼。

本章我们将讨论关于属性值排序的重要性、正确使用无单位的值、隐藏元素的几种方式、控制边框样式的方法、列表技巧以及打印样式的开发等内容。3.1 验证

这可能已经是老生常谈了,你或许想知道为什么我会在这么显而易见的话题上浪费笔墨纸张。然而,你究竟多久才会真正进行一次验证呢?是在项目结束时验证一次,还是自始至终都进行验证呢?

当然,我并不是让你在编辑文档中每次单击“保存”时都进行验证,而是建议在构建页面的过程中养成定期验证的好习惯,这样就能在问题还没有影响到整个页面之前及时地发现它们。

目前已经有一些很成熟的HTML和CSS验证器了。在HTML领域,或许应用最广泛的验证器就是W3C自己提供的(地址为validator.w3.org,如图3-1所示),而它以CSS为中心的“表兄弟”也同样很受欢迎,网址为jigsaw.w3.org/css-validator/。

如果你是被卡在某个防火墙后面进行开发,或者全部开发工作都在笔记本上的本地Web服务器上进行的话,该怎么办呢?别担心,这时你可以使用Firebug或者其他开发工具的“本地验证”功能。只要你能浏览网络,就可以验证任何能看到的页面,而不用考虑正在浏览的页面是否能被公开浏览。(即使页面是在公共的网站上,我也习惯使用“本地验证”,只是为了养成习惯而已。)图3-1 W3C的HTML验证器3.2 调整字体值的顺序

这是CSS的一个小“怪癖”,很多人都会栽在这里,甚至有时候根本不知道问题出在了哪里。

大多数允许使用多个关键字的CSS属性都允许以任何顺序书写关键字(想想background的例子,它允许你以任何顺序指定1~5个关键字),但是千万别说它们每个都是这样。font(字体)属性是很少见的两个例外之一,它不仅对最基本的关键字组合有限制,还要求按照特定的顺序进行书写。

下面是一个最基本的font声明:font: <font-size> <font-family>;

当然,你可以把括起来的词换成实际的值,就像这样:font: 100% sans-serif;

问题的关键是,你必须同时包含这两个值并且按照既定的顺序进行书写,即首先是字号(font-size),然后是字体族(font-family)。如果颠倒了顺序,或者漏掉了其中的一个,则任何现代浏览器都会完全忽略这条声明。

此外,如果在声明中包含了其他关键字,则它们全部(有一个例外,将在下一节讨论)都得放在这两个必备的值前面,即:font: bold italic 100% sans-serif;font: italic small-caps 125% Georgia, serif;font: italic bold small-caps 200% Helvetica, Arial, sans-serif;

注意,这些在字号前面的属性值的顺序可以随意打乱,只要确保它们都在字号的前面即可。再次强调,如果放到字号的后面,浏览器就会忽略整条声明。3.3 玩转行高

如果你认为上一节建立的font值模式有点古怪,那么下面将是彻头彻尾的时髦技术。

之前我说过,字体的属性值至少要包含两个值,并且必须以“先字号后字体族”的顺序书写。这是对的,但是有时候也可以在字号值上放一个可选的行高值来作为字号的某种附属值(如图3-2所示)。它看起来就像这样:

font: 100%/2.5 Helvetica, sans-serif;图3-2 增大后的行高(另见彩插图3-2)

在字号和行高值之间没有空格,只有一个斜杠。

为font声明添加行高值的操作总是可选的,但是如果已经包含了行高值,则它的放置位置就是固定的了,必须紧跟在字号后面用一个斜杠再加上行高值才行。3.4 无单位的行高值

line-height(行高)属性既可以接受无单位的数值,也可以使用带单位的行高值——尽管一般情况下不推荐这么做。

那么它们之间有什么区别呢?当你定义了一个有单位的值,例如1em或者100%时,就会将计算后的行高值传给全部的后代元素。例如,假设将下面的CSS应用到包含下列标记的文档中:ul {font-size: 15px; line-height: 1em;}li {font-size: 10px;}small {font-size: 80%;}<ul><li>I’m a list item with <small>small text</small>.</li></ul>

无序列表(ul)元素的行高计算值为15 px,这是因为对于行高来说,基于em的值是使用元素本身计算后的字号值来进行计算的,对于百分比也是一样的。由于直接声明了字号值,因而我们可以得出字号计算后的像素值。

这里有一个可能会令人惊讶的特性:这个15 px的计算值会传给后代元素。换句话说,li(列表项)元素和small元素会继承一个15 px的行高值。它们不会再根据自身的字号值改变行高值。事实上,它们根本不会再改变行高值,而只是直接获取并使用那个15 px,就跟我这么写的效果是一样的:ul {font-size: 15px; line-height: 1em;}li {font-size: 10px; line-height: 15px;}small {font-size: 80%; line-height: 15px;}

好了,现在假设我拿掉行高值的em单位,则样式会变成:ul {font-size: 15px; line-height: 1;}li {font-size: 10px;}small {font-size: 80%;}<ul><li>I’m a list item with <small>small text</small>.</li></ul>

现在传给后代元素(li和small元素)的是这个原始数字,用来表示后代元素所使用的一个换算系数(比如一个乘数),而不是计算后的结果值。

因此,所有继承了这个1的元素会把这个值同它们自身的字号计算值相乘。声明了font-size:10 px的列表项元素会有一个10 px的计算后的行高值,并且会将这个1传给small元素,而small元素也会把这个值与自身的计算字号值相乘,因此这个8 px的字号计算后得到的行高值也是8 px。

最终结果与这样写的效果是相同的:ul {font-size: 15px; line-height: 1;}li {font-size: 10px; line-height: 10px;}small {font-size: 80%; line-height: 8px;}

如图3-3所示,这是一个非常重大的差异。这就是总是推荐在诸如html或body元素以及任何可能包含后代的元素上应用行高值时使用无单位数值的缘故。图3-3 有单位行高值和无单位行高值之间的差异3.5 避免缺少样式的边框值

边框可以为任何设计增添良好的体验,但是如果没有边框样式,那么你想通过border(边框)声明来达到效果就不太可能了。

当我说“缺少样式”的时候,不是指CSS样式,而是指border-style的值。例如,假设你写了这样的规则:

form {border: 2px gray;}

很好,但是表单周围并不会出现边框。原因很简单,因为省略了border-style的值意味着将会应用border-style的默认值,而默认值是什么呢?none!所以前面的规则相当于:form {border: 2px gray none;}

无论把border-width的值设得多大,border-style值为none的边框都永远不会被绘制出来,因为一个不存在的边框是不会有宽度的。3.6 使用颜色控制边框外观

你可能时不时地会遇到创建立体的内嵌边框(inset)或者外置边框(outset)的需求(或者只是希望达到这样的效果)。我并不是在这里评判好坏,只是指出一个可能的陷阱。考虑下面的代码:div {border: 5px red outset;}

够简单了吧?但是我们看看它在不同浏览器中是如何被处理的,如图3-4所示。

这并不是一个错误,而且每个浏览器都没有错。因为CSS规范中并没有说明应该如何修改边框的颜色来产生立体的效果,规范中只说:

关于边框的“groove”、“ridge”、“inset”和“outset”这几个值的颜色绘制,取决于元素的边框颜色属性,但是用户代理可能会选择自己的算法计算实际使用的颜色(参见www.w3.org/TR/CSS21/box.html#border-style-properties)。图3-4 不同浏览器中内嵌边框和外置边框的不同表现

注意最后一部分:“用户代理可能会选择自己的算法……”,这是一个长期建立起来的关于Web开发的真理,那就是永远要保留可选择的机会,而浏览器则一如既往地这么做了。

可能你并不介意这些边框的差异,如果是这样的话,那就太酷了。再次说明,我不在这里评判好坏。如果你想让这些边框的效果在各个浏览器中保持一致的话(如图3-5所示),则你需要声明一个实线的边框并自己设置各边的颜色。#innie {border-color: #800 #F88 #F88 #800;}#outie {border-color: #F88 #800 #800 #F88;}

很明显,这招只对内嵌边框和外置边框起作用。如果想创建表现一致的彩色凹槽(groove)边框和山脊状(ridge)的边框(如图3-6所示),你就必须在元素的外面或里面增添一层容器,并且为每条边单独指定颜色来做出想要的视觉效果,就像这样:#innie {border-color: #800 #F88 #F88 #800;}form {border: 3px solid; border-color: #F88 #800 #800 #F88;}<form><div class="innie">(content and form inputs and so on here)</div></form>图3-5 通过彩色实线边框实现表现一致的内嵌边框和外置边框效果(另见彩插图3-5)图3-6 表现一致的“山脊”状的边框(另见彩插图3-6)3.7 抑制元素的显示

你有没有想过让一个元素在页面上消失,但不在文档源代码中删除它呢?这里就有几种方法可用,不过每种方法各有利弊。本节及下面几节将具体讨论这些方法。

最显而易见的让元素消失的办法就是关掉它的显示:.hide {display: none;}

当然,这会抑制全部含有hide这个类的元素的显示,这意味着任何这样的元素都不会生成元素框。因此它不会对其他元素的布局产生任何影响,就像它根本不存在一样,或者说像一个忍者。

这里有两个关于display: none的陷阱,一个是潜在的,一个是固有的。潜在的问题是,如果直接通过JavaScript将显示值设为none,那么想复原时该怎么做呢?这可比它看起来要棘手得多,假设你写了:var obj = document.getElementById(`linker´);obj.style.display = `none´;

然后,在JS中想让这个元素再显示出来的话,你会给它设置什么值呢?这其实依赖于元素,不是吗?如果是span元素的话,你可能希望这个值是inline,而如果是p(段落)元素,则可能希望这个值为block。话说回来,可能也不是这些值,因为你可以很轻松地让span生成块状(block)框或者让div生成行内(inline)框。

下面是一个非常普遍的解决方案,即不指定任何值:obj.style.display = `´;

这会使元素的显示(display)值默认恢复为在其余CSS中设置的值,或者恢复为浏览器的内置样式值。

另一个普遍的解决方案是不直接设置显示值,而是添加一个可以隐藏元素的类。当你之后需要显示元素的时候,去掉这个类就可以了。这就有点儿复杂了,因为你需要编写(或者通过Google找)可以添加和删除类的JavaScript代码,不过这确实是个很有效的解决方案。

那个固有的问题是,(截至撰写本书之时)显示值为none的元素无法被绝大多数的辅助技术(比如屏幕阅读器)“看到”。因为元素没有呈现在屏幕上,所以阅读器无法找到它,更不会读出它。尽管有时候这确实是我们想要的效果,但另外一些时候,也确实可能不是想要的效果。

例如,假设在页面中有一个辅助链接(通常叫做“跳过链接”),你希望通过它们能让使用屏幕阅读器的读者直接跳到指定文档,但不希望视力正常的人在屏幕上看到它们。那么,如果把它们的容器设置成display: none的话,那它们就对于所有人都消失了,无论他们视力正常与否,而真正需要它们的人也听不到了。

类似地,如果有一个对视力正常者隐藏的下拉菜单(缺少鼠标动作),如果是通过display:none隐藏的话,则屏幕阅读器也无法找到这个菜单。3.8 抑制元素的可见性

跟抑制元素的显示方式类似,你可以通过将visibility(可见性)的值声明为hidden把元素的可见性降为0。.hide {visibility: hidden;}

该规则会使元素不可见,这可能听上去像是元素没有display属性。然而,这里有个很关键的差异:被设置成visibility: hidden的元素仍然参与页面布局,在图3-7中可见一斑。

那么除了多占了点儿地方,不可见的元素还有什么好处吗?用鼠标的人无法与它交互,甚至不能通过键盘访问它,并且毫无疑问你也看不到它。那么何必呢?

好吧,这对于绝对定位元素来说没有什么问题,因为它们已经不参与页面布局了。(绝对定位元素位于其他元素的上方,并且在对其他元素进行布局时不被考虑在内。)因此,你可以将它们的可见性值在hidden和visible之间随意切换,而这不会影响页面布局。一个好处是,你还可以在不妨碍元素显示角色的情况下显示或隐藏它们,从而避开前面章节提到的潜在问题。图3-7 不可见元素

遗憾的是,同样的无障碍访问问题仍然存在:被设置成visibility: hidden的元素会被绝大多数屏幕阅读器所忽略,而通过这种方式隐藏的下拉菜单对于可朗读的浏览器也是隐藏的。3.9 将元素移出屏幕

那么,如果想对视力正常者隐藏一个元素,同时让屏幕阅读器也能识别的话,该怎么做呢?这里有个办法:

.hide {position: absolute; top: -10000em; left: -10000em;}

完成之后,第三段(图3-7中留有很大一片空白的段落)其实已经被从页面上移除了,如图3-8所示。

是的,CSS获取了第三段,然后使它绝对定位,再将它移出了屏幕。这样做会将元素移到视野之外,但是至少仍能被某些屏幕阅读器识别。这就是这种技术会被当做隐藏元素的更佳选择的原因。图3-8 通过定位将元素移出屏幕

然而从技术上讲,你是把元素移到了它的上部,距离它的左上角10 000 em(也即1万倍的元素字号大小)的位置,然后再将元素移到它的包含块左上角的左侧10 000 em的位置上。在很多情况下,包含块是根元素,譬如html元素。而另外一些情况下,包含块可能是文档中的其他元素。无论哪种情况,上面的样式都具有压倒性的优势,它会将元素定位在视线之外很远很远的地方。

那么,如果想让它再回来的话,有几个选择可用。如果想让它在可见的时候是绝对定位的,则可以简单地设置top和left值将它放到特定的位置上。基本上像是这样:.show {top: 0; left: 0;}

另一方面,如果你想让它回到正常的文档流中,则可以将它的position(定位)属性设置成CSS的默认值。.show {position: static;}

如果采用这种方式,就不需要重设top和left的值了,因为在静态定位的元素中这些属性将完全被忽略掉。无论你是否重设它们,都不会有任何差异。

如果想让元素回到正常的文档流中,但是还需要让它作为一个包含块来包含其他的元素,那么第三种方法就派上用场了。这种情况发生在你想要绝对定位一些元素,并且这些元素还包含在你想移回屏幕的元素内部时。在这样的情况下,可以对元素采用相对定位,但是必须声明偏移量。.show {position: relative; top: 0; left: 0;}

如果省掉了top: 0; left: 0;部分,则元素会从正常文档流中原始的位置开始偏移。这会使页面上元素本来应该出现的地方(元素没有被向上和向左移10 000 em时的位置,变成一个“洞”。

当然,这里你不必一定使用10 000 em这个值。在一些非常老的浏览器中,你可以使用不超过65 535的任何数字,而在Safari 3中这个上限是16 777 271,在其余的浏览器中则是2 147 483 647。你也可以使用其他任何有效的CSS度量单位,比如相对高度(em)、像素(px)、派卡(pica)和英寸(inch)都可以。关键是要设置一个非常大的数值,以确保你调用它之前该元素基本上不会有任何机会被看到。3.10 图像替换

图像替换是CSS中生命力最持久的设计技术之一,它是一类允许你用图像替换文本的技术,通过这种方式,文本仍然是可打印、可访问的。IR(Image Replacement,图像替换)一般用在小型的、有限的应用上,诸如公司标志、页面标题(如图3-9所示)等。它不适合用来替换整段的文本。图3-9 用图像替换标题

最受欢迎的图像替换技术通常被认为是Phark,也叫Rundle方法。(有多受欢迎?有人特地为它制作T恤呢。)基本上你需要做的就是,使用负的文本缩进把文本移到元素的左侧。h1 {height: 140px; text-indent: -9999px;background: url(page-header.gif);}

这在很多方面类似于使用绝对定位隐藏整个元素的技巧,但是相反地,实际上此处我们是在没有移动元素框的情况下将元素的文本内容移到了屏幕之外。

注意,背景图像几乎是不会被打印的。打印背景的选项是有的,但是默认是不打印背景图的,而且几乎没有人更改过这个选项。因此,在打印样式表(详见3.11节)中,可以简单地这样写:h1 {text-indent: 0; background: none;}

这个background: none只是个预防措施,几乎没人会开启打印背景图的选项。不过,为了以防万一还是应该这么做,因为这可以避免一级标题(h1)文本被打印在背景图像上。

有一种极端的情况可能会使这种替换方法失败,即当用户的浏览器允许使用CSS但却禁止显示图像时,而更普遍的情况是当图像由于某种原因而没有载入时。如图3-10所示,在这些情况下标题文本会消失,但不会被背景图像替换。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载