考察下面的 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 系列的瀏覽器支持情況還是蠻理想的。可以放心使用。
更多資料
- MDN :nth-child doc
- MDN :nth-of-type doc
- :nth Tester
- The Difference Between :nth-child and :nth-of-type
- Useful :nth-child Recipes
- nth-child doesn't respond to class
- Can I combine :nth-child() or :nth-of-type() with an arbitrary selector