使用 CSS 隔離

CSS 隔離透過允許瀏覽器將頁面的某個子樹與頁面的其餘部分隔離開來,從而提高了網頁的效能。如果瀏覽器知道頁面的某一部分獨立於其他內容,就可以最佳化渲染並提高效能。

containcontent-visibility 屬性讓開發者能夠告知使用者代理,一個元素是否應該渲染其內容,以及當它在螢幕外時是否應該渲染其內容。然後,使用者代理會在適當的時候對元素應用隔離,可能會推遲佈局和渲染直到需要時才進行。

本指南介紹了 CSS 隔離的基本目標,以及如何利用 containcontent-visibility 來提供更好的使用者體驗。

基本示例

網頁通常包含多個在邏輯上相互獨立的區域。CSS 隔離使得它們在渲染時可以被真正地獨立對待。

例如,部落格通常包含多篇文章,每篇文章都包含標題和內容,如下面的標記所示。

html
<h1>My blog</h1>
<article>
  <h2>Heading of a nice article</h2>
  <p>Content here.</p>
</article>
<article>
  <h2>Another heading of another article</h2>
  <p>More content here.</p>
</article>

使用 CSS,我們為每篇文章應用值為 contentcontain 屬性。content 值是 contain: layout paint style 的簡寫。

css
article {
  contain: content;
}

從邏輯上講,每篇文章都獨立於頁面上的其他文章。這些資訊通常是建立頁面的 Web 開發者所知道的,而且很可能是顯而易見的。然而,瀏覽器不知道你的內容的意圖,不能假設一篇文章或其他內容區域是完全自包含的。

這個屬性提供了一種向瀏覽器解釋這一點並明確允許其進行效能最佳化的方法。它告訴瀏覽器,該元素的內部佈局與頁面的其餘部分完全分離,並且該元素的所有內容都在其邊界內繪製。任何內容都不能在視覺上溢位。

透過在每個 <article> 上設定 contain: content,我們已經表明了這一點;我們告訴瀏覽器每篇文章都是獨立的。然後,瀏覽器可以利用這些資訊來決定如何渲染每個 <article> 的內容。例如,它可能不會渲染可視區域之外的文章。

當在頁面末尾附加更多文章時,瀏覽器不需要重新計算佈局或重繪之前的內容;它也不需要觸及隔離元素子樹之外的任何區域。但是,如果盒模型屬性是相互依賴的,瀏覽器將需要重新計算佈局和重繪。例如,如果 <article> 的樣式使其大小依賴於其內容(例如,使用 height: auto),那麼瀏覽器將需要考慮其大小的變化。

關鍵概念和術語

contain 的值

有四種類型的隔離:佈局(layout)、繪製(paint)、尺寸(size)和樣式(style)。使用 contain 屬性,透過包含這些型別的任意組合,來指定要應用於元素的隔離型別。

佈局隔離

css
article {
  contain: layout;
}

佈局通常作用於整個文件,這意味著如果移動一個元素,整個文件都需要被當作任何地方都可能發生移動來處理。透過使用 contain: layout,你可以告訴瀏覽器它只需要檢查這個元素——元素內部的一切都作用於該元素,不影響頁面的其餘部分,並且該隔離盒會建立一個獨立的格式化上下文

此外

  • float 佈局將在指定元素內部獨立執行。
  • 外邊距不會跨越佈局隔離邊界進行摺疊。
  • 佈局容器是其 absolute 定位和 fixed 定位後代的包含塊
  • 該隔離盒會建立一個堆疊上下文,因此可以使用 z-index

注意:當使用 container-typecontainer-name 屬性時,containstylelayout 值會自動應用。

繪製隔離

css
article {
  contain: paint;
}

繪製隔離本質上是將盒子裁剪到主盒子的內邊距邊緣。不會有可見的溢位。與layout 隔離相同的附加說明也適用於 paint 隔離(見上文)。

另一個優點是,如果應用了隔離的元素在螢幕外,瀏覽器就不需要繪製它的子元素——因為它們完全被該盒子包含,所以它們也都在螢幕外。

尺寸隔離

css
article {
  contain: size;
}

單獨使用尺寸隔離在效能最佳化方面作用不大。然而,尺寸隔離意味著應用了尺寸隔離的元素的子元素大小不能影響元素本身的大小——其大小的計算就好像它沒有子元素一樣。

如果你在元素上設定了 contain: size,你需要使用 contain-intrinsic-size 或者其普通屬性 contain-intrinsic-widthcontain-intrinsic-height 來指定該元素的大小。如果沒有設定尺寸,在大多數情況下該元素可能會變成零尺寸。

css
article {
  contain: size;
  contain-intrinsic-size: 100vw auto none;
}

樣式隔離

css
article {
  contain: style;
}

儘管名為樣式隔離,但它並不提供像 Shadow DOM@scope 那樣的作用域樣式。style 值的主要用途是防止在一個元素中更改 CSS 計數器,從而影響到樹的其餘部分的情況。

使用 contain: style 可以確保 counter-incrementcounter-set 屬性建立的新計數器僅作用於該子樹。

你可以透過包含多個以空格分隔的值來指定多種隔離型別,例如 contain: layout paint,或者使用兩個特殊值之一。

特殊值

contain 有兩個特殊值,它們是前三種或所有四種隔離型別的簡寫。

  • content
  • strict

我們在上面的例子中遇到了第一個。使用 contain: content 會開啟 layoutpaintstyle 隔離。由於它省略了 size,所以這是一個可以廣泛應用的安全值。

contain: strict 宣告的行為與 contain: size layout paint style 宣告(包含四個以空格分隔的值)相同,提供了最強的隔離。使用它風險更大,因為它應用了 size 隔離;存在盒子因依賴其子元素大小而最終變為零尺寸的風險。

為了消除這種風險,在使用 strict 時務必設定尺寸。

css
article {
  contain: strict;
  contain-intrinsic-size: 80vw auto none;
}

以上程式碼與以下程式碼相同:

css
article {
  contain: size layout paint style;
  contain-intrinsic-size: 80vw auto none;
}

content-visibility

當你有大量內容可以從強隔離中受益,並且這些內容經常在螢幕外時——例如,如果你所有的部落格文章都可以在部落格主頁上作為無限滾動的部落格檢視——可以使用 content-visibility: auto 來一次性應用所有隔離。

content-visibility 屬性控制一個元素是否渲染其內容,並強制應用一組強隔離,允許使用者代理潛在地省略大量的佈局和渲染工作,直到需要時才進行。它使得使用者代理可以跳過一個元素的渲染工作(包括佈局和繪製),直到需要時才進行——這使得初始頁面載入速度快得多。

其可能的值有:

  • visible:預設行為——元素的內容會像往常一樣進行佈局和渲染。
  • hidden:元素會跳過其內容。被跳過的內容將無法被使用者代理的功能訪問,例如頁內查詢、Tab 鍵順序導航等,也無法被選中或聚焦。
  • auto:元素開啟佈局隔離、樣式隔離和繪製隔離,就像設定了 contain: content 一樣。如果該元素與使用者無關,它也會跳過其內容。與 hidden 不同,被跳過的內容仍然可用於使用者互動,保持可聚焦、可選擇、在正常的 Tab 鍵順序中,並且可用於內容內搜尋。

與使用者相關

使用者代理有一個概念,即內容與使用者相關。如果滿足以下任一條件,元素就變得“與使用者相關”:

  • 元素出現在視口中,或出現在使用者代理定義的視口周圍的邊距內(視口尺寸的 50%,以便為元素可見性變化時給應用準備時間)。
  • 元素或其內容獲得焦點。
  • 元素或其內容被選中,例如透過用滑鼠游標拖動文字或透過其他高亮操作。
  • 元素或其內容被放置在頂層中。

當設定 content-visibility: auto,並且瀏覽器確定內容與使用者相關時,瀏覽器將渲染該內容。

跳過其內容

當你在一個元素上設定 content-visibility: hidden 時,你是在告訴瀏覽器它與使用者無關,因此其內容應該被跳過並且不被渲染。這有助於提高效能。

當一個元素上設定了 content-visibility: auto,並且瀏覽器確定其內容與使用者相關時,瀏覽器也會跳過該元素的內容。

當一個元素跳過其內容時:

  • 它會開啟佈局、樣式、繪製和尺寸隔離。
  • 其內容不會被繪製,就好像在其上設定了 visibility: hidden 一樣。
  • 其內容不會接收指標事件,就好像在其上設定了 pointer-events: none 一樣。

這在上述兩種情況下都會發生,但是對於 content-visibility: auto,內容可以被搜尋、接收焦點,並以其他方式從不相關變為相關。而對於 content-visibility: hidden 則不是這樣。

注意:要為從 content-visibility: hidden 到可見值的過渡新增動畫,你需要設定 transition-behavior: allow-discrete@starting-style 樣式。更多資訊請參閱displaycontent-visibility 的過渡

另見