深入理解CSS選擇器優先順序

zhanyuzhang發表於2018-11-08

什麼是選擇器優先順序(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):

  1. If the styles are applied via the style attribute, a=1; otherwise, a=0.
  2. b is equal to the number of ID selectors present.
  3. c is equal to the number of class selectors, attribute selectors, and pseudoclasses present.
  4. d is equal to the number of type selectors and pseudoelements present.

翻譯過來就是

優先順序是由 ABCD 的值來決定的,其中它們的值計算規則如下:

  1. 如果存在內聯樣式,那麼 A = 1, 否則 A = 0;
  2. B 的值等於 ID選擇器 出現的次數;
  3. C 的值等於 類選擇器屬性選擇器偽類 出現的總次數;
  4. D 的值等於 標籤選擇器偽元素 出現的總次數 。

這樣子直接看好像也還是很明白 ,那先上個例子:

#nav-global > ul > li > a.nav-link
複製程式碼

套用上面的演算法,依次求出 A B C D 的值:

  1. 因為沒有內聯樣式 ,所以 A = 0;
  2. ID選擇器總共出現了1次, B = 1;
  3. 類選擇器出現了1次, 屬性選擇器出現了0次,偽類選擇器出現0次,所以 C = (1 + 0 + 0) = 1
  4. 標籤選擇器出現了3次, 偽元素出現了0次,所以 D = (3 + 0) = 3;

上面算出的ABCD 可以簡記作:(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-widthwidth 是兩個不同的屬性。之所以舉這個例子,是要告訴大家,有時候不管怎麼設定容器的 width 都不生效,檢查一下是不是有人寫了 max-width 坑了你哈。

OK,優先順序先寫到這裡啦,朋友們有問題歡迎留言討論~

相關文章