【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

大志設計_專注Web前端開發發表於2019-04-26

前言

配圖是小孩疊的積木,覺得剛好符合這個主題,所以就拿來做封面了哈。

什麼是CSS權重?

CSS權重,我們可以簡單理解為CSS優先順序。來看下MDN官網對優先順序的描述:

瀏覽器通過優先順序來判斷哪一些屬性值與一個元素最為相關,從而在該元素上應用這些屬性值。優先順序是基於不同種類選擇器組成的匹配規則。

我們通過一個小例子來加深理解下:

<p class="text" id="text">我是一段簡單的文字。</p>
複製程式碼
.text {
    color: red; // 紅色
}

#text {
    color: blue; // 藍色
}
複製程式碼

執行後,可以看到p標籤文字的顏色為藍色

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

那麼為什麼會是藍色的?這就是CSS權重來決定的。決定最終用哪個樣式作用到該p標籤上。

上面程式碼說明:ID選擇器的權重大於類選擇器,所以CSS權重跟選擇器的分類密切相關。如果您對CSS選擇器還不太瞭解,或者說不太清楚CSS有哪些選擇器,個人建議你先閱讀以下幾篇文章,這樣更有助於幫助你閱讀本文後面的內容:

《CSS3基本選擇器》

《CSS3屬性選擇器》

《CSS3偽類選擇器》

選擇器的型別

  1. 型別選擇器(例如,h1)和偽元素選擇器(例如,:before)
  2. 類選擇器(例如,.example)和屬性選擇器(例如,[type="radio"]),偽類選擇器(例如,:hover)
  3. ID選擇器(例如,#example)

上面列表中,選擇器型別的優先順序是遞增的。

萬用字元選擇器(*)和關係選擇器(~,+,>,)和否定偽類:not()對優先順序沒有影響。(但是在:not()內部宣告的選擇器會影響優先順序)。

給元素新增內聯樣式(例如,style="font-weight: bold;")總會覆蓋外部樣式表的任何樣式,因此可看作是具有最高的優先順序。除了一個例外的規則!important,這個看下MDN官網對其的詳細講解

CSS權重概述

  • 權重決定了你的CSS規則怎麼樣被瀏覽器解析直到生效
  • 權重決定了哪一條規則會被瀏覽器應用到元素上
  • 每個選擇器都有自己的權重
  • 如果兩個選擇器作用到同一元素上,則權重高者生效
  • 如果兩個選擇器權重相同,則最後定義的規則被計算到權重中(後面定義的CSS規則權重要更大,會覆蓋前面的CSS規則)

選擇器權重的計算

除了上面通過選擇器的型別,我們可以大概知道誰的權重值更高,但是如果想更深入的理解CSS權重和更好的利用CSS權重來編寫更加靈活的樣式,我們還需要明白選擇器的權重是如何計算的。

  • A:如果樣式是寫在標籤的style屬性中(內聯樣式),這A=1,否則,A=0。對於內聯樣式,由於沒有選擇器,所以B、C、D的值都為0,即A=1,B=0,C=0,D=0(簡寫為1,0,0,0,下同)。
  • B:計算選擇器中ID的數量。(例如,#header這樣的選擇器,計算為0,1,0,0)。
  • C:計算選擇器中類選擇器、屬性選擇器和偽類的數量。(例如.logo[id='site-logo']這樣的選擇器,計算為0,0,2,0)
  • D:計算選擇器中型別選擇器和偽元素的數量。例如(p:first-letter這樣的選擇器,計算為0,0,0,2)
  • 忽略通用選擇器

CSS2規範中給出的一些例子:

* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
複製程式碼

根據這樣的定義,很多網站的文章簡單的把CSS權重歸納為:內聯樣式的權重值是1000,ID選擇器的權重是100,class選擇器的權重值是10,標籤選擇器的權重值是1,整條規則的權重值相加得到整個樣式規則的權重值,數值越大權重就越高。

還有一個我們應該很熟悉的公式:important > 內聯 > ID > 類|屬性|偽類 > 標籤|偽元素 > 萬用字元 > 繼承

大多數情況下,按照這樣的理解得出的結論是沒問題的,但是遇到下面的情況就出現問題了:

/* 樣式一 */
body header div nav ul li div p a span em { color: red; }

/* 樣式二 */
.count { color: blue; }
複製程式碼

按照錯誤的計算方法,樣式一的權重值為11,樣式二的權重值為10,如果這兩條規則作用於同一個元素,則元素的文字顏色應該為紅色,而實際上卻是藍色。

我們把上面兩條規則應用到一個em元素上,在瀏覽器中來看個究竟:

<em class="count">我最終的文字顏色是?</em>
複製程式碼

可以看到最終的顏色是:藍色

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

權重值的比較

為什麼會出現例子中的情況,這就涉及到權重值的比較規則,我們一起來了解下。

按照四組正確的計算方法,上面例子中樣式一的權重值應該是0,0,0,11,樣式二的權重值是0,0,1,0

按照規範,計算權重值時,A,B,C,D四組值,從左到有,分組比較,如果A相同,比較B,如果B相同,比較C,如果C相同,比較D,如果D相同,後定義的優先。

樣式一和樣式二的A,B相同,所以比較C,而樣式二的C大於樣式一,所以,不管D值如何,樣式二的權重都大於樣式一。這才是正確的計算權重的方法。

舉個更容易理解的栗子哈:1張百元大鈔大,還是99張1元的加起來大?明白了吧,哈哈~

例外的!important規則

在按照ABCD四組計算比較之外,還可以對某一個屬性使用!important規則。需要特別注意的是,這裡的“!”與其在程式語言中的意義恰好相反,不是代表“不重要”二是代表“很重要”。

CSS2 規範中規定:!important 用於單獨指定某條樣式中的單個屬性。對於被指定的屬性,有 !important 指定的權重值大於所有未用 !important 指定的規則。

我們來看個例子:

<header id="header">
  <nav>
    <ul>
      <li class="current has-important">首頁</li>
    </ul>
  </nav>
</header>
複製程式碼
/* 樣式一 */
#header nav ul li.current {
  color: red;
  font-weight: bold;
}

/* 樣式二 */
li.has-important {
  color: blue !important;
  font-weight: normal;
}
複製程式碼

就整條規則而言,樣式一的權重值為0, 1, 1, 3,而樣式二的權重值僅為0, 0, 1, 1。所以應用於相同元素時,應該樣式一生效。但是對於color屬性,由於在樣式二中,用!important做了指定,因此color將應用樣式二的規則,而font-weight還是用樣式一的規則。在瀏覽器看下實際效果:

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

那如果多條規則中都對同一屬性指定了!important呢?我們再來簡單寫個例子測試下:

<div id="multipleImportant" class="multiple-important">
  如果多條規則中都對同一屬性指定了`!important`,誰生效?
</div>
複製程式碼
/* 樣式一 */
#multipleImportant {
  color: red !important;
}

/* 樣式二 */
.multiple-important {
  color: blue !important;
}
複製程式碼

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

其實這時候!important的作用已經相互抵消了,依然按照ABCD四組計算來比較。很明顯,id選擇器的權重要比class選擇器權重高,所以最終樣式一生效,文字顯示紅色

但是,使用!important是一個壞習慣,我們應該儘量避免。因為這破壞了樣式表中固有的級聯規則,使得除錯bug變得更加困難了。

來自MDN上的一些經驗法則:

  • 一定要優先考慮使用樣式規則的優先順序來解決問題而不是!important
  • 只有在需要覆蓋全站或外部CSS(例如引用的 ExtJs 或者 YUI )的特定頁面中使用!important
  • 永遠不要在全站範圍的css上使用!important
  • 永遠不要在你的外掛中使用!important

取而代之,你可以:

  • 更好的利用CSS級聯屬性
  • 使用更具體的規則。在您選擇的元素之前增加一個或多個元素,使選擇器變得更加具體,並獲得更高的優先順序。

比如下面這個例子:

<div id="test">
  <span>Text</span>
</div>
複製程式碼
div#test span { color: green }
span { color: red }
div span { color: blue }
複製程式碼

上面例子中,無論你CSS語句的順序是什麼樣的,文字都會是綠色的(green),因為這一條規則是最具有針對性,優先順序最高的。(同理,無論語句順序怎麼樣,藍色(blue)的規則都會覆蓋紅色的規則(red))。

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

什麼情況下可以使用!important

A)一種情況

  1. 你的網站上有一個設定了全站樣式的CSS檔案
  2. 同時你(或是你同事)寫了一些很差的內聯樣式

在這些情況下,你就可以在你全域性的CSS檔案中寫一些!important的樣式來覆蓋掉那些直接寫在元素上的行內樣式。

活生生的例子比如:一些寫得很糟糕的jQuery外掛裡面使用的內聯樣式。

B)另一種情況

#someElement p { color: blue; } p.awesome { color: red; }
複製程式碼

在外層有#someElement的情況下,你怎樣能使awesome的樣式變成紅色呢?如果不使用!important,第一條的規則永遠比第二條規則權重更高。

怎樣覆蓋!important

A)很簡單,只需再新增一條帶!important的CSS規則,要麼給這個選擇器更高的優先順序(新增一個標籤,ID或類);或是新增一樣的選擇器,把它的位置放在原有宣告的後面。

一些擁有更好的優先順序的例子,從上到下,優先順序是遞增的:

table td    { height: 50px !important; }
.myTable td { height: 50px !important; }
#myTable td { height: 50px !important; }
複製程式碼

B)或者使用相同的選擇器,但是置於已有的樣式之後:

td { height: 50px !important; }
複製程式碼

C)或乾脆改寫原來的規則,以避免使用!important

關於inherit

除了直接指定到元素上的樣式規則外,每個屬性值還有一個可能為inherit(繼承)的值。表示元素的樣式屬性繼承自父元素,與父集元素的定義一致。

看個例子:

<ul class="list">
    <li class="item">
        <span>文字文字文字</span>
    </li>
</ul>
複製程式碼
.list .item { color: red; }
複製程式碼

上面的例子中,我們並沒有給span新增color屬性,但是span的文字顏色會是紅色的,這就是因為spancolor屬性預設值為inherit

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

對於inherit屬性,只需要記住一點:繼承而來的值權重永遠低於明確指定到元素的定義。只有當一個元素的某個屬性沒有被直接指定時,才會繼承父級元素的值。

再看個例子:

<ul class="list">
    <li class="item">
        <span>文字文字文字</span>
    </li>
</ul>
複製程式碼
.list .item { color: red; }

span { color: blue; }
複製程式碼

上面的例子中,第一條規則按照ABCD四組計算的權重為0,0,2,0;第二條規則的權重為0,0,0,1;雖然第一條規則的權重更高,但是它是針對li元素的直接指定,並不是針對span元素定義的,所以計算spancolor屬性權重值時,實際上就是inherit的紅色與直接指定的藍色對比。按照規則,只要有直接指定的值(藍色),就不會再繼承父級元素的值(紅色),所以最終span的文字顏色為藍色

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

關於inherit規則,最典型的場景就是連結文字的顏色。通常瀏覽器自帶的樣式表都有針對a標籤的顏色及下劃線的直接指定,所以我們自定義的樣式表中對a標籤的父級指定color屬性和text-decoration屬性,通常是不會起作用的。我們來看個例子:

<p class="txt">
  <a href="#">父級元素設定的color和text-decoration對我不起作用</a>
</p>
複製程式碼
.txt {
  color: red;
  text-decoration: none;
}
複製程式碼

我們在瀏覽器可以看到,儘管我們給a標籤的父級p設定了顏色紅色和去除下劃線,a標籤依然是藍色的和帶下劃線的。

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

即使你給它們都加上!important,也無效。

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

但是我們可以通過下面的reset來改變這一點:

a {
  color: inherit;
  text-decoration: inherit;
}

.txt {
  color: red;
  text-decoration: none;
}
複製程式碼

再來看下效果:

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

可以看到,父級設定的樣式生效了。由於我們的樣式表對a標籤直接指定了colortext-decoration屬性值,覆蓋了瀏覽器的預設樣式,所以在沒有特別指定a標籤的顏色和下劃線定義的前提下,會從父級元素p繼承,因此連結會顯示紅色和沒有下劃線。

注意:inherit 在 CSS1 規範中並未定義,所以 IE6, IE7 以及 IE8 的 QuirksMode 不支援。

有意思的a標籤

我們直接看例子:

<a href="#">滑鼠劃入時,我的顏色是?</a>
複製程式碼
a:hover {
  color: red;
}

a:link {
  color: blue;
}
複製程式碼

我們希望的效果是滑鼠移入a標籤的時候,文字變成紅色的。實際上呢?仍然是藍色。

為什麼會這樣呢?

其實,a標籤的四個偽類(:link:hoveractivevisited)的優先順序是一樣的,所以這時候就看他們在樣式檔案中的順序了,後面的會覆蓋前面的,這回你知道為什麼了吧。

為了避免出現這樣的情況,我們在寫a標籤的偽類的時候,要注意它們的順序,遵循::link:visited:hover:active

在網上看到的例子

之前在其他網站上,看到這樣一個例子,大概是這樣的:

<p class="blue red">我的顏色是?</p>
<p class="red blue">我的顏色是?</p>
複製程式碼
.red {
  color: red;
}

.blue {
  color: blue;
}
複製程式碼

問:兩個p標籤的文字顏色分別是什麼?

答案是:都是藍色的

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

這邊要注意的是:class中的類名的順序並不會影響樣式的優先順序,而是由它們在樣式檔案中的先後順序決定的,後面定義的優先順序更高。

總結

  1. 權重的大小跟選擇器的型別和數量有關
  2. 樣式的優先順序跟樣式的定義順序有關,後面的覆蓋前面的
  3. 一條樣式規則的整體權重值包含四個獨立的部分:[A,B,C,D]
  4. A表示內聯樣式,只有1或0兩個值
  5. B表示選擇器中ID的數量
  6. C表示選擇器中類、屬性、偽類選擇器的數量
  7. D表示選擇器中偽元素及標籤的數量
  8. 比較時,從低位到高位(從A到D)分別比較,高位相同時才比較低位
  9. 標籤選擇器的權重永遠都比一個類選擇器的權重低,無論有多少個,除非使用!important
  10. !important標記指定的屬性權重值最高;多次指定時,相互抵消;應儘量減少!important的使用
  11. inherit而來的屬性定義,優先順序低於任何直接指定的屬性值

最後

這篇文章斷斷續續寫了好多天,但也算比較詳細,自己也比較深入的學習了CSS權重相關的知識。

感謝耐心讀完,如果還有什麼疑問或者建議,可以多多交流。

好了,本文到此結束,希望對你有幫助 :)

參考

developer.mozilla.org/zh-CN/docs/…

www.w3cplus.com/css/css-spe…

www.cnblogs.com/luckyflower…

blog.cssforest.org/2011/05/19/…

關注

感謝大家關注我的公眾號,一起學習成長~

【前端幫幫忙】第5期 關於CSS權重,你瞭解多少?

相關文章