前幾天我們介紹了不同類型的選擇器。
包括元素、類別、ID、通用等基本的選擇器,
依任意屬性挑選元素的屬性選擇器,
依不存在的分類挑選元素的偽類選擇器,
依不存在的元素挑選內容的偽元素選擇器。
也提到怎麼將這些選擇器組合在一起使用。
不過了解這些之後,還需要了解選擇器之間的優先順序。
不同選擇器之間的優先順序,取決於他們的Specificity(這裡譯作具體性)。
當檔案內透過多個選擇器宣告的樣式彼此衝突時,會比較這些選擇器的具體性,再決定要套用哪一個選擇器的樣式。
選擇器的具體性會根據選擇器類型對應的權重做計算。
權重總共分成三種等級:
需要注意的是:[1]
計算具體性時,就是在算每種權重的選擇器有多少個。
可以將每種權重的選擇器的總數列成數列,以A級, B級, C級
的形式呈現每個選擇器的具體性。
所以上面三種權重的選擇器,具體性可以寫成:
由於選擇器也可以組合起來變成新的選擇器,這類由多個選擇器組成的選擇器(複合選擇器、複雜選擇器)計算具體性時,會將其內包含的選擇器對應的具體性相加。
選擇器列表的具體性則會依當下符合的條件而定。先一一計算列表中每個選擇器的具體性,接著從符合條件的選擇器中找出最高的值,作為列表的具體性。
當元素的屬性同時經檔案內的多個規則設定為不同的值,需要比較選擇器的具體性,由具體性比較高的規則決定屬性最終的值。
比較選擇器的具體性時會由左至右,依序比較A級、B級跟C級權重選擇器的數量。
就像是玩大老二時會先比撲克牌的數字,當數字相同時再比花色。
如果在前一級就已經分出勝負,不會繼續往下比其他級的權重選擇器有多少;
只有前一級的權重選擇器的數量相同時,才會繼續往下比。
如果選擇器的具體性一樣,則會繼續比較元素與規則間的scoping proximity(作用域鄰近性);
如果還是比不出來,則會比較規則出現於檔案的先後順序。[1]
關於scoping proximity,之後會再提,這裡先帶過~
以下舉例說明怎麼計算選擇器的具體性:
*
0,0,0
忽略通用選擇器。
ul ol+li
0,0,3
C級權重有3個:3個元素選擇器;
忽略組合器。
h1 + *[rel="up"]
0,1,1
B級權重有1個:1個屬性選擇器;
C級權重有1個:1個元素選擇器。
忽略通用選擇器跟組合器。
ul ol li.red
0,1,3
B級權重有1個:1個類別選擇器;
C級權重有3個:3個元素選擇器。
[id="password"]
0,1,0
B級權重有1個:1個屬性選擇器。
(雖然是id
屬性,但不是用ID選擇器)
input:focus
0,1,1
B級權重有1個:1個偽類選擇器;
C級權重有1個:1個元素選擇器。
:root #myApp input:required
1,2,1
A級權重有1個:1個ID選擇器;
B級權重有2個:2個偽類選擇器;
C級權重有1個:1個元素選擇器。
:where()
:where()
被稱作具體性調整偽類(specificity-adjustment pseudo-class),會將括號內的選擇器的具體性替換為0
。[2]
例如以下由規範提供的例子中,選擇器的具體性是0,1,0。
因為括號內的選擇器的具體性會變成0,0,0,加上:where()
前面.qux
的具體性0,1,0,結果會是0,1,0。
.qux:where(em, #foo#bar#baz)
:nth-child()
、:nth-last-child()
如果:nth-child()
跟:nth-last-child()
的括號內放入了選擇器列表,則具體性會是偽類本身的值,再加上列表中具體性最高的選擇器的值。[2]
例如以下由規範提供的例子中,選擇器的具體性是0,2,0。
因為列表中,具體性最高的選擇器是類別選擇器.item
,值為0,1,0。加上:nth-child
偽類本身的具體性0,1,0,最後會是0,2,0。
:nth-child(even of li, .item)
:is()
、:has()
、:not()
:is()
、:has()
跟:not()
本身的具體性,會被括號內的選擇器取代。[2]
例如以下由MDN提供的例子:
:is(p)
0,0,1:is()
的具體性由p
取代,而p
的具體性是0,0,1。
h2:has(~ h2)
0,0,2:has()
的具體性由~ h2
取代,~ h2
的是0,0,1;
加上前面h2
的0,0,1,結果會是0,0,2。
div:not(.inner) p
0,1,2:not()
的具體性由.inner
取代,.inner
的是0,1,0;
加上前面div
的0,1,0,以及後面p
的0,0,1,結果會是0,1,2。
如果括號內是選擇器列表,:is()
、:has()
跟:not()
本身的具體性會由列表中具體性最高的選擇器取代。[2]
例如以下由MDN提供的例子:
:is(p, #fakeId)
:is()
的具體性由值較高的#fakeId
取代,結果會是#fakeId
的1,0,0。
h1:has(+ h2, > #fakeId)
:has()
的具體性由值較高的#fakeId
取代,#fakeId
的具體性是1,0,0;
加上前面h1
的0,0,1,結果會是1,0,1。
p:not(#fakeId)
:not()
的具體性由值較高的#fakeId
取代,#fakeId
的具體性是1,0,0;
加上前面p
的0,0,1,結果會是1,0,1。
div:not(.inner, #fakeId) p
:not()
的具體性由值較高的#fakeId
取代,#fakeId
的具體性是1,0,0;
加上前面div
的0,0,1,後面p
的0,0,1,結果會是1,0,2。
在CSS中,可以將規則以多層的巢狀結構改寫,讓程式碼更簡潔。[3]
使用選擇器列表組成巢狀結構的規則,類似於使用:is()
。計算整個選擇器的具體性時,會由列表中具體性最高的選擇器,得出選擇器列表的具體性。[1]
例如以下由MDN提供的例子,選擇器列表p, #fakeId
的具體性,會是#fakeId
的1,0,0,而非p
的0,0,1。
再加上span
的0,0,1,會得出整個選擇器的具體性為1,0,1。所以由這個巢狀規則拆出的兩個規則的選擇器,p span
跟#fakeId span
,具體性都會是1,0,1。
p,
#fakeId {
span {
/* 1-0-1 */
}
}
當多個規則設定的樣式衝突時,可以減少其他規則的選擇器的具體性。這麼一來,就相對提高想要套用的規則的優先順序。
id
前面提過,如果以屬性選擇器去挑選特定ID的元素時,權重會是屬性選擇器的B級,而非ID選擇器的A級。
運用這個特性,以屬性選擇器替換原先的ID選擇器,就能降低選擇器的具體性了。
:where()
前面也提過,:where()
會把括號內的選擇器的具體性歸零。
這麼一來,即使不得不把選擇器的條件寫得更明確,也不會增加選擇器的具體性。
如果無法減少其他規則的具體性,也可以想辦法提高想要套用的規則的優先順序。
一個增加選擇器具體性的方法是,在原先的選擇器前,以後代或子代組合器,加入元素的祖代或親代元素。
例如以下由MDN提供的例子中,在原本的元素選擇器h1
前面,另外加了選擇上層元素的選擇器,並透過後代組合器相接。
如果是加了ID選擇器#myContent
,選擇器的具體性會由原先的0,0,1提升為1,0,1;
如果是加了屬性選擇器[id="myContent"]
,則會提升為0,1,1。
#myContent h1 {
color: green; /* 1-0-1 */
}
[id="myContent"] h1 {
color: yellow; /* 0-1-1 */
如果不得已,需要蓋過其他具體性非常高的選擇器時,也可以讓原先就有的A級或B級權重選擇器重複好幾次,快速提高選擇器的具體性。
例如以下由MDN提供的例子中,將#myId span
的ID選擇器#myId
重複兩次,變成#myId#myId#myId span
,讓選擇器的具體性增加2,0,0,變成3,0,1;
如果是將.myClass span
的類別選器重複兩次,則會讓選擇器的具體性增加0,2,0,變成0,3,1。
#myId#myId#myId span {
/* 3-0-1 */
}
.myClass.myClass.myClass span {
/* 0-3-1 */
}
這個方法也可以透過:is()
、:has()
、:not()
等偽類來達成。如此,即便無法對上層元素添加id
屬性,也可快速提高選擇器的具體性。
例如以下由MDN提供的例子,將任意的ID選擇器#fakeID
重複相接,帶入:not()
跟:is()
裡,分別將選擇器的具體性由原先的0,0,1,提升為3,0,1跟3,0,0。
:not(#fakeID#fakeId#fakeID) span {
/* 3-0-1 */
}
:is(#fakeID#fakeId#fakeID, span) {
/* 3-0-0 */
}
不過重複同樣的選擇器並不是好的做法。
如果可以,應該優先減少其他選擇器的具體性。
如果無法減少其他選擇器的具體性,不得不這麼做的時候,也應該以註解說明這麼做的理由。[1]
今天提到選擇器的優先順序,由它的具體性Specificity決定。
計算選擇器的具體性時,會依據對應的權重將選擇器分類,算出每種權重的選擇器有多少個。
比較選擇器的具體性以決定優先順序時,會先比較最高權重的選擇器總數;
分不出勝負時,會由高到低,繼續往下比較其他權重的選擇器總數。
不過,在比較選擇器之前,其實需要先考慮規則來自哪個樣式表,或是有沒有加上!important
等條件。
只有當衝突的規則有同樣的來源、重要性時,才會比較選擇器的具體性。
不過時間不多了,讓我們下集待續吧~
[1]: MDN, Specificity
[2]: W3C, Selectors Level 4, specificity-rules
[3]: MDN, Using CSS nesting