CSS特指度
說明
這篇部落格在在兩臺電腦上分別完成的,故而有些截圖是Firefox,有些是Chrome,有些改動了瀏覽器的使用者樣式表,有些沒改,但不會影響閱讀,特此說明,勿怪。
CSS選擇器
單個CSS選擇器
css選擇器決定css樣式能渲染到對應的元素上。
元素選擇器:
Class選擇器:
ID選擇器:
多個CSS選擇器
瀏覽器在處理單獨的id、class和元素選擇器時如何渲染的問題很好理解,但要在兩種甚至三種不同選擇器同時作用於相同的元素的情況下,瀏覽器又該如何渲染呢?
比如3個選擇器作用在同一個元素身上的例子:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>ICE計算公式</title>
<style type="text/css">
#title {
color : blue;
}
.red {
color : red;
}
p {
color : brown;
}
</style>
</head>
<body>
<p id="title" class="red">這是標題</p>
</body>
</html>
得出結果:
當3個不同的選擇器(ID、Class、元素)同時作用於同一元素上時,瀏覽器渲染時選擇的樣式為ID選擇器宣告的樣式,這是因為ID選擇器的特指度高於Class選擇器,而Class選擇器的特指度高於元素選擇器......
那麼應該怎麼計算每個選擇器特指度,要解決這個問題,不得不提到ICE計算公式:ID-Class-Element。
ICE計算公式
關於ICE計算公式錯誤的理解
注:我在過去很長一段時間都是按照這種錯誤的思想來理解ICE計算公式的,務必注意。
關於ICE計算公式,經常看到這樣的說法(這是錯誤的):
!important | ID | Class | Element | |
---|---|---|---|---|
特指度 | 1000 | 100 | 10 | 1 |
Element特指度為1,class特指度為10,ID特指度為100,!important為1000。
如果按照這樣理解,是不是可以說11個class的特指度為10 * 11 === 110,它是肯定比1個Id的特指度要大的,但在我實踐後,我發現不能這樣去理解,就比如下面的例子:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>11個class是否比1個ID特指度大</title>
<style type="text/css">
#id {
color : green;
}
.class1.class2.class3.class4.class5.class6.class7.class8.class9.class10.class11 {
color: red;
}
</style>
</head>
<body>
<p id="id" class="class1 class2 class3 class4 class5 class6 class7 class8 class9 class10 class11">p標籤</p>
</body>
</html>
如果按照class特指度為10、id為100的邏輯來理解,110 大於 100,最後p標籤的color屬性值應該為red,但是實際情況卻是:
標籤的color屬性值為green。造成這樣結果的原因是因為不管有多少個class選擇器,Id選擇器的特指度就是比class選擇器的特指度大。就好比奧運會排名,先比較金牌,金牌數量多者排前面,數量少者排後面,在出現金牌數量相同時才會以銀牌的數量來比較。Id選擇器就好比金牌,而class選擇器就好比銀牌。當Id選擇器數量不一致時,後續操作壓根就不會去比較class選擇器,所以class選擇器的數量再多,也是無效的。
不同選擇器對應的特指度
選擇器共有:
- ID選擇器
- Class選擇器
- 元素選擇器
- 屬性選擇器
- 偽類選擇器
- 偽元素選擇器
ID選擇器
\(\color{#FF3030}{Selector Specificity: (1, 0, 0)}\)
#title {
color : blue;
}
Class選擇器
\(\color{#FF3030}{Selector Specificity: (0, 1, 0)}\)
.red {
color : red;
}
元素選擇器
\(\color{#FF3030}{Selector Specificity: (0, 0, 1)}\)
p {
color : brown;
}
屬性選擇器
\(\color{#FF3030}{Selector Specificity: (0, 1, 0)}\)
[id="title"] {
color : black;
}
偽類選擇器
\(\color{#FF3030}{Selector Specificity: (0, 1, 0)}\)
注意:p:hover的特指度為 (0, 1, 1),因為它包含了元素選擇器和偽類選擇器
p:hover {
color: #FF00FF
}
偽元素選擇器
\(\color{#FF3030}{Selector Specificity: (0, 0, 1)}\)
注意:p::after的特指度為 (0, 0, 2),因為它包含了元素選擇器和偽元素選擇器
p::after {
content: "元";
}
程式碼:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>6種選擇器的特指度</title>
<style type="text/css">
#title {
color : blue;
}
.red {
color : red;
}
p {
color : brown;
}
[id="title"] {
color : black;
}
p:hover {
color: yellow;
}
p::after {
content: "元";
}
</style>
</head>
<body>
<p id="title" class="red">100</p>
</body>
</html>
其餘還有組合選擇器、分組選擇器、上下文選擇器、子元素選擇器、相鄰兄弟選擇器、後續兄弟選擇器,但是這幾種更像是選擇器的組合,它們也都是由上述6種選擇器 \(\color{#FF3030}{(ID選擇器、Class選擇器、元素選擇器、屬性選擇器、偽類選擇器、偽元素選擇器)}\)組合而成,所以它們的特指度由組成它們的選擇器的特指度相加即可獲得。
比如:
組合選擇符
\(\color{#FF3030}{Selector Specificity: (0, 2, 0)}\)
注意:此處是對Class屬性中同時含有red和indigo兩個值的元素進行宣告,它的特指度為(0, 2, 0)
.red.indigo {
color : brown;
}
分組選擇符
\(\color{#FF3030}{Selector Specificity: (0, 0, 1)}\)
注意:此處是對p、em、strong3個元素分別進行宣告,而它們3個的特指度都為(0, 0, 1)
p , em , strong {
color : red;
}
上下文選擇符
\(\color{#FF3030}{Selector Specificity: (0, 0, 2)}\)
注意:此處是對被包含在p元素中的em元素(\(\color{#FF3030}{此時的em元素可以為p元素的子孫元素}\))進行宣告,它的特指度為(0, 0, 2)
p em {
color : red;
}
子元素選擇符
\(\color{#FF3030}{Selector Specificity: (0, 0, 2)}\)
注意:此處是對被包含在p元素中的em元素(\(\color{#FF3030}{此時的em元素只能為p元素的子元素}\))進行宣告,它的特指度為(0, 0, 2)
p > em {
color : green;
}
相鄰兄弟選擇符
\(\color{#FF3030}{Selector Specificity: (0, 0, 2)}\)
注意:此處是對與p元素擁有相同父元素的em元素(\(\color{#FF3030}{此時的em元素必須緊跟在p元素的後面}\))進行宣告,它的特指度為(0, 0, 2)
p + em {
color : red;
}
一般兄弟選擇符
\(\color{#FF3030}{Selector Specificity: (0, 0, 2)}\)
注意:此處是對與p元素擁有相同父元素的em元素(\(\color{#FF3030}{此時的em元素必須在p元素的後面,但不一定緊跟}\))進行宣告,它的特指度為(0, 0, 2)
p ~ em {
color : red;
}
特殊情況
沒有特指度的繼承樣式
繼承樣式不考慮特指度,由下面兩個例子能說明:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>繼承樣式不考慮特指度</title>
<style type="text/css">
.yellow {
color : yellow;
}
</style>
</head>
<body>
<div class="yellow">
<p>p標籤</p>
</div>
</body>
</html>
上述例子中p元素的color屬性值為red(來自使用者樣式表),繼承自div元素的color屬性值yellow被覆蓋。
(\(\color{#FF3030}{注:此瀏覽器的使用者樣式表被我修改,沒有修改過的p元素的color屬性的值應該是black。}\))
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>繼承樣式不考慮特指度</title>
<style type="text/css">
.yellow {
color : yellow;
}
p {
color : green;
}
</style>
</head>
<body>
<div class="yellow">
<p>p標籤</p>
</div>
</body>
</html>
上述例子中p元素的color屬性值為green(來自元素選擇器),繼承自div元素的color屬性值yellow被覆蓋。
超脫特指度的 !important
!important是超脫特指度的存在
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>!important是超脫特指度的存在</title>
<style type="text/css">
#id {
color : green;
}
p {
color : brown;
}
</style>
</head>
<body>
<p id="id">p標籤</p>
</body>
</html>
修改程式碼,給元素選擇器中color屬性加上!important後:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>!important是超脫特指度的存在/title>
<style type="text/css">
#id {
color : green;
}
p {
color : brown !important;
}
</style>
</head>
<body>
<p id="id">p標籤</p>
</body>
</html>
元素選擇器中加了!important的宣告覆蓋了ID選擇器中沒加!important的宣告。
特指度相當時 以 後宣告的屬性為準
當兩個選擇器的特指度相當時,特指度相當時 以 後宣告的屬性為準。
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>特指度相當時 以 後宣告的屬性為準</title>
<style type="text/css">
p {
color : green;
}
p {
color : brown;
}
</style>
</head>
<body>
<p>p標籤</p>
</body>
</html>
後宣告的屬性覆蓋了先宣告的屬性。
總結
單個選擇器的特指度按照 !important > id選擇符 > class選擇符、屬性選擇符和偽類 > 元素選擇符和偽元素的規律排序,如果遇到組合選擇器,則按其邏輯將其拆分為單個選擇器後相加再進行判斷。如果選擇器中的宣告包含了!important,則這條屬性會覆蓋同屬性所有不包含了!important的宣告,只有在同屬性的宣告也包含!important,並且同屬性的宣告位於的選擇器的特指度大於先前的選擇器,亦或兩個選擇器的特指度相當,但同屬性的宣告位於的選擇器位於先前的選擇器之後時,才會覆蓋先前的選擇器的加了!important的屬性。
如果上面的話很難理解,可以看這裡的:
第一條:比較各自所在的選擇器的特指度,特指度越大的優先順序越高;
第二條:加了!important的宣告的屬性優先順序最高,在都加了!important的情況下按第一條為準;
第三條:在特指度相等的情況下,後宣告的優先順序大於先宣告的;
第四條:優先順序高的樣式覆蓋優先順序低的樣式。