真正理解"CSS選擇器的優先順序"

Jin_C發表於2018-03-29

當我們在討論CSS選擇器優先順序的時候,我們再討論什麼?

其實很多人都對此有點模糊,那我換個方式問: 一個CSS屬性的最終值是怎麼來?

回答 : CSS屬性的最終值是通過層疊計算得來的。

那什麼是層疊計算呢?

我通俗的理解,其實就是先計算重疊

層疊是CSS的一個基本特徵,它是一個定義瞭如何合併來自多個源的屬性值的演算法。它在CSS處於核心地位,CSS的全稱層疊樣式表正是強調了這一點。

計算過程

計算的過程指的是使用者代理(瀏覽器只是使用者代理的一種“例項”)在渲染HTML的時候,對CSS進行層疊計算的過程(這裡不討論瀏覽器的渲染、重繪等觸發時機)。

為了方便理解,這裡只針對一個屬性值(padding)進行討論,其他的屬性值都是一樣的過程。


demo:

<div class="taobao_com" id="taobao_com" data-show="true">
    <div class="taobao"></div>
    <p>taobao.com</p>
</div>
複製程式碼
div{
  padding:1px;
}
.taobao_com{
  padding:12px;
}
div .taobao{
  padding:123px;
}
.taobao_com .taobao{
  padding:1234px;
}
複製程式碼

在屬性的計算之前,會對每個文件元素的每個屬性上的值進行排序 (W3C文件地址):

1. Transition declarations [CSS3-TRANSITIONS]
2. Important user agent declarations
3. Important user declarations
4. Important override declarations [DOM-LEVEL-2-STYLE]
5. Important author declarations
6. Animation declarations [CSS3-ANIMATIONS]
7. Normal override declarations [DOM-LEVEL-2-STYLE]
8. Normal author declarations
9. Normal user declarations
10.Normal user agent declarations
複製程式碼

排序靠前的會比靠後的優先順序要高。 1 > 2 > 3 > 4 > 5 ...

假設我們的使用者代理是Chrome瀏覽器,那麼當我們要計算A:<div class="taobao"></div>padding值的時候,會有這麼一個排序過程(簡化):

1.  瀏覽器中對該標籤的``padding``預設值
2.  開發者對該標籤的 ``padding`` 進行宣告的值
3.  瀏覽器中對該標籤的 ``padding`` 進行 **!important** 宣告的值
4.  開發者對該標籤的 ``padding`` 進行 **!important** 宣告的值
複製程式碼

那麼在demo裡面,分別寫了4條padding的的宣告,但都會被排在層疊順序開發者對該標籤的 padding 進行宣告的值之中,而且padding沒有更高權重的宣告瞭,那麼就會對這個宣告佇列裡的宣告進行優先順序的計算。


所以,!important 跟選擇器優先順序是什麼關係?

優先順序的計算

優先順序的計算首先是 選擇器權重 的優先順序計算,然後是 宣告先後順序 的優先順序計算。

選擇器優先順序的權重計算

這時候,才到了選擇器優先順序登場。

選擇器的權重並不是網上很多人說的

選擇器通過權重值 1(div等)、10(class)、100(id)

這樣的方式進行的,這只是別人理解出來的東西,真正的計算原理可以翻閱W3C文件選擇器權重的計算

文件指出:

A selector’s specificity is calculated for a given element as follows:

1.count the number of ID selectors in the selector (= A) 2.count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B) 3.count the number of type selectors and pseudo-elements in the selector (= C) 4.ignore the universal selector

文件也給了例子:

Examples:
*               /* a=0 b=0 c=0 */
LI              /* a=0 b=0 c=1 */
UL LI           /* a=0 b=0 c=2 */
UL OL+LI        /* a=0 b=0 c=3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 */
UL OL LI.red    /* a=0 b=1 c=3 */
LI.red.level    /* a=0 b=2 c=1 */
#x34y           /* a=1 b=0 c=0 */
#s12:not(FOO)   /* a=1 b=0 c=1 */
.foo :matches(.bar, #baz)
                /* Either a=1 b=1 c=0
                   or a=0 b=2 c=0, depending
                   on the element being matched. */
複製程式碼

大致意思就是,把權重分為了 A,B,C 三個級別,A > B > C , A,B,C 直接各自計算。也就是會優先計算 A 的權重,如果相等會計算 B 的權重,以此類推。

所以才有了100、10、1的說法,但很不準確。

結合之前的demo,我們來計算下權重值:

1:

div { /*type selectors*/
  padding:1px;   /*a = 0 , b = 0 , c = 1*/
}
複製程式碼

2:

.taobao_com{  /*class selectors*/
  padding:12px;  /*a = 0 , b = 1 , c = 0*/
}
複製程式碼

3:

div .taobao{ /*type selectors + class selectors*/
  padding:123px;  /*a = 0 , b = 1 , c = 1*/
}
複製程式碼

4:

.taobao_com .taobao{ /* class selectors + class selectors */
  padding:1234px;   /*a = 0 , b = 2 , c = 0*/
}
複製程式碼

由於A位相等,B位差異的最大值在 4 ,得到的結果將會是 第4條宣告 勝出: 1. a = 0 , b = 0 , c = 1 2. a = 0 , b = 1 , c = 0 3. a = 0 , b = 1 , c = 1 4. a = 0 , b = 2 , c = 0

宣告先後順序

當 A 、B 、C 所計算的權重都相等時(ABC三個值相等)相等時,後面宣告的值將會是最終的計算值。

最終得到了CSS屬性的值。


文章優先發布在GitHub

相關文章