おい、最近のWebアプリ、DOMが数万個に膨れ上がってメインスレッドがカクカクになる現象に悩まされてないか? 「リッチなUIにしたい」「無限スクロールを実装したい」のは分かるが、ブラウザの身になってくれよ。画面の端っこで見えてすらいない孫要素のスタイル計算やレイアウト(Reflow)を、ブラウザは律儀に裏で計算させられているんだ。
そこで今日お前たちに叩き込みたいのが、この「Display Locking API(コンテンツ-visibilityやcontain-intrinsic-sizeの裏側を支える概念)」だ。
現代のブラウザレンダリングの急所を突く、この強力な仕組みの裏側と実践的な使い方を、シニアの俺が徹底的に紐解いてやろう。
—
なぜブラウザは重くなるのか? レンダリングの「見えないコスト」
まずは、ブラウザが裏側で何をやってるかのおさらいだ。お前たちが書いてるHTMLは、大まかに以下のステップで画面にピクセルとして描き出される。
1. HTMLパース & DOMツリー構築
2. CSSOMツリー構築
3. レンダーツリー構築(DOM + CSSOM)
4. レイアウト(Layout / Reflow): 各要素の正確な位置とサイズを計算
5. ペイント(Paint / Rasterize) & 合成(Compositing)
ここで問題になるのは、「画面外(Viewport外)にあろうが、CSSで `display: none` になっていなかろうが、DOMサブツリーが存在する限り、ブラウザはスタイル計算やレイアウトのコストを支払い続ける」という残酷な事実だ。
例えば、10,000件のアイテムを持つECサイトの商品リスト。ユーザーが見ているのは最初の10件だけなのに、ブラウザは残りの9,990件のレイアウトツリーの構築にCPUパワーをドブ食いさせられている。これがメインスレッドをブロックし、スクロールの「カクつき(Jank)」を引き起こす元凶というわけだ。
Display Locking API とは何か?
この絶望的な状況に風穴を開けるために考案されたのが、Display Locking API(およびそれをCSSから手軽に叩けるようにした `content-visibility` プロパティ)だ。
一言で言えば、「特定のDOMサブツリーに対して、ブラウザのレンダリング作業(スタイル計算、レイアウト、ペイント)を一時的にロック(凍結)し、計算をサボらせる仕組み」だ。
ブラウザの内部アーキテクチャ的に言うと、ロックされたサブツリーは「レンダーツリーの一部から切り離された状態」になる。ユーザーの目に見えない、あるいは今すぐ計算する必要がないと判断された領域のライフサイクルを強制的に止めることで、メインスレッドを解放するわけだ。
押さえておくべき主要な概念
- スタイルとレイアウトの分離: ロックされた要素の中身は、ブラウザから見たら「黒い箱」だ。中身がどう変化しようが、親や周囲のレイアウトに影響を与えない(インディペンデントな状態を作る)。
- 必要に応じたアンロック(Activation): ユーザーがスクロールしてその要素が画面に近づいたときや、フォーカスが当たったとき、あるいはスクリプトから強制的に指示されたときに、ブラウザは自動的あるいは手動でロックを解除し、レンダリングを再開する。
—
現場で即効性のある実践コード:`content-visibility` の活用
厳密なJavaScriptのDisplay Locking API(`element.requestUpdate()` など)は、まだブラウザの実験的機能や仕様の変遷が激しい。しかし、それを実務で最も安全かつ強力に使える形にしたのが、CSSの `content-visibility: auto` だ。
後輩の お前たちに、今日からプロダクション環境のリスト表示などで即座に使える決定版のコードを置いておく。しっかり目に焼き付けておけ。
カードタイトル 1
ここに長文のコンテンツが入ります。ブラウザはこの要素が画面内に入るまで、内部のテキストのラップやスタイルの計算を遅延させます。
カードタイトル 2
DOMの数は増えても、メインスレッドが悲鳴を上げることはもうありません。
コードの急所:`contain-intrinsic-size` を忘れるな
さっきのサンプルで、`contain-intrinsic-size: 0 150px;` というプロパティを入れたのにお前たちは気づいたか? ここ、実務ですごく重要だからテストに出すぞ。
`content-visibility: auto` によって画面外の要素がロックされると、ブラウザはその要素の中身を無視するため、「要素の高さが 0px」として扱われる。もし `contain-intrinsic-size` を設定していないと、1,000件のカードがすべて高さ0pxと誤認され、ページ全体のスクロールバーが突如として激しく縮む「レイアウトシフト(CLSの悪化)」を引き起こす。ユーザービリティが最悪になるわけだ。
「大体このくらいの高さになるだろう」という予測値(Intrinsic Size)をあらかじめ教えておくことで、ブラウザはスクロールバーの寸法を正しく維持できる。この配慮ができるかどうかが、プロとアマの分かれ道だ。
—
シニアからの実務上のアドバイスと注意点
Display Lockingの概念(および `content-visibility`)は劇薬だ。適当に当てればいいってもんじゃない。以下のポイントを現場の共通認識にしておいてくれ。
1. 何でもかんでも適用するな
画面内に常に収まる小さなコンポーネントや、極端に数が少ないDOMに対して使っても、逆にブラウザの管理オーバーヘッドが増えて逆効果になる。数が多いリスト、重いウィジェット、複雑なSVGツリーなど、「明らかにメインスレッドを殺しているボトルネック」に対してピンポイントで投入しろ。
2. パフォーマンス計測(DevTools)を怠るな
Chrome DevToolsの「Performance」タブを開き、レイアウト(Layout)やスタイル再計算(Recalculate Style)のミリ秒数がどう変わったか必ずビフォーアフターで計測しろ。「なんとなく速くなった気がする」ではプロの仕事とは言えない。
3. アクセシビリティ(a11y)への配慮
画面外でロックされている要素であっても、スクリーンリーダーなどの補助技術からはアクセス可能であるべきだ。ブラウザの標準仕様では、`content-visibility: auto` の要素が隠れていてもアクセシビリティツリーには保持されるよう配慮されているが、複雑なインタラクションを絡める場合は必ず実機やリーダーで検証する癖をつけろ。
ブラウザの内部挙動を理解し、レンダリングエンジンの気持ちになってコードを書けるようになると、フロントエンド開発は圧倒的に楽しく、そして深くなる。
さあ、お前らのプロジェクトの重いリストを、この知見でサクサクにリファクタリングしてこい!

コメント