當你打開一個長頁面——像是新聞資訊流、後台表格、或文件文章——瀏覽器會對上面的每個元素做版面配置與繪製。500 列表格列全部都會處理。300 張資訊流卡片全部都會處理。還有這次瀏覽中你永遠不會捲到的那些章節,也一樣。接著只要有任何變動,它又會再做一次;視窗大小改變時,也會再做一次。這些工作都是真實存在的,它會耗費時間,而且大部分現在對使用者來說都是看不見的。
content-visibility: auto 是一個 CSS 屬性,意思是:在元素接近視窗之前,先跳過它的渲染。元素仍然留在 DOM 中,無障礙樹也會包含它,頁面內搜尋也找得到它——但在使用者捲動到足夠接近之前,瀏覽器不會把版面配置與繪製的預算花在它身上。
瀏覽器的渲染不只是把像素放到畫面上而已。主執行緒必須先為每個元素計算樣式,再做版面配置來計算大小與位置,最後才進行繪製。每一步都依賴前一步,而瀏覽器必須先處理完所有元素,才能開始顯示任何內容。
瀏覽器在做版面配置時並不知道哪些元素是可見的。它會先計算所有元素的位置,然後在合成階段再裁切到視窗範圍——但到那時,昂貴的工作早就已經完成了。對一個有 500 列的頁面來說,這表示在使用者看到任何東西之前,就有 500 個元素把完整的流程都跑過一遍。
content-visibility: auto.feed-card {
content-visibility: auto;
contain-intrinsic-size: auto 300px;
}
這就是全部的實作。瀏覽器會把每個 .feed-card 納入一個 containment(包含)情境。對於任何不接近視窗的卡片,它會完全跳過版面配置與繪製。當使用者往它捲動時,瀏覽器會在它進入視野前先把它渲染出來。當使用者再把它捲過去之後,瀏覽器可以丟棄其繪製結果並回收記憶體。
在真實世界的長頁面上,這種改善不是小幅增量,而是很明顯的跳躍。Google 測量到,在新聞頁面的文章卡片套用 content-visibility 後,渲染時間快了 7 倍。對於內容量大、裝置又慢的頁面來說,這個差異就是「已載入」和「還在載入」之間的分界。
contain-intrinsic-size 不是可選項當瀏覽器跳過畫面外元素的版面配置時,它不知道那個元素有多高。如果沒有尺寸,元素就會塌縮成 0 高度。500 個塌縮元素的頁面會有錯誤的捲動條,而且隨著元素逐一渲染進來,捲動位置會到處跳動——這就是典型的「為什麼頁面一直在移動」體驗。
contain-intrinsic-size 會替被跳過的元素提供一個預設尺寸:
contain-intrinsic-size: auto 300px;
這裡的 auto 很重要。當瀏覽器第一次渲染某個元素後,它會記住實際高度,之後在未顯示於畫面時的循環中就會使用這個高度。300px 則是初始估計值——給還沒看過的元素用的占位尺寸。它不需要完全精準,只要夠接近,就能在初次渲染時維持捲動條穩定。
如果沒有 auto,瀏覽器就會永遠使用提示值,而且不會根據實際版面配置更新,這會讓可變高度元素永久失效。
<!-- playground:start -->
500 張卡片、即時計時器,以及左右對照比較——兩個按鈕都跑看看,觀察數字變化。
<!-- playground:end -->
常見的疑慮是:如果瀏覽器跳過渲染,無障礙、鍵盤操作、或頁面內搜尋會不會壞掉?
不會。瀏覽器無論是否使用 content-visibility,都會維持 DOM 與無障礙樹。Ctrl+F / Cmd+F 可以找到被跳過元素中的文字,並在過程中把它們捲動進視野,觸發該元素的渲染。按 Tab 移動到被跳過容器內的可聚焦元素時,會立即觸發該容器的渲染。螢幕閱讀器是遍歷 DOM,而不是渲染樹——畫面外元素仍然可以存取。
content-visibility: auto 唯一跳過的是視覺渲染工作:版面配置、繪製與合成。元素是畫面外,不是不存在。
這個屬性最適合有清楚邊界、而且會重複出現的元素:資訊流卡片、表格列、留言串、清單專案、文件章節。如果某個元素是許多類似元素中垂直堆疊的一個,而且在載入時大多數都在畫面外,那它就是很好的候選物件。
/* 適合的候選項 */
.comment { content-visibility: auto; contain-intrinsic-size: auto 80px; }
.data-row { content-visibility: auto; contain-intrinsic-size: auto 48px; }
.article-section { content-visibility: auto; contain-intrinsic-size: auto 400px; }
不要把它用在需要在載入時回報自身尺寸的元素上——像是黏性頁首、在捲動前就用 getBoundingClientRect() 由 JavaScript 測量的元素,或任何依賴所有子元素必須同時完整完成版面配置的排版。對這些情況來說,瀏覽器的跳過會讓量測結果變成 0,計算也就壞掉了。
content-visibility 是 Baseline 2024:Chrome 85(2020 年 8 月)、Firefox 125(2024 年 4 月)、Safari 18.0(2024 年 9 月)。Safari 在 2024 年底完成支援後,若以現代瀏覽器為基準,就可以放心用於正式環境而不需要 fallback。對較舊的 Safari 來說,這個屬性會被靜默忽略——頁面仍會正常渲染,只是沒有這項最佳化。
<!-- quiz:start -->
覺得有懂了嗎?來做這份 8 題測驗 →
即時回饋、每題提示,以及每個答案的說明——不論對錯。
<!-- quiz:end -->
在你最長的頁面上打開 DevTools,載入時進行一次 Performance 錄製,然後看看主執行緒上的「Layout」和「Paint」專案。如果它們很寬,而且頁面要很久才能變得可互動,那麼把 content-visibility: auto 用在那些重複元素上,往往是目前最快的修正方式——只要一個 CSS 屬性,就能把資料量大頁面的渲染時間砍半。再加上 contain-intrinsic-size: auto <你的估計值> 來保持捲動條準確,這個變更就能直接上正式環境,不需要其他修改。
感謝閱讀!讓我們保持聯繫: