你不知道的 CSS - 層疊樣式表

aniiantt發表於2018-10-17

層疊樣式表

層疊規則

你或許知道 CSS 是 Cascading Style Sheets(層疊樣式表)的縮寫。但你不一定真正的理解了其中層疊的含義。

你可能會以為層疊指的是選擇器的優先順序,但這是不準確的。

對於瀏覽器頁面上某個元素的某個屬性值,它可能會有多個來源(Cascading Origins):

  1. 使用者代理 (user agent)提供的樣式
  2. 使用者自定義的樣式
  3. 網站提供的樣式

使用者代理即是指瀏覽器,cs.chromium.org/chromium/sr… 這裡是 chromium 的樣式表。現在我們常常會使用 reset.css 或者 normalize.css 使的各個瀏覽器之間預設樣式統一。

使用者自定義樣式雖然規範中有,但從 chrome 33 起,開始不支援使用者自定義樣式表,而是建議使用擴充套件來實現。

網站提供的樣式表,則是我們所提供的 css 的樣式。

來源之間是存在優先順序的(和選擇器的優先順序是兩回事),優先順序高的會覆蓋優先順序低。我們來驗證一下:

你不知道的 CSS - 層疊樣式表

codepen.io/gygy/pen/mz…

可以看到 ruby > rt { font-size: 50% } 是來自 user agent stylesheet。而 rt { font-size: 24px } 來自網站作者,如果單論選擇器的權重,它是低於 ruby > rt 的。 但是它依然覆蓋了 user agent stylesheet 的 font-size 屬性。原因就是 css 會優先根據屬性的來源判斷。對於相同來源的屬性,再應用權重規則。

我們也可以通過 !important 宣告某個屬性的重要性,再結合它的來源,於是有如下的優先順序規則(忽略使用者自定義的樣式):

  1. 使用者代理
  2. 來源使用者代理的 !important 屬性
  3. 網站作者
  4. CSS 動畫(Animation 和 Transition)
  5. 網站作者 !important

其中對於 CSS 動畫,在給定時間中 CSS 只會從某一個 @keyframes 中獲取值,而不是某幾個 @keyframe 的混合。@keyframes 裡定義的值會覆蓋普通值,但是優先順序低於 !important。

權重規則

對於同一來源的 CSS。我們要確定某個元素的某個屬性的值,涉及到該元素的選擇器的權重問題。選擇器大致分為幾類:

  • 元素選擇器(Elemental selectors):標籤名稱。
  • 屬性選擇器(Attribute selectors):id class 某個屬性。
  • 偽類(Pseudo-classes):匹配處於確定狀態的一個或多個元素,表現的像一個 class。
  • 偽元素(Pseudo-elements):匹配處於相關的確定位置的一個或多個元素,表現的像一個 element。
  • 組合選擇器(Combinators):div > span 之類的複合選擇器。
  • 多重選擇器(Multiple selectors)多個選擇器之間用逗號隔開。

它們之間的權重規則計算:

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

If the selector is a selector list, this number is calculated for each selector in the list. For a given matching process against the list, the specificity in effect is that of the most specific selector in the list that matches.

簡單的說來 id 優先順序最高,其次是類和偽類再次是元素和偽元素。然後根據它們各自的數量判斷。

其實說到底不建議寫過於複雜的選擇器,會影響效能和維護程式碼。建議採用 BEM 規範,書寫 CSS 選擇器。

CSS 的值的計算過程

為了得到 CSS 的值的生成有很多個步驟:

  1. 收集應用到這個元素上面的所有的宣告值(Declared values),可能有 0 個或者多個,包括瀏覽器的樣式表和網站作者樣式表中定義的。
  2. 根據宣告值和上述的層疊規則得到這個屬性的層疊值(Cascaded Values),層疊值只能有一個或者為空。
  3. 如果層疊值存在,則指定值(Specified Values)等於層疊值。否則,則會應用 CSS 中屬性繼承的規則,得到一個繼承值(Inherited value),層疊值等於這個繼承值。如果該屬性不適用於繼承,指定值會等於這個屬性的初始值(Initial value)。每個元素的每個屬性都有且只有一個指定值。
  4. 一些屬性值是屬於相對單位 如 1em 會被轉化為 px,在這個過程中相對值會被絕對化,得到計算值(Computed Values),這個值可以被用於繼承。
  5. 應用值(Used Values)是瀏覽器根據計算器,然後完成剩餘的計算,得到理論上佈局的值。如 width: auto 在此過程中,會被計算得出精確的畫素值。
  6. 實際值(Actual Values)是指瀏覽器根據現實情況佈局所用到值。比如 px 如果是小數,會被轉化為整數佈局,因為物理上 1px 不可分。

舉幾個例子說明一下上述步驟:

  • 對於 font-size,由於它是可繼承的值,所以即使你沒為某個元素設定 font-size 大小,它的層疊值為空,它也會在第三步的時候,將指定值賦值於獲取他的繼承值。(這個繼承者來源於父級元素的計算值)。
  • 對於 float 不為 none 的元素,即使你手動宣告瞭 display: inline,此時 display 在第四步的時候,會被賦值為 block。即 float 不為 none 的元素的 display 計算值為 block。

參考連結

相關文章