iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Modern Web

前端迷走中:從零開始的新手之路系列 第 29

[Day 29] 讓屬性繼承、重置與回溯──CSS 屬性關鍵字 inherit、 initial、unset、revert、revert-layer

  • 分享至 

  • xImage
  •  

昨天我們介紹了CSS的屬性值處理流程。

由於有這樣的處理流程,即使同時對某些元素的某個屬性宣告多個樣式,仍然可以依據樣式表來源、重要性等因素,決定要優先使用哪個樣式。

就算沒有對屬性宣告任何樣式,也會依據屬性是否可以繼承,決定要以繼承值或初始值作為屬性的預設值

不過在宣告時,除了真的設定一個樣式,我們也可以使用inheritinitialunset等關鍵字,指定要以繼承值或初始值作為屬性值,即使那個屬性原本無法繼承。

或是也可以使用revertrevert-layer等關鍵字,指定要改用其他樣式表來源或分層中,原先因為優先度較低而被淘汰的其他宣告值。

inherit,指定要繼承

如果作為Cascaded value勝出的宣告值為關鍵字inherit,則無論這個屬性原本是否會繼承,都會指定元素從上層元素繼承這個屬性值。

屬性在繼承時,繼承的是上層元素的計算值Computed value;
繼承來的值,會是這個屬性的指定值Specified Value跟計算值。

initial,設為初始值

如果宣告屬性值時使用關鍵字inherit,則無論這個屬性原本是否會繼承,都會以初始值作為屬性的指定值。

每個屬性都有定義一個初始值,可以參考規範MDN整理的表格。

unset,等同於沒有設定

如果宣告屬性值時使用關鍵字unset,則像是設定成不做任何設定一樣。會依據屬性原本是否會繼承,判斷要以繼承值或初始值作為屬性的指定值。

如果屬性本來會繼承,會視作inherit,以繼承值作為屬性的指定值與計算值;
如果屬性本來不會繼承,則會視作initial,以初始值作為屬性的指定值。

revert,回到優先度次一級的來源找宣告值

如果宣告屬性值時使用關鍵字revert,則在屬性值作為Cascaded value勝出後,會回到Cascading的流程,從優先度次於原先來源的樣式表中,找出最優先的宣告值作為指定值。

所以revert的效果,會因為宣告的來源而不同。

如果宣告來自作者樣式表,會從使用者樣式表跟使用者代理樣式表中,找出最優先的宣告值作為屬性的指定值。假如有其他更優先的動畫類型宣告,也會略過。

如果宣告來自使用者樣式表,會從使用者代理樣式表中,找出最優先的宣告值作為屬性的指定值。

如果宣告來自使用者代理樣式表,則結果等同於使用unset,因為沒有其他優先度較低的樣式表。

另外,如果標記語言的呈現提示被視為作者呈現提示樣式表(author presentational hint origin),那麼在revert尋找替代宣告值的過程中,這些呈現提示會被視為作者樣式表的一部分。

revert-layer 回到優先度次一級的分層找宣告值

類似於revert,如果宣告屬性值時使用關鍵字revert-layer,則在屬性值作為Cascaded value勝出後,仍然會回到Cascading的流程找出指定值。

但與revert不同的是,revert-layer是從其他優先度較低的分層中,找出最優先的宣告值作為屬性的指定值。

所以revert-layer的效果,會因為宣告來自哪個分層而不同。

如果宣告原本不在任何分層內,會以這些分層中最優先的宣告值,作為屬性的指定值;
如果宣告來自後面出現的分層,會以前面分層中最優先的宣告值,作為屬性的指定值;
如果宣告來自最前面的分層,則效果類似於revert。會在優先度次於原先來源的樣式表中,找出最優先的宣告值作為指定值。因為沒有其他優先度更低的分層。

就算原本就沒有將宣告分層,也可以使用revert-layer。但效果會類似revert,因為沒有其他優先度更低的分層。

如果不是行內樣式,使用revert-layer時加上!important,不會受到反轉的優先順序影響。
仍然會按照原先的分層順序,從優先度較低的分層中找出最優先的宣告值。[1]

舉例來說,設定為revert-layer !important的宣告,如果來自最後面的分層,仍然會以前面分層中最優先的宣告值,作為屬性的指定值。
不會因為分層的優先順序被重要性反轉,就改以分層外最優先的宣告值,作為屬性的指定值。

如果是行內樣式,則會受到反轉的優先順序影響

一般情況下,在元素的style屬性內使用revert-layer,會以分層外最優先的宣告值作為屬性的指定值。因為分層外的宣告值次於行內樣式。

不過,加上!important之後,則會依照反轉後的優先順序,從優先度較低的分層中找出最優先的宣告值。因為以行内樣式宣告的屬性值,優先度始終高於其他透過選擇器宣告的屬性值。
如果動畫類型的宣告,revert-layer也不會像revert一樣略過他們。

舉例來說,如果其他分層內的宣告值都有加上!important,則設定為revert-layer !important的行内樣式,會以最前面分層內的宣告值作為指定值,因為此時前面分層的優先度比後面分層高。

可能也會選到文件語言的呈現提示

在Cascading的過程中,標記語言的呈現提示可能會被視為使用者代理樣式表,或是優先順序介於使用者樣式表與作者樣式表中間的作者呈現提示樣式表。

有別於revert將作者呈現提示樣式表視為作者樣式表的一部份。revert-layer仍然會區分作者呈現提示樣式表與作者樣式表。[2]

因此當revert-layer找不到其他優先度更低的分層,改到其他來源的樣式表尋找替代的宣告值時,就有可能會以呈現提示作為屬性的指定值。

搭配all屬性使用

inheritinitialunsetrevertrevert-layer等關鍵字,可以搭配all屬性使用。

如此可以同時對unicode-bididirection跟自訂屬性以外的所有屬性,設定屬性值。

範例

比較五種關鍵字的效果

以下透過範例比較五種關鍵字的效果。

<section>內總共有七個<p>,每個<p>裡面有一個連結<a>

<section>
  <p>
    <a
      class="unstyled"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >沒有設定樣式</a
    >
  </p>

  <p>
    <a href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層A,設定樣式</a
    >
  </p>
  <p>
    <a
      class="inherit"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層外,設為inherit</a
    >
  </p>

  <p>
    <a
      class="unset"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層外,設為unset</a
    >
  </p>

  <p>
    <a
      class="initial"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層外,設為initial</a
    >
  </p>

  <p>
    <a
      class="revert"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層外,設為revert</a
    >
  </p>

  <p>
    <a
      class="revert-layer"
      href="https://ithelp.ithome.com.tw/users/20116445/ironman/8702"
      >分層外,設為revert-layer</a
    >
  </p>
</section>

以元素選擇器p,將文字顏色設為綠色,並加上邊框。

對於使用關鍵字宣告樣式的連結,則分別以類別選擇器,將文字顏色與邊框設為對應的關鍵字。

同時,在分層A內以選擇器a:not(.unstyled),對類別為.unstyled以外的所有連結設定樣式。
將文字顏色設為紅色,邊框設為點狀的橘色。

@layer A {
  a:not(.unstyled) {
    color: red;
    border: 2px dotted orange;
  }
}

p {
  color: darkgreen;
  border: 2px solid darkcyan;
}

.inherit {
  color: inherit;
  border: inherit;
}

.initial {
  color: initial;
  border: initial;
}

.unset {
  color: unset;
  border: unset;
}

.revert {
  color: revert;
  border: revert;
}

.revert-layer {
  color: revert-layer;
  border: revert-layer;
}

最終會呈現為:

https://ithelp.ithome.com.tw/upload/images/20251013/201164458iLG0V1qlX.png

第一個連結,因為被選擇器a:not(.unstyled)排除,所以沒有宣告任何樣式。
不過遊覽器預設會將連結設為藍色且帶有底線,所以仍然會是藍色。

第二個連結,會適用於分層A內的a:not(.unstyled)。於是文字顏色為紅色,且帶有橘色的點狀邊框。

第三個連結,會優先適用分層外的類別選擇器.inherit
屬性值設為inherit,會繼承<p>的樣式。因此文字為綠色,且帶有青色邊框。

第四個連結,會優先適用分層外的類別選擇器.unset,將屬性值設為revert
文字顏色是預設會繼承的屬性,等同inherit,會繼承<p>的綠色;
邊框是預設不會繼承的屬性,等同initial,會採用初始值,沒有邊框。

第五個連結,會優先適用分層外的類別選擇器.initial
由於採用初始值,文字會是黑色,且沒有邊框。

第六個連結,會優先適用分層外的類別選擇器.revert。屬性值設為revert,會採用瀏覽器預設的樣式。所以看起來跟第一個連結一樣。

第七個連結,會優先適用分層外的類別選擇器.revert-layer
不過屬性值設為revert-layer,仍然會採用分層A內以a:not(.unstyled)設定的樣式。所以看起來跟第二個連結一樣。

測試revert-layer的效果

以分隔線的顏色,來測試10種情況下revert-layer的效果。

前2條分隔線用於測試沒有呈現提示的情況,沒有加上color屬性;
其他分隔線用於測試有呈現提示的情況,都有用<hr>本身的color屬性設定顏色(紫色)。

最後2條分隔線用於測試行內樣式最優先的情況,另外以style屬性設定顏色。

<div>
  <p>重要,且內層A最優先的情況下,如果沒有呈現提示,會用其他來源的樣式。</p>
  <hr class="important a_revert-layer" />
  <p>正常,且內層A最優先的情況下,如果沒有呈現提示,會用其他來源的樣式。</p>
  <hr class="normal a_revert-layer" />

  <p>重要,且內層A最優先的情況下,如果有呈現提示,會用呈現提示的樣式。</p>
  <hr class="important a_revert-layer" color="purple" />
  <p>正常,且內層A最優先的情況下,如果有呈現提示,會用呈現提示的樣式。</p>
  <hr class="normal a_revert-layer" color="purple" />

  <p>重要,且外層B最優先的情況下,會用內層A的樣式。</p>
  <hr class="important b_revert-layer" color="purple" />
  <p>正常,且外層B最優先的情況下,會用內層A的樣式。</p>
  <hr class="normal b_revert-layer" color="purple" />

  <p>重要,且層外最優先的情況下,會用外層B的樣式。</p>
  <hr class="important outside_revert-layer" color="purple" />
  <p>正常,且層外最優先的情況下,會用外層B的樣式。</p>
  <hr class="normal outside_revert-layer" color="purple" />

  <p>重要,且行內樣式最優先的情況下,會用內層A的樣式。</p>
  <hr
style="border: revert-layer !important"
class="important inline_revert-layer"
color="purple"
  />
  <p>正常,且行內樣式最優先的情況下,會用層外的樣式。</p>
  <hr
style="border: revert-layer"
class="normal inline_revert-layer"
color="purple"
  />
</div>

針對正常或重要時,行內樣式、層外宣告、外層宣告、內層宣告分別為最優先宣告的8種情況,以8個類別選擇器設定樣式。

層外宣告的樣式為橘色,外層B宣告的樣式為綠色,內層A宣告的顏色為藍色。

因為要確認優先度次一級的分層是哪個,在層外、外層B、內層A三處同時都以這8個類別選擇器設定樣式。

為了確保某一層是某一種情況下最優先的分層,如果其他層設定樣式或加上!important會蓋過,其他層就不會設定樣式或加上!important

.important.inline_revert-layer {
  border: 1px solid orange !important;
}
.important.outside_revert-layer {
  border: revert-layer !important;
}
.important.b_revert-layer {
  border: 1px solid orange !important;
}
.important.a_revert-layer {
  border: 1px solid orange !important;
}

.normal.inline_revert-layer {
  border: 1px solid orange;
}
.normal.outside_revert-layer {
  border: revert-layer;
}
.normal.b_revert-layer {
  /*  因為要讓B層優先,不設定值 */
}
.normal.a_revert-layer {
  /*  因為要讓A層優先,不設定值 */
}

@layer A {
  .important.inline_revert-layer {
    border: 1px solid blue !important;
  }
  .important.outside_revert-layer {
    border: 1px solid blue;
    /* 因為要讓層外優先,不加上重要性 */
  }
  .important.b_revert-layer {
    border: 1px solid blue;
    /* 因為要讓B層優先,不加上重要性 */
  }
  .important.a_revert-layer {
    border: revert-layer !important;
  }

  .normal.inline_revert-layer {
    border: 1px solid blue;
  }
  .normal.outside_revert-layer {
    border: 1px solid blue;
  }
  .normal.b_revert-layer {
    border: 1px solid blue;
  }
  .normal.a_revert-layer {
    border: revert-layer;
  }
}

@layer B {
  .important.inline_revert-layer {
    border: 1px solid green !important;
  }
  .important.outside_revert-layer {
    /* 因為要讓層外優先,不加上重要性 */
    border: 1px solid green;
  }
  .important.b_revert-layer {
    border: revert-layer !important;
  }
  .important.a_revert-layer {
    border: 1px solid green !important;
  }

  .normal.inline_revert-layer {
    border: 1px solid green;
  }
  .normal.outside_revert-layer {
    border: 1px solid green;
  }
  .normal.b_revert-layer {
    border: revert-layer;
  }
  .normal.a_revert-layer {
    /*  因為要讓A層優先,不設定值 */
  }
}

如下圖所示,行內樣式以外的其他宣告不會受到!important反轉順序的影響,不論是在層外或層內宣告。

正常情況下,行內樣式如果使用revert-layer,會改用層外的樣式(橘色);
加上!important的重要情況下,則會改用內層的樣式(藍色)。

最後8條分隔線都有以color屬性設定樣式(紫色),但只有前2條分隔線有成功套用,因為呈現提示的優先度不如作者樣式表。

不過由於呈現提示優先於使用者代理樣式表,如果revert-layer找不到其他優先度更低的分層,要改由其他來源尋找宣告值時,就會優先採用呈現提示設定的樣式;
如果沒有以呈現提示設定樣式,則會像是最前面的2條分隔線,使用瀏覽器的預設樣式。

https://ithelp.ithome.com.tw/upload/images/20251013/20116445viFuCxSiWO.png

小結

今天我們介紹了宣告屬性值時,可以使用的五個關鍵字。

使用inherit,會繼承上層元素的樣式,以其計算值為指定值。就算屬性原本不會繼承也一樣。

使用initial,會以初始值作為指定值。就算屬性原本會繼承也一樣。

使用unset,就像是不做任何設定一樣。如果屬性原本會繼承,就等同inherit;如果不會繼承,則等同initial

使用revert,會回到Cascading的流程,從優先度次一級的樣式表來源中,找出最優先的宣告值作為指定值。

使用revert-layer,會回到Cascading的流程,從優先度次一級的分層中,找出最優先的宣告值作為指定值。

如果本來就沒有分層,或是本來就在優先度最低的分層,則類似於revert,但不會略過文件語言本身的呈現提示。

一般來說,revert-layer不會受到!important反轉的優先順序影響。但如果是行內樣式,則會受到影響。

參考資料

[1]: CSS-Tricks, Cascade Layers Guide
[2]: MDN, revert-layer


上一篇
[Day 28] 宣告到渲染,樣式哪裡來── CSS的屬性值處理流程
下一篇
[Day 30] 結語──未完待續的迷走之旅
系列文
前端迷走中:從零開始的新手之路30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言