深入理解CSS選擇器優先級
題外話
今天把 《CSS REFACTORING》(中文名叫《CSS重構:樣式表性能調優》)電子書粗略的瀏覽了一遍,這本書很薄,150頁左右,首先是介紹了什麽是重構並舉了兩個簡單的重構例子,然後介紹了CSS的選擇器優先級,再然後介紹了CSS的最佳實踐, 再然後就介紹如何重置瀏覽器的默認樣式,最後比較虛的、純理論的介紹了CSS重構的策略,然後就沒有然後了。這書整體內容很簡單,但是,其中對於 CSS選擇器優先級計算
作了比較深入的講解。
什麽是選擇器優先級(Specificity)
直接復制了MDN對優先級的定義 上的解釋:
瀏覽器通過優先級來判斷哪一些屬性值與一個元素最為相關,從而在該元素上應用這些屬性值。優先級是基於不同種類選擇器組成的匹配規則。
這句話也是很抽象,暫且先不管它了。但是我們可以先看一個例子:
- HTML:
<div id="content" class="content">
我是什麽顏色
</div>
- CSS:
#content {
color: #f00;
}
.content {
color: #0f0;
}
那最後文字是什麽顏色呢?答案很簡單:紅色。這就涉及到了優先級問題,同一塊內容,我們同時用了 ID選擇器
和 類選擇器
,因為 ID選擇器
優先級大於 類選擇器
, 所以最終顯示為紅色。
優先級的計算規則
相信每位寫過CSS的朋友都知道,CSS選擇器的優先級關系是:
內聯 > ID選擇器 > 類選擇器 > 標簽選擇器。
但是,瀏覽器具體的優先級算法是怎樣的?可能還有些人不知道 。《CSS REFACTORING》 中提到了算法的過程 。
A specificity is determined by plugging numbers into (a, b, c, d):
- If the styles are applied via the style attribute, a=1; otherwise, a=0.
- b is equal to the number of ID selectors present.
- c is equal to the number of class selectors, attribute selectors, and pseudoclasses present.
- d is equal to the number of type selectors and pseudoelements present.
翻譯過來就是
優先級是由 A
、B
、C
、D
的值來決定的,其中它們的值計算規則如下:
- 如果存在內聯樣式,那麽
A = 1
, 否則A = 0
; B
的值等於ID選擇器
出現的次數;C
的值等於類選擇器
和屬性選擇器
和偽類
出現的總次數;D
的值等於標簽選擇器
和偽元素
出現的總次數 。
這樣子直接看好像也還是很明白 ,那先上個例子:
#nav-global > ul > li > a.nav-link
套用上面的算法,依次求出 A
B
C
D
的值:
- 因為沒有內聯樣式 ,所以
A = 0
; - ID選擇器總共出現了1次,
B = 1
; - 類選擇器出現了1次, 屬性選擇器出現了0次,偽類選擇器出現0次,所以
C = (1 + 0 + 0) = 1
; - 標簽選擇器出現了3次, 偽元素出現了0次,所以
D = (3 + 0) = 3
;
上面算出的A
、 B
、C
、D
可以簡記作:(0, 1, 1, 3)
。
為了熟悉掌握優先級算法 ,我們再來做一些練習:
li /* (0, 0, 0, 1) */
ul li /* (0, 0, 0, 2) */
ul ol+li /* (0, 0, 0, 3) */
ul ol+li /* (0, 0, 0, 3) */
h1 + *[REL=up] /* (0, 0, 1, 1) */
ul ol li.red /* (0, 0, 1, 3) */
li.red.level /* (0, 0, 2, 1) */
a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11 /* (0, 0, 11,0) */
#x34y /* (0, 1, 0, 0) */
li:first-child h2 .title /* (0, 0, 2, 2) */
#nav .selected > a:hover /* (0, 1, 2, 1) */
html body #nav .selected > a:hover /* (0, 1, 2, 3) */
OK, 現在已經弄清楚了優先級是怎麽算的了。但是,還有一個問題,怎麽比較兩個優先級的高低呢?
比較規則是: 從左往右依次進行比較 ,較大者勝出,如果相等,則繼續往右移動一位進行比較 。如果4位全部相等,則後面的會覆蓋前面的
再來看一下例子:
- html:
<div class="nav-list" id="nav-list">
<div class="item">nav1</div>
<div class="item">nav2</div>
</div>
- CSS:
#nav-list .item {
color: #f00;
}
.nav-list .item {
color: #0f0;
}
算出 #nav-list .item
的優先級是 (0, 1, 1, 0)
, .nav-list .item
的優先級是 (0, 0, 2, 0)
。 左邊第一位都是0, 再看看左邊第二位,前者是1,後者是0, 所以(0, 1, 1, 0)
的大於 (0, 0, 2, 0)
,即 #nva-list .item
大於 .nav-list .item
,所以字體會是紅色。
優先級的特殊情況
經過上面的優先級計算規則,我們可以知道內聯樣式的優先級是最高的,但是外部樣式有沒有什麽辦法覆蓋內聯樣式呢?有的,那就要 !important
出馬了。因為一般情況下,很少會使用內聯樣式 ,所以 !important
也很少會用到!如果不是為了要覆蓋內聯樣式,建議盡量不要使用 !important
。、
那可能有人會想,那如果我內聯樣式用了 !important
,是不是外部樣式就沒有辦法了呢?比如下面的代碼:
- HTML:
<div class="app" style="color:#f00!important">666</div>
- CSS:
.app {
color: 0f0!important;
}
是的,你贏了,這時候內聯樣式已經強大到不管你外部樣式怎麽寫都無法覆蓋它了。這種情況在實際代碼中是要杜絕的!記住,千萬不要在內聯樣式中使用 !important
最後 , !important
真的是的無法超越的王者嗎?其實不是的,一些情況,我們可以超越 !important
, 請看下面的例子:
html:
<div class="box" style="background: #f00; width: 300px!important;"><div>
css:
.box {
max-width: 100px;
}
這時候 .box
的寬度只有 100px
, 而不是 300px
, 可見,max-width
可以超越 width!important
!但是,這實際上不是優先級的問題,因為優先級是比較相同屬性的,而 max-width
和 width
是兩個不同的問題。之所以舉這個例子,是要告訴大家,有時候不管怎麽設置容器的 width
都不生效,檢查一下是不是有人寫了 max-width
坑了你哈。
OK,優先級先寫到這裏啦,朋友們有問題歡迎留言討論~
深入理解CSS選擇器優先級