选择器
CSS 的一个主要优势——尤其对设计者而言——是它能够轻松地把一组样式应用到同一类型的所有元素。印象不够深刻?想象这样的场景:通过编辑单行 CSS,你可以修改所有标题的颜色。不喜欢正在使用的蓝色?修改一行代码,把它们都变成紫色、黄色、栗色等等任何想要的颜色。这可以让你,设计师,专注于设计而不是繁琐的工作。下次会议中,有人想看绿色标题,你只需要编辑样式然而点击重新加载。瞧!几秒钟就完成了,每个人都可以看到。
当然,CSS 不能解决所有问题——比如,它不能用来改变 PNG 图片的颜色空间,至少现在还不能——但它确实让全局修改变得容易多了。我们先从选择器和结构开始。
写好css会让你的代码从性能和体积上会有质的变化,这也是前端优化的一条途径,今天主要详细分析下css选择器。
基本样式规则
如上所述,CSS 的核心特性是将某些样式应用于文档中的整个元素类型的能力。例如,如果你想把所有h2元素的文本显示为灰色,使用老套的 HTML,你必须在所有的h2元素中插入<FONT COLOR="gray">...</FONT>
标签;使用style属性也不好,这需要你在所有的h2元素上添加style="color: gray;"
:
<h2><font color="gray">This is h2 text</font></h2>
<h2 style="color: gray;">This is h2 text</h2>
复制代码
显然,如果你的文档里面有许多h2元素,修改过程将是乏味的。更糟糕的是,如果你之后又想把所有的h2从灰色变成绿色,你又得重新开始手动设置一遍便签。(没错,以前就是这么干的!),这种模式会让开发者发疯,所以有了CSS 让你可以创建易于编辑的规则,并把它们应用于所有你定义的文本元素(下一部分将解释规则如何生效)。例如,简单地写一次下面的规则,把所有的h2元素都变成灰色:
h2 {color: gray;}
复制代码
如果你想把所有h2的文本变成另一种颜色——比如银色——只要简单地选择(属性)值:
h2 {color: silver;}
复制代码
元素选择器
元素选择器通常都是 HTML 元素,但也有例外。例如,如果 CSS 文件的样式是用于 XML 文档的,换句话说,文档的元素被用作最基本的选择器。在 XML 中,选择器可以是任何东西,因为 XML 允许创建新的标记语言,任何东西都可以作为元素名称。另一方面,如果为 HTML 文档添加样式,选择器一般是许多 HTML 元素之一,如p,h3,em,a,甚至html元素本身。例如:
html {color: black;}
h1 {color: gray;}
h2 {color: silver;}
复制代码
展示结果如下:
声明和关键字
声明块包含一或多条声明。一条声明的格式总是一个属性后面跟着一个冒号,然后一个值后面跟着一个分号。冒号和分号后面可以有零或多个空白。几乎所有的值都是单个关键字或者空白分隔的当前属性允许的多个关键字列表。如果在一条声明中使用了错误的属性或值,整条规则都会被忽略。因此,下面这两条声明是无效的:
brain-size: 2cm; /* unknown property 'brain-size' */
color: ultraviolet; /* unknown value 'ultraviolet' */
复制代码
分组
如我们所见,把一个简单样式应用在一个简单选择器上是非常简单的,但是如果你想把相同的样式引用在多个元素上应该怎么做呢?这种场景下,你会想要把多个选择器或多个样式应用在一个或一组元素上。
分组选择器
如果你想让h2元素和段落都显示灰色文本,最简单的方式是使用下面的声明:
h2, p {color: gray;}
复制代码
把h2和p选择器放置在规则左边并用逗号分隔,这种方式定义了一条把右边样式(color: gray;)应用于两个选择器的规则。逗号告诉浏览器规则里面是两个不同的选择器,如果去掉逗号,会使语句变成另外一条含义完全不同的规则。(参见后面的章节“后代选择器”。)
分组的选择器数目没有限制,例如,如果你想把一大堆元素都设置成灰色,你可以用这样的规则:
body, table, th, td, h1, h2, h3, h4, p, pre, strong, em, b, i {color: gray;}
复制代码
通配选择器
CSS2 引入了一个新的简单选择器叫做通配选择器,使用星号(*)标注。这个选择器就像一张百搭牌,可以匹配所有元素。例如,要把文档中的每个元素(文本)都设置成红色,可以这样写:
* {color: red;}
复制代码
此声明等效于一个列出文档中所有元素的分组选择器。通配选择器允许你以一种有效的方式为文档中每个元素的color属性设置一个值red。但是,要注意,虽然通配选择器很方便,它的特度是 0-0-0,但因为会匹配所有元素,它可能带来意外的后果,我们将在后面的部分讨论。
旧浏览器中的新元素
作为 HTML 的升级,HTML5 规范引入了新的元素。比新元素更早的浏览器不能识别它们。例如,IE9 以前的浏览器无法选择它们不支持的元素。解决办法是在 DOM 中创建元素,以此告知浏览器元素的存在。
例如,IE8 <main>
,下面这行 JavaScript 代码告诉 IE8 main元素的存在:
document.createElement('main');
复制代码
执行这行代码,旧版本的 IE 浏览器可以识别到这些元素,并允许选择它们以及为它们添加样式。
类和ID选择器
我们已经以各种方式组合使用了选择器和声明,但我们使用的选择器都非常简单。这些选择器只能用文档元素本身来选择,它们很好用,但有时你需要更专门的选择器。
除了原始的文档元素,还有类选择器和 ID 选择器,它们允许以独立于文档元素的方式分配样式。这些选择器可以单独使用,也可以与元素选择器结合使用。但是,它们只有在文档被正确标记的时候才能生效,因此使用它们通常需要做一些预想和规划。
类选择器
忽略元素类型应用样式的最常用方式,是使用类选择器。但是在使用它们之前需要设置文档标记,以便选择器能够生效。添加class属性:
<p class="warning">When handling plutonium, care must be taken to avoid
the formation of a critical mass.</p>
<p>With plutonium, <span class="warning">the possibility of implosion is
very real, and must be avoided at all costs</span>. This can be accomplished
by keeping the various masses separate.</p>
复制代码
要把类选择器和一个元素联系起来,必须为class属性设置一个合适的值。在上面的代码中,一个值为warning的类被分配给两个元素:第一个段落和第二个段落里的span元素。
现在只需要一种方式把样式应用在这些元素上。在HTML文档中,可同class名称紧跟一个句点(.),并且可以与元素选择器一起使用:
.warning {font-weight: bold;}
p.warning {font-weight: bold;}
复制代码
多类
我们在前面看到了包含单个单词的class的属性值。在HTML中,使用空白分隔的单词列表可以作为单个class的值。例如,如果你想把某个特定元素标记为既是紧急的又是一个警告,可以这样写:
<p class="urgent warning">When handling plutonium, care must be taken to
avoid the formation of a critical mass.</p>
<p>With plutonium, <span class="warning">the possibility of implosion is
very real, and must be avoided at all costs</span>. This can be accomplished
by keeping the various masses separate.</p>
复制代码
(class值中)单词的顺序没有影响,写成warning urgent会产生完全相同的结果。
假如你想让所有class值为warning的元素加粗,把class值为urgent的设为斜体,而同时包含两个值的元素设置银色背景,写法是这样:
.warning {font-weight: bold;}
.urgent {font-style: italic;}
.warning.urgent {background: silver;}
复制代码
连接两个类选择器,可以选择那些只有同时具有两个类名的元素,无论类名的顺序如何。正如示例,HTML 代码中含有class=”urgent warning”,但 CSS 选择器却写作.warning.urgent。这条规则依然可以把“When handling plutonium . . .”这段设置为银色背景,如图 1-8 所示,这是因为单词书写的顺序并不重要。
ID选择器
从某些方面说,ID 选择器和类选择器类似,但它们有一些重要区别。首先,ID 选择器使用井号(#)开头,一条规则可能是这样的:
*#first-para {font-weight: bold;}
#first-para {font-weight: bold;}
复制代码
和类选择器一样,ID 选择器中的通配选择符可以忽略。
决定使用Class还是ID
类可以分配给任意多的元素,类名warning可以分配给一个p元素或者一个span元素,或者更多其他元素。另一方面,ID在一个 HTML 文档中使用且仅使用一次。因此如果有了一个id值为lead-para的元素,该文档中的其他元素都不能有lead-para的id值。
在实际中,浏览器并不总会检查 HTML 中 ID 的唯一性。如果你为多个元素设置了相同的 ID 属性值,它们可能都会被设置为相同的样式。这是不正确的实现,但这种情况很普遍。在一个文档中存在多个 ID 值相同的元素还会导致 DOM 脚本的问题,因为像getElementById()这样的函数依赖于文档中只存在一个 ID 为特定值的元素。
与类选择器不同,ID 选择器不能联合使用,因为 ID 属性不允许使用空白分隔的单词列表。
还要注意类和 ID 可能是大小写敏感的,这取决于文档的语言。HTML 语言把类和 ID 定义为大小写敏感的,因此类和 ID 选择器的大小写要和文档中的匹配。在下面的 CSS 和 HTML 中,元素文字不会被设置为粗体:
p.criticalInfo {font-weight: bold;}
复制代码
<p class="criticalinfo">Don't look down.</p>
复制代码
属性选择器
当你使用类和 ID 选择器时,实际上是在选择属性的值。类选择器和 ID 选择器专用于 HTML、XHTML、SVG和 MathML 文档(到撰写本文时),但在其它的标记语言中,这两个选择器可能是不可用的(属性可能不存在)。因此,CSS2 引入了属性选择器,使用元素的任意属性和值来选择元素。属性选择器有四种基本类型:简单属性选择器、准确属性值选择器、部分匹配属性选择器和头值属性选择器。
简单属性选择器
如果想选择具有某个特定属性的元素,而无论属性的值是什么,可以使用简单属性选择器。例如,选择所有具备class属性的h1元素,然后将其文本设置为银色:
h1[class] {color: silver;}
复制代码
对应代码:
<h1 class="hoopla">Hello</h1>
<h1>Serenity</h1>
<h1 class="fancy">Fooling</h1>
复制代码
结果如下:
在HTML中可以用一些创新的方式使用这个特性。例如可以为所有包含alt属性的图片设置样式,因此突出形式规范的图片:
img[alt] {border: 3px solid red;}
复制代码
(这个特殊例子更适用于检查而不是设计目的,用来查看图片是否被设置了完全规范的标记。)
大部分浏览器在鼠标放到元素上的时候,会显示元素的title属性值,称为“tool tip”。如果想把所有包含title信息的元素粗体显示,可以这样:
*[title] {font-weight: bold;}
复制代码
也可以基于多个属性选择元素,只需简单地并列多个属性选择器即可。例如,把包含href和title属性的HTML超链接设置为粗体:
a[href][title] {font-weight: bold;}
复制代码
第一个链接会设置为粗体,第二个和第三个不会:
<a href="http://www.w3.org/" title="W3C Home">W3C</a><br />
<a href="http://www.webstandards.org">Standards Info</a><br />
<a name="dead" title="Not a link">dead.letter</a>
复制代码
基于准确属性值选择
可以进一步缩小选择器范围选择那些某个属性为某个确定值的元素。例如,把指向服务器上某个特定文档的链接设置为粗体:
a[href="http://www.css-discuss.org/about.html"] {font-weight: bold;}
planet[moons="1"] {font-weight: bold;}
复制代码
所有href和moons属性相等的元素会被设置为粗体。任何修改,即使去掉了www也会造成无法匹配。
这种方式需要属性值的精确匹配。属性值为多个空白分隔的值列表时,匹配可能会因多个值的顺序不同而产生问题(如 HTML 的属性 class)。例如下面的代码片段:
<planet type="barren rocky">Mercury</planet>
复制代码
匹配这个元素的唯一方式是使用准确的属性值:
planet[type="barren rocky"] {font-weight: bold;}
复制代码
如果使用planet[type="barren"]
,规则不会匹配是这个示例。即使是HTML中的class属性,也会出现这种情况。
同样地,ID 选择器和使用id属性的属性选择器也不是恰好相同的。换句话说,在h1#page-title
和h1[id="page-title"]
之间,存在着细微但很重要的区别。这种区别将在章节“特度”解释。
基于部分属性值选择
你可能想基于属性值的一部分而不是整个值来选择元素,这种情况下 CSS 提供了一些选择,匹配属性值中的子串。它们总结在表格 1-1 中。
表格1-1:子串匹配属性选择器
类型 描述
[foo~=”bar”] 选择所有带有foo属性、且foo属性被空白分隔的单词列表中含有单词bar的元素。
[foo*=”bar”] 选择所有带有foo属性、且foo属性值中含有子串bar的元素。
[foo^=”bar”] 选择所有带有foo属性、且foo属性值以bar开头的元素。
[foo$=”bar”] 选择所有带有foo属性、且foo属性值以bar结束的元素。
[foo|=”bar”] 选择所有带有foo属性、且foo属性值以bar开头后接一个短线(U+002D)或者属性值是bar的元素。
类型 | 描述 |
---|---|
[foo~=”bar”] | 选择所有带有foo属性、且foo属性被空白分隔的单词列表中含有单词bar的元素。 |
[foo*=”bar”] | 选择所有带有foo属性、且foo属性值中含有子串bar的元素。 |
[foo^=”bar”] | 选择所有带有foo属性、且foo属性值以bar开头的元素。 |
[foo$=”bar”] | 选择所有带有foo属性、且foo属性值以bar结束的元素。 |
[foo|=”bar”] | 选择所有带有foo属性、且foo属性值以bar开头后接一个短线(U+002D)或者属性值是bar的元素。 |
匹配列表中的一个单词开头后接一个短线(U+002D)或者属性值是bar的元素。
基于部分属性值的选择器中的最后一个,展示它的用法比描述它更简单。看下面的规则:
*[lang|="en"] {color: white;}
复制代码
这条规则会选择任何lang属性等于en或者以en-开头的元素。因此,下面的示例中前三个标签会被选中,而后两个则不会:
<h1 lang="en">Hello!</h1>
<p lang="en-us">Greetings!</p>
<div lang="en-au">G'day!</div>
<p lang="fr">Bonjour!</p>
<h4 lang="cy-en">Jrooana!</h4>
复制代码
一般来说,[att|=”val”]的格式可以用于任何属性和属性值。如果HTML文档中有一系列图片,文件名都像“figure-1.gif”和“figure-3.jpg”这样,可以使用这样的选择器匹配所有这样的图片:
img[src|="figure"] {border: 1px solid gray;}
复制代码
如果你正在创建一个 CSS 框架或模式库,不必创建许多繁琐的类:”btn btn-small btn-arrow btn-active”,你可以声明”btn-small-arrow-active”,然后这样匹配元素的类:
*[class|="btn"] { border-radius: 5px;}
复制代码
<button class="btn-small-arrow-active">Click Me</button>
复制代码
这个属性选择器最常用的场景是用来匹配语言值。
匹配空白分隔的列表中的一个单词
任何使用空白分隔单词列表的属性,都可以基于这些单词中的任意一个选择元素。HTML 中最经典的例子是class属性,该属性可以使用一或多个单词作为值。看这个常用例子:
<p class="urgent warning">When handling plutonium, care must be taken to
avoid the formation of a critical mass.</p>
复制代码
如果要选择class属性中包含单词warning的元素,可以使用这样的属性选择器:
p[class~="warning"] {font-weight: bold;}
复制代码
注意选择器中的波浪线(~),这是基于属性值中分离单词进行选择的关键字。如果忽略了波浪线,选择器就变成了前面讨论过的精确值匹配的属性选择器。
这个选择器跟“决定使用Class还是ID”中的点-类选择器是等同的。也就是说,用于 HTML 文档时,p.warning
和p[class~="warning"]
是相等同的。这是前面提到过的“PlanetML”标记例子的一个 HTML 版本:
<span class="barren rocky">Mercury</span>
<span class="cloudy barren">Venus</span>
<span class="life-bearing cloudy">Earth</span>
复制代码
既然效果相同,为什么还要在HTML中使用波浪线-等号属性选择器呢?因为它可被用于任何属性,而不仅仅是class。例如:一个文档中包含许多图片,其中一部分是图表,你可以使用匹配部分属性值的选择器选择title属性的文字,来选中那些是图表的图片:
img[title~="Figure"] {border: 1px solid gray;}
复制代码
这条规则会选择所有title文字中包含Figure的单词。因此,如果所有的图表的title中都有像“Figure 4. A bald- headed elder statesman,”这样的文字,这条规则会匹配所有的图表。同样的,选择器img[title~=”Figure”]也会匹配title属性值是“How to Figure Out Who’s in Charge.”这样内容的图片。所有没有title属性的图片,或者title值中不包含单词“Figure”的图片,都不会被匹配。
匹配属性值中的子串
有时需要匹配属性值的一部分,但并不是空格分隔的单词。这种情况下,可以使用[att*="val"]
的形式匹配属性值中的任何位置。例如:下面的CSS匹配任何class属性值中包含子串cloud的span元素,所以两个“cloudy”的行星都会被匹配,如图1-12。
span[class*="cloud"] {font-style: italic;}
复制代码
<span class="barren rocky">Mercury</span>
<span class="cloudy barren">Venus</span>
<span class="life-bearing cloudy">Earth</span>
复制代码
这种匹配方式是精确匹配——如果选择器中包含空白,属性值中必须包含空白才能匹配。只有当依赖的文档语言要求区分大小写时,属性名和值才必须区分大小写。类名、标题和 ID 值都是大小写敏感的,但 HTML 的属性关键字值,比如input元素的类型,不区分大小写:
input[type="CHeckBoX"] {margin-right: 10px;}
复制代码
<input type="checkbox" name="rightmargin" value="10px">
复制代码
匹配属性值开头的子串
当需要基于属性值开头的子串选择元素时,可以使用属性选择器模式[attr^="val"]
。这个匹配模式在给不同类别的链接设置样式时特别有用:
a[href^="https:"] {font-weight: bold;}
a[href^="mailto:"] {font-style: italic;}
复制代码
还有一种用法就是获取所有alt是以Figure开头的img:
img[alt^="Figure"] {border: 2px solid gray; display: block; margin: 2em auto;}
复制代码
任何alt属性值以“Figure”开头的img标签都会被选择,而无论它是不是一个说明图。显然,是否产生这个问题是由文档决定的(而不是选择器)。
另一个使用场景是选择所有周一的日程。这种场景中,我们约定所有的事件都有个title属性,它以这样的日期格式Monday,March 5th,2012开头。选择它们只需要简单地使用[title^="Monday"]
。
匹配属性值结尾的字串
与开头子串匹配相对应的是结尾子串匹配,使用选择器模式[att$="val"]
。最常见的使用场景是基于链接的资源类型添加样式,例如图1-14所示,为指向 PDF 文档的链接添加特定样式。
a[href$=".pdf"] {font-weight: bold;}
复制代码
类似地,可以(无论因何)基于格式选择图片:
img[src$=".gif"] {...}
img[src$=".jpg"] {...}
img[src$=".png"] {...}
复制代码
上一节中日程的例子,可以基于给定的年份选择所有事件,使用一个类似这样的选择器[title$="2015"]
(title属性以年份结尾)。
注意
你可能注意到了,属性选择器中的所有属性值都用了引号包围。当属性值包含任何特殊字符、以短线或数字开头、或其他非法标识符时,需要使用引号把它标记成字符串。为了安全,建议在任何情况下都为属性选择器中的属性值添加引号,尽管只有非法标识符导致它需要被标记成字符串的时候引号才是必需的。
忽略大小写标识符
CSS Selectors level 4 为属性选择器引入了忽略大小写选项。在中括号关闭之前使用i允许选择器匹配属性值的时候忽略大小写,无视文档语言的规则。
例如,你想选择所有指向 PDF 文档的链接,但并不确定它们以.pdf、.PDF还是.Pdf结束,可以这样:
a[href$='.PDF' i]
复制代码
添加一个小小的i表示选择器匹配任何href属性以.pdf结束的a元素,无论其中是否包含大写的P、D或F。
忽略大小写选项适用于前面涉及的所有属性选择器,但要注意它只用于属性选择器的值,而不能用于属性名本身。因此,在大小写敏感的语言中,planet[type*="rock" i]
可以匹配下面所有的元素:
<planet type="barren rocky">Mercury</planet>
<planet type="cloudy ROCKY">Venus</planet>
<planet type="life-bearing Rock">Earth</planet>
复制代码
但不能匹配下面的元素,因为属性TYPE不匹配type:
<planet TYPE="dusty rock">Mars</planet>
复制代码
再次说明,是否在元素和属性语法中强制区分大小写是由语言决定的,XHTML 是这样的语言。在大小写不敏感的语言中没有这样的问题,例如 HTML5。
至 2017 年底,Opera Mini、Android 浏览器和 Edge 不支持此能力。
使用文档结构
如前面所说,CSS 之强大在于它利用文档结构决定(元素的)样式和样式作用于元素的方式。结构确实在样式作用于文档时扮演了非常重要的角色,在继续讨论更强大的选择形式之前,我们先来讨论结构。
理解父-子关系
CSS 的能力很大程度基于于元素的父-子关系。HTML 文档(事实上绝大部分结构化文档)基于元素层级结构,构成文档的“树状”视图(见图1-15)。在这种层级结构中,每个元素都处在整个文档结构中的某个适当位置上,每个元素都是其他元素的父或者子,常常既是父又是子。
在文档层级结构中,如果一个元素在另一个元素的紧邻的上方,就被称作那个元素的父元素。例如,在图1-15中,第一个p元素是em和strong元素的父元素,同时strong是一个锚点(a)元素的父元素,这个a元素又是另一个em元素的父元素。反过来,如果在文档层级中,一个元素在另一个元素的紧邻的下方,就被称作那个元素的子元素。因此,图1-15中的锚点元素是strong元素的子元素,strong元素又是p元素的子元素,等等。
“父”和“子”是祖先和后代的特例。它们的区别是:在树状视图中,如果一个元素在另一个元素上面一级,那么它们是父-子关系。如果一个元素到另一个元素的路径有两级或者更多,那么它们是祖先-后代关系,但不是父-子关系。(当然,子也是后代,同时父也是祖先。)在图 1-15 中,第一个ul元素是两个li元素的父元素,同时也是它的li元素所有后代元素的祖先元素,直到嵌套路径最深的li元素。
同时,在图1-15中,存在一个锚点元素既是strong元素的子元素,又是p、body和html元素的后代元素。body元素是浏览器默认显示的所有元素的祖先元素,html是整个文档中所有其他元素的祖先元素。因此在 HTML 或 XHTML 文档中,html元素也被叫做根元素。
后代选择器
理解文档模型的第一个用处是可以定义后代选择器(也叫做上下文选择器)。定义后代选择器可以创建只作用于特定结构的规则。例如,如果想要只为那些是h1元素的后代的em元素设置样式。你可以为h1中的em元素添加一个class属性,但这样做会跟使用font标签一样耗费时间。很明显,声明一个只匹配h1元素中的em元素的规则更加方便。
实现这个规则,可以这样写:
h1 em {color: gray;}
复制代码
在后代选择器中,选择器由两个或更多空白分隔的选择器组成。选择器之间的空格是一个组合器的例子。每个空格组合器都可以被译作“在……中”、“是……的一部分”或“是……的后代”,前提是选择器从右向左读。因此,h1 em可以被译作“把样式作用于任何em元素,如果它是h1元素的后代”。(如果选择器从左向右读,则是:“选择任何h1,如果它包含一个em元素,规则将会作用于它包含的em”)。
后代选择器非常有用,它使得 HTML 中(至少是不使用怪异的font标签时)办不到的事情变成可能。一个常见的场景是:如果文档中有一个侧边栏和一个主区域,侧边栏的背景是蓝色,而主区域的背景是白色,它们都包含链接。不能把链接设置为蓝色,因为如果这样的话,侧边栏中的链接就看不到了。
解决方法是:后代选择器。在这种情况下,给包含侧边栏的元素(一般是一个div)添加一个class值sidebar,把主区域的class命名为main,然后使用如下样式:
.sidebar {background: blue;}
.main {background: white;}
.sidebar a:link {color: white;}
.main a:link {color: blue;}
复制代码
后代选择器的一个容易被忽略的地方是,元素和后代元素之间可以间隔无限代其他元素。例如,如果使用规则ul em,将会选择ul元素后代中的任何em元素,无论em元素嵌套多么深。因此,对下面的代码,ul em会匹配到其中的em元素。
例如,考虑下面的代码(包含我们将会在本章“否定伪类”中讨论的选择器):
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元素被设置为红色”。对示例中的HTML代码来说,两条规则都会被作用于span元素。
因为两条规则有相同的权重,且“红色”规则被写在后面,span会被设置为红色。虽然div class=”aside”比div class=”help”相比,与span元素更“紧密”,但实际上这种紧密程度与规则的选择毫不相关。亦即:后代选择器没有紧密程度的概念。两条规则都匹配了元素,只有一种颜色可以生效,根据CSS的工作方式,红色“胜利”了。(下边讨论)
选择子元素
有时,我们并不想选择全部的后代元素,而是把选择范围控制在元素的子级,例如选择一个是h1元素的子元素(而不是任意后代元素)的strong元素,这种情况下,可以使用子元素组合器,它是一个大于号(>):
h1 > strong {color: red;}
复制代码
这条规则将会把下面的第一个h1元素中的strong元素设置为红色,第二个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是完全等价的。
观察文档的树状结构视图,子元素选择器把匹配限制在直接连接的元素上。图1-19显示了部分文档树。
图1-19:一个文档树片段
在这文档树片段中,可以很清楚的观察到父-子关系。例如,a元素是strong元素的父元素,同时是p元素的子元素。在这个片段中,可以使用p > a和a > strong选择元素,但无法使用p > strong选择元素,因为strong是p的后代元素但不是子元素。
在同一个选择器中可以结合使用后代选择器和子元素原则器。table.summary td > p选择是td元素子元素的p元素,同时这个td元素需要是一个class属性值包含summary的table元素的后代元素。
选择相邻兄弟元素
假如想要为一个紧跟着标题的段落设置样式,或者给一个紧跟着段落的列表添加一个边距,可以使用相邻兄弟组合器来选择在同一个父级元素下紧跟着另一个元素的元素,组合器使用加号(+)。就像子级选择器一样,这个符号也可以在两边添加或省略空格。
移除一个紧跟h1元素的段落的上边距:
h1 + p {margin-top: 0;}
复制代码
和所有组合器一样,相邻兄弟选择器可以用于很复杂的选择器中,如div#content h1 + div ol。这个选择器是:“选择任何是div元素后代的ol元素,同时这个div元素是一个h1元素的紧邻兄弟元素,而这个h1元素是一个id属性值为content的div元素的子元素。”
选择跟随兄弟元素
Selectors Level 3 引入了一个新的兄弟组合器叫做一般兄弟选择器。这个组合器允许选择同一个父元素下,跟随(不一定是紧跟随)在某个元素后面的所有元素,使用波浪线符号(~)。
如下例,为同一个父元素下跟随在一个h2元素后面的任何ol元素设置斜体,可以写作h2 ~ ol {font-style: italic;}。两个ol元素不必都是紧邻兄弟,尽管是紧邻兄弟的话也会被这条规则匹配。
<div>
<h2>Subheadings</h2>
<p>It is the case that not every heading can be a main heading. Some headings must be subheadings. Examples include:</p>
<ol>
<li>Headings that are less important</li>
<li>Headings that are subsidiary to more important headlines</li>
<li>Headings that like to be dominated</li>
</ol>
<p>Let's restate that for the record:</p>
<ol>
<li>Headings that are less important</li>
<li>Headings that are subsidiary to more important headlines</li>
<li>Headings that like to be dominated</li>
</ol>
</div>
复制代码
伪类选择器
伪类选择器非常有趣,它们是一些根据元素状态变化而产生作用的幽灵类。伪类选择器可以根据某些确定元素的状态、文档中的标记模式甚至文档本身的状态选择元素并添加样式。
“幽灵类”的说法可能看起来有点怪,但这个词非常贴切地体现了伪类的工作方式。例如,假如你想要把一个数据表格每隔一行设置为高亮,你可以每隔一行在行元素上加一个class=”even”,然后写一段CSS把class值有even的行设置为高亮。但是你也可以使用伪类选择器更简便地实现相同的效果,后面会很快看到它的用法。
组合伪类
在开始之前,先提一下“链式”。CSS允许(链式)组合伪类选择器,例如,当鼠标停留在(hover)一个未访问过的链接(<a>
)上时,将其设置为红色,当鼠标停留在已经访问过的链接上时,将其设置为栗色:
a:link:hover {color: red;}
a:visited:hover {color: maroon;}
复制代码
(伪类的)顺序无关紧要,a:hover:link
和a:link:hover
的效果是一样的。同样的,可以为特定语言的不同状态的链接设置不同的样式,例如德语:
a:link:hover:lang(de) {color: gray;}
a:visited:hover:lang(de) {color: silver;}
复制代码
注意不要组合互斥的伪类,例如,一个链接不可能既是访问过的又是没访问过的,所以a:link:visited
没有任何意义,它并不会匹配任何东西。
结构性伪类
大部分伪类都是结构性的,既它们是与文档的标记结构相关的。大部分伪类由标签内的结构决定,例如某个伪类选择器选择(某个文档片段中的)第三个段落(p
)。其他一些选择器允许你处理特定类型的元素。所有的伪类都以一个冒号(:)开头,没有例外,而且它们可以出现在选择器的任何位置。
在开始之前,关于伪类有一点需要明确:伪类永远只指向他们关联的元素,而不是其他元素。这点似乎非常明显而没有必要特意强调,之所以要明确它,是因为在实际使用中,有一些结构型伪类常常被错误地当成后代元素的描述符。
为了进一步说明这点,许多人用了选择器#ericmeyer:first-child
。问题是这个选择器选择的是#ericmeyer
,而且只有当我是我父级的第一个子集的时候才会选择。如果要选择#ericmeyer
的第一个子集,选择器应该是#ericmeyer > :first-child
。
这种混淆是可以理解的,这就是为何我会在这里提到它,后面的章节我们会经常想起它。只要记住,伪类的作用是给它们绑定的元素添加一些“影子类”,就不容易犯(前面的)错误了。
选择唯一子元素
如果你想过选择所有被链接元素包含的图片,:only-child
伪类正是为你准备的。它会选择那些是其他元素的唯一子元素的元素。如果你想为所有是其他元素唯一子元素的图片添加边框,可以这样写:
img:only-child {border: 1px solid black;}
复制代码
这个选择器会匹配所有符合标准的图片,因此,如果有一个段落元素(p
)包含一个图片并且没有其他子元素,这个图片也会被匹配,而会忽略图片周围的文本。如果你想要选择那些在链接中的的唯一子元素的图片,可以这样设置选择器
a[href] img:only-child {border: 2px solid black;}
复制代码
<a href="http://w3.org/"><img src="w3.png" alt="W3C"></a>
<a href="http://w3.org/"><img src="w3.png" alt=""> The W3C</a>
<a href="http://w3.org/"><img src="w3.png" alt=""> <em>The W3C</em></a>
复制代码
结果显示第三个的边框不会有变化,因为当前元素下img元素不是唯一的。
选择第一个和最后一个子元素
一个很常见的场景是,给某个元素的第一个或最后一个子元素添加特殊样式。最常见的例子是为一个导航栏的链接添加样式,为第一个或最后一个(或两者都)设置特别的视觉效果。过去需要为元素添加特殊的class,现在可以使用伪类来设置。
p:first-child {font-weight: bold;}
p:last-child {font-weight: bold;}
复制代码
另外,两个伪类可以结合起来成为:only-child选择器的另一个版本,下面的两条规则选择相同的元素:
p:only-child {color: red;}
p:first-child:last-child {background: red;}
复制代码
上述代码的结果是一样的,子集元素又是第一个,又是最后一个,:only-child
的效果是一样的,但是这样做不是特别好的方式。
选择第一个和最后一个特定类型的元素
以选择第一个和最后一个子元素的形式,同样可以选择元素中第一个某种类型的子元素。这种方式可以实现选择某个元素中的第一个table元素,而不必关心它前面有哪些其他元素。
table:first-of-type {border-top: 2px solid gray;}
复制代码
这条规则并不是作用于整个文档的——并不是选择整个文档的第一个table然而忽略其他所有table。它只会选择某个子元素里有表格的元素中的第一个table元素,而忽略整个元素的子元素中的其他table。因此,在图 1-29 的文档结构里,被圈起来的节点是被选中的元素。
选择每第 n 个子元素
既然能选择第一个子元素、最后一个子元素、唯一的子元素,如何选择第二个子元素、第三个子元素、第九个子元素?为了避免定义无限多个伪类名称,CSS 使用:nth-child()
伪类。通过将整数或简单的算术表达式填充到括号中,可以选择任意任意编号的子元素。
我们先从一个与:first-child
等价的:nth-child()
伪类开始,既::nth-child(1)
。下面的例子中,被选中的元素的第一个段落和第一个列表项。
p:nth-child(1) {font-weight: bold;}
li:nth-child(1) {text-transform: uppercase;}
复制代码
假如在一个无序列表中,每三个元素选择第一个,从整个列表的第一项开始,样式可以这样写:
ul > li:nth-child(3n + 1) {text-transform: uppercase;}
复制代码
要注意,当你想使用负数的b时,必须把加号去掉,否则整个选择器都将失效。下面的两条规则,只有第一条有效,第二条将会被解析器忽略:
tr:nth-child(4n - 2) {background: silver;}
tr:nth-child(3n + −2) {background: red;}
复制代码
如果要选择从第 9 行开始的每一行,可以使用下面两种方式中的任意一个。他们很相似,都选择第 9 行开始的所有行,但后面的方式有更高的特度,将在后边讨论。
tr:nth-child(n + 9) {background: silver;}
tr:nth-child(8) ~ tr {background: silver;}
复制代码
如你所愿,有一个对应的伪类:nth-last-child()
,它与:nth-child()
效果是一样的,除了是从最后一个元素开始直到第一个元素。如果你想在表格中每隔一行设置高亮,同时需要保证最后一行是高亮的,下面的两条规则可以任选一个:
tr:nth-last-child(odd) {background: silver;}
tr:nth-last-child(2n+1) {background: silver;} /* equivalent */
复制代码
只要符合算术规则,每个元素都可以被既被:nth-child()
又被:nth-last-child()
选中,下面的规则,效果显示在图中:
li:nth-child(3n + 3) {border-left: 5px solid black;}
li:nth-last-child(4n - 1) {border-right: 5px solid black;}
复制代码
选择没第 n 个特定类型的子元素
类似之前的模式,:nth-child()
和:nth-last-child()
伪类存在类似的两个选择器::nth-of-type()
和:nth-last-of-type()
。例如,在一个段落中,每隔一个选择链接子元素,从第二个开始选择,可以使用p > a:nth-of-type(even)
。这个选择器将会忽略所有其他元素(span、strong等)而只考虑链接元素:
p > a:nth-of-type(even) {background: blue; color: white;}
复制代码
动态伪类
除了结构性伪类之外,还有一些伪类是与(页面标记的)结构相关的,但与前述伪类的不同之处在于:页面的状态会在渲染之后发生变化,而这些伪类会随页面状态的变化而变化。换句话说,样式基于某些文档结构之外的规则来决定是否作用于文档片段,因而无法仅通过文档标记来推断文档片段展示的最终样式。
听起来好像我们引入了一些随机样式,但实际并非如此。相反,我们是基于某些无法预测的临时条件来应用样式。事实上,样式在何种情况下出现是被明确定义的。这样来想:在某项体育赛事中,每当主队得分,人们就会欢呼。你可能无法预测主队什么时候得分,但一旦主队得分,人群会欢呼这件事却是可以预测的。因此不可以预测的是事件出现的时刻,但可以预测是一旦事件出现时它会导致的行为。
例如锚点元素(a
),它(在 HTML 或其它类似文档标准中)定义了从一个文档到另一个文档的链接。锚点一直是锚点,但一些锚点指向的是已经访问过的文档页面,而另一些指向未曾访问过的页面。通过 HTML 标签是无法分辨这种差异的,因为在文档标记中所有的锚点都是一样的。只有通过对比文档中的链接与用户浏览器中的历史记录才能判断两种基本的锚点类型:已访问过的和未曾访问的过的。
UI状态伪类
Name | Description |
---|---|
:enabled | Refers to user-interface elements (such as form elements) that are enabled; that is, available for input. |
:disabled | Refers to user-interface elements (such as form elements) that are disabled; that is, not available for input. |
:checked | Refers to radio buttons or checkboxes that have been selected, either by the user or by defaults within the document itself. |
:indeterminate | Refers to radio buttons or checkboxes that are neither checked nor unchecked; this state can only be set via DOM scripting, and not due to user input. |
:default | Refers to the radio button, checkbox, or option that was selected by default. |
:valid | Refers to a user input that meets all of its data validity semantics |
:invalid | Refers to a user input that does not meet all of its data validity |
:in-range | Refers to a user input whose value is between the minimum and maximum values` |
:out-of-range | Refers to a user input whose value is below the minimum or above the maximum values allowed by the control |
:required | Refers to a user input that must have a value set |
:optional | Refers to a user input that does not need to have a value set |
:read-write | Refers to a user input that is editable by the user |
:read-only | Refers to a user input that is not editable by the user |
可用和禁用的 UI 元素
借助 DOM 脚本 和 HTML5, 可以把用户界面元素(或一组)设置为禁用(disabled)。
未禁用的元素被定义为可用。
使用 :enabled
和 :disabled
伪类选择两个状态
选中状态
类型为 “checkbox” 和 “radio” 的 input 元素,Selectors level 3 提供 :checked状态伪类,以及unchecked
。:interminate伪类匹配既不是 checked
也不是 unchecked
。只有 radio 和 checkbox 可以被 check。其他所有元素和这两个元素的未选中状态都是:not(:checked)
。
“interminate”状态只能被 DOM 脚本或用户代理设置。可以用来表示用户没有选中或者取消选中某个元素。
默认选项伪类
:default
伪类。同名 radio 中最初被设置为选中的匹配:default
,即便用户修改了状态不再匹配 :checked
时。checkbox 在页面一加载就选中,:default
匹配。select 元素初始选中的 option
匹配 :default
。
[type="checkbox"]:default + label { font-style: italic; }
复制代码
<input type="checkbox" id="chbx" checked name="foo" value="bar">
<label for="chbx">This was checked on page load</label>
复制代码
选项伪类
:required
匹配表单必填项,表示设置了 HTML5 的required属性。:optional
表示没有required
属性,或者required
属性有值false
。
input:required { border: 1px solid #f00;}
input:optional { border: 1px solid #ccc;}
复制代码
<input type="email" placeholder="enter an email address" required>
<input type="email" placeholder="optional email address">
<input type="email" placeholder="optional email address" required="false">
复制代码
第一个元素匹配:required,后面两个匹配:optinal。
input[required] { border: 1px solid #f00;}
input:not([required]) { border: 1px solid #ccc;}
复制代码
不是表单 input
的元素既不是必选项也不是可选项。
合法性伪类
:valid
:invalid
用于可验证数据合法性的元素,div永远不会匹配这两个选择器。
这两个伪类由用户代理自己的样式系统决定,可能不会如你预期。例如 2017 年底在多个用户代理中,空的 email input 会匹配 :valid,尽快实际上 null 输入不是一个合法的 email 地址。除非验证的使用习惯改进,最好谨慎对待合法性伪类。
范围伪类
HTML5 的min
和max
属性。:in-range
和:out-of-range
伪类。
可变性伪类
可编辑的 input。
:read-write
、:read-only
例如,在HTML中,非禁用的、非只读的输入元素是:read-write
,任何具有contenteditable属性的元素都是这样。其他所有匹配项::read-only
否定伪类
Selector level 3 :not()
括号中填写简单选择器
,简单选择器在 W3C 的定义:类型选择器、通配选择器、属性选择器、类选择器、ID选择器或伪类中的一个。简单选择器是不包含祖先-后代关系的选择器。
伪元素选择器
CSS2 定义了四个基本的微元素选择器,元素的第一个字母,元素的第一行,before 和 after。
样式化第一个字母
p::first-letter {color: red;}
复制代码
样式化第一行
p::first-line {color: red;}
复制代码
::first-letter和::first-line的限制
只能用于块级元素,不能用于行内元素。允许使用的属性:
::first-letter | ::first-line |
---|---|
All font properties | All font properties |
All background properties | All background properties |
All text decoration properties | All margin properties |
All inline typesetting properties | All padding properties |
All inline layout properties | All border properties |
All border properties | All text decoration properties |
box-shadow | All inline typesetting properties |
color | color |
opacity | opacity |
在元素之前或之后样式化(或创建)内容
h2::before {content: "]]"; color: silver;}
body::after {content: "The End.";}
复制代码
总结
通过使用基于文档语言的选择器,作者可以创建应用于大量类似元素的CSS规则,就像他们可以构建应用于非常狭窄环境的规则一样简单。将选择器和规则组合在一起的能力使样式表保持紧凑和灵活,这会导致更小的文件大小和更快的下载时间。
选择器是用户代理通常必须正确使用的一件事,因为无法正确解释选择器几乎阻止了用户代理使用CSS。另一方面,对于作者来说,正确地编写选择器是至关重要的,因为错误会阻止用户代理按预期应用样式。正确理解选择器以及如何将它们组合在一起的一个重要部分是,在确定元素的样式时,对选择器与文档结构的关系以及继承和级联等机制如何发挥作用有很强的理解。