伪类选择符

伪类元素符(presudo-class selector)为文档中不一定存在的结构指定样式,或者为某些元素(或文档本身)的特定状态赋予“幽灵类”。

举个例子:为突出显示表格中相隔的行,我们可能会为相隔的行设置 class=”even” 属性,但其实使用 CSS 伪类选择符 :nth-child(even) 就能实现这个效果,而无需添加额外的类名。而这里 :nth-child(even) 起的作用就像是给元素加了类名,但其实没有,所有说是“幽灵类”。

拼接伪类

CSS 允许把伪类拼接(串联)在一起。

  1. /*
  2. 鼠标悬停:
  3. 1. 未访问链接红色,
  4. 2. 已访问链接红褐色
  5. */
  6. a:link:hover {color: red;}
  7. a:visited:hover {color: maroon;}

注意,不要拼接相互排斥的伪类。

结构伪类

伪类大多都是结构上的,所有伪类都是一个冒号(:)后面跟一个词的形式。

要明确都是:伪类始终指代所依附的元素。因为有几个伪类,会令人误以为指代的是后代。举例:

  • ericmeyer:first-child:选择的是我,且是我爸妈的第一个孩子

  • ericmeyer > :first-child:选择的是我的孩子,且是我的第一个孩子。

选择根元素

:root 伪类选择文档的根元素。在 HTML 中,根元素始终是 html。在不同的 XML 语言中,根元素有所不同,比如 RSS2.0 的根元素是 rss。

  1. :root {
  2. border: 10px dotted gray;
  3. }

选择空元素

:empty 伪类匹配没有任何子代的元素。这里的子代包括文本节点(文本或空白)在内。

比如,通过下面的方式,能禁止显示空段落。

  1. p:empty {display: none;}

对下面几个元素来说,只有第一和最后一个元素会被 p:empty 匹配。

  1. <p></p>
  2. <p> </p>
  3. <p>
  4. </p>
  5. <p><!-- 注释 --></p>

注意,注释不是内容,也不是空白。

这并不意味着就可以使用 *:empty {display: none;} 隐藏所有空元素了!这里有一个陷阱——会匹配所有 HTML 空元素(img 和 input 等)、甚至连有效的没内容的 textarea 都会被匹配!

  1. <!-- 不要这样写!下面的标签,也会被 *:empty {display: none;} 匹配 -->
  2. <img class="rocket light-only" src="https://logrocket-assets.io/img/logo.png" alt="Logo">
  3. <br>
  4. <input type="number" min="-1" max="1" step=".01" />
  5. <textarea></textarea>

:empty 是 Selectors Level 3 中唯一一个在匹配时,会考虑文本节点的选择符。

选择唯一子代

:only-child 用来匹配唯一子元素。以下面代码为例:

  1. img:only-child {border: 2px solid black;}

如果父元素中只有一个图片,这张图片就会应用上面的边框样式。

而下面的样式则表示:在带有地址链接的 a 标签范围内,为在父元素中是唯一子元素的图片应用样式。

  1. a[href] img:only-child {border: 2px solid black;}

如果只匹配 a 元素只有一个图片子元素时的图片样式,就要使用子元素连结符了。

  1. a[href] > img:only-child {border: 2px solid black;}

更进一步的,如果只匹配 a 元素只有一个图片时的图片样式,就得使用 :only-of-type 了。

  1. 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 指代元素,而非其他的任何东西。以下面代码为例:

  1. <style>p.unique:only-of-type {color: red;}</style>
  2. <div>
  3. <p class="unique">这是具有 class="unique" 属性的段落。</p>
  4. <p>这个段落是没有类名属性的段落。</p>
  5. </div>

这里,两个段落都不会被选中。不要被这里的类名干扰,它无关紧要。整个选择符“p.unique:only-of-type”表示的含义是“选择的 p 元素包含 class=”unique” 属性,而且 p 元素是同胞中唯一的一个”。

选择第一个和最后一个子代

:first-child 用来选择同胞元素中的第一个,或者说是某元素的第一个子元素。

  1. <style>p:first-child { color: red; }</style>
  2. <div>
  3. <p>This text is selected!</p> <!-- 匹配,是 div 中的第一个子元素,且是 p -->
  4. <p>This text isn't selected.</p>
  5. </div>
  6. <div>
  7. <h2>This text isn't selected: it's not a `p`.</h2> <!-- 不匹配,是 div 中的第一个子元素,但却是 h2,而非 p! -->
  8. <p>This text isn't selected.</p>
  9. </div>

效果如下:

image.png

于 :first-child 对应的就是 :last-child,用来选择同胞元素中的最后一个,或者说是某元素的最后一个子元素。

  1. <style>p:last-child { color: red; }</style>
  2. <div>
  3. <p>This text isn't selected!</p>
  4. <p>This text is selected.</p> <!-- 匹配,是 div 中的最后一个子元素,且是 p -->
  5. </div>
  6. <div>
  7. <h2>This text isn't selected.</h2>
  8. <p>This text is selected.</p><!-- 匹配,是 div 中的最后一个子元素,且是 p -->
  9. </div>

效果如下:

image.png

有趣的是,上述两个伪类结合在一起使用的效果相当于 :only-child。

  1. p:only-child {color: red;}
  2. /* 效果相当于 */
  3. p:first-child:last-child {color: red;}

选择第一个和最后一个某种元素

选择某个元素中的第一个 table 元素,可以这样:

  1. table:first-of-type {broder: 2px solid gray;}

效果如下:

image.png

同理,选择某行中的第一个单元格:

  1. td:first-of-type {border-left: 1px solid red;}

与 :first-of-type 对应的是 :laast-of-type:

  1. table:last-of-type {broder: 2px solid gray;}

效果如下:

image.png

而把上述两个伪类结合连在一起,达到的效果与 :only-of-type 一样。

  1. table:first-of-type {color: red;}
  2. /* 效果等价于 */
  3. table:first-of-type:last-of-type {color: red;}

选择第 n 个子元素

:first-child 与 :last-child 的功能很有限,只能选择第一个和最后一个子元素,不太灵活。CSS 为我们提供了 :nth-child、:nth-last-child 伪类,支持填入整数或一个代数式,选择任何想选择的子元素。

填入整数:

  1. p:first-child {font-weight: bold;} /* 效果等同于 */
  2. p:nth-child(1) {font-weight: bold;}
  3. p:last-child { font-weight: bold; } /* 效果等同于 */
  4. p:nth-last-child(1) { font-weight: bold; }
  5. /* 选择第二个子代 */
  6. p:nth-child(2) {...}
  7. /* 选择倒数第二个子代 */
  8. p:nth-last-child(2) {...}

填入一个代数式:

代数式是 an + b 的形式,a、b 是具体的整数,n 原封不动,是自然数。+ b、- b 是可选的,如果不需要可以去掉。

  1. /* 选择奇数行: 1、3、5、7、9…… */
  2. ul > li:nth-child(2n + 1) { ... } /* 等效的关键字版本 */
  3. ul > li:nth-child(odd) { ... }
  4. /* 选择偶数行: 2、4、6、8、10…… */
  5. ul > li:nth-child(2n) { ... } /* 等效的关键字版本 */
  6. ul > li:nth-child(even) { ... }
  7. /* 其他例子 */
  8. ul > li:nth-child(3n + 1) { ... } /* 1、4、7、10、13…… */
  9. 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 用来从同胞元素中选择某一种元素。只不过前者是从前往后找,后者是从后往前找。

举个例子:将段落中从第二个链接开始选择间隔的超链接——就可以通过下面的方式选择:

  1. p > a:nth-of-type(even) { background-color: blue; color: white; }

效果如下:

image.png

动态伪类

还有一些与结构有关的伪类,是在页面渲染后根据页面变化而变化的。

以锚记元素(a)来说,链接有两个基本状态,访问和未访问。而从 HTML 标记中是看不出这一区别的。这就需要借助超链接伪类的帮助了。

超链接伪类

CSS2.1 定义了两个超链接伪类,:link 和 :visited,HTML 中,这两个伪类只能用在具有 href 属性的 a 元素上。

伪类 说明
:link 匹配未访问链接
:visited 匹配已访问链接。处于安全考虑,应用到已访问链接的样式属性是有限制的

举一个例子:

  1. a:link {color: blue;} /* 未访问链接蓝色 */
  2. 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 匹配激活元素。在按下鼠标按键的那段时间

举例:

  1. a:link {color: navy;}
  2. a:visited {color: gray;}
  3. a:focus {color: orange;}
  4. a:hover {color: red;}
  5. 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 脚本操作。

  1. :enabled {font-weight: bold;}
  2. :disabled {opacity: 0.5;}

效果如下:

image.png

选择状态

Selectors Level 3 为 HTML 的复选框和单选按钮共定义了 3 种状态伪类::checked、:unchecked 以及 :indeterminate。

  • :checked 表示选中,
  • :unchecked 表示未选中,类似于 :not(:checked)
  • :indeterminate 表示不确定,这个状态暂时只能由 DOM 脚本设定。

:indeterminate 伪类存在的目的,是从视觉上提示用户需要选中(或不选中)元素,不影响元素底层状态。元素底层状态只能是选中或者未选中。

可以结合 :checked 和紧邻同胞连接符装饰复选框和单选按钮的 label:

  1. <style>
  2. input[type="checkbox"]:checked + label {
  3. color: red;
  4. font-style: italic;
  5. }
  6. </style>
  7. <input id="chbx" type="checkbox"><label for="chbx">I am a label</label>

默认选项伪类

:default 伪类匹配同名单选按钮或复选框中,最初选中的那个元素。即使后面用户改变 UI,这个伪类依旧会在最初选中的那个元素上生效。

  1. [type="checkbox"]:default + label {
  2. font-style: italic;
  3. }
  1. <input type="checkbox" id="cbx" name="foo" checked>
  2. <label for="cbx">页面加载时即被选中</label>

效果:

image.png

可选性伪类

:required 匹配必填表单控件,:optional 匹配没有 required 属性的表单控件。

  1. input:required {
  2. border: 1px solid red;
  3. }
  4. /*
  5. 等效查询
  6. input[required] {border: 1px solid red;}
  7. */
  8. input:optional {
  9. border: 1px solid blue;
  10. }
  11. /*
  12. 等效查询
  13. input:not([required]) {border: 1px solid blue;}
  14. */
  15. body {
  16. margin: 4rem;
  17. }
  1. <input type="email" placeholder="enter an email address" required>
  2. <input type="email" placeholder="optional email address">
  3. <input type="email" placeholder="optional email address" required="false">

效果:

image.png

有效性伪类

  1. input:focus:valid {
  2. background-color: lightgreen;
  3. }
  4. input:focus:invalid {
  5. background-color: pink;
  6. }
  1. <form>
  2. <div class="field">
  3. <label for="url_input">Enter a URL:</label>
  4. <input type="url" id="url_input">
  5. </div>
  6. <div class="field">
  7. <label for="email_input">Enter an email address:</label>
  8. <input type="email" id="email_input" required>
  9. </div>
  10. </form>

效果:

GIF 2020-11-14 20-49-04.gif

范围伪类

  1. <input type="number" min="0" max="100">
  1. input:focus:in-range {
  2. background-color: lightgreen;
  3. }
  4. input:focus:out-of-range {
  5. background-color: pink;
  6. }

效果:

GIF 2020-11-14 20-49-04.gif
input[type=”number”] 还有一个 step 属性,如果一个值不匹配步进值,即使是在 min、max 范围内的,也会被认为是无效的。

举个例子:

  1. input[type="number"]:focus:invalid {
  2. background-color: pink;
  3. }
  4. input[type="number"]:focus:in-range {
  5. border: 10px solid greenyellow;
  6. }
  1. <input type="number" min="0" max="100" step="10">

效果:23 在范围内(边框青色),但是不能被 10 整除(背景粉色)。

image.png

可变性伪类

:read-write 和 :read-only。

  • :read-write:匹配可编辑表单元素
  • :read-only:匹配只读(不可编辑)表单元素
  1. <style>
  2. textarea:read-only {opacity: .75;}
  3. pre:read-write:hover {border: 1px dashed green;}
  4. </style>
  5. <textarea cols="12" rows="3" disabled></textarea>
  6. <pre contenteditable>Type your own code!!</pre>

效果:

GIF 2020-11-14 20-49-04.gif

:target 伪类

URL 地址中可以包含一个片段标识符(fragment identifier),比如下面这个 URL:

  1. https://www.w3.org/TR/selectors-3/#target-pseudo

这里的 #target-pseudo 就是片段标识符了,由 # 符号标记。如果页面中包含一个 ID 值为 target-pseudo 的元素,那么这个元素就是片段标识符的目标。

我们可以通过 :target 伪类给目标元素设置样式。

  1. <h4 id="target-pseudo"><span class="secno">6.6.2. </span>The target
  2. pseudo-class :target</h4>
  1. :target {
  2. background-color: skyblue;
  3. }

效果:

image.png

:lang 伪类

使用 :lang 可以根据文本使用的语言来选择元素。匹配方式上,:lang() 与 |= 属性选择符类似。

  1. :lang(fr) {font-style: italic;}
  2. [lang|=fr] {font-style: italic;}

不过,伪类比属性选择更可靠。因为前者的语言来源可能来自元素自身,也可能继承自设置了语言的祖先元素。

Selector Level 3 规定,HTML 中,语言可以通过 lang 属性判断,也可以通过 meta 元素和协议(HTTP 首部)判断。

否定伪类

前面介绍的都是肯定选择符,选择的都是满足的条件的元素。如果反过来,选择不满足条件的元素,就要使用 :not() 伪类了。

比如,想让除了 .mereinfo 之外的列表元素倾斜显式,可以这样声明样式:

  1. /* 匹配的 li 元素要求不含有 .moreinfo 这个类名 */
  2. li:not(.moreinfo) {font-style: italic;}

:not 依附于元素之上,括号中的是简单的选择符。简单的选择符指:类型选择符、通用选择符、属性选择符、类选择符、ID 选择符或伪类——就是没有祖辈-后代关系的选择符(注意,也没有伪元素),且只能使用一个。

更多例子:

  1. /* 选择不是 section 子代的所有表格 */
  2. :not(section) > table {...}
  3. /* 选择不在表头中的表头单元格 */
  4. :not(thead) > tr > th {...}

还有一个有趣的例子:

  1. <style>
  2. div:not(.one) p {font-weight: normal;}
  3. div.one p {font-weight: weight;}
  4. </style>
  5. <div class="one">
  6. <div class="two">
  7. <p>我是一个自然段</p>
  8. </div>
  9. </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 用于装饰所有非行内元素的首字母(或标点符号)。

  1. p::first-letter {color: red;}

这个规则其实会让用户代理将每个 p 元素的首字母用一个虚拟元素包围。类似下面这样:

  1. <p><p-first-letter>T</p-first-letter>his is a p element, with a styled first letter.</p>

::first-letter 只对虚构元素里的内容应用样式。而用户是无法通过技术手段获得“”这个伪元素的。

装饰首行

::first-line 用于装饰元素的首行文本。举个例子:

  1. /* 段落首行字号变大,并显示为紫色 */
  2. p::first-line {
  3. font-size: 150%;
  4. color: purple;
  5. }

这个规则其实会让用户代理将每个 p 元素的首行文本用一个虚拟元素包围。类似下面这样:

  1. <p>
  2. <p-first-line>Mocha(发音"摩卡")诞生于2011年,是现在最流行的JavaScript</p-first-line>
  3. 测试框架之一,在浏览器和Node环境都可以使用。所谓"测试框架",就是运行测试的工
  4. 具。通过它,可以为JavaScript应用添加测试,从而保证代码的质量。
  5. </p>

注意,这里的“首行”是指显示在屏幕上的首行,具体几个字视视口大小而定,总之是给首行应用样式的。

::first-letter 和 ::first-line 的限制

  • 这两个伪元素只能应用在块级元素上(例如标题段落),不能应用在行内元素上(例如超链接)。
  • 这两个伪元素可以使用的 CSS 属性也是有限的。

图片.png

装饰(或创建)前置和后置内容元素

::before:在 h2 元素前面加上两个银色的方括号:

  1. h2::before {content: "]]"; color: silver;}

效果:
图片.png

content 属性指定插入的内容,我们给它设置了一个银色文本效果。

::after 用于在元素之后添加内容。比如,在文档末尾添加结束语:

  1. body::after {content: "The End.";}

总结

  • 使用选择符可以创建丰富的 CSS 规则,规则可以应用于:
    • 少数元素
    • 大量类似的元素
  • 选择符和规则可以组合在一起,更省体积
  • 牢固掌握选择符与文档结构的关系

但了解继承和层叠的机制在样式上所起的作用,会帮助我们更加准确的理解选择符以及如何组合选择符。