【现代 CSS】:nth-child 选择器 of S 新特性

科技   2024-10-31 09:11   中国台湾  

大家好,我是 Coco。

今天我们来讨论了一个非常有意思与 CSS 选择器相关的题目。题目如下:

假设我们如下的 HTML 结构:

<div class="box">
    <p class="aa">aaaaaaaaaaaa</p>
    <p class="bb">bbbbbbbbbbbb</p>
    <p class="cc">cccccccccccc</p>
    <p class="aa">aaaaaaaaaaaa</p>
    <p class="bb">bbbbbbbbbbbb</p>
    <p class="cc">cccccccccccc</p>
    <p class="aa">aaaaaaaaaaaa</p>
    <p class="bb">bbbbbbbbbbbb</p>
    <p class="cc">cccccccccccc</p>
</div>

效果如下:

如何在不添加类名的情况下,快速的选取第一个 class 为 .cc 的元素

当然,上面的结构示意图只是一种可能的情况,这里想表述的场景的意思是:

  1. 我们希望选取的 .cc 元素,在其父元素下,存在多个 .cc所以需要精准定位第一个
  2. .cc 元素一定不是其所有兄弟元素中的第一个
  3. 所以子元素的标签类型是一样的,都是 <p> 元素或者某个同类标签
  4. 不允许使用类似 .bb + .cc 的方式直接进行选取,因为第一个 .cc 元素的上一个元素不一定是 .bb,可以是其他任何元素;

基于上述的限定条件,你可以暂停阅读,思考 20 秒,可能的方式有哪些?

使用 :nth-child 或者 :nth-of-type

一般而言,比较容易想到的,肯定是首先使用 :nth-child:nth-of-type 伪类选择器进行尝试。

但是,遗憾的是,这两个选择器都无法做到在上述结构下,成功选取第一个 .cc 元素。

代码如下:

.box .cc:nth-child(1) {
    color: red;
    font-size24px;
}

或者

.box .cc:nth-of-type(1) {
    color: red;.box .cc:nth-child(1) {
    color: red;
    font-size24px;
}

也就是说,上述两种方式,都是不行的。简单解释一下:

  1. 对于 :nth-child 而言,:nth-child() 伪类[1]根据元素在父元素的子元素列表中的索引来选择元素。最为核心的是,选择器,它是根据父元素内的所有兄弟元素的位置来选择子元素

这里最重要的是,它是根据父元素内的所有兄弟元素的位置来选择子元素,而我们无法得知在实际业务场景下,第一个 .cc 到底处于第几个索引。因此,这个选择器明显没法胜任。

  1. 对于 :nth-of-type 而言,基于相同类型(标签名称)的兄弟元素中的位置来匹配元素

非常重要的一点是,使用此选择器无法选择基于相同类名的元素的位置,来匹配元素

什么意思呢?

如果我们把上述 DEMO 改造,改造成这样:

<div class="box">
    <p class="aa">aaaaaaaaaaaa</p>
    <span class="bb">bbbbbbbbbbbb</span>
    <div class="cc">cccccccccccc</div>
    <p class="aa">aaaaaaaaaaaa</p>
    <span class="bb">bbbbbbbbbbbb</span>
    <div class="cc">cccccccccccc</div>
    <p class="aa">aaaaaaaaaaaa</p>
    <span class="bb">bbbbbbbbbbbb</span>
    <div class="cc">cccccccccccc</div>
</div>

再基于上述结构下,选择第一个 class 为 .cc 的元素,就可以利用 :nth-of-type 实现。因为,所有的 .cc 都是 div 元素,且没有其它 div 元素:

.box div:nth-of-type(1) {
    color: red;
    font-size24px;
}

效果如下:

当然,实际情况远没有如此乐观。在所有子元素标签情况无法确定的情况下,基于上述讨论,使用 :nth-child 或者 :nth-of-type 都不可行。还有什么办法呢?

使用 :nth-of-class 伪类?

按照上面说的,如果存在一个伪类 :nth-of-class,可以实现,基于相同类名的元素的位置,来匹配元素,那么问题就解决了。

但是实际情况是,目前 CSS 规范仍未支持 :nth-of-class 伪类选择器。

我们必须另辟蹊径。

巧用 :not() 与后代选择器 ~

好,现在我们换个思路,如果要我们选择,非第一个 class 为 .cc.cc 元素,可以怎么实现呢?

可以利用 ~ 后续兄弟选择器实现:

.box .cc ~ .cc {
    color: red;
    font-size24px;
}

效果如下:

成功了!此时,我们只需要基于上述结果,取反即可。在现代 CSS 中,我们可以利用 :not() 伪类实现。

:not() 伪类::not() CSS[2] 伪类[3]用来匹配不符合一组选择器的元素。由于它的作用是防止特定的元素被选中,它也被称为反选伪类(negation pseudo-class)。

改造一下上述的代码:

.box .cc:not(.cc ~ .cc) {
    color: red;
    font-size24px;
}

这样,我们就成功的选取到了第一个 .cc 元素。

还有办法吗?

是的,在现代 CSS 中,:nth-child() 提供了一种更为强大的方式供我们使用。

使用 :nth-child 伪类的现代特性 of S 规则

从 Chrome 111,:nth-child() 提供了一种更为强大的特性,让我们能够方便的实现上述的效果,这种语法就是 :nth-child(1 of .foo)

其中,上述代码中的 1 of .foo 就是所谓的 of S 规则。

看看 Can I Use - :nth-child(1 of .foo)[4]

我们能够通过 :nth-child(1 of .foo) 这个特性,间接实现 :nth-of-class 的效果:

.box .cc:nth-child(1 of .cc) {
    color: red;
    font-size24px;
}

效果如下:

当然,利用这个特性,可以快速的选择任意 index 的 .cc 元素:

譬如第二个:

.box .cc:nth-child(2 of .cc) {
    color: red;
    font-size24px;
}

效果如下:

完整的 DEMO,你可以戳这里:CodePen Demo -- 选择子元素下第一个类名为 xx 的元素[5]

此功能由规范 Selectors Level 4 - :nth-child() pseudo-class[6] 提出,感兴趣的可以看看规范原文定义。

进阶理解新特性 of S 规则及实际应用演示

在掌握 of S 这个规则之后,我们就可以轻松的 Cover 非常多在之前选取起来非常困难的一些场景。

我们举几个例子一起看看,假设我们有如下的结构:

<div class="g-container"> 
    <div class="g-item-triangle"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
    <div class="g-item-triangle"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
    <div class="g-item-triangle"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
    <div class="g-item-triangle"></div>
    <div class="g-item-triangle"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
    <div class="g-item-triangle"></div>
    <div class="g-item-star"></div>
    <div class="g-item-hexagon"></div>
</div>

简单的 CSS 代码如下:

.g-container {
    position: relative;
    width800px;
    height300px;
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    
    & > div {
        width120px;
        height120px;
        flex-shrink0;
        background#fc0;
    }
    
    .g-item-triangle {
        clip-pathpolygon(50% 0100% 100%0 100%);
    }
    
    .g-item-star {
        clip-pathpolygon(50% 0%61% 35%98% 35%68% 57%79% 91%50% 70%21% 91%32% 57%2% 35%39% 35%);
    }
    .g-item-hexagon {
        clip-pathpolygon(50% 0%100% 25%100% 75%50% 100%0% 75%0% 25%);

    }
}

即可得到如下结构:

由于上述的三角形 .g-item-triangle、星形 .g-item-star以及六边形 .g-item-hexagon 都在同一个父容器下,并且它们都是使用 div 表示,只是有不同的 class 进行表示。

1.选取第二个六边形元素

首先,我们来看看,我们的诉求是,如何选取所有元素下的第二个六边形元素?

你可能认为可以使用如下代码?

.g-item-hexagon:nth-child(2) {
    background#f00;
}

按照我们上面文章提到的,这显然是选不到的。

但是如果你了解了 :nth-childof S 特性,这个问题就非常简单了:

:nth-child(2 of .g-item-hexagon) {
    background#f00;
}

这样,成功选取到了所有元素下的第二个六边形元素:

2. 选取偶数位数的三角形

继续,此外,of S 规范还支持 2nevenodd 这样匹配规则。

因此,当我们想选取偶数位数的三角形时,可以写成:

:nth-child(2n of .g-item-star) {
    background#f00;
}
// OR, 下述写法也可以
:nth-child(even of .g-item-star) {
    background#f00;
}

效果如下:

3. 选取去掉星形元素后的所有奇数个数元素

最后,of S 规则还可以在内部再使用 :not() 伪类,实现某些特定元素的剔除。

譬如,我们希望去掉所有元素内部的星星元素,在剩下的元素中,再选取所有奇数序号的元素。

听起来有点麻烦,写起来很简单:

:nth-child(2n+1 of :not(.g-item-star)) {
    background#f00;
}

此时,效果如下:

这里应用 :not() 规则后,相当于把 .g-item-star 去掉,剩余的 .g-item-hexagon.g-item-triangle 当成一个新的整体,应用 of S 前的规则。需要好好理解一下。

完整的 DEMO,你可以戳这里:CodePen Demo -- nth-child of S rule Demo[7]

最后

好了,本文到此结束,一个非常有意思的现代选择器功能,你学会了吗?希望本文对你有所帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS[8] ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

参考资料
[1]

伪类: https://developer.mozilla.org/zh-CN/docs/Web/CSS/Pseudo-classes

[2]

CSS: https://developer.mozilla.org/zh-CN/docs/Web/CSS

[3]

伪类: https://developer.mozilla.org/zh-CN/docs/Web/CSS/Pseudo-classes

[4]

Can I Use - :nth-child(1 of .foo): https://caniuse.com/?search=%3Anth-child

[5]

CodePen Demo -- 选择子元素下第一个类名为 xx 的元素: https://codepen.io/Chokcoco/pen/mdYYdeY

[6]

Selectors Level 4 - :nth-child() pseudo-class: https://www.w3.org/TR/selectors-4/#nth-child-pseudo

[7]

CodePen Demo -- nth-child of S rule Demo: https://codepen.io/Chokcoco/pen/jOjLVYW

[8]

Github -- iCSS: https://github.com/chokcoco/iCSS


点赞在看是最大的支持 ⬇️❤️⬇️

iCSS前端趣闻
不止于 CSS,不止于前端。关注回复 “css”,入群参与大前端技术讨论,答疑解惑,共同成长进步。
 最新文章