天天看點

CSS 新特性 contain,控制頁面的重繪與重排

在介紹新的 CSS 屬性 ​

​contain​

​ 之前,讀者首先需要了解什麼是頁面的重繪與重排。

之前已經描述過很多次了,還不太了解的可以先看看這個​​提高 CSS 動畫性能的正确姿勢​​。

OK,下面進入本文正題,

​contain​

​ 為何?

​contain​

​ 屬性允許我們指定特定的 DOM 元素和它的子元素,讓它們能夠獨立于整個 DOM 樹結構之外。目的是能夠讓浏覽器有能力隻對部分元素進行重繪、重排,而不必每次都針對整個頁面。

The contain property allows an author to indicate that an element and its contents are, as much as possible, independent of the rest of the document tree. This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited area of the DOM and not the entire page.

​contain​

​ 文法

看看它的文法:

{
  /* No layout containment. */
  contain: none;
  /* Turn on size containment for an element. */
  contain: size;
  /* Turn on layout containment for an element. */
  contain: layout;
  /* Turn on style containment for an element. */
  contain: style;
  /* Turn on paint containment for an element. */
  contain: paint;

  /* Turn on containment for layout, paint, and size. */
  contain: strict;
  /* Turn on containment for layout, and paint. */
  contain: content;
}      

除去 ​

​none​

​,取值還有 6 個,我們一個一個來看看。

contain: size

contain: size: 設定了 ​

​contain: size​

​ 的元素的渲染不會受到其子元素内容的影響。

The value turns on size containment for the element. This ensures that the containing box can be laid out without needing to examine its descendants.

我開始看到這個定義也是一頭霧水,光看定義很難明白到底是什麼意思。還需實踐一番:

假設我們有如下簡單結構:

<div class="container">
   
</div>
.container {
    width: 300px;
    padding: 10px;
    border: 1px solid red;
}

p {
    border: 1px solid #333;
    margin: 5px;
    font-size: 14px;
}      

并且,借助 jQuery 實作每次點選容器添加一個 ​

​<p>Coco</p>​

​ 結構:

$('.container').on('click', e => {
    $('.container').append('<p>Coco</p>')
})      

那麼會得到如下結果:

​​

CSS 新特性 contain,控制頁面的重繪與重排

​​

可以看到,容器 ​

​.container​

​ 的高度是會随着元素的增加而增加的,這是正常的現象。

此刻,我們給容器 ​

​.container​

​​ 添加一個 ​

​contain: size​

​​,也就會出現上述說的:設定了 ​

​contain: size​

​ 的元素的渲染不會受到其子元素内容的影響。

.container {
    width: 300px;
    padding: 10px;
    border: 1px solid red;
+   contain: size
}      

再看看會發生什麼:

​​

CSS 新特性 contain,控制頁面的重繪與重排

​​

正常而言,父元素的高度會因為子元素的增多而被撐高,而現在,子元素的變化不再影響父元素的樣式布局,這就是 ​

​contain: size​

​ 的作用。

contain: style

接下來再說說 ​

​contain: style​

​​、​

​contain: layout​

​​ 、​

​contain: paint​

​。先看看 contain: style。

截止至本文書寫的過程中,​

​contain: style​

​ 暫時被移除了。

​​CSS Containment Module Level 1​​: Drop the at-risk “style containment” feature from this specification, move it Level 2。

嗯,官方說辭是因為存在某些風險,暫時被移除,可能在規範的第二版會重新定義吧,那這個屬性也暫且放一放。

contain: paint

contain: paint:設定了 ​

​contain: paint​

​ 的元素即是設定了布局限制,也就是說告知 User Agent,此元素的子元素不會在此元素的邊界之外被展示,是以,如果元素不在螢幕上或以其他方式設定為不可見,則還可以保證其後代不可見不被渲染。

This value turns on paint containment for the element. This ensures that the descendants of the containing box don’t display outside its bounds, so if an element is off-screen or otherwise not visible, its descendants are also guaranteed to be not visible.

這個稍微好了解一點,先來看第一個特性:

設定了 ​

​contain: paint​

​ 的元素的子元素不會在此元素的邊界之外被展示

  • 設定了​

    ​contain: paint​

    ​ 的元素的子元素不會在此元素的邊界之外被展示

這個特點有點類似與 ​

​overflow: hidden​

​,也就是明确告知使用者代理,子元素的内容不會超出元素的邊界,是以超出部分無需渲染。

簡單示例,假設元素結構如下:

<div class="container">
    <p>Coco</p>
</div>
.container {
    contain: paint;
    border: 1px solid red;
}

p{
    left: -100px;
}      

我們來看看,設定了 ​

​contain: paint​

​ 與沒設定時會發生什麼:

​​

CSS 新特性 contain,控制頁面的重繪與重排

​​

​​CodePen Demo -- contain: paint Demo​​

設定了 ​

​contain: paint​

​ 的元素在螢幕之外時不會渲染繪制

通過使用 ​

​contain: paint​

​, 如果元素處于螢幕外,那麼使用者代理就會忽略渲染這些元素,進而能更快的渲染其它内容。

contain: layout

contain: layout:設定了 ​

​contain: layout​

​ 的元素即是設定了布局限制,也就是說告知 User Agent,此元素内部的樣式變化不會引起元素外部的樣式變化,反之亦然。

This value turns on layout containment for the element. This ensures that the containing box is totally opaque for layout purposes; nothing outside can affect its internal layout, and vice versa.

啟用 ​

​contain: layout​

​ 可以潛在地将每一幀需要渲染的元素數量減少到少數,而不是重新渲染整個文檔,進而為浏覽器節省了大量不必要的工作,并顯着提高了性能。

使用 ​

​contain:layout​

​,開發人員可以指定對該元素任何後代的任何更改都不會影響任何外部元素的布局,反之亦然。

是以,浏覽器僅計算内部元素的位置(如果對其進行了修改),而其餘DOM保持不變。是以,這意味着幀渲染管道中的布局過程将加快。

存在的問題

描述很美好,但是在實際 Demo 測試的過程中(截止至2021/04/27,Chrome 90.0.4430.85),僅僅單獨使用 ​

​contain:layout​

​ 并沒有驗證得到上述那麼美好的結果。

設定了 ​

​contain: layout​

​​ 的指定元素,改元素的任何後代的任何更改還是會影響任何外部元素的布局,點選紅框會增加一條 ​

​<p>Coco<p>​

​​ 元素插入到 ​

​container​

​ 中:

簡單的代碼如下:

<div class="container">
    <p>Coco</p>
    ...
</div>
<div class="g-test"></div>
html,
body {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 10px;
}

.container {
    width: 150px;
    padding: 10px;
    contain: layout;
    border: 1px solid red;
}

.g-test {
    width: 150px;
    height: 150px;
    border: 1px solid green;
}      

​​​​

​​CodePen Demo -- contain: layout Demo​​

目前看來,​

​contain: layout​

​​ 的實際作用不那麼明顯,更多的關于它的用法,你可以再看看這篇文章:​​CSS-tricks - contain​​

contain: strict | contain: content

這兩個屬性稍微有點特殊,效果是上述介紹的幾個屬性的聚合效果:

  • ​contain: strict​

    ​​:同時開啟 layout、style、paint 以及 size 的功能,它相當于​

    ​contain: size layout paint​

  • ​contain: content​

    ​​:同時開啟 layout、style 以及 paint 的功能,它相當于​

    ​contain: layout paint​

是以,這裡也提一下,contain 屬性是可以同時定義幾個的。

Can i Use -- CSS Contain

截止至 2021-04-27,Can i Use 上的 CSS Contain 相容性,已經可以開始使用起來:

​​

CSS 新特性 contain,控制頁面的重繪與重排

​​

參考文獻

  • ​​CSS Containment Module Level 1​​
  • ​​CSS containment​​
  • ​​CSS Containment in Chrome 52​​