content-visibility: レンダリング パフォーマンスを向上させる新しい CSS プロパティ

画面外コンテンツのレンダリングをスキップすることで、初期読み込み時間を改善します。

Chromium 85 でリリースされた content-visibility プロパティは、ページ読み込みのパフォーマンスを向上させる新しい CSS プロパティの 1 つです。content-visibility を使用すると、ユーザー エージェントは、必要になるまで、要素のレンダリング作業(レイアウトやペイントなど)をスキップできます。レンダリングがスキップされるため、コンテンツの大部分が画面外にある場合、content-visibility プロパティを使用すると、最初のユーザー読み込みが大幅に高速化されます。また、画面上のコンテンツをより迅速に操作できます。とても便利です。

ネットワークの結果を表す図を示すデモ
この記事のデモでは、チャンクされたコンテンツ領域に content-visibility: auto を適用すると、初期読み込み時のレンダリング パフォーマンスが 7 倍に向上しています。以下で詳しくご説明します。

ブラウザ サポート

対応ブラウザ

  • 85
  • 85
  • 124

ソース

content-visibilityCSS Containment 仕様内のプリミティブに依存します。現在のところ、content-visibility は Chromium 85 でのみサポートされています(Firefox では「プロトタイピングに値する」と考えられます)が、Containment Spec は最新のブラウザでサポートされています。

CSS の封じ込め

CSS コンテインメントの主要な目標は、ページの他の部分から DOM サブツリーを予測可能な方法で分離することで、ウェブ コンテンツのレンダリング パフォーマンスを改善できるようにすることです。

基本的に、デベロッパーはページのどの部分をコンテンツのセットとしてカプセル化するかをブラウザに伝えることができるため、ブラウザはサブツリーの外部の状態を考慮しなくてもコンテンツについて推測できます。コンテンツのどの部分(サブツリー)に分離されたコンテンツが含まれているかを知ることで、ブラウザはページ レンダリングの最適化を判断できます。

CSS コンテインメントには 4 つのタイプがあり、それぞれ contain CSS プロパティに指定できる値で、スペース区切りの値リストにまとめることができます。

  • size: 要素にサイズを含めると、子孫を調べることなく、要素のボックスをレイアウトできるようになります。つまり、必要なのは要素のサイズだけである場合、子孫のレイアウトをスキップできる可能性があります。
  • layout: レイアウト包含とは、子孫がページ上の他のボックスの外部レイアウトに影響しないことを意味します。これにより、他のボックスをレイアウトするだけで、子孫のレイアウトをスキップできる可能性があります。
  • style: スタイルの包含により、子孫以外にも影響を与える可能性があるプロパティ(カウンタなど)がエスケープされないようにします。これにより、他の要素のスタイルを計算するだけの場合は、子孫のスタイル計算をスキップできます。
  • paint: 包含ペイントにより、包含ボックスの子孫が境界外に表示されないことが保証されます。要素を目に見える形ではみ出すことはできません。また、要素が画面外にある場合や表示されない場合、その子孫も表示されません。これにより、要素が画面外にある場合に、子孫のペイントをスキップする可能性があります。

content-visibility でレンダリング処理をスキップする

ブラウザの最適化は適切なセットが指定されている場合にのみ有効になるため、どの包含値を使用するか判断するのが難しい場合があります。値をいろいろ試して、最も効果的なものを確認できます。また、content-visibility という別の CSS プロパティを使用して、必要な包含要素を自動的に適用することもできます。content-visibility を使用すると、デベロッパーが最小限の労力でブラウザで実現できる最大限のパフォーマンス向上を実現できます。

content-visibility プロパティには複数の値を指定できますが、auto を使用すると、すぐにパフォーマンスが向上します。content-visibility: auto が指定された要素は、layoutstylepaint のコンテインメントを取得します。要素が画面外にある場合(ユーザーに関係ない場合、関連する要素はサブツリーにフォーカスまたは選択がある要素です)、size コンテインメントも取得されます(コンテンツのペイントヒットテストが停止します)。

これはどういう意味ですか?つまり、要素が画面外にある場合、その子孫はレンダリングされません。ブラウザは、その内容を考慮せずに要素のサイズを決定し、そこで処理を終了します。要素のサブツリーのスタイルやレイアウトなど、レンダリングの大部分はスキップされます。

要素がビューポートに近づくと、ブラウザは size コンテインメントを追加せず、要素のコンテンツのペイントとヒットテストを開始します。これにより、ユーザーに表示されるタイミングに合わせてレンダリング処理を実行できます。

ユーザー補助に関する注意事項

content-visibility: auto の特長の 1 つは、画面外コンテンツがドキュメント オブジェクト モデル(visibility: hidden とは異なり)で引き続き使用できることです。つまり、読み込みを待ったり、レンダリング パフォーマンスを犠牲にしたりすることなく、ページ上でコンテンツを検索し、そのコンテンツに移動できます。

ただし、その裏側には、display: nonevisibility: hidden などのスタイル対象物を含むランドマーク要素は、画面外でもユーザー補助ツリーに表示されます。これは、ブラウザがビューポートに入るまでこれらのスタイルをレンダリングしないためです。これらがユーザー補助ツリーに表示されず、煩雑になるのを防ぐため、aria-hidden="true" も必ず追加してください。

例: 旅行ブログ

この例では、右側に旅行ブログの基準を設定し、左側のチャンク領域に content-visibility: auto を適用します。その結果、最初のページ読み込み時のレンダリング時間が 232 ミリ秒から 30 ミリ秒に短縮されたことがわかります。

旅行ブログには通常、数枚の画像と説明テキストを含む一連の記事が掲載されています。一般的なブラウザで旅行ブログに移動すると、次のようになります。

  1. ページの一部が、必要なリソースとともにネットワークからダウンロードされます。
  2. ブラウザは、コンテンツがユーザーに表示されているかどうかを考慮せずに、ページのすべてのコンテンツをスタイル設定してレイアウトします。
  3. ページとリソースがすべてダウンロードされるまで、ブラウザはステップ 1 に戻ります。

ステップ 2 では、ブラウザはすべてのコンテンツを処理して、変更された部分がないかどうかを調べます。新しい要素のスタイルとレイアウトに加え、新しい更新の結果としてシフトした要素も更新されます。これがレンダリング処理ですこれには時間がかかります。

旅行ブログのスクリーンショット。
旅行ブログの例。Codepen でのデモをご覧ください。

次に、ブログ内の各記事に content-visibility: auto を設定するとどうなるかを考えてみましょう。一般的なループは同じです。ブラウザがページのチャンクをダウンロードしてレンダリングします。ただし、ステップ 2 で実行される処理の量が異なります。

content-visibility は、現在ユーザーに表示されている(画面上にある)すべてのコンテンツのスタイルとレイアウトを設定します。ただし、完全に画面外にあるストーリーを処理する場合、ブラウザはレンダリング作業をスキップし、要素ボックス自体のスタイルとレイアウトのみを設定します。

このページの読み込みパフォーマンスは、画面上にストーリー全体が表示され、画面外のストーリーがそれぞれ空白のボックスで表示されているかのように行われます。これによりパフォーマンスが大幅に向上し、読み込みのレンダリング コストと比べて 50% 以上削減が予想されます。この例では、レンダリング時間が 232 ミリ秒から 30 ミリ秒に短縮されています。パフォーマンスが 7 倍に向上します。

これらのメリットを享受するために必要な作業は何ですか?まず、コンテンツをセクションに分割します。

CSS クラスを使用してコンテンツをセクションに分割したアノテーション付きスクリーンショット。
content-visibility: auto を受け取るために、story クラスが適用されたセクションにコンテンツをチャンクする例。Codepen でのデモをご覧ください。

次に、以下のスタイルルールをセクションに適用します。

.story {
  content-visibility: auto;
  contain-intrinsic-size: 1000px; /* Explained in the next section. */
}

contain-intrinsic-size を使用して要素の自然なサイズを指定する

content-visibility の潜在的なメリットを実現するには、ブラウザでサイズの包含を適用して、コンテンツのレンダリング結果が要素のサイズに影響しないようにする必要があります。つまり、要素が空であるかのように配置されます。通常のブロック レイアウトで要素の高さが指定されていない場合、高さは 0 になります。

スクロールバーのサイズが移動し、各記事の高さが 0 以外であることに依存するため、この方法は理想的ではない場合があります。

幸いなことに、CSS には別のプロパティ contain-intrinsic-size が用意されています。このプロパティは、要素がサイズ包含の影響を受ける場合に、要素の自然なサイズを実質的に指定します。この例では、セクションの高さと幅の推定値として 1000px に設定しています。

つまり、「intrinsic-size」ディメンションの子が 1 つあるかのようにレイアウトされるため、サイズ設定されていない div がスペースを占有します。contain-intrinsic-size は、レンダリングされたコンテンツの代わりにプレースホルダ サイズとして機能します。

Chromium 98 以降では、contain-intrinsic-size に新しい auto キーワードがあります。指定すると、ブラウザは最後にレンダリングされたサイズを記憶し、デベロッパーが指定するプレースホルダ サイズの代わりにそのサイズを使用します。たとえば、contain-intrinsic-size: auto 300px を指定した場合、要素は各寸法で 300px 固有のサイズ設定で開始しますが、要素のコンテンツがレンダリングされると、レンダリングされた固有のサイズを保持します。その後のレンダリング サイズの変更も保存されます。つまり、content-visibility: auto が適用された要素をスクロールした後、画面外にスクロールすると、自動的に最適な幅と高さが保持され、プレースホルダのサイズに戻されません。この機能は、ユーザーがページを探索するにつれて、時間の経過とともにサイズ予測を自動的に改善する無限スクローラーで特に役立ちます。

content-visibility: hidden でコンテンツを非表示にする

キャッシュされたレンダリング状態のメリットを活用しながら、画面上に表示されているかどうかにかかわらず、コンテンツをレンダリングしないままにしたい場合はどうすればよいでしょうか。「content-visibility: hidden」と入力します。

content-visibility: hidden プロパティには、content-visibility: auto が画面外で行う場合と同じ、レンダリングされていないコンテンツとキャッシュされたレンダリング状態のメリットがすべて備わっています。ただし、auto とは異なり、画面上のレンダリングは自動的に開始されません。

これにより、より詳細な制御が可能になり、要素のコンテンツを非表示にして、後ですばやく再表示できるようになります。

これを、要素のコンテンツを非表示にする一般的な方法と比較します。

  • display: none: 要素を非表示にし、レンダリング状態を破棄します。つまり、要素の再表示は、同じ内容の新しい要素をレンダリングするのと同じコストになります。
  • visibility: hidden: 要素を非表示にして、レンダリング状態を維持します。これによってドキュメントから要素が完全に削除されるわけではありません。その要素(およびサブツリー)は引き続きページの幾何学的スペースを占有し、クリックできるためです。また、非表示の場合でも、必要に応じてレンダリング状態を更新します。

一方、content-visibility: hidden は、レンダリング状態を保持したまま要素を非表示にします。そのため、変更が必要な場合は、要素が再度表示されたとき(content-visibility: hidden プロパティが削除されたとき)にのみ変更が行われます。

content-visibility: hidden の優れたユースケースには、高度な仮想スクローラーを実装したり、レイアウトを測定したりする場合が挙げられます。また、シングルページ アプリケーション(SPA)にも適しています。非アクティブなアプリビューは、content-visibility: hidden が適用された DOM 内に残して表示を防ぎますが、キャッシュされた状態は維持されます。これにより、ビューが再びアクティブになったときに、すばやくレンダリングできるようになります。

Interaction to Next Paint(INP)への影響

INP は、ページがユーザー入力に確実に応答できるかどうかを評価する指標です。レンダリング処理など、メインスレッドで大量の処理が発生すると、応答性に影響が出ることがあります。

特定のページのレンダリング作業を削減できれば、メインスレッドがユーザー入力により速く応答する機会が与えられます。これにはレンダリング作業が含まれます。また、必要に応じて content-visiblity CSS プロパティを使用すると、レンダリング作業(特にレンダリングとレイアウト作業のほとんどが完了している起動時)を減らすことができます。

レンダリング処理の削減は、INP に直接影響します。content-visibility プロパティを適切に使用しているページをユーザーが操作して画面外要素のレイアウトとレンダリングを遅らせようとすると、メインスレッドがユーザーに表示される重大な作業に応答する機会が与えられます。これにより、場合によってはページの INP が向上する可能性があります。

おわりに

content-visibility と CSS Containment Spec により、CSS ファイルのパフォーマンスが大幅に向上します。これらのプロパティの詳細については、以下をご覧ください。