圖解 CSS:CSS 層疊和繼承
CSS 中有三個概念是必須要掌握的:層疊、繼承和權重。今天我們主要來了解 CSS 中的層疊和繼承,對於 CSS 權重這一部分將放到 CSS 的選擇器中來介紹,因爲這一部分和 CSS 的選擇器耦合的更爲緊密。不管是初學者還是有一定工作經驗的同學,花點時間閱讀這篇文章都是很有必要的,這樣有利於你對 CSS 更清楚的瞭解和理解。
在很多 Web 開發人員眼中,CSS 不是一門程序語言,但它真真切切的是一門計算機語言。主要用來爲結構化文檔,比如 HTML、XML 等添加樣式,其主要由 W3C 定義和維護。而 CSS 是由 Cascading Style Sheets 三個詞的首字母縮寫,很多人將其稱爲層疊樣式表或者級聯樣式表。接下來要聊的第一個概念就是 CSS 中的層疊,也對應的是 CSS 中的第一個字母 C 。看到這裏,或許你就知道爲什麼會說層疊是 CSS 的重要概念之一了。
層疊
層疊是 CSS 固有的一個東東,它賦予了層疊樣式表層疊性,也常稱級聯。層疊是一個很強大的工具,如果錯誤的使用它可能會導致樣式表的脆弱性,會致使 Web 開發人員在任何時候不得不進行更改時都感到頭痛。比如說:初學 CSS 的同學,特別是 Web 其他端的程序員(比如服務端、客戶端)在獨立編寫 CSS 的時候,經常會碰到這樣的一個現象:“爲什麼寫的樣式不起作用呢? ” 面對這樣的場景很多人會採用非常暴力的手段來處理,比如通過添加 !important
或直接在 HTML 的元素上添加內聯 CSS。這就是令很多同學感到疑惑的一點?爲什麼要這樣做呢?其實這和接下來要介紹的層疊(也有很多人稱之爲級聯)有很大的關係。
層疊定義
因爲我們將要深入的學習和討論 CSS 層疊是如何工作的相關細節,因此我們有必要的瞭解 W3C 規範是如何定義它的:
The cascade takes a unordered list of declared values for a given property on a given element, sorts them by their declaration’s precedence, and outputs a single cascaded value.
大致意思是:該層疊將獲取給定元素上給定屬性的聲明值的無序列表,按聲明的優先級對它們進行排序,並輸出單個層疊值。
CSS 層疊是一種算法,瀏覽器通過它來決定將哪些 CSS 樣式規則應用到一個元素上。很多人喜歡把它看作是 “獲勝” 的樣式,按照 CSS 中的術語來說,它的權重更高(後面我們會深入介紹)。
爲了更好的理解 CSS 層疊,將 CSS 聲明看作具體的 “屬性”(Property)。這些屬性可以是聲明的各個部分,比如說 CSS 選擇器或 CSS 屬性,甚至是 CSS 聲明的位置相關(比如它的原始或源代碼中的位置)。
CSS 層疊將會根據自己的算法,採用這些屬性中的一些,併爲每個屬性分配一個權重。如果 CSS 規則在高優先級上獲勝(選擇器權重高),那麼這個樣式規則就會獲勝,即生效。但是,如果在給定的權重下有兩個規則衝突,算法將繼續” 向下層疊”,並且會檢查低優先級屬性,直到找到一個勝出的規則。
簡單的說,當多個相互衝突的 CSS 聲明應用於同一個元素時,CSS 層疊算法會根據一定的機制,從最高權重到最低權重的順序列出:
-
來源和重要性
-
選擇器權重
-
出現的順序
-
初始和繼承屬性(默認值)
接下來圍繞這幾點來進行展開。
來源和重要性
層疊檢查的最高加權屬性是給定規則的重要性和來源的組合。就 CSS 規則的來源而言,規則主要來自三個地方:
-
編寫者規則(Author) :這是 HTML 文檔聲明的 CSS。也就是前端開發人員編寫的,根據文檔語言(比如 HTML)約定給源文檔指定樣式表。這也是我們能夠控制的唯一來源
-
用戶(User) :這是由瀏覽器的用戶定義和控制的。不是每個人都會有一個,但是當人們添加一個時,通常是爲了覆蓋樣式和增加網站的可訪問性。比如,用戶可以指定一個具有樣式表的文件,或者用戶代理可能會提供一個用來生成用戶樣式(或者表現得像這樣做了一樣)的界面
-
用戶代理(User-Agent) :這些是瀏覽器爲元素提供的默認樣式。這就是爲什麼
input
在不同的瀏覽器上看起來略有不同,這也是人們喜歡使用 CSS 重置樣式,以確保重寫用戶代理樣式的原因之一
注意,用戶可能會修改系統設置(例如,系統配色),這會影響默認樣式表。然而,有些用戶代理實現讓默認樣式表中的值不可改變
這三種樣式表將在一定範圍內重疊,並且它們按照層疊互相影響。
CSS 層疊給每個樣式規則賦予了權重,應用這幾條規則時,權重最大的優先。默認情況下,編寫者樣式表中的規則比用戶樣式表中的規則權重高。CSS 聲明的重要性由適當命名的 !important
語法決定。!important
的 CSS 規則自動將它跳到層疊算法的前面,這也是爲什麼不鼓勵在樣式中使用 !important
的原因之一。覆蓋使用 !important
的樣式只能使用其他的 !important
的規則來完成,如果你的項目足夠大,用的 !important
又足夠地多,那麼你的 CSS 就會變得更爲脆弱,更難以維護。對於 !important
的使用,建議你只在其他所有方法都失效的情況之下使用。
那麼 CSS 中層疊算法又是如何判斷哪個聲明獲勝:
CSS 層疊在判斷哪個聲明獲勝時考慮這兩個屬性的組合。每個組合都有一個權重(類似聲明的部分權重),權重最高的規則獲勝。以下是瀏覽器考慮的各種來源和重要性的組合,按從最高權重到最低權重的順序列出:
-
帶
!important
的用戶代理規則 -
帶
!important
的用戶規則 -
帶
!important
的編寫者規則 -
CSS 動畫
@keyframes
中的規則(這是較爲特殊的一部分,它仍然來自編寫者規則,但是由於動畫是臨時的或短暫的,所以瀏覽器對它們的權重略高於正常的編寫者規則) -
編寫者規則
-
用戶規則
-
用戶代理規則
當瀏覽器遇到兩個或更多衝突的 CSS 聲明,其中一個在來源和重要性級別獲勝時,CSS 層疊就會解決這個規則。但是,如果相互衝突的聲明具有相同的重要性和來源級別,則層疊將繼續考慮選擇器的權重。
選擇器權重
CSS 層疊中的第二個權重是選擇器的權重。在這個層中,瀏覽器查看 CSS 聲明中使用的選擇器。作爲前端開發人員,我們只能控制編寫者樣式規則。因爲我們無法對來源的規則做太多的更改。但是,你會發現,只要在代碼中不使用!important
,對於 CSS 層疊中的選擇器權重這一層,還是有較多的方式可以控制。
對於一個選擇器的權重,將會按下面這樣的規則進行計算:
-
如果聲明來自一個行內樣式(
style
屬性)而不是一條選擇器樣式規則,算1
,否則就是0
(=a
)。HTML 中,一個元素的style
屬性值是樣式規則,這些屬性沒有選擇器,所以a=1, b = 0, c = 0, d = 0
,即1, 0, 0, 0
-
計算選擇器中
ID
屬性的數量 (= b
) -
計算選擇器中其它屬性和僞類的數量 (
= c
) -
計算選擇器中元素名和僞元素的數量 (
= d
)
4
個數連起來a-b-c-d
(在一個基數很大的數字系統中)表示特殊性,比如下面這樣的示例:
{} /* a=0 b=0 c=0 d=0 -> 選擇器權重 = 0,0,0,0 * /
li {} /* a=0 b=0 c=0 d=1 -> 選擇器權重 = 0,0,0,1 * /
li:first-line {} /* a=0 b=0 c=0 d=2 -> 選擇器權重 = 0,0,0,2 * /
ul li {} /* a=0 b=0 c=0 d=2 -> 選擇器權重 = 0,0,0,2 * /
ul ol+li {} /* a=0 b=0 c=0 d=3 -> 選擇器權重 = 0,0,0,3 */
h1 + [rel=up] {} /* a=0 b=0 c=1 d=1 -> 選擇器權重 = 0,0,1,1 * /
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> 選擇器權重 = 0,0,1,3 * /
li.red.level {} /* a=0 b=0 c=2 d=1 -> 選擇器權重 = 0,0,2,1 * /
#x34y {} /* a=0 b=1 c=0 d=0 -> 選擇器權重 = 0,1,0,0 * /
style="" /* a=1 b=0 c=0 d=0 -> 選擇器權重 = 1,0,0,0 */
在互聯網上也有很多很形象的圖來解釋 CSS 選擇器權重的,比如下圖:
上圖來自於 @Estelle Weyl 的《 CSS Specificity 》一文。
出現的順序
CSS 層疊算法的最後一個主要層是按源碼中出現的順序來計算。當兩個選擇器具有相同的權重時,在源代碼中最後的聲明獲勝。
因爲 CSS 在層疊中考慮源順序,所以加載樣式表的順序就顯得尤爲重要。如果在 HTML 文檔的<head>
引入了兩個樣式表,那麼第二個樣式表將覆蓋第一個樣式表中的規則。
初始和繼承屬性
雖然初始值initial
和繼承值inherit
並不是 CSS 層疊中真正組成部分,但是如果沒有針對元素的 CSS 聲明,它們將確定發生什麼。通過這種方式,它們確定元素的默認樣式值。
有關於初始和繼承更詳細的介紹,將在下一節中進行詳細地闡述。感興趣的同學,請繼續往下閱讀。
參與層疊計算的 CSS 實體
只有 CSS 聲明,就是屬性名值對,會參與層疊計算。這表示包含 CSS 聲明以外實體的@
規則不參與層疊計算,比如包含描述符的@font-face
。對於這些情形,@
規則是做爲一個整體參與層疊計算,比如@font-face
的層疊是由其描述符font-family
確定的。如果對同一個描述符定義了多次 @font-face
,則最適合的 @font-face
是做爲一個整體而被考慮的。
包含在大多數@
規則的 CSS 聲明是參與層疊計算的,比如包含於@media
、@documents
或者@supports
的 CSS 聲明,但是包含於@keyframes
的聲明不參與計算,正如@font-face
,它是作爲一個整體參與層疊算法的篩選。
注意
@import
和@charset
遵循特定的算法,並且不受層疊算法影響。
CSS 的 @layer 規則
爲了解決諸如此類的問題(級聯與權重),現代 CSS 新增了一個級聯層的規則,即 @layer 。
簡單地說: 級聯層提供了一種結構化的方式來組織和平衡單一來源中的 CSS 規則,最終決定誰獲勝!。
由於 CSS 的級聯層在 CSS 級聯中有着獨特的地位,使用它有一些好處,使開發者對級聯有更多的控制。CSS 的級聯層一般位於 “Style 屬性”(Style Attribute)和 CSS 選擇器權重(Specificity)之間,即:
我使用下圖來闡述有級聯層 @layer
前後,CSS 級聯的差異:
帶來的直接變化是權重計算規則變了:
有了 CSS 級聯層 @layer
特性之後,你可以拋棄以前的一些 CSS 方法論(例如 ITCSS),因爲 @layer
能更好的幫助你管理 CSS 的級聯。
簡單地說,級聯層 @layer
是 CSS 的一個新特性,它影響着樣式規則的應用和優先級。以下是級聯層的一些關鍵細節:
-
添加到級聯層: 可以通過
@layer
規則將樣式添加到指定的級聯層中。這使得開發者能夠更有序地組織樣式。 -
匿名級聯層: 如果未使用
@layer
規則,樣式將被放入默認的匿名級聯層。這樣可以確保未指定級聯層的樣式不會影響到指定級聯層的樣式。 -
預定義級聯層順序: 級聯層可以預定義一個順序,確保在樣式規則中未明確指定級聯層的情況下,樣式按照默認的預定義級聯層順序應用。
-
加載外部 CSS 文件到級聯層: 可以通過
@layer
規則加載外部 CSS 文件到指定的級聯層中,這樣可以更靈活地組織和加載樣式。 -
無級聯層樣式: 可以使用
@layer unlayered
規則將樣式添加到無級聯層的樣式層,這樣的樣式在默認情況下不會被任何級聯層繼承。 -
級聯層嵌套: 可以將一個級聯層嵌套在另一個級聯層中,以創建更復雜的樣式層次結構。
-
回滾級聯層: 可以通過
@layer
規則回滾到之前的級聯層狀態,以便撤銷某些樣式的更改。
級聯層爲開發者提供了更精細的樣式管理和組織的能力,使得在大型項目中更容易維護和擴展樣式。
有關於 CSS 級聯層
@layer
特性更詳細的介紹,請移步閱讀《現代 CSS》中的《CSS 分層:@layer 》課程!
繼承
在 CSS 中,每個 CSS 屬性定義的概述都指出了這個屬性是默認繼承的還是默認不繼承的。這決定了當你沒有爲元素的屬性指定值時該如何計算值。當元素的一個 繼承屬性沒有指定值時,則取父元素的同屬性的計算值。只有文檔根元素取該屬性的概述中給定的初始值;當元素的一個非繼承屬性沒有指定值時,則取屬性的初始值。比如:
html {
font-size: small;
}
這個規則在 HTML 文檔的根元素 html
設置了一個 font-size
屬性,而這個屬性是會被繼承的(在 CSS 中有些屬性天性就會被繼承)。正如上面所示,html
元素的所有後代元素都將被繼承這個屬性,比如下圖中藍框中顯示的一樣:
上圖藍框中告訴 body
元素繼承 html
元素的 font-size: small;
。開發者工具中會提示我們 “Inherited from html”。那麼問題來了,在 CSS 中哪些屬性是會被繼承的?其實在 W3C 規範中各個屬性的描述已經很清楚的告訴我們了。比如說 border
屬性,在描述其語法時,在列表中有一個 Inherited 描述項的值爲 no。這也就告訴我們 border
屬性是不能被繼承的。反之,再看 font-size
屬性,語法描述的列表中同樣有一個 Inherited 描述項,只不過它的值不是 no,而是 yes,也就是說 font-size
屬性是會被繼承的。
這決定了當你沒有爲元素的屬性指定值時該如何計算值。
如果你平時閱讀規範仔細的話,不難發現,在介紹每個屬性的語法參數的時候,都會有一個 Initial 參數,該參數主要指定每個屬性的初始值。CSS 屬性已經給出的初始值針對不同的繼承和非繼承屬性有不同的含義:
-
對於繼承屬性,初始值只能被用於沒有指定值的根元素上
-
對於非繼承屬性,初始值可以被用於任意沒有指定的元素上
這樣我們引出兩個概念:初始值和繼承值,除了這兩個概念之外,在 CSS 屬性中還有一個計算值,該值由指定的值計算而來:
-
處理特殊的值
inherit
和initial
-
根據屬性的摘要中關於計算值描述的方法計算出值
計算屬性的計算值通常包括將相對值轉換成絕對值,比如 em
和 %
這樣的單位。例如:
font-size: 16px;
padding-top: 2em;
其中 padding-top: 2em
就是一個計算值,其計算出來的值將根據 font-size
做爲基數計算(在此示例中),在此計算出來的值是 32px
。然而,有些屬性的百分比值會轉換成百分比的計算值(比如 width
)。另外, line-height
屬性值是沒有單位的數字,其值也是一個計算值。
對於 CSS 的計算值,在不同的瀏覽器中其計算出來的值有時候會略有偏差。
如果你感興趣的話,可以打開瀏覽器的開發者工具,查看對應的計算值(比如,Chrome 開發者工具,有一個 Computed 選項,該選項展示的就是對應的 CSS 計算值),如下圖所示:
其中計算值的最主要用處是繼承,包括
inherit
關鍵詞。
最後總結兩點:
-
當元素的一個繼承屬性沒有指定值時,則取父元素的同屬性的計算值,只有文檔根元素取該屬性的概術中給定的初始值
-
當元素的一個非繼承屬性沒有指定值時,則取屬性的初始值
看到這裏,或許你知道了什麼叫繼承和非繼承,以及他們取值方式。但你可能還在糾結,在 CSS 中到底哪些屬性是繼承屬性,哪些不是繼承屬性?其實這個問題我也沒辦法準確的回答您,因爲我也沒有做過這方面的統計。不過我可以告訴大家兩個小經驗:
-
在 CSS 中一些關於字體、文本和顏色等屬性都是可繼承屬性
-
在 CSS 中一些跟佈局和盒子模型的屬性都是非繼承屬性
如果你想準確的知道答案,可以通過這裏整理的屬性表格進行查看。只要 Inherited 選項是 Yes 的都表示是繼承屬性,否則都是非繼承屬性。
處理 CSS 繼承的機制
在 CSS 中提供了處理 CSS 繼承的機制,簡單地講就是 CSS 提供了幾個屬性值,可以用來處理屬性的繼承。這幾個屬性值就是initial
、inherit
、unset
和 revert
。其實除了這四個屬性值之外,還有一個 all
屬性值。雖然這幾個屬性值主要用來幫助大家處理 CSS 屬性繼承的,但他們之間的使用,還是有一定的差異化。先用一張圖來闡述它們之間的差異:
接下來我們一看看這幾個屬性值的實際使用以及對應的差異化。
initial
在 CSS 中,每個屬性都具有一個初始值,其實也就是 CSS 屬性的默認值。在 CSS 規範中,都對每個屬性的初始值做出了相關的定義。比如text-align
的初始值是 left
,display
的初始值是 inline
。
而這裏,我們要說的是 CSS 的關鍵詞 initial
:
If the cascaded value is the initial keyword, the property’s initial value becomes its specified value.
大致的意思是:“如果層疊值是 initial
關鍵詞,則屬性的初始值將成爲其指定值”。換句話說,如果你在元素樣式的設置中顯式的設置某個屬性的值爲initial
時,其實就表示設置了該屬性的默認值。
從文字上理解可能有點困惑,我們通過一個小例來幫助大家理解。假設我們有一個 <p>
元素,接觸過 CSS 的同學都知道,它是一個塊元素,爲了好看,咱們添加一點修飾的樣式代碼:
p {
background: #f36;
padding: 2rem;
font-size: 2rem;
color: #fff;
}
效果看起來像下面這樣:
如果我們希望 p
元素變成行內元素時,按照我們以前的處理方式,需要手動處理瀏覽器默認樣式(User-Agent 用戶代理樣式),也就是顯示的重置:
p{
dispaly: inline;
}
block
和 inline
效果對比如下:
前面提到過 inline
是 display
的初始值(也就是默認值),而在規範中也提到過:
你在元素樣式的設置中顯示的設置某個屬性的值爲
initial
時,其實就表示設置了該屬性的默認值。
也就是說,我們可以給 display
設置initial
關鍵詞:
p {
display: initial;
}
這個時候得到的效果其實和使用 display:inline
是一樣的:
如果我們通過瀏覽器檢查器中的計算值(Computed)一項可以看出來,display
設置爲 initial
時,會覆蓋用戶代理的樣式值 block
:
接下來,我們再來看一個繼承屬性 color
。所以 <p>
元素的後代元素 <strong>
也會繼承 <p>
元素中設置的 color: #fff
值。如果我們顯式的在 strong
中設置 color
的值爲 initial
時,那麼 strong
的 color
將重置爲默認值。由於我們沒有設置默認的 color
顏色,那麼這個時候,瀏覽器將會把一個計算值賦予成 color
的初始值:
inherit
CSS 添加了一個 inherit
關鍵詞屬性值,可以讓元素強制繼承父元素的某個屬性的值。前面也說過,CSS 中有些屬性自動就是可繼承屬性,比如 font-size
、color
之類,但也有很多屬性又是非繼承屬性,比如 border
、border-radius
之類的。在這裏,如果在非常繼承的屬性上顯示的設置了 inherit
關鍵詞,表示該元素將繼承父元素指定的屬性值或者計算值。
爲了同樣的能更好理解 inherit
,來看一個示例,在這個示例中,我們用 border
來做例子:
<div>
<div>...</div>
</div>
從效果上可以看出來,div.wrapper
上的 border
樣式並沒有繼承其子元素 div
上。如果我們想讓 .wrapper
的子元素 div
也要具有與 .wrapper
元素的 border
樣式,就需要在該元素 div
上顯式設置相同的 border
樣式:
<div>
<div>...</div>
</div>
有了 inherit
關鍵詞之後,事情要變得簡單地多,只需要在 .wrapper
的子元素上設置 border: inherit;
:
<div>
<div>...</div>
</div>
得到的效果將是一樣的:
上面的示例是父元素設置了 border
樣式,所以其繼承了父元素的 border
樣式。那如果將上面的示例稍做修改,在元素外套一個 div
,而這個 div
不做任何樣式的設置。將又會變成一個什麼樣呢?直接上示例吧:
<div>
<div>
<div>...</div>
</div>
</div>
效果如下:
可以看到 div.ele
僅繼承了其父元素 div
的 border
屬性的計算值,並未繼承其祖先元素 .wrapper
的border
屬性的設置值,通過瀏覽器開發者工具,可以看得一目瞭然:
這個示例說明:僅管元素自身顯式的設置了 inherit
關鍵詞,但是,如果其父元素沒有明確指定樣式,那麼其最終效果將和 revert
的效果一致。即繼承的是其父元素的計算值,也就是瀏覽器默認樣式(User Agent Stylesheet) 。
revert
revert
值早前被稱之爲 default
。表示沒有使用任何屬性值。
我們都知道,如果沒有使用作者樣式表(也就是 Web 開發人員自己寫的樣式表),那麼瀏覽器將會按這樣的過程去檢測,元素調用的樣式:
-
瀏覽器首先會檢測元素是否有使用作者樣式表的屬性
-
如果沒有找到,將會檢測客戶端默認樣式
-
如果沒有找到客戶端樣式,相當於元素應用了
unset
還是拿示例來說吧。比如我們一個 div
元素,我們並沒有顯式的在自己的樣式表中設置其 display
屬性的值。對於最後的渲染結果,瀏覽器將會使用用戶代理規則的樣式 display:block
。
根據前面介紹的,就算是我們在 div
中顯式的設置 display:revert
,該元素也將使用用戶代理規則中的 display:block
樣式。同樣,我們在另一個 div
元素中使用 display:initial
,根據前面介紹的,那在這個 div
將會採用 display
的初始值 inline
。比如下面的效果:
我們再來看一個繼承屬性的運用場景。因爲在 div
元素上設置了 color:#fff;
樣式,這是用戶寫的樣式,而且 color
是一個繼承屬性,只要是 div
的後代元素都將會繼承 color
的屬性值。根據前面所說 revert
的檢測機制是,先檢測用戶自己寫的樣式,然後再檢測用戶代理樣式,如果兩都沒有,纔會設置 unset
的樣式。所以最終我們看到的效果如下:(第二個設置了 color:initial;
)。
unset
unset
是 initial
和 inherit
的組合。當屬性設置爲 unset
時,如果它是一個繼承屬性,那麼它相當於是 inherit
;如果它不是,則相當於 initial
。
有一些屬性,如果沒有明確指定,將默認是 inherit
。比如,我們給元素設置一個 color
,那它將適用於所有默認的子元素。而其它屬性,如 border
則默認是非繼承屬性。
<div>
<div>...</div>
</div>
此時效果如下:
示例中 color
屬性被繼承了,但 border
屬性沒有被繼承。將上面的示例代碼稍作調整:
<div>
<div>...</div>
</div>
div.ele
元素的 border
和 color
都設置了 unset
值。也就是說它將運用 initial
或者 inherit
的值。具體取決於哪個值,這得根據屬性的默認行爲是什麼來決定。如果默認屬性是 inherit
,那將運用的是 inherit
;如果默認屬性是 initial
,那將運用的是 initial
。
上面的示例中,border
屬性採用的是 initial
,color
屬性採用的是 inherit
。
all
在 CSS 中,all
是一個簡寫屬性,其重設除了 unicode-bidi
和 direction
之外的所有屬性至它們的初始值或繼承值。all
有三個值:
-
initial
:該關鍵字代表改變該元素或其父元素的所有屬性至初始值。 -
inherit
:該關鍵字代表改變該元素或其父元素的所有屬性的值至他們的父元素屬性的值。 -
unset
:該關鍵字代表如果該元素的屬性的值是可繼承的,則改變該元素或該元素的父元素的所有屬性的值爲他們父元素的屬性值,反之則改變爲初始值。
來看示例。比如我們一個這樣的一個 HTML 結構:
<div>...<strong>...</strong> ...</div>
給他們設置一些樣式:
body {
padding: 2vw;
}
div {
background: #f36;
padding: 2rem;
font-size: 2rem;
color: #fff;
margin-bottom: 3rem;
}
strong {
font-size: 3rem;
}
看到的效果如下:
這效果正是我們想要的。div
顯式的指定了background
、padding
、font-size
、color
和margin-bottom
屬性值,而其中background
、padding
、margin-bottom
是非繼承屬性, 而color
和font-size
是繼承屬性。除此之外,div
還有一個客戶端代理樣式display:block
,這個屬性也是一個非繼承屬性。另外strong
元素設置了一繼承屬性font-size
,這個元素默認情況下繼承了共父元素的color
屬性,同時還繼承了html
元素的font-family
和line-height
屬性。當然,strong
元素也有一個客戶端代理樣式font-weight:bold
。
上面看到的效果是我們平時使用的時候效果。如果這個時候,我們在div
和strong
同時設置all:inherit;
時,得到的效果和前面的效果完全不一樣:
這個時候div
和strong
重置了當初自己設置的屬性,並且繼承了各自父元素的一些屬性:
-
div
元素繼承了body
元素的padding
、font-family
、line-height
,同時也繼承了body
代理客戶端的樣式color
、background
和display
-
strong
元素繼承了div
元素的樣式
所以最後你看到的效果就像上圖那樣子。我們再把all
的值設置爲initial
:
這個時候div
和strong
樣式都重置到了對應的初始樣式,也就是對應的屬性的默認樣式,包括代理客戶端樣式也重置爲對應屬性的初始值。
最後來看all
的值設置爲unset
的效果,下面的示例,我只在strong
元素上設置all:unset
,其效果就足以說明一切:
效果中的第一個是沒設置all:unset
,第二個設置了all:unset
。這個時候strong
元素的font-size
和font-weight
繼承了其父元素的font-size
和font-weight
。
all
在 CSS 中有時候是一個屬性,比如這裏說的就是屬性,但有的時候它還是 CSS 中某些屬性的值。比如我們常在transition
中用到的all
,那這個時候就是屬性值。到目前爲止,CSS 中的all
屬性也得到了衆多瀏覽器的支持。
如果你想更深入的瞭解 initial
、inherit
、revert
、unset
,那麼請移步閱讀《現代 CSS》的《CSS 顯式默認值:inherit 、initial 、revert 和 unset》
CSS 樣式的計算
CSS 屬性的最終值會經過四步計算:
-
通過指定來確定值,常稱之爲指定值(Specified Value)
-
接着處理得到一個用於繼承的值,常稱之爲計算值(Computed Value)
-
然後如果有必要的話轉化爲一個絕對值,常稱之爲應用值(Used Value)
-
最後根據本地環境限 tmhj 進行轉換,常稱之爲實際值(Actual Value)
那麼什麼是指定值、計算值、應用值和實際值呢?
指定值
用戶代理必須先根據下列機制(按優先順序)給每個屬性賦值一個指定值:
-
如果層疊產生了一個值,就使用它
-
否則,如果屬性是繼承的並且該元素不是文檔樹的根元素,使用其父元素的計算值
-
否則使用屬性的初始值,每個屬性的初始值都在屬性定義中指出了
計算值
指定值通過層疊被處理爲計算值,例如,URI
被轉換成絕對的,em
和ex
單位被計算爲像素或者絕對長度。計算一個值並不需要用戶代理渲染文檔。用戶代理規則無法處理爲絕對 URI 的話,該URI
的計算值就是指定值。
一個屬性的計算值由屬性定義中 Computed Value 行決定。當指定值爲inherit
時,計算值的定義可以依據繼承中介紹的規則來計算。即使屬性不適用(於當前元素),其計算值也存在,定義在'Applies To'行。然而,有些屬性可能根據屬性是否適用於該元素來定義元素屬性的計算值。
應用值
處理計算值時,儘可能不要格式化文檔。然而,有些值只能在文檔佈局完成時確定。例如,如果一個元素的寬度是其包含塊的特定百分比,在包含塊的寬度確定之前無法確定這個寬度。應用值是把計算值剩餘的依賴(值)都處理成絕對值後的(計算)結果。
實際值
原則上,應用值應該用於渲染,但用戶代理可能無法在給定的環境中利用該值。例如,用戶代理或許只能用整數像素寬度渲染邊框,因此不得不對寬度的計算值做近似處理,或者用戶代理可能被迫只能用黑白色調而不是全綵色。實際值是經過近似處理後的應用值。
小結
本文涉及了大量的內容,希望它能幫助大家理解樣式是如何受到我們編寫和應用的影響。特別是 CSS 的層疊和繼承。在實際使用的時候,如果很好的運用這些概念和手段,可以更好的幫助大家少寫很多樣式代碼,而且更易於維護自己的 CSS 代碼。
用張圖來表示如下:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/qK6s7zvZ5dOQKtbJIPBqXQ