伪类选择符
伪类元素符(presudo-class selector)为文档中不一定存在的结构指定样式,或者为某些元素(或文档本身)的特定状态赋予“幽灵类”。
举个例子:为突出显示表格中相隔的行,我们可能会为相隔的行设置 class=”even” 属性,但其实使用 CSS 伪类选择符 :nth-child(even) 就能实现这个效果,而无需添加额外的类名。而这里 :nth-child(even) 起的作用就像是给元素加了类名,但其实没有,所有说是“幽灵类”。
拼接伪类
CSS 允许把伪类拼接(串联)在一起。
/*
鼠标悬停:
1. 未访问链接红色,
2. 已访问链接红褐色
*/
a:link:hover {color: red;}
a:visited:hover {color: maroon;}
注意,不要拼接相互排斥的伪类。
结构伪类
伪类大多都是结构上的,所有伪类都是一个冒号(:
)后面跟一个词的形式。
要明确都是:伪类始终指代所依附的元素。因为有几个伪类,会令人误以为指代的是后代。举例:
选择根元素
:root 伪类选择文档的根元素。在 HTML 中,根元素始终是 html。在不同的 XML 语言中,根元素有所不同,比如 RSS2.0 的根元素是 rss。
:root {
border: 10px dotted gray;
}
选择空元素
:empty 伪类匹配没有任何子代的元素。这里的子代包括文本节点(文本或空白)在内。
比如,通过下面的方式,能禁止显示空段落。
p:empty {display: none;}
对下面几个元素来说,只有第一和最后一个元素会被 p:empty 匹配。
<p></p>
<p> </p>
<p>
</p>
<p><!-- 注释 --></p>
注意,注释不是内容,也不是空白。
这并不意味着就可以使用 *:empty {display: none;} 隐藏所有空元素了!这里有一个陷阱——会匹配所有 HTML 空元素(img 和 input 等)、甚至连有效的没内容的 textarea 都会被匹配!
<!-- 不要这样写!下面的标签,也会被 *:empty {display: none;} 匹配 -->
<img class="rocket light-only" src="https://logrocket-assets.io/img/logo.png" alt="Logo">
<br>
<input type="number" min="-1" max="1" step=".01" />
<textarea></textarea>
:empty 是 Selectors Level 3 中唯一一个在匹配时,会考虑文本节点的选择符。
选择唯一子代
:only-child 用来匹配唯一子元素。以下面代码为例:
img:only-child {border: 2px solid black;}
如果父元素中只有一个图片,这张图片就会应用上面的边框样式。
而下面的样式则表示:在带有地址链接的 a 标签范围内,为在父元素中是唯一子元素的图片应用样式。
a[href] img:only-child {border: 2px solid black;}
如果只匹配 a 元素只有一个图片子元素时的图片样式,就要使用子元素连结符了。
a[href] > img:only-child {border: 2px solid black;}
更进一步的,如果只匹配 a 元素只有一个图片时的图片样式,就得使用 :only-of-type 了。
a[href] > img:only-of-type {border: 2px solid black;}
这样,即使 a 元素中包含 b 和 img 两个子元素,因为 img 是 a 中唯一的子代图片,也会被匹配选中。
由此,就能看出 :only-of-type 与 :only-child 的区别:
- :only-of-type:匹配在同胞元素中唯一的那种元素
- :only-child:匹配没有同胞元素,是所在父元素中唯一子代元素的元素
还有需要澄清的是,:only-of-type 指代元素,而非其他的任何东西。以下面代码为例:
<style>p.unique:only-of-type {color: red;}</style>
<div>
<p class="unique">这是具有 class="unique" 属性的段落。</p>
<p>这个段落是没有类名属性的段落。</p>
</div>
这里,两个段落都不会被选中。不要被这里的类名干扰,它无关紧要。整个选择符“p.unique:only-of-type”表示的含义是“选择的 p 元素包含 class=”unique” 属性,而且 p 元素是同胞中唯一的一个”。
选择第一个和最后一个子代
:first-child 用来选择同胞元素中的第一个,或者说是某元素的第一个子元素。
<style>p:first-child { color: red; }</style>
<div>
<p>This text is selected!</p> <!-- 匹配,是 div 中的第一个子元素,且是 p -->
<p>This text isn't selected.</p>
</div>
<div>
<h2>This text isn't selected: it's not a `p`.</h2> <!-- 不匹配,是 div 中的第一个子元素,但却是 h2,而非 p! -->
<p>This text isn't selected.</p>
</div>
效果如下:
于 :first-child 对应的就是 :last-child,用来选择同胞元素中的最后一个,或者说是某元素的最后一个子元素。
<style>p:last-child { color: red; }</style>
<div>
<p>This text isn't selected!</p>
<p>This text is selected.</p> <!-- 匹配,是 div 中的最后一个子元素,且是 p -->
</div>
<div>
<h2>This text isn't selected.</h2>
<p>This text is selected.</p><!-- 匹配,是 div 中的最后一个子元素,且是 p -->
</div>
效果如下:
有趣的是,上述两个伪类结合在一起使用的效果相当于 :only-child。
p:only-child {color: red;}
/* 效果相当于 */
p:first-child:last-child {color: red;}
选择第一个和最后一个某种元素
选择某个元素中的第一个 table 元素,可以这样:
table:first-of-type {broder: 2px solid gray;}
效果如下:
同理,选择某行中的第一个单元格:
td:first-of-type {border-left: 1px solid red;}
与 :first-of-type 对应的是 :laast-of-type:
table:last-of-type {broder: 2px solid gray;}
效果如下:
而把上述两个伪类结合连在一起,达到的效果与 :only-of-type 一样。
table:first-of-type {color: red;}
/* 效果等价于 */
table:first-of-type:last-of-type {color: red;}
选择第 n 个子元素
:first-child 与 :last-child 的功能很有限,只能选择第一个和最后一个子元素,不太灵活。CSS 为我们提供了 :nth-child、:nth-last-child 伪类,支持填入整数或一个代数式,选择任何想选择的子元素。
填入整数:
p:first-child {font-weight: bold;} /* 效果等同于 */
p:nth-child(1) {font-weight: bold;}
p:last-child { font-weight: bold; } /* 效果等同于 */
p:nth-last-child(1) { font-weight: bold; }
/* 选择第二个子代 */
p:nth-child(2) {...}
/* 选择倒数第二个子代 */
p:nth-last-child(2) {...}
填入一个代数式:
代数式是 an + b 的形式,a、b 是具体的整数,n 原封不动,是自然数。+ b、- b 是可选的,如果不需要可以去掉。
/* 选择奇数行: 1、3、5、7、9…… */
ul > li:nth-child(2n + 1) { ... } /* 等效的关键字版本 */
ul > li:nth-child(odd) { ... }
/* 选择偶数行: 2、4、6、8、10…… */
ul > li:nth-child(2n) { ... } /* 等效的关键字版本 */
ul > li:nth-child(even) { ... }
/* 其他例子 */
ul > li:nth-child(3n + 1) { ... } /* 1、4、7、10、13…… */
tr:nth-child(n + 9) {...} /* 9、10、11、12…… */
:nth-last-child 的效果与 :nth-child 类似,不过是后往前技术,应用样式的。
选择每第 n 个某种元素
:nth-child 和 :nth-last-child 伪类有对应的 :nth-of-type 和 :nth-last-of-type。
:nth-of-type 和 :nth-last-of-type 用来从同胞元素中选择某一种元素。只不过前者是从前往后找,后者是从后往前找。
举个例子:将段落中从第二个链接开始选择间隔的超链接——就可以通过下面的方式选择:
p > a:nth-of-type(even) { background-color: blue; color: white; }
效果如下:
动态伪类
还有一些与结构有关的伪类,是在页面渲染后根据页面变化而变化的。
以锚记元素(a)来说,链接有两个基本状态,访问和未访问。而从 HTML 标记中是看不出这一区别的。这就需要借助超链接伪类的帮助了。
超链接伪类
CSS2.1 定义了两个超链接伪类,:link 和 :visited,HTML 中,这两个伪类只能用在具有 href 属性的 a 元素上。
伪类 | 说明 |
---|---|
:link | 匹配未访问链接 |
:visited | 匹配已访问链接。处于安全考虑,应用到已访问链接的样式属性是有限制的 |
举一个例子:
a:link {color: blue;} /* 未访问链接蓝色 */
a:visited {color: red;} /* 未访问链接红色 */
为何限制已访问链接样式?
处于安全的考虑。
当前,只能把颜色相关样式属性应用给已访问状态的连接。包括: color、background-color、column-rule- color、outline-color、border-color,以及边框颜色(border-top-color)属性。除此之外的属性会被忽略。 限制的原因。举个例子,对链接使用 :visited {font-weight: bold;} 的样式,就可以使用 JS 找出所有加粗链接,得出用户访问过哪些网站,这会被钓鱼网站利用。
因此,如果通过 DOM 查询已访问样式,返回值跟未访问时一样。以上面代码为例,通过 DOM 查询已访问链接的文本颜色,返回的是蓝色,而非紫色。
用户操作伪类
根据用户的操作改变文档外观。
伪类 | 说明 |
---|---|
:focus | 匹配获得焦点元素 |
:hover | 匹配处于鼠标指针悬停其上的元素 |
:active | 匹配激活元素。在按下鼠标按键的那段时间 |
举例:
a:link {color: navy;}
a:visited {color: gray;}
a:focus {color: orange;}
a:hover {color: red;}
a:active {color: yellow;}
UI 状态伪类
用户界面伪类包括:
伪类 | 说明 |
---|---|
:enabled | 匹配启用元素。如可输入表单元素 |
:disabled | 匹配禁用元素。如不可输入表单元素 |
:checked | 匹配选中的单选按钮或复选框 |
:indeterminate | 匹配未完全选中的单选按钮或复选框。此伪类只能通过 DOM 脚本设定。 |
:default | 匹配默认选中的单选按钮、复选框或选项 |
:valid | 匹配满足有效性语义的输入框 |
:invalid | 匹配不满足有效性语义的输入框 |
:in-range | 匹配输入值满足在最小与最大之间的输入框 |
:out-of-range | 匹配输入值超出最小与最大值范围外的输入框 |
:required | 匹配必填输入框 |
:optional | 匹配可选输入框 |
:read-write | 匹配可编辑输入框 |
:read-only | 匹配只读(不可编辑)输入框 |
启用和禁用 UI 元素
禁用元素无法选择、激活与用户交互。可通过在 HTML5 标签中使用 disabled 属性启用,或者通过 DOM 脚本操作。
:enabled {font-weight: bold;}
:disabled {opacity: 0.5;}
效果如下:
选择状态
Selectors Level 3 为 HTML 的复选框和单选按钮共定义了 3 种状态伪类::checked、:unchecked 以及 :indeterminate。
- :checked 表示选中,
- :unchecked 表示未选中,类似于 :not(:checked)
- :indeterminate 表示不确定,这个状态暂时只能由 DOM 脚本设定。
:indeterminate 伪类存在的目的,是从视觉上提示用户需要选中(或不选中)元素,不影响元素底层状态。元素底层状态只能是选中或者未选中。
可以结合 :checked 和紧邻同胞连接符装饰复选框和单选按钮的 label:
<style>
input[type="checkbox"]:checked + label {
color: red;
font-style: italic;
}
</style>
<input id="chbx" type="checkbox"><label for="chbx">I am a label</label>
默认选项伪类
:default 伪类匹配同名单选按钮或复选框中,最初选中的那个元素。即使后面用户改变 UI,这个伪类依旧会在最初选中的那个元素上生效。
[type="checkbox"]:default + label {
font-style: italic;
}
<input type="checkbox" id="cbx" name="foo" checked>
<label for="cbx">页面加载时即被选中</label>
效果:
可选性伪类
:required 匹配必填表单控件,:optional 匹配没有 required 属性的表单控件。
input:required {
border: 1px solid red;
}
/*
等效查询
input[required] {border: 1px solid red;}
*/
input:optional {
border: 1px solid blue;
}
/*
等效查询
input:not([required]) {border: 1px solid blue;}
*/
body {
margin: 4rem;
}
<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">
效果:
有效性伪类
input:focus:valid {
background-color: lightgreen;
}
input:focus:invalid {
background-color: pink;
}
<form>
<div class="field">
<label for="url_input">Enter a URL:</label>
<input type="url" id="url_input">
</div>
<div class="field">
<label for="email_input">Enter an email address:</label>
<input type="email" id="email_input" required>
</div>
</form>
效果:
范围伪类
<input type="number" min="0" max="100">
input:focus:in-range {
background-color: lightgreen;
}
input:focus:out-of-range {
background-color: pink;
}
效果:
input[type=”number”] 还有一个 step 属性,如果一个值不匹配步进值,即使是在 min、max 范围内的,也会被认为是无效的。
举个例子:
input[type="number"]:focus:invalid {
background-color: pink;
}
input[type="number"]:focus:in-range {
border: 10px solid greenyellow;
}
<input type="number" min="0" max="100" step="10">
效果:23 在范围内(边框青色),但是不能被 10 整除(背景粉色)。
可变性伪类
:read-write 和 :read-only。
- :read-write:匹配可编辑表单元素
- :read-only:匹配只读(不可编辑)表单元素
<style>
textarea:read-only {opacity: .75;}
pre:read-write:hover {border: 1px dashed green;}
</style>
<textarea cols="12" rows="3" disabled></textarea>
<pre contenteditable>Type your own code!!</pre>
效果:
:target 伪类
URL 地址中可以包含一个片段标识符(fragment identifier),比如下面这个 URL:
https://www.w3.org/TR/selectors-3/#target-pseudo
这里的 #target-pseudo 就是片段标识符了,由 # 符号标记。如果页面中包含一个 ID 值为 target-pseudo 的元素,那么这个元素就是片段标识符的目标。
我们可以通过 :target 伪类给目标元素设置样式。
<h4 id="target-pseudo"><span class="secno">6.6.2. </span>The target
pseudo-class :target</h4>
:target {
background-color: skyblue;
}
效果:
:lang 伪类
使用 :lang 可以根据文本使用的语言来选择元素。匹配方式上,:lang() 与 |= 属性选择符类似。
:lang(fr) {font-style: italic;}
[lang|=fr] {font-style: italic;}
不过,伪类比属性选择更可靠。因为前者的语言来源可能来自元素自身,也可能继承自设置了语言的祖先元素。
Selector Level 3 规定,HTML 中,语言可以通过 lang 属性判断,也可以通过 meta 元素和协议(HTTP 首部)判断。
否定伪类
前面介绍的都是肯定选择符,选择的都是满足的条件的元素。如果反过来,选择不满足条件的元素,就要使用 :not() 伪类了。
比如,想让除了 .mereinfo 之外的列表元素倾斜显式,可以这样声明样式:
/* 匹配的 li 元素要求不含有 .moreinfo 这个类名 */
li:not(.moreinfo) {font-style: italic;}
:not 依附于元素之上,括号中的是简单的选择符。简单的选择符指:类型选择符、通用选择符、属性选择符、类选择符、ID 选择符或伪类——就是没有祖辈-后代关系的选择符(注意,也没有伪元素),且只能使用一个。
更多例子:
/* 选择不是 section 子代的所有表格 */
:not(section) > table {...}
/* 选择不在表头中的表头单元格 */
:not(thead) > tr > th {...}
还有一个有趣的例子:
<style>
div:not(.one) p {font-weight: normal;}
div.one p {font-weight: weight;}
</style>
<div class="one">
<div class="two">
<p>我是一个自然段</p>
</div>
</div>
这里的 p 最终显式为粗体,而非常规字重。分析:两条规则都会应用到 p 元素上,div:not(.one) p 会匹配 .two 元素,div.one p 则匹配 .one 元素,:not 本身是不占权重的,因此上下两条规则权重一样,根据层叠规则,后面的样式会覆盖之前的,因此 p 段落中的文字最终显式为粗体。
注意:结构并不会影响样式匹配。比如,并没有因为 div.two 比 div.one 距离 p 更近,就应用 div.two 上声明的样式。而是在综合考虑权重和层叠的规则下,应用样式的。
伪元素选择符
与伪类类似,伪类选择符通过在文档中插入虚构元素,实现特定效果。CSS2 定义了四个基本伪元素:
- ::first-letter:用于装饰元素首字母
- ::first-line:用于装饰元素首行
- ::before & ::after:用于装饰元素的“前置”和“后置”内容
CSS2 之后又定义了例如 ::marker 这样的伪元素。
装饰首字符
::first-letter 用于装饰所有非行内元素的首字母(或标点符号)。
p::first-letter {color: red;}
这个规则其实会让用户代理将每个 p 元素的首字母用一个虚拟元素包围。类似下面这样:
<p><p-first-letter>T</p-first-letter>his is a p element, with a styled first letter.</p>
::first-letter 只对虚构元素里的内容应用样式。而用户是无法通过技术手段获得“
装饰首行
::first-line 用于装饰元素的首行文本。举个例子:
/* 段落首行字号变大,并显示为紫色 */
p::first-line {
font-size: 150%;
color: purple;
}
这个规则其实会让用户代理将每个 p 元素的首行文本用一个虚拟元素包围。类似下面这样:
<p>
<p-first-line>Mocha(发音"摩卡")诞生于2011年,是现在最流行的JavaScript</p-first-line>
测试框架之一,在浏览器和Node环境都可以使用。所谓"测试框架",就是运行测试的工
具。通过它,可以为JavaScript应用添加测试,从而保证代码的质量。
</p>
注意,这里的“首行”是指显示在屏幕上的首行,具体几个字视视口大小而定,总之是给首行应用样式的。
::first-letter 和 ::first-line 的限制
- 这两个伪元素只能应用在块级元素上(例如标题段落),不能应用在行内元素上(例如超链接)。
- 这两个伪元素可以使用的 CSS 属性也是有限的。
装饰(或创建)前置和后置内容元素
::before
:在 h2 元素前面加上两个银色的方括号:
h2::before {content: "]]"; color: silver;}
效果:
content
属性指定插入的内容,我们给它设置了一个银色文本效果。
::after
用于在元素之后添加内容。比如,在文档末尾添加结束语:
body::after {content: "The End.";}
总结
- 使用选择符可以创建丰富的 CSS 规则,规则可以应用于:
- 少数元素
- 大量类似的元素
- 选择符和规则可以组合在一起,更省体积
- 牢固掌握选择符与文档结构的关系
但了解继承和层叠的机制在样式上所起的作用,会帮助我们更加准确的理解选择符以及如何组合选择符。