# 彻底搞懂伪类, 还不明白拿刀来见
在很多时候我们的需求中需要用到css伪类来帮助我们选择其中的某个子节点,最常见的需求就是: 第一个结点加上某些特殊的样式,最后一个结点加上某些特殊的样式,3倍数的结点加上某些特殊的样式,选中一堆class中的第一个结点...,以上这些需求我们都可以用下面这些伪类来完成
:first-child
:last-child
:nth-child
:first-of-type
:last-of-type
:nth-of-type
但是很多时候我们自信满满的写下代码时,却发现它并没有生效,今天我们就来好好捋一捋这些伪类.
# 生效规则
其实这些东西在MDN
上都有介绍过, 但是我们今天不去看MDN
上的介绍, 用我自己的话给大家介绍一下这些伪类. 因为MDN
上的东西说的有点反人类, 看了不一定能真正理解他的意思. 我们可以把整个生效规则分为三步:
- 通过写的
css
圈选出指定结点列表 - 通过
first
、last
、nth
等参数找到指定位置结点 - 判断该结点是否符合要求,符合则样式生效,否则不生效
对于xxx-child
和xxx-type
的伪类来说, 都是这三个步骤,他们的区别在于圈选出来的结点列表不一样,下面我们分别举例说明.
这里面最重要的步骤就是第一步,很多同学没能搞清楚第一步,所以才会出现写出来的css没有选中自己真正希望选中的结点!!
# xxx-child
对于xxx-child
的伪类来说,圈选出来的结点列表就是所有和它同级的结点,比如下面这个


我们在item
上运用了伪类,那么结点列表就是所有和这个class
同级的子结点,也就是红框内的结点.那么我们的第一步就完成了.
第二步找到指定位置的结点,我们这条css
写的是first-child
,所以就会在这个结点列表中选中第一条,也就是p1
.那么我们的第二步也完成了.
来到第三步,判断该结点是否符合要求.也就是说,在我们运用伪类的css上,去掉伪类能否匹配上这个结点,在这里来说就是
.item {
color: red;
}
能否匹配上第一个p
标签?我们可以看到第一个p
标签并没有带item
的class
,所以这条css
并不会生效.至此我们整个流程走完了,结论就是我们写的这条css
并不会让任何元素变红.
如果我们想让p1
变红怎么办,很简单,在p1
上加上item
的class
.


这样我们就能成功的选中p1
,如果我们想选中div1
怎么办,用nth-of-child(3)
就能匹配到第三条,而使用nth-of-child(3)
时同样走我们上面的三个步骤去判断,最后会匹配到div1
,而它又带了item
的class
,所以这条规则是生效的,那么它就会变红.


# xxx-type
相对于xxx-child
,xxx-type
所圈定的结点列表很多人都不知道究竟是什么,这也是为什么大家写出来的很多css
都会不生效的原因.xxx-type
的圈定规则和结点类型,比如div
,p
,h1
有关,它会圈选出所有同级结点中和自己相同类型的.举个例子:


并不是把所有带item class
的结点分成一组!而是会把拥有该class
的对应结点类型拎出来分成一组!
并不是把所有带item class
的结点分成一组!而是会把拥有该class
的对应结点类型拎出来分成一组!
并不是把所有带item class
的结点分成一组!而是会把拥有该class
的对应结点类型拎出来分成一组!
在上面的例子中,有两个地方带了item
的class
,所以就会选出两个结点类型p
标签和div
标签,所以圈定出来的列表就有两组,其中p
标签是一组,div
标签又是一组.

注意,虽然他们是各自穿插在其他结点中间,但是type
只会选择同类型的结点.选出这两组来之后,我们的第一步又完成了.
那么接下来的事情又简单了,我们写的是first
,自然是选择第一个,对于p
标签来说,第一个p
结点带了item
的class
,所以符合要求,那么就会匹配上规则,所以变红了.但是对于div
标签来说,第一个div
并没有带item
的class
,所以并不符合规则,这条css
样式自然没有生效.那么这里我们就把第二步和第三步完成了,所以结果就是只有p1
变红.
如果我们想div1
也变红该怎么做?给第一条div
也加上item
的class
就行了


是不是so easy
.我们可以看到,当我们完成第一步之后,剩下的东西其实就非常简单了.所以当我们理解了它们的圈选规则之后,就能随心所欲的运用这些伪类了.
# 小测验
试着凭借自己的推断来判断下面哪些结点会变成红色


我们简单讲解一下第一张图,首先,分组,每个div
下的子结点都有带item
的div
,所以它们被分成了三组:
第二步,我们将选中这三个组中的第一个结点.然后第三步,发现只有第三张图是符合我们写的class
,所以只有第三张图的div1
会变成红色

那么我们把first-child
改成first-of-type
之后,又会有哪几个结点变成红色呢,这个就留给大家去试试了.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div.item:first-of-type {
color: red;
}
</style>
</head>
<body>
<div>
<p>p1</p>
<p>p2</p>
<p>p3</p>
<div class="item">div</div>
</div>
<div>
<p class="item">p1</p>
<p>p2</p>
<p>p3</p>
<div class="item">div</div>
</div>
<div>
<div class="item">div1</div>
<p>p1</p>
<p class="item">p2</p>
<div>div2</div>
</div>
</body>
</html>
同时抛一个当时遇见的问题,我的需求是需要选中拥有相同class
的第一个结点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.item:first-of-type {
color: red;
}
</style>
</head>
<body>
<div>
<div></div>
<div></div>
<p></p>
<h1></h1>
<!-- 这个结点是我想选中的 -->
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</body>
</html>
最符合直觉的写法是下面这种
.item:first-of-type {
color: red;
}
但是结合我们之前讲解的内容,这种写法会把上面两个div
也纳入到圈选结点中来,所以我们会选中第一个div
,但是它并不带class item
,所以不会生效.最后的解决办法如下:


改变圈选范围,就能很好的满足我本次需求
# 结语
那么至此,child
和type
的用法和区别我们就讲清楚了,他们的主要区别就在于圈选的结点列表不一样.只要你真正理解了这三个步骤,那么我相信下次再有这种需求出现的时候,我们写出来的css
一定能正确的选中我们想要的结点.