CSS 伪类选择器
伪类选择器 (Pseudo-class selectors) 可以让我们将样式分配给实际上是由某些元素的状态、文档内的标记模式,甚至文档本身的状态推断出来的幻像类。
幻像类 (phantom classes) 这个术语可能看起来有点奇怪,但它确实是理解伪类如何工作的最佳方式。
例如,假设您想突出显示数据表的每隔一行。 不使用伪类选择器的情况下, 你需要给你的数据包的每一行每隔一行标记类似 class="even",然后编写 CSS 来突出显示具有该类的行 你也可以使用伪类选择器来实现相同的效果,下面来看怎么做。
所有伪类都是一个单词或带连字符的术语,前面有一个冒号 (:),可以出现在选择器中的任何位置
1. 组合伪类
CSS 允许组合伪类。
例如,当你想实现如下效果:
- 当鼠标悬停在未访问的链接上时, 将链接显示为红色
- 当鼠标悬停在已访问的链接上时, 将链接显示为栗色
可以这样实现:
a:link:hover {color: red;}
a:visited:hover {color: maroon;}
组合伪类时, 伪类的前后顺序不影响最终效果。 因此 a:hover:link
和 a:link:hover
是同样的效果。
但是需要注意, 互相冲突的伪类是不能被合并的。 例如,一个链接不能同时被访问过和未被访问过,因此 a:link:visited
没有任何意义,也永远不会匹配任何内容。
2. 结构伪类
结构伪类: Structural Pseudo-Classes
2.1 root
伪类 :root
用来选中当前文档的根元素,也就是 HTML 中的 <html>
元素。
例子:
:root {border: 10px dotted gray;}
body {border: 10px solid black;}
2.2 empty
伪类 :empty
,用来选择任何不包含任何子元素的元素。
常用于将 HTML中没有内容的元素隐藏掉, 比如,p:empty {display: none;}
将阻止显示任何空段落。
空元素必须是"真正"的空元素, 不能包含任何子元素或者文本, 空格, 可见内容,或者后代元素都会被认为不是空元素。
<p></p> ==> empty
<p> </p> ==> not empty
<p>
</p> ==> not empty
<p><!—-a comment--></p> ==> empty
这里有一个隐藏的陷阱: <img>
, <hr>
, <br>
, 和 <input>
, <textarea>
会被认为是空元素。
2.3 only-child
:only-child
当元素是另一个元素的唯一子元素时,选择该元素。
例如, 如果一个元素的唯一子元素是一个图片, 你想给这样的图片加个边框, 可以这样实现:
img:only-child {border: 1px solid black;}
关于 :only-child
, 需要注意以下两点:
- 应该总是将它应用于你希望是唯一子元素的元素,而不是应用于父元素。
- 当你在后代选择器中使用
:only-child
时,选中的元素和当前元素不一定是父子关系。
比方说, a[href] img:only-child
匹配任何属于 a 元素唯一子元素且继承自 a 元素的图像,无论它是否是 a 元素的子元素。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
a[href] img:only-child {border: 5px solid black;}
</style>
</head>
<body>
<a href="http://w3.org/"><img src="w3.png" alt="W3C"></a>
<a href="http://w3.org/"><span><img src="w3.png" alt="W3C"></span></a>
<a href="http://w3.org/">A link to <span>the <img src="w3.png" alt="W3C">
web</span> site</a>
</body>
</html>
上面例子中 a[href] img:only-child
匹配到 3 的 a 元素中的 img 元素。 能匹配上的原因:
- img 元素是它的父元素的唯一孩子。 这里我们看到上面三个 img 元素无论是嵌套在 span 中或者 a 中, 都是父元素的唯一子。
- 满足
img:only-child
条件的 img 元素,是一个带有 href 的 a 元素的后代。
这里没有要求:满足 img:only-child
条件的 img 元素必须是带有 href 的 a 元素的子,或者唯一子。
2.4 only-of-type
当你想匹配 img 元素,要求匹配的 img 元素是超链接元素中唯一的 img 元素, 此时超链接元素中可能包含其他元素, 你应该怎么办。
比如说:
<a href="http://w3.org/"><b>•</b><img src="w3.png" alt="W3C"></a>
此时 only-child
是无法使用的,因为 img 元素有兄弟节点。
此时,就可以用 :only-of-type
了。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
a[href] img:only-of-type {border: 5px solid black;}
</style>
</head>
<body>
<a href="http://w3.org/"><b>•</b><img src="w3.png" alt="W3C"></a>
<a href="http://w3.org/"><span><b>•</b><img src="w3.png" alt="W3C"></span></a>
</body>
</html>
这里 only-of-type 匹配到了上述两个 img, 因为 only-of-type 不要求 img 是父元素的唯一子, 它只要求它的兄弟节点(如果有)中没有 img 类型的元素即可。
再看一个例子:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
p.unique:only-of-type {color: red;}
</style>
</head>
<body>
<div>
<p class="unique">This paragraph has a 'unique' class.</p>
<p>This paragraph doesn't have a class at all.</p>
</div>
</body>
</html>
注意, 上述的代码中 p.unique:only-of-type
没有匹配到任何 p 元素, 为什么没有匹配呢?
p.unique:only-of-type
需要这样理解: 选择任何 p 元素,该元素是其兄弟元素中唯一的 p 元素,并且它也具有 unique 类。
而不是这样理解:选择任何 class 属性包含单词 unique 的 p 元素,前提是该元素是唯一符合该条件的.
这里再次强调:
only-of-type 永远指向的是元素,而不是其他
2.5 first-child
:first-child
用于选中某个元素的第一个子元素。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
p:first-child {font-weight: bold;}
li:first-child {text-transform: uppercase;}
</style>
</head>
<body>
<div>
<p>These are the necessary steps:</p>
<ul>
<li>Insert key</li>
<li>Turn key <strong>clockwise</strong></li>
<li>Push accelerator</li>
</ul>
<p>Do <em>not</em> push the brake at the same time as the accelerator.</p>
</div>
</body>
</html>
上述例子中的第一个子元素有: 第一个 p, 第一个 li, 第一个 strong, 第一个 em.
2.6 last-child
:last-child
用于选中某个元素的最后一个子元素。
把 first-child 章节的代码复制过来,使用 last-child 替换 first-child:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
p:last-child {font-weight: bold;}
li:last-child {text-transform: uppercase;}
</style>
</head>
<body>
<div>
<p>These are the necessary steps:</p>
<ul>
<li>Insert key</li>
<li>Turn key <strong>clockwise</strong></li>
<li>Push accelerator</li>
</ul>
<p>Do <em>not</em> push the brake at the same time as the accelerator.</p>
</div>
</body>
</html>
一个冷知识: 组合 first-child
和 last-child
可以达到 only-child
的效果
p:only-child {color: red;}
== p:first-child:last-child {color: red;}
2.7 first-of-type 和 last-of-type
你可以使用 first-of-type
和 last-of-type
选中某个父元素中第一个特定类型的元素或者最后一个特定类型的元素。
比如: table:first-of-type
会匹配到某个父元素中第一个 table 元素,无论它之前是否有其他非 table 的元素。
下图中, 圆圈标记的节点便是被上面规则匹配的 table 元素:
我们再看看 table:last-of-type
的效果:
2.8 nth-child 和 nth-last-child
nth-child()
通过在括号中填写整数甚至基本的代数表达式,您可以选择任意编号的子元素。
比如: :nth-child(1)
等价于 :first-child
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
p:nth-child(1) {font-weight: bold;}
li:nth-child(1) {text-transform: uppercase;}
</style>
</head>
<body>
<div>
<p>These are the necessary steps:</p>
<ul>
<li>Insert key</li>
<li>Turn key <strong>clockwise</strong></li>
<li>Push accelerator</li>
</ul>
<p>Do <em>not</em> push the brake at the same time as the accelerator.</p>
</div>
</body>
</html>
将上述代码中 1
改成 2
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
p:nth-child(2) {font-weight: bold;}
li:nth-child(2) {text-transform: uppercase;}
</style>
</head>
<body>
<div>
<p>These are the necessary steps:</p>
<ul>
<li>Insert key</li>
<li>Turn key <strong>clockwise</strong></li>
<li>Push accelerator</li>
</ul>
<p>Do <em>not</em> push the brake at the same time as the accelerator.</p>
</div>
</body>
</html>
上面例子中:
- 没有 p 被选中。 因为在 p 的父元素 div 中,第二个孩子是 ul 元素。
- 但是第二个 li 被选中。
你还可以在 :nth-child()
括号中使用简单的表达式。 表达式格式为 an+b
或 an-b
. 其中 a 和 b 是整数, n 以其本身形式存在。
假设我们要选择无序列表中从第一个开始的每三个列表项。 可以这样写:
ul > li:nth-child(3n + 1) {text-transform: uppercase;}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
ul > li:nth-child(3n + 1) {text-transform: uppercase;}
</style>
</head>
<body>
<div>
<p>These are the necessary steps:</p>
<ul>
<li>Insert key</li>
<li>Turn key <strong>clockwise</strong></li>
<li>Grip steering wheel with hands</li>
<li>Push accelerator</li>
<li>Steer vehicle</li>
<li>Use brake as necessary</li>
</ul>
<p>Do <em>not</em> push the brake at the same time as the accelerator.</p>
</div>
</body>
</html>
:nth-last-child()
它和 :nth-child()
类似,区别是它是从后向前的。
2.9 nth-of-type 和 nth-last-of-type
略。
3. 位置伪类
位置伪类不再基于 HTML 文档结构来匹配元素,而是根据元素 "状态" 来匹配元素。
3.1 超链接相关的伪类
名称 | 描述 |
---|---|
:link | 指任何超链接(即具有 href 属性)且指向尚未访问过的地址的锚点。 |
:visited | 指向哪些超链接地址已经被访问过的链接。 出于安全原因,可应用于已访问链接的样式受到严格限制 |
:any-link | 指代任何与 :link 或 :visited 匹配的元素 |
:local-link | 指向哪些和当前页面具有相同 URL 的链接。 注意:自 2023 年初起不再支持。 |
a:link
和 a
的区别:(比如下面代码)
a {color: blue;}
a:link {color: blue;}
a
会匹配到所有的<a>
, 而 a:link
只会匹配哪些包含 href
属性的元素
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
a:link {color: blue;}
</style>
</head>
<body>
<a id="section004">4. The Lives of Meerkats</a>
</body>
</html>
这里看到 a:link
没有匹配到这里的 <a>
元素
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
a {color: blue;}
</style>
</head>
<body>
<a id="section004">4. The Lives of Meerkats</a>
</body>
</html>
这里看到 a
匹配到了这里的 <a>
元素
3.2 其他伪类
名称 | 描述 |
---|---|
:target | 指的是 id 属性值与加载页面的 URL 中的片段选择器(fragment selector)匹配的元素,即 URL 明确定位的元素。 |
:target-within | 指 URL 的目标元素,或包含以此为目标的元素。注意:自 2023 年初起不再支持 |
:scope | 指作为选择器匹配的参考点的元素。 |
当一个 URL 包含片段标识符时,它指向的文档片段在 CSS 中被称为目标(target)。因此,你可以使用 :target
伪类为 URL 片段标识符的目标元素设置样式。
片段标识符 (fragment identifier)
例如: http://www.w3.org/TR/css3-selectors/#target-pseudo
#target-pseudo
就是片段标识符
4 用户操作伪类
名称 | 描述 |
---|---|
:hover | 指鼠标指针所指的元素 |
:active | 指由用户输入激活的元素 |
:focus | 指当前具有输入焦点的元素 |
:focus-within | 指当前具有输入焦点的元素,或包含具有输入焦点的元素的元素 |
:focus-visible | 指当前具有输入焦点的元素,但前提是用户代理认为该元素类型应该具有可见焦点 |
下面是一个突出显示已准备好接受键盘输入的表单元素的例子:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input:focus {background: silver; font-weight: bold;}
</style>
</head>
<body>
<form>
<table>
<tr>
<th>Name</th>
<td><input type="text"></td>
</tr>
<tr>
<th>Title</th>
<td><input type="text"></td>
</tr>
<tr>
<th>E-mail</th>
<td><input type="text"></td>
</tr>
</form>
</body>
</html>
5 UI 状态伪类
名称 | 描述 |
---|---|
:enabled | 指启用(即可接受输入)的 UI 元素(例如表单元素) |
:disabled | 指禁用(即不可用于输入)的 UI 元素(例如表单元素) |
:checked | 指已被用户选中或默认选中的单选按钮或复选框 |
:indeterminate | 指既未选中也未取消选中的单选按钮或复选框;此状态只能通过 DOM 脚本设置,而不能通过用户输入设置 |
:default | 指默认选中的单选按钮、复选框或选项 |
:autofill | 指浏览器自动填充的用户输入 |
:placeholder-shown | 指预先填充了占位符(而非值)文本的用户输入 |
:valid | 指满足有效性要求的用户输入 |
:invalid | 指用户输入不满足数据有效性要求 |
:in-range | 指用户输入的值介于最小值和最大值之间 |
:out-of-range | 指用户输入的值不在最小值和最大值之间 |
:required | 指必须设置值的用户输入 |
:optional | 指那么不强制要求设置值的用户输入 |
:read-write | 指用户可编辑的用户输入 |
:read-only | 指用户无法编辑的用户输入 |
UI 元素的状态会随着用户操作而改变,但是需要注意脚本也可以改变 UI 元素的状态。
5.1 启用和禁用 UI 元素。
被禁用的元素仍然会被显示,但用户无法选中、激活或以其他方式与之交互. 可以通过 HTML 或者脚本来修改元素的 enable disable 状态。
对于 HTML 方式, 只需要给元素加上 disabled
属性,元素便会处于禁用状态。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
:enabled {font-weight: bold;}
:disabled {opacity: 0.5;}
</style>
</head>
<body>
<form>
<table>
<tr>
<th>Name</th>
<td><input type="text" value="your full name"></td>
</tr>
<tr>
<th>Title</th>
<td><input type="text" value="your job title"></td>
</tr>
<tr>
<th>E-mail</th>
<td><input type="text" value="no email is needed" disabled></td>
</tr>
</form>
</body>
</html>
5.2 选中和非选中状态
对于复选框和单选按钮, 还定义了以下伪类 :checked
, unchecked
和 indeterminate
.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
:checked {background: blue;}
:indeterminate {border: 2px solid red;}
</style>
</head>
<body>
<form>
<table>
<tr>
<th>Rating</th>
<td>
<input type="radio" name="rating1">1
<input type="radio" name="rating2">2
<input type="radio" name="rating3" checked>3
<input type="radio" name="rating4">4
<input type="radio" name="rating5">5
</td>
</tr>
</form>
<script>
window.onload = function() {
const rating5 = document.getElementsByName("rating5");
rating5[0].indeterminate = true;
}
</script>
</body>
</html>
5.3 默认值伪类
这里设计三个伪类: :default
, :placeholder-shown
和 :autofill
.
:default
: 匹配一组相似元素中默认的 UI 元素。这通常适用于上下文菜单项、按钮和选择列表/菜单。
如果有多个同名的单选按钮,则最初选中的那个(如果有)将匹配 :default
,即使用户已更新 UI 使其不再匹配 :checked
。
如果在页面加载时选中了复选框,则 :default
将匹配该复选框。
:default
伪类还会匹配表单的默认按钮,该按钮通常是 DOM 顺序中第一个属于给定表单的按钮元素。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
[type="checkbox"]:default + label { font-style: italic; }
</style>
</head>
<body>
<div>
<input type="checkbox" id="chbx" checked name="foo" value="bar">
<label for="chbx">This was checked on page load</label>
</div>
<div>
<input type="checkbox" id="chbx2" name="foo2" value="bar2">
<label for="chbx">This was not checked on page load</label>
</div>
</body>
</html>
:placeholder-shown
伪类会选择任何定义了占位符文本(placeholder text)的输入,只要该占位符文本可见。当输入有值时,占位符将不再显示。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input:placeholder-shown {opacity: 0.5;}
</style>
</head>
<body>
<input type="text" id="firstName" placeholder="Your first name">
<input type="text" id="lastName" placeholder="Your last name">
</body>
</html>
默认情况下, placeholder 属性的值会显示在文本框中, 此刻, input:placholder-shown
将会被匹配。 当你文本框中输入内容之后, placeholder 属性的值便会被隐藏, 此时不再和 input:placholder-shown
匹配。
:autofill
伪类匹配由浏览器自动填充或自动完成值的元素。
当你在一个页面上填写某个表格时, 通常浏览器会自动填充你的姓名、电子邮件、邮寄地址等。已填充的输入字段通常会具有独特的样式,例如淡黄色的背景。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input:autofill {border: thick solid maroon;}
</style>
</head>
<body>
<input type="text" id="email" placeholder="Your email">
</body>
</html>
选中预览界面中的文本框,浏览器应该会弹出一个下拉框,让你选择用户填充的邮箱地址, 选择任何一个,你便会看到效果。
5.4 可选性伪类
:required
伪类匹配任何必需的用户输入元素,即带有 required
属性的元素。
:optional
伪类匹配不具有 required
属性或其 required
属性值为 false 的用户输入元素。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input:required { border: 1px solid #f00;}
input:optional { border: 1px solid #ccc;}
</style>
</head>
<body>
<input type="email" placeholder="enter an email address" required>
<input type="email" placeholder="optional email address">
</body>
</html>
5.5 有效性伪类
:valid
伪类指的是用户输入满足其数据有效性要求。而 :
invalid` 伪类指的是用户输入不满足其数据有效性要求。
有效性伪类 :valid
和 :invalid
仅适用于具有数据有效性要求的元素:
<div>
不会匹配两者中的任何一个,但 <input>
可以匹配倒塌们,具体取决于当前状态。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input[type="email"]:invalid {
border: 1px solid #f00;
}
input[type="email"]:valid {
border: 1px solid #008000;
}
</style>
</head>
<body>
<input type="email">
</body>
</html>
但是需要注意,这些伪类状态可能不会像你预期的那样运行。例如,截至 2022 年底,任何非必填的空电子邮件地址输入都会匹配 :valid
。即使空输入不是有效的电子邮件地址,但对于可选输入,未输入电子邮件地址也是一种有效的响应。如果你尝试填写格式错误的地址或只是一些随机文本,则由于它不是有效的电子邮件地址,所以会被 :invalid
匹配。
5.6 范围伪类
范围伪类包括 :in-range
(指用户输入的值介于 HTML min 和 max 属性设置的最小值和最大值之间)和 :out-of-range
(指用户输入的值低于控件允许的最小值或高于控件允许的最大值)。
范围伪类仅适用于具有范围限制的元素。没有范围限制的用户输入将不会被这两个伪类匹配。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input[type="number"]:out-of-range {
border: 2px solid #f00;
}
input[type="number"]:in-range {
border: 2px solid #008000;
}
</style>
</head>
<body>
<input id="grams" type="number" min="1" max="1000" />
</body>
</html>
HTML 也有一个 step
属性。如果某个值因不符合 step 值而无效,但仍介于最小值和最大值之间或等于最小值,则它将匹配 :invalid
,同时
也匹配 :in-range
。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Simple styling of a simple document</title>
<style type="text/css">
html { background-color: white; }
input[type="number"]:invalid {color: red;}
input[type="number"]:in-range {font-weight: bold;}
</style>
</head>
<body>
<input id="by-tens" type="number" min="0" max="1000" step="10" value="23" />
</body>
</html>
5.7 可变性伪类
可变性伪类包括 :read-write
,表示可编辑的用户输入, 以及 :read-only
,表示不可编辑的用户输入,包括单选按钮和复选框。只有那些值可被用户输入更改的元素才可以匹配 :read-write
。
例如,在 HTML 中,非禁用、非只读的输入元素为 :read-write,任何具有 contenteditable 属性的元素也是如此。其他所有元素都匹配 :read-only。
例如:
/* 不能匹配到 textarea,因为 textarea 默认是可编辑的。*/
textarea:read-only {opacity: 0.75;}
/* 不能匹配到 pre,因为 pre 默认是不能编辑的。*/
pre:read-write:hover {border: 1px dashed green;}
如下修改,便可以正常工作了:
<textarea disabled></textarea>
<pre contenteditable>Type your own code!</pre>