# 彻底搞懂伪类, 还不明白拿刀来见

在很多时候我们的需求中需要用到css伪类来帮助我们选择其中的某个子节点,最常见的需求就是: 第一个结点加上某些特殊的样式,最后一个结点加上某些特殊的样式,3倍数的结点加上某些特殊的样式,选中一堆class中的第一个结点...,以上这些需求我们都可以用下面这些伪类来完成

  1. :first-child
  2. :last-child
  3. :nth-child
  4. :first-of-type
  5. :last-of-type
  6. :nth-of-type

但是很多时候我们自信满满的写下代码时,却发现它并没有生效,今天我们就来好好捋一捋这些伪类.

# 生效规则

其实这些东西在MDN上都有介绍过, 但是我们今天不去看MDN上的介绍, 用我自己的话给大家介绍一下这些伪类. 因为MDN上的东西说的有点反人类, 看了不一定能真正理解他的意思. 我们可以把整个生效规则分为三步:

  1. 通过写的css圈选出指定结点列表
  2. 通过firstlastnth等参数找到指定位置结点
  3. 判断该结点是否符合要求,符合则样式生效,否则不生效

对于xxx-childxxx-type的伪类来说, 都是这三个步骤,他们的区别在于圈选出来的结点列表不一样,下面我们分别举例说明.

这里面最重要的步骤就是第一步,很多同学没能搞清楚第一步,所以才会出现写出来的css没有选中自己真正希望选中的结点!!

# xxx-child

对于xxx-child的伪类来说,圈选出来的结点列表就是所有和它同级的结点,比如下面这个

我们在item上运用了伪类,那么结点列表就是所有和这个class同级的子结点,也就是红框内的结点.那么我们的第一步就完成了.

第二步找到指定位置的结点,我们这条css写的是first-child,所以就会在这个结点列表中选中第一条,也就是p1.那么我们的第二步也完成了.

来到第三步,判断该结点是否符合要求.也就是说,在我们运用伪类的css上,去掉伪类能否匹配上这个结点,在这里来说就是

.item {
  color: red;
}

能否匹配上第一个p标签?我们可以看到第一个p标签并没有带itemclass,所以这条css并不会生效.至此我们整个流程走完了,结论就是我们写的这条css并不会让任何元素变红.

如果我们想让p1变红怎么办,很简单,在p1上加上itemclass.

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

# xxx-type

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

并不是把所有带item class的结点分成一组!而是会把拥有该class的对应结点类型拎出来分成一组! 并不是把所有带item class的结点分成一组!而是会把拥有该class的对应结点类型拎出来分成一组! 并不是把所有带item class的结点分成一组!而是会把拥有该class的对应结点类型拎出来分成一组!

在上面的例子中,有两个地方带了itemclass,所以就会选出两个结点类型p标签和div标签,所以圈定出来的列表就有两组,其中p标签是一组,div标签又是一组.

注意,虽然他们是各自穿插在其他结点中间,但是type只会选择同类型的结点.选出这两组来之后,我们的第一步又完成了.

那么接下来的事情又简单了,我们写的是first,自然是选择第一个,对于p标签来说,第一个p结点带了itemclass,所以符合要求,那么就会匹配上规则,所以变红了.但是对于div标签来说,第一个div并没有带itemclass,所以并不符合规则,这条css样式自然没有生效.那么这里我们就把第二步和第三步完成了,所以结果就是只有p1变红.

如果我们想div1也变红该怎么做?给第一条div也加上itemclass就行了

是不是so easy.我们可以看到,当我们完成第一步之后,剩下的东西其实就非常简单了.所以当我们理解了它们的圈选规则之后,就能随心所欲的运用这些伪类了.

# 小测验

试着凭借自己的推断来判断下面哪些结点会变成红色

我们简单讲解一下第一张图,首先,分组,每个div下的子结点都有带itemdiv,所以它们被分成了三组:

第二步,我们将选中这三个组中的第一个结点.然后第三步,发现只有第三张图是符合我们写的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,所以不会生效.最后的解决办法如下:

改变圈选范围,就能很好的满足我本次需求

# 结语

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