精彩绝伦的CSS第二章:选 择 器_精彩绝伦的CSS第二章:选 择 器试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > web > 精彩绝伦的CSS > 第二章:选 择 器

精彩绝伦的CSS——第二章:选 择 器

我们能真切地感觉到,选择器是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 :checked 2.2 为目标元素添加样式 当希望指向文档中的某个片段时,目标(target)会非常有用。什么,怎么实现?其实非常简单: Section 2.7 任何人单击这个链接(如果浏览器处理正确的话)将不止跳转到目标页,而且还会跳到页面中文档片段标识符(地址中的#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-caps medium-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(Uniform Resource 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.urgent li规则继承来的加粗(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 向打印样式表中插入链接URL

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《精彩绝伦的CSS》其他试读目录

• 前  言
• 第二章:选 择 器 [当前]