層疊 CSS 是 **Cascading Style Sheets** 的縮寫,這暗示層疊(cascade)的概念是很重要的。在最基本的層面上,它表明CSS規則的順序很重要,但它比那更複雜。什麼選擇器在層疊中勝出取決於三個因素(這些都是按重量級順序排列的——前面的的一種會否決後一種): 1. 重要... ...
當有多個選擇器作用在一個元素上時,哪個規則最終會應用到元素上?
其實這是通過層疊機制來控制的,這也和樣式繼承(元素從其父元素那裡獲得屬性值)有關。
元素的最終樣式可以在多個地方定義,它們以複雜的形式相互影響。這些複雜的相互作用使CSS變得非常強大,但也使其非常難於調試和理解。
層疊
CSS 是 Cascading Style Sheets 的縮寫,這暗示層疊(cascade)的概念是很重要的。在最基本的層面上,它表明CSS規則的順序很重要,但它比那更複雜。什麼選擇器在層疊中勝出取決於三個因素(這些都是按重量級順序排列的——前面的的一種會否決後一種):
- 重要性(Importance)
- 專用性(Specificity)
- 源代碼次序(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;
}
這將生成以下:
讓我們一起來看看發生了什麼。
你可以看到第三條規則
color
和padding
被運用了, 但background-color
沒有,為什麼?實際上,這三種情況都應該應用,因為在源順序後面的規則通常會覆蓋較早的規則。然而, 在前面的規則被運用了,因為 IDs/class 選擇器優先於element選擇器。
這兩個元素都有
class
並帶有better
屬性, 但是第二個元素有id
值為winning
。 因為比起class而言id專用性更高(在一個頁面上id是唯一的, 但很多元素可以擁有相同的class — ID 選擇器在它們的目標中是非常優先的),紅色背景色和1pixel的黑色邊框都應應用於第二元素,第一個元素獲得灰色背景色,沒有邊框,如類所指定。第二個元素獲得紅色背景色,但沒有邊框。為什麼?因為
!important
在第二條規則中的聲明——在border: none
之後寫入它意味著儘管id具有更高的優先性,該聲明也將優先於前面規則中的邊界值聲明。
註意: 重載這個 !important
聲明的唯一方法是在後面的源碼或者是一個擁有更高特殊性的源碼中包含相同的 !important
特性的聲明。
知道 !important
存在是很有用的,這樣當你在別人的代碼中遇到它時,你就知道它是什麼了。但是!我們建議你千萬不要使用它,除非你絕對必須使用它。您可能不得不使用它的一種情況是,當您在CMS中工作時,您不能編輯核心的CSS模塊,並且您確實想要重寫一種不能以其他方式覆蓋的樣式。 但是,如果你能避免的話,不要使用它。由於 !important
改變了層疊正常工作的方式,因此調試CSS問題,尤其是在大型樣式表中,會變得非常困難。
要註意一個CSS聲明的重要性取決於它被指定在什麼樣式表內——用戶可以設置自定義樣式表覆蓋開發商的樣式,例如用戶可能有視力障礙,想設置字體大小對所有網頁的訪問是雙倍的正常大小,以便更容易閱讀。
相互衝突的聲明將按以下順序適用,後一種將覆蓋先前的聲明:
- 在用戶代理樣式表的聲明 (例如:瀏覽器在沒有其他聲明的預設樣式).
- 用戶樣式表中的普通聲明(由用戶設置的自定義樣式)。
- 作者樣式表中的普通聲明(這是我們設置的樣式,Web開發人員)。
- 作者樣式表中的重要聲明
- 用戶樣式表中的重要聲明
Web開發者的樣式表覆蓋用戶的樣式表是合理的,所以設計可以保持預期,但是有時候用戶有很好的理由來重寫web開發人員樣式,如上所述,這可以通過在用戶的規則中使用!important
。
專用性
專用性基本上是衡量選擇器的具體程度的一種方法——它能匹配多少元素。如上面所示的示例所示,元素選擇器具有很低的專用性。類選擇器具有更高的專用性,所以將戰勝元素選擇器。ID選擇器有甚至更高的專用性, 所以將戰勝類選擇器. 戰勝ID選擇器的唯一方法是使用 !important
。
一個選擇器具有的專用性的量是用四種不同的值(或組件)來衡量的,它們可以被認為是千位,百位,十位和個位——在四個列中的四個簡單數字:
- 千位:如果聲明是在
style
屬性中該列加1分(這樣的聲明沒有選擇器,所以它們的專用性總是1000。)否則為0。 - 百位:在整個選擇器中每包含一個ID選擇器就在該列中加1分。
- 十位:在整個選擇器中每包含一個類選擇器、屬性選擇器、或者偽類就在該列中加1分。
- 個位:在整個選擇器中每包含一個元素選擇器或偽元素就在該列中加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;
}
我們從這個代碼中得到的結果如下:
這裡發生了什麼?首先,我們只對本例的前七個規則感興趣,正如您將註意到的,我們已經在每個註釋中包含了它們的專用性值。
- 前兩個選擇器正在競爭鏈接的背景顏色的樣式——第二個贏得並使背景色為藍色,因為它有一個額外的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:
在這個例子中,因為專用性的關係,第一條規則中的color
屬性覆蓋掉了第二條中的顏色值。但是,對於第一條中的 background-color
和第二條中的text-decoration
的屬性都在strong
元素之中得到了體現。你也註意到了這個元素之中的字體是加粗的:這是瀏覽器預設的樣式規則。
繼承
CSS繼承是我們需要研究的最後一部分,以獲取所有信息並瞭解什麼樣式應用於元素。
其思想是,應用於某個元素的一些屬性值將由該元素的子元素繼承,而有些則不會。
例如,對
font-family
和color
進行繼承是有意義的,因為這使得您可以很容易地設置一個站點範圍的基本字體,方法是應用一個字體到<html>
元素;然後,您可以在需要的地方覆蓋單個元素的字體。如果要在每個元素上分別設置基本字體,那就太麻煩了。再如,讓
margin
padding
,border
和background-image
不被繼承是有意義的。想象一下,如果您將這些屬性設置在一個容器元素上,並將它們繼承到每個子元素,然後不得不將它們全部放在每個單獨的元素上,那麼將會出現的樣式/佈局混亂。
哪些屬性預設被繼承哪些不被繼承大部分符合常識。如果你想確定,你可以參考CSS參考資料—— 每個單獨的屬性頁都會從一個彙總表開始,其中包含有關該元素的各種詳細信息,包括是否被繼承。
註:
font-family
允許您通過給定一個有先後順序的,由字體名或者字體族名組成的列表來為選定的元素設置字體。 屬性的值用逗號隔開。瀏覽器會選擇列表中第一個該電腦上有安裝的字體,或者是通過 ‘ @font-face
指定的可以直接下載的字體。
padding
屬性設置一個元素的內邊距,padding 區域指一個元素的內容和其邊界之間的空間,該屬性不能為負值。
background-image
屬性用於為一個元素設置一個或者多個背景圖像。圖像在繪製時,以z方向堆疊的方式進行。先指定的圖像會在之後指定的圖像上面繪製。因此指定的第一個圖像最接近用戶。
控制繼承
CSS為處理繼承提供了四種特殊的通用屬性值:
inherit
: 該值將應用到選定元素的屬性值設置為與其父元素一樣。initial
:該值將應用到選定元素的屬性值設置為與瀏覽器預設樣式表中該元素設置的值一樣。如果瀏覽器預設樣式表中沒有設置值,並且該屬性是自然繼承的,那麼該屬性值就被設置為inherit
。unset
:該值將屬性重置為其自然值,即如果屬性是自然繼承的,那麼它就表現得像inherit
,否則就是表現得像initial
。revert
:如果當前的節點沒有應用任何樣式,則將該屬性恢復到它所擁有的值。換句話說,屬性值被設置成自定義樣式所定義的屬性(如果被設置), 否則屬性值被設置成用戶代理的預設樣式。
註意: initial
和 unset
不被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:
讓我們解釋這裡發生了什麼:
- 我們首先設置
<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 周四