CSS學習摘要-層疊和繼承

鐵樂貓發表於2018-06-01

當有多個選擇器作用在一個元素上時,哪個規則最終會應用到元素上?

其實這是通過層疊機制來控制的,這也和樣式繼承(元素從其父元素那裡獲得屬性值)有關。

元素的最終樣式可以在多個地方定義,它們以複雜的形式相互影響。這些複雜的相互作用使CSS變得非常強大,但也使其非常難於除錯和理解。

層疊

CSS 是 Cascading Style Sheets 的縮寫,這暗示層疊(cascade)的概念是很重要的。在最基本的層面上,它表明CSS規則的順序很重要,但它比那更復雜。什麼選擇器在層疊中勝出取決於三個因素(這些都是按重量級順序排列的——前面的的一種會否決後一種):

  1. 重要性(Importance)
  2. 專用性(Specificity)
  3. 原始碼次序(Source order)

重要性

在CSS中,有一個特別的語法可以讓一條規則總是優先於其他規則:!important。(常見於應用於iconfont圖示屬性後面)把它加在屬性值的後面可以使這條宣告有無比強大的力量。

讓我們看一下這個例子:

<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>
#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

這將生成以下:

CSS學習摘要-層疊和繼承

讓我們一起來看看發生了什麼。

  1. 你可以看到第三條規則 colorpadding被運用了, 但 background-color沒有,為什麼?實際上,這三種情況都應該應用,因為在源順序後面的規則通常會覆蓋較早的規則。

  2. 然而, 在前面的規則被運用了,因為 IDs/class 選擇器優先於element選擇器。

  3. 這兩個元素都有 class並帶有 better屬性, 但是第二個元素有 id 值為winning 。 因為比起class而言id專用性更高(在一個頁面上id是唯一的, 但很多元素可以擁有相同的class — ID 選擇器在它們的目標中是非常優先的),紅色背景色和1pixel的黑色邊框都應應用於第二元素,第一個元素獲得灰色背景色,沒有邊框,如類所指定。

  4. 第二個元素獲得紅色背景色,但沒有邊框。為什麼?因為 !important 在第二條規則中的宣告——在 border: none之後寫入它意味著儘管id具有更高的優先性,該宣告也將優先於前面規則中的邊界值宣告。

注意: 過載這個 !important 宣告的唯一方法是在後面的原始碼或者是一個擁有更高特殊性的原始碼中包含相同的 !important 特性的宣告。

知道 !important存在是很有用的,這樣當你在別人的程式碼中遇到它時,你就知道它是什麼了。但是!我們建議你千萬不要使用它,除非你絕對必須使用它。您可能不得不使用它的一種情況是,當您在CMS中工作時,您不能編輯核心的CSS模組,並且您確實想要重寫一種不能以其他方式覆蓋的樣式。 但是,如果你能避免的話,不要使用它。由於 !important 改變了層疊正常工作的方式,因此除錯CSS問題,尤其是在大型樣式表中,會變得非常困難。

要注意一個CSS宣告的重要性取決於它被指定在什麼樣式表內——使用者可以設定自定義樣式表覆蓋開發商的樣式,例如使用者可能有視力障礙,想設定字型大小對所有網頁的訪問是雙倍的正常大小,以便更容易閱讀。

相互衝突的宣告將按以下順序適用,後一種將覆蓋先前的宣告

  1. 在使用者代理樣式表的宣告 (例如:瀏覽器在沒有其他宣告的預設樣式).
  2. 使用者樣式表中的普通宣告(由使用者設定的自定義樣式)。
  3. 作者樣式表中的普通宣告(這是我們設定的樣式,Web開發人員)。
  4. 作者樣式表中的重要宣告
  5. 使用者樣式表中的重要宣告

Web開發者的樣式表覆蓋使用者的樣式表是合理的,所以設計可以保持預期,但是有時候使用者有很好的理由來重寫web開發人員樣式,如上所述,這可以通過在使用者的規則中使用!important

專用性

專用性基本上是衡量選擇器的具體程度的一種方法——它能匹配多少元素。如上面所示的示例所示,元素選擇器具有很低的專用性。類選擇器具有更高的專用性,所以將戰勝元素選擇器。ID選擇器有甚至更高的專用性, 所以將戰勝類選擇器. 戰勝ID選擇器的唯一方法是使用 !important

一個選擇器具有的專用性的量是用四種不同的值(或元件)來衡量的,它們可以被認為是千位,百位,十位和個位——在四個列中的四個簡單數字:

  1. 千位:如果宣告是在style 屬性中該列加1分(這樣的宣告沒有選擇器,所以它們的專用性總是1000。)否則為0。
  2. 百位:在整個選擇器中每包含一個ID選擇器就在該列中加1分。
  3. 十位:在整個選擇器中每包含一個類選擇器、屬性選擇器、或者偽類就在該列中加1分。
  4. 個位:在整個選擇器中每包含一個元素選擇器或偽元素就在該列中加1分。

注意: 通用選擇器 (*), 複合選擇器 (+, >, ~, ` `) 和否定偽類 (:not) 在專用性中無影響。

下表顯示了幾個示例。試著通過這些,並確保你理解他們為什麼具有我們給予他們的專用性。

選擇器 千位 百位 十位 個位 合計值
h1 0 0 0 1 0001
#important 0 1 0 0 0100
h1 + p::first-letter 0 0 0 3 0003
li > a[href*="zh-CN"] > .inline-warning 0 0 2 2 0022
#important div > div > a:hover, 在一個元素的 <style>屬性裡 1 1 1 3 1113

在我們繼續之前,讓我們看看一個行動中的例子。
這是我們將要使用的HTML:

<div id="outer" class="container">
  <div id="inner" class="container">
    <ul>
      <li class="nav"><a href="#">One</a></li>
      <li class="nav"><a href="#">Two</a></li>
    </ul>
  </div>
</div>

下面是CSS的示例:

/* specificity: 0101 */
#outer a {
  background-color: red;
}

/* specificity: 0201 */
#outer #inner a {
  background-color: blue;
}

/* specificity: 0104 */
#outer div ul li a {
  color: yellow;
}

/* specificity: 0113 */
#outer div ul .nav a {
  color: white;
}

/* specificity: 0024 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* specificity: 0023 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* specificity: 0033 */
div div .nav:nth-child(2) a:hover {
  border: 10px double black;
}

a {
  display: inline-block;
  line-height: 40px;
  font-size: 20px;
  text-decoration: none;
  text-align: center;
  width: 200px;
  margin-bottom: 10px;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
}

我們從這個程式碼中得到的結果如下:

CSS學習摘要-層疊和繼承

這裡發生了什麼?首先,我們只對本例的前七個規則感興趣,正如您將注意到的,我們已經在每個註釋中包含了它們的專用性值。

  • 前兩個選擇器正在競爭連結的背景顏色的樣式——第二個贏得並使背景色為藍色,因為它有一個額外的ID選擇器在鏈中:其專用性值為201比101。
  • 第三個和第四個選擇器在連結文字顏色的樣式上進行競爭——第二個選擇器獲勝,使文字變白,因為缺少一個元素選擇器,缺少的選擇器被換成類選擇器,它的值是十,而不是個位。所以專用性值為113和104。
  • 選擇器5 – 7在徘徊在連結附近時的樣式進行競爭。選擇器六明顯地輸給了了五,其專用性值為23和24——它在鏈中少了一個元素選擇器。然而選擇器七同時擊敗了五和六——它有與五相同數量的子選擇器在鏈中,但一個元素已被換為了一個類選擇器。所以獲勝的專用性值是33比23和24。

原始碼次序

如上所述,如果多個相互競爭的選擇器具有相同的重要性和專用性,那麼第三個因素將幫助決定哪一個規則獲勝——後面的規則將戰勝先前的規則。例如:

p {
  color: blue;
}

/* This rule will win over the first one */
p {
  color: red;
}

而在這個例子中的第一個規則將獲勝,因為專用性高於原始碼順序:

/* This rule will win */
.footnote {
  color: blue;
}

p {
  color: red;
}

關於規則混合的一點註記

在考慮所有這些層疊理論和什麼樣式優先於其他樣式被應用時,你應該記住的一件事是,所有這些都發生在屬性級別上——屬性覆蓋其他屬性,但你不會讓整個規則凌駕於其他規則之上。
當多個CSS規則匹配相同的元素時,它們都被應用到該元素中。只有在這之後,任何相互衝突的屬性才會被評估,以確定哪種風格會戰勝其他型別。

簡記:只有相同的屬性應用發生衝突後才會產生覆蓋

讓我們看一個例子。首先,一些HTML:

<p>I`m <strong>important</strong></p>

現在一些CSS風格與它:

/* specificity: 0002 */
p strong {
  background-color: khaki;
  color: green;
}

/* specificity: 0001 */
strong {
  text-decoration: underline;
  color: red;
}

Result:

CSS學習摘要-層疊和繼承

在這個例子中,因為專用性的關係,第一條規則中的color屬性覆蓋掉了第二條中的顏色值。但是,對於第一條中的 background-color和第二條中的text-decoration 的屬性都在strong元素之中得到了體現。你也注意到了這個元素之中的字型是加粗的:這是瀏覽器預設的樣式規則。

繼承

CSS繼承是我們需要研究的最後一部分,以獲取所有資訊並瞭解什麼樣式應用於元素。

其思想是,應用於某個元素的一些屬性值將由該元素的子元素繼承,而有些則不會。

  • 例如,對 font-familycolor進行繼承是有意義的,因為這使得您可以很容易地設定一個站點範圍的基本字型,方法是應用一個字型到 <html> 元素;然後,您可以在需要的地方覆蓋單個元素的字型。如果要在每個元素上分別設定基本字型,那就太麻煩了。

  • 再如,讓 margin paddingborderbackground-image 不被繼承是有意義的。想象一下,如果您將這些屬性設定在一個容器元素上,並將它們繼承到每個子元素,然後不得不將它們全部放在每個單獨的元素上,那麼將會出現的樣式/佈局混亂。

哪些屬性預設被繼承哪些不被繼承大部分符合常識。如果你想確定,你可以參考CSS參考資料—— 每個單獨的屬性頁都會從一個彙總表開始,其中包含有關該元素的各種詳細資訊,包括是否被繼承。

注:
font-family允許您通過給定一個有先後順序的,由字型名或者字型族名組成的列表來為選定的元素設定字型。 屬性的值用逗號隔開。瀏覽器會選擇列表中第一個該計算機上有安裝的字型,或者是通過 ‘ @font-face 指定的可以直接下載的字型。

padding屬性設定一個元素的內邊距,padding 區域指一個元素的內容和其邊界之間的空間,該屬性不能為負值。

background-image 屬性用於為一個元素設定一個或者多個背景影像。影像在繪製時,以z方向堆疊的方式進行。先指定的影像會在之後指定的影像上面繪製。因此指定的第一個影像最接近使用者。

控制繼承

CSS為處理繼承提供了四種特殊的通用屬性值:

  • inherit: 該值將應用到選定元素的屬性值設定為與其父元素一樣。
  • initial :該值將應用到選定元素的屬性值設定為與瀏覽器預設樣式表中該元素設定的值一樣。如果瀏覽器預設樣式表中沒有設定值,並且該屬性是自然繼承的,那麼該屬性值就被設定為 inherit
  • unset:該值將屬性重置為其自然值,即如果屬性是自然繼承的,那麼它就表現得像 inherit,否則就是表現得像 initial
  • revert:如果當前的節點沒有應用任何樣式,則將該屬性恢復到它所擁有的值。換句話說,屬性值被設定成自定義樣式所定義的屬性(如果被設定), 否則屬性值被設定成使用者代理的預設樣式。

注意: initialunset 不被IE支援。

initial 是將屬性的初始值( initial value)賦給元素 . initial 適用於所有的css 屬性(屬性的initial值可在屬性表中查到),包括css 簡寫屬性(全域性屬性)all.

inherit 值是最有趣的——它允許我們顯式地讓一個元素從其父類繼承一個屬性值。

讓我們看一個例子。首先有以下一段HTML:

<ul>
  <li>Default <a href="#">link</a> color</li>
  <li class="inherit">Inherit the <a href="#">link</a> color</li>
  <li class="initial">Reset the <a href="#">link</a> color</li>
  <li class="unset">Unset the <a href="#">link</a> color</li>
</ul>

現在用CSS給它新增樣式:

body {
  color: green;
}

.inherit a {
  color: inherit;
}

.initial a {
  color: initial
}

.unset a {
  color: unset;
}

Result:

CSS學習摘要-層疊和繼承

讓我們解釋這裡發生了什麼:

  • 我們首先設定<body>color為綠色。
  • 由於color屬性是自然繼承的,所有的body子元素都會有相同的綠色。需要注意的是預設情況下瀏覽器設定連結的顏色為藍色,而不是自然繼承color屬性,因此在我們列表中的第一個連結是藍色的。
  • 第二個規則設定一個類 inherit 的元素內的連結,並從父類繼承它的顏色。在這種情況下, 意思是說連結繼承了父元素<li> 的顏色,預設情況下<li>的顏色來自於它的父元素 <ul> , 最後<ul>繼承自 <body>元素,而<body>color 根據第一條規則設定成了綠色。
  • 第三個規則選擇了在元素上使用類 initial 的任意連結然後設定他們的顏色為 initial 。通常, initial 的值被瀏覽器設定成了黑色,因此該連結被設定成了黑色。
  • 最後一個規則選擇了在元素上使用類 unset 的所有連結然後設定它們的顏色為 unset ——即我們不設定值。因為color屬性是一個自然繼承的屬性,它實際上就像把值設定成 inherit 一樣。結果是,該連結被設定成了與body一樣的顏色——綠色。

重新設定所有的屬性值

CSS速寫屬性 all 能夠被應用到每一個繼承屬性,一次性應用所有的繼承屬性。這裡的值可以是繼承屬性裡的任何一個 (inherit, initial, unset, or revert)。對於撤銷對樣式的修改,這是非常方便的一種方式。然後你就可以在開始新的修改之前,返回到一個已知的開始點。

全文摘要自MDN網路開發者網站
end
2018-5-31 週四

相關文章