在介绍更强大的选择符之前,先花点儿时间讨论文档结构。
2.5.1理解父子关系
为了弄清选择符和文档的关系,我们要再一次分析文档的结构。以下述十分简单的HTML文档为例:
<html>
<head>
<base href=”http://www.meerkat.web/”>
<title>Meerkat Central</title>
</head>
<body>
<h1>Meerkat <em>Central</em></h1>
<p>
Welcome to Meerkat <em>Central</em>, the <strong>best meerkat web site
on <a href=”inet.html”>the <em>entire</em> Internet</a></strong>!</p>
<ul>
<li>We offer:
<ul>
<li><strong>Detailed information</strong> on how to adopt a meerkat</li>
<li>Tips for living with a meerkat</li>
<li><em>Fun</em> things to do with a meerkat, including:
<ol>
<li>Playing fetch</li>
<li>Digging for food</li>
<li>Hide and seek</li>
</ol>
</li>
</ul>
</li>
<li>…and so much more!</li>
</ul>
<p>
Questions? <a href=”mailto:suricate@meerkat.web”>Contact us!</a>
</p>
</body>
</html>
CSS发挥功能在很大程度上依赖于元素的父子关系。HTML文档中的元素是一种层次结构(其实,多数结构化文档都是如此),从文档的“树状”视图可见一斑(见图2-15)
图2-15:文档的树状结构
在文档的层次结构中,如果一个元素的位置直接在另一个元素的上方,我们说前者是后者的父元素。反过来,如果—个元素的位置直接在另一个元素的下方,前者是后者的子元素。
“父元素”和“子元素”是“祖辈元素”和“后代元素”的特例。二者之间有区别:在树状视图中,如果两个元素所在的层级是连续的,它们之间是父子关系,如果两个元素之间跨两个层级以上,它们之间是祖辈和后代的关系,而不是父子关系(子元素也是后代,父元素也是祖辈)。
2.5.2后代选择符
理解这个结构模型之后,首先得到的好处是定义后代选择符(descendant selector,也叫上下文选择符,contextual selector)。后代选择符定义的规则针对特定的结构。
此时,样式规则可以写成这样:
h1 em {color: gray;}
这个规则把作为hi元素后代的em元素中的文本显示为灰色。其他em元素,例如段落中的或引文中的,不受这个规则影响。从图2-16中可以清楚地看出这一点。
图2-16:根据上下文选择元素
在后代选择符中,规则中的选择符由两个或多个空格分隔的选择符构成。选择符之间的空格是一种连结符。如果从右向左读,空格连结符可以理解为“在内部”、“是其一部分”或者“是其后代”。因此,h1 em可以理解为“作为h1元素后代的em元素”
后代选择符中不只可以使用两个单独的选择符。例如:
ul ol ul em {color: gray;}
此时,一个无序列表中的有序列表中的无序列表中的强调文字将显示为灰色,如图2-17所示。显然,这针对的是非常具体的结构。
图2-17: 一个非常具体的后代选择符
假设一个文档中有一个侧边栏和一个主区域,侧边栏的背景为蓝色,主区域的背景是白色,而且两个区域中都有一个链接列表。此时不能把所有链接都设为蓝色,否则侧边栏中的链接就看不到了。
这种情况便可以使用后代选择符。我们先把侧边栏元素的class属性设为sidebar,把主区域元素的class属性设为main,然后编写如下的样式:
.sidebar {background: blue;}
main {background: white;}
.sidebar a:link {color: white;}
main a:link {color: blue;}
结果如图2-18所示。
图2-18:使用后代选择符为相同类型的元素应用不同的样式
再举个例子。假如想把blockquote中的b元素和常规段落中的b元素都显示为灰色,可以这么写:
blockquote b, p b {color: gray;}
结果是,段落或引文块中的b元素都显示为灰色。
人们经常忽略后代选择符的一点是,两个元素之间的层级间隔可以是无限的。比如说,ul em选择的是作为ul元素后代的任何em元素,不管em嵌套的有多深。因此,ul em能选择下述标记中的em元素:
<ul>
<li>List item 1
<ol>
<li>List item 1-1</li>
<li>List item 1-2</li>
<li>List item 1-3
<ol>
<li>List item 1-3-1</li>
<li>List item <em>1-3-2</em></li>
<li>List item 1-3-3</li>
</ol>
</li>
<li>List item 1-4</li>
</ol>
</li>
</ul>
后代选择符一个更容易被人忽视的细节是,它对元素的距离一无所知。也就是说,文档树中两个元素的距离对是否应用规则没有影响。遇到特指度(后文介绍)和相互抵消的规则时这一点很重要。
例如,对下述规则和标记来说(其中有个选择符在2.6.7节讨论):
div:not(.help) span {color: gray;}
div.help span {color: red;}
<div class=”help”>
<div class=”aside”>
This text contains <span>a span element</span> within.
</div>
</div>
第一个样式规则的意思是,“class属性中不包含help这个词的div元素中的span元素显示为灰色”;而第二个样式规则的意思是,“class属性中包含help这个词的div元素中的span元素显示为红色”。对上述标记片段来说,两个规则都将应用到span元素上。
2.5.3选择子元素
有时,我们不想选择所有后代元素,而是想缩小范围,只选择一个元素的子元素。为此,要使用子代连结符,即大于号(>):
h1 > strong {color: red;}
这个规则将把第一个h1中的strong元素显示为红色,第二个则不受影响:
<h1>This is <strong>very</strong> important.</h1>
<h1>This is <em>really <strong>very</strong></em> important.</h1>
从右向左读,h1> strong的意思是,“选择的strong元素是h1元素的直接子代”。子代连结符两侧的空格是可选的,因此,h1 > strong、h1> strong和h1>strong是等效的。
在文档的树状结构中可以清楚地看出,子代选择符选择的是直接连接的元素。图2-19是文档树的一部分。
图2-19: 一个文档树片段
在这个文档树片段中有几个父子关系。例如,a是strong的父元素,而它又是p的子元素。匹配这些元素可以使用p>a和a>strong选择符,但是不能使用p>strong,因为strong是p的后代,而非直接子元素。
2.5.4选择紧邻同胞元素
假设你想装饰紧跟在一个标题后面的段落,或者为紧跟在一个段落后面的列表设定特殊的外边距。若想选择同一个父元素中紧跟在另一个元素后面的一个元素,要使用紧邻同胞连结符(adjacent-sibling combinator),即一个加号(+)。与子元素连结符类似。
若想把紧跟在h1元素后面的段落的上外边距去掉,这样写:
h1 + p {margin-top: 0;}
这个选择符的意思是,“选择的p元素紧跟在h1元素后面,而且二者同属一个父元素”。
为了说清这个选择符的工作方式,再次以一个文档树片段为例,如图2-20所示。
图2-20:另一个文档树片段
在这个片段中,div元素有两个后代,一个是有序列表,一个是无序列表,而且二者都有三个列表项目。这两个列表是紧邻同胞,每个列表中的列表项目也是紧邻同胞。然而,第一个列表中的列表项目与第二个列表中的列表项目不是紧邻同胞,因为它们分属不同的父元素。
注意,使用一个连结符只能选择紧邻同胞中的第二个元素。因此,对li + li {font-weight: bold;}来说,只有第二个和第三个项目会显示为粗体,第一个项目不受影响,如图2-21所示。
图2-21:选择紧邻同胞
注意,两个元素之间的文本不影响紧邻同胞连结符的作用。以下述标记片段为例(文档树与图2-19 一样):
<div>
<ol>
<li>List item 1</li>
<li>List item 1</li>
<li>List item 1</li>
</ol>
This is some text that is part of the ‘div’.
<ul>
<li>A list item</li>
<li>Another list item</li>
<li>Yet another list item</li>
</ul>
</div>
即使两个列表之间有文本,仍然不妨碍使用ol + ul匹配第二个列表。这是因为处在中间的文本不算是同胞元素,而是父元素div的一部分。如果把那段文本放在p元素中,ol + ul就无法匹配第二个列表。此时,可以写成ol+p+ul。
如下面的示例所示,紧邻同胞连结符可以与其他连结符搭配使用:
html > body table + ul{margin-top: 1.5em;}
这个选择符的意思是,“选择的ul元素紧跟在身为同胞的table元素后面,这个table元素是body元素的后代,而body是html的子元素”。
与其他连结符一样,紧邻同胞连结符也可以实现复杂的选择符,例如div#content h1 + div ol。这个选择符的意思是,“选择的ol元素是div元素的后代,div元素是h1的紧邻同胞,而h1是id属性的值为content的div元素的后代。”
2.5.5选择后续同胞
Selectors Level 3引入一个新的同胞连结符,名为一般同胞连结符(general sibling combinator)。这个连结符使用波浪号(~)表示,选择一个元素后面同属一个父元素的另一个元素。
举个例子。若想让h2后面与它同属一个父元素的ol元素的文本倾斜,可以编写h2~ol{font-style: italic;)。两个元素不一定非得是紧邻同胞,不过是的话也能匹配。对下面的标记来说,应用这个规则后得到的结果如图2-22所示。
图2-22:选择后续同胞
可以看出,两个有序列表中的文本都倾斜了。这是因为两个ol元素都在h2后面,而且同属一个父元素(div元素)。