考察下面的 HTML 代码片段:
<div>
<section>section 1</section>
<section>section 2</section>
<ul>
<li>item 1</li>
<li>
<ul>
<li>sub item 1</li>
<li>sub item 2</li>
<li>sub item 3</li>
</ul>
</li>
<li>item 3</li>
<li>item 4</li>
<li>item 5</li>
<li>item 6</li>
<li>item 7</li>
<li>item 8</li>
<li>item 9</li>
</ul>
<section>section 3</section>
<section>section 4</section>
<section>section 5</section>
</div>
单凭 section
可以让我们选中所有的<section>
标签,what if we wanna specific ones? 譬如只选中第一个。
那你可能已经知道:first-child
伪类选择器了,所以选中第一个也不是什么麻烦事情。类似地可以用:last-child
选中最后一个指定的元素。
section:first-child,section:last-child {
color: red;
}
here comes out the result:
当场景再复杂一些的时候,譬如选中第2个,第3个,第基数个,很自然地,我们会想到引入一个变量来完成任务。
nth 系列荣誉登场
CSS3中的 nth 系列选择器便是这样一种支持变量计算的选择器,可以完成上述复杂的选择需求。
譬如高亮前面示例 HTML 片段中第基数个 section
和 li
标签可以这样做:
section:nth-child(2n+1),li:nth-child(2n+1){
color:red;
}
and here comes out the result again:
:nth-child
完整的语法为:nth-child(an+b)
,它匹配父容器下面中第an+b
个子元素。例如:nth-child(3n+1)
将会选中位置位于第1(3*0+1),4(3*1+1),7(3*2+1)...的元素。
像:nth-child
这样厉害的选择器还有3个!它们分别是:
-
:nth-last-child(an+b)
原理同:nth-child
,只不过方向相反,从满足条件的兄弟子节点后面开始计数 -
:nth-of-type(an+b)
匹配第 an+b 个相同标签的元素 -
:nth-last-of-type(an+b)
同nth-of-type
,只不过方向相反,从最后开始计数。
借助于这样灵活的选择器,在编写样式时使我们更加得心应手,甚至有了很多花样玩法。
:nth-child
:nth-child(an+b)
会匹配所有兄弟节点中位置位于an+b
位置的元素。 其中 n 是从0开始的正整数。
除了像前面所说的可以通过完整的表达式匹配到连续规律位置的元素外,如果我们将 a 设为0的话,就可以匹配指定的单个元素。
譬如考察下面的 HTML 片段:
<div>
<p>foo</p>
<p>bar</p>
<p>baz</p>
</div>
高亮第二个元素:
p:nth-child(2){
color: red;
}
同理,:nth-child(3)
会选中第三个元素。
这个示例中,也可以用:nth-last-child
:
p:nth-last-child(2){
color: red;
}
效果当然是一样的,因为:nth-last-child(2)
从后面开始数第二个,正好与顺位数第二个是同一元素。
:nth-of-type
:nth-of-type(an+b)
用法上没有区别,但它只会匹配相同标签的兄弟元素。也就是在:nth-child
的基础上加了一条限制:标签要一致。
还是考察刚刚的 HTML 片段,我们要选中第二个<p>
标签,仍然是指定位置为2即可:
p:nth-of-type(2){
color: red;
}
但情况变一下,我们在第2个<p>
标签前面加上另外一个元素譬如<section>
,考察更新后的 HTML 片段:
<div>
<p>foo</p>
<section>quz</section>
<p>bar</p>
<p>baz</p>
</div>
此时我们仍然想要选中第2个<p>
标签。
p:nth-child(2){
color: red;/*会匹配失败,因为第二个子元素不是 p 标签*/
}
p:nth-of-type(2){
color: red;/*仍然匹配成功*/
}
此时用:nth-child(2)
不会选中任何元素,因为它的意思是选中div
下面子元素中的第2个元素,并且这个元素是一个<p>
标签。而上面 HTML 片段中,第二个子元素明显不是<p>
标签,所以匹配失败。
而通过:nth-of-type(2)
来选择则仍然生效。因为不管第2个<p>
元素前面插几个<section>
标签,此时内容为bar
的<p>
标签仍然是父容器所有子节点中顺位第二个类型为<p>
的标签。
:nth-child
与:nth-of-type
的区别
通过前面的示例可以看出,:nth-of-type
在你始终需要选择第 N 个特定类型的元素时更为可靠,它首先会提取出所限定的元素类型,然后再从这个没有杂质的集合中去匹配顺序。
因此:nth-of-type
在大多数时候可能更满足你的需要,毕竟很多时候需求是选中第3个<span>
,第5个<p>
。而不是第7个元素,无论是什么类型的节点。
这里有一个页面:nth Tester可以方便地把玩 nth 系列的四大金钢。通过可视化操作应该能够更好地理解。
扩展的花样玩法
前面说道,表达式an+b
可以将 a 取值为0,这样就可以选中第 b 个元素。如果将 a 取值为1的话,我们就可以选中从第 b 个元素开始的所有元素。
譬如选中从第三个元素开始的所有<p>
标签:
<div>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
p:nth-child(n+3){
color: red;
}
虽然 n 是从0开始的正整数,但 a 其实可以取负值的。当我们将 a 取值为-1的时候,可以达到只选取前 b 个元素的目的。
示例:选中前3个元素
<div>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
p:nth-child(-n+3){
color: red;
}
另外,选取基数和偶数元素时,可以通过指定值为odd
与even
来完成,这和2n+1
与2n
效果是一样的。
css 选偶数元素 p:nth-child(2n){ color: red; } /*或者*/ p:nth-child(even){ color: red; }
css 选基数元素 p:nth-child(2n+1){ color: red; } /*或者*/ p:nth-child(odd){ color: red; }
https://css-tricks.com/useful-nth-child-recipies/
需要注意的地方
与 class 的搭配
博主在使用过程中刚好遇到一个问题,可以拿出来分享一下。
那就是 nth 系列对元素的类名是不生效的,也就是说它只对标签名起作用,如果你使用时指定为 class 名则不会生效。
譬如考察下面的 HTML 片段与 CSS:
<div>
<p>1</p>
<p class="foo">2</p>
<p class="foo">3</p>
<p class="foo">4</p>
<p class="foo">5</p>
</div>
/*从带 class 为'foo'的 p 标签中选取第2个将字体设为红色*/
p.foo:nth-of-type(2){
color: red;
}
/*从带 class 为'foo'的 p 标签中选取第3个将字体设为绿色*/
p.foo:nth-child(3){
color: green;
}
上述 CSS 中,我们只希望对带 class 且值为foo
的<p>
标签进行操作,于是使用了类选择器进行限制,但最终结果其实是这样的:
我们预期值为3的应该为红色,因为它是带 class 且值为foo
这种类型里面的第二个,但其实值为2的 <p>
标签被选中了。因为第一个不带 class 的 <p>
标签其实也参与了进来,证明 class 选择器其实没有生效,并没有起到限制的作用。
对于:nth-child
同理。
推而广之,其实对于其他嵌套 CSS 语法组合(arbitrary selector),譬如属性选择[type=text]
,:nth-child
,:nth-of-type
都是会忽略的。
对于:nth-child
,纳入考量的永远是同属同一个父容器下同一级所有的兄弟元素。而对于:nth-of-type
来说,则是同一父容器下,同一级所有兄弟元素中标签类型相同的元素。
与 querySelector 的搭配
既然是伪类选择器,所以就无法使用 querySelector 来进行选择。我想你已经读出另外一层意思,即所有伪类选择器在 querySelector 中都不起作用,而不只是 nth 系列。原因见W3C Spec。
浏览器兼容性
拿 :nth-child
为例,nth 系列的浏览器支持情况还是蛮理想的。可以放心使用。