CSS 结构性伪类(Structural Pseudo-Class)详解
最近在看 CSS 相关文章,在《CSS3 专业网页开发》的第 4 章的结构伪类(Structural Pseudo-Class)小节讲的不是很容易理解,所以在此写个笔记,结合在线手册、W3C 规范和其他人写的博客把这一部分详细解析一下。
1. 关于伪类
首先要明白的是,伪类是对元素不同状态或者类型进行区分的选择器。在 CSS 中定义了 :active、:focus、:hover、:visited 等用来描述元素状态的伪类和 :first-child 这个用来描述结构的伪类,具体可以参考 MDN 中文手册 伪类部分 和 Pseudo-classes - CSS Level 2 (Revision 1)。
CSS 的 Selectors Level 3 (通俗的说也就是 CSS3) 中新增了近二十个新的伪类,但本文仅选择结构性伪类中的 :nth-child 和 :nth-last-child, :nth-of-type 和 :nth-last-of-type, :last-child 来解析。
结构性伪类的作用很明显,可以帮助我们省去 HTML 中描述结构的 class 或 ID,其中一个重要用途就是表格的个性化显示。
2. :first-child
1
E:first-child { /* properties */ }
这是在 CSS Level 2 里定义的伪类,用来选择 E 中的每个元素在其兄弟元素中的位置是处在第一的 E 的集合,伪类名中的 child 表示每个元素的父元素的所有子元素,即兄弟元素。:last-child 和 :first-child 类似,只不过是选择位置是在最后一个的 E,下面的例子可以看出来。
为了展示不同的效果,故在第 4 行不符合规范地放了一个 p 元素。后面内容均使用这段 HTML,所以可以稍微留意一下结构
示例 HTML
1
2
3
4
5
6
7
8
9
10
11
<ul>
<li>1. List 1-1</li>
<ul>
<p>2. Paragraph 2-1</p>
<li>3. List 2-2</li>
<li>4. List 2-3</li>
<li>5. List 2-4</li>
</ul>
<li>6. List 1-3</li>
<li>7. List 1-4</li>
</ul>
示例 CSS
1
2
li:first-child { color: red; }
li:last-child { color: blue; }
效果
- 1. List 1-1
- 3. List 2-2
- 4. List 2-3
- 5. List 2-4
- 6. List 1-3
- 7. List 1-4
2. Paragraph 2-1
3. :nth-child 和 :nth-last-child
1
2
E:nth-child(n) { /* properties */ }
E:nth-last-child(n) { /* properties */ }
这两个是 CSS Level 3 里定义的伪类,其中括号里的 n 可以替换成 整数 ( 1 ~ 兄弟元素个数 )、关键字 ( odd / even ) 或 公式 ( an+b )
:nth-child 用来选择 E 中的每个元素在其兄弟元素(无论是否符合 E)中的位置索引符合括号里的表达式的 E 的集合。 注意,第一个兄弟元素的位置索引是 1 ,往后依次加 1 ,遇到非亲结点兄弟元素(如堂兄弟元素)将重置计数 ;如果括号里是公式 an+b,则其中 n 的值将是使 an+b 的值尽可能覆盖位置索引的非负整数。
:nth-last-child 用来选择 E 中的每个元素在其兄弟元素(无论是否符合 E)中的逆向位置索引符合括号里的表达式的 E 的集合。 注意,这里的逆向位置索引和上面的正好反过来,即最后一个元素的位置索引是 1,往前依次加 1
用法示例:
tr:nth-child(1),等同于tr:first-child,选择第一行tr:nth-child(3),选择第三行tr:nth-child(2n+1),等同于tr:nth-child(odd),选择奇数行tr:nth-child(2n),等同于tr:nth-child(even),选择偶数行tr:nth-child(-n+3),选择前三行,tr:nth-last-child(-n+3),选择最后三行
示例 HTML
1
2
3
4
5
6
7
8
9
10
11
<ul>
<li>1. List 1-1</li>
<ul>
<p>2. Paragraph 2-1</p>
<li>3. List 2-2</li>
<li>4. List 2-3</li>
<li>5. List 2-4</li>
</ul>
<li>6. List 1-3</li>
<li>7. List 1-4</li>
</ul>
示例 CSS
1
2
3
4
5
li:nth-child(1) { text-decoration: line-through; } /* 第一个 */
li:nth-child(3) { text-decoration: underline; } /* 第三个 */
li:nth-child(odd) { text-transform: uppercase; } /* 奇数个 */
li:nth-child(-n+2) { font-weight: bold; } /* 前两个 */
li:nth-last-child(-n+2) { font-style: italic; } /* 后两个 */
效果
- 1. List 1-1
- 3. List 2-2
- 4. List 2-3
- 5. List 2-4
- 6. List 1-3
- 7. List 1-4
2. Paragraph 2-1
由于 CSS 中能够相互覆盖的属性有限,所有接下来使用 JavaScript 来为每个选中的元素附上其被选中的规则
示例 JavaScript (部分)
1
2
3
4
5
6
(function() {
var nth_child_1 = document.querySelectorAll("li:nth-child(1)");
Array.prototype.forEach.call(nth_child_1, function( node ){
node.innerText += "<span style='color: red;'> nth-child(1)</span>";
});
})();
- 1. List 1-1
- 3. List 2-2
- 4. List 2-3
- 5. List 2-4
- 6. List 1-3
- 7. List 1-4
2. Paragraph 2-1
4. :nth-of-type 和 :nth-last-of-type
1
2
E:nth-of-type(n) { /* properties */ }
E:nth-last-of-type(n) { /* properties */ }
:nth-of-type 用来选择 E 中的每个元素在其只符合 E 的兄弟元素中的位置索引符合括号里的表达式的 E 的集合
:nth-last-of-type 与 :nth-last-child 同理,把 :nth-of-type 位置索引逆序查找即可
示例 HTML
1
2
3
4
5
6
7
8
9
10
11
<ul>
<li>1. List 1-1</li>
<ul>
<p>2. Paragraph 2-1</p>
<li>3. List 2-2</li>
<li>4. List 2-3</li>
<li>5. List 2-4</li>
</ul>
<li>6. List 1-3</li>
<li>7. List 1-4</li>
</ul>
示例 CSS
1
2
3
4
5
li:nth-of-type(1) { text-decoration: line-through; } /* 第一个 */
li:nth-of-type(3) { text-decoration: underline; } /* 第三个 */
li:nth-of-type(odd) { text-transform: uppercase; } /* 奇数个 */
li:nth-of-type(-n+2) { font-weight: bold; } /* 前两个 */
li:nth-last-of-type(-n+2) { font-style: italic; } /* 后两个 */
效果
- 1. List 1-1
- 3. List 2-2
- 4. List 2-3
- 5. List 2-4
- 6. List 1-3
- 7. List 1-4
2. Paragraph 2-1
同样,下面使用 JavaScript 来使结果更加直观
- 1. List 1-1
- 3. List 2-2
- 4. List 2-3
- 5. List 2-4
- 6. List 1-3
- 7. List 1-4
2. Paragraph 2-1
通过对比可以明显发现 :nth-child 和 :nth-of-type 的区别。比如第 5 行,尽管这个 li 处于父 ul 的第 4 的位置,但是由于兄弟元素的第一个为 p,所以要从第二个元素开始计数的第 3 就是第 5 行的这个 li