DOM サイズの大きいことがインタラクティビティに及ぼす影響と対処方法

DOM サイズが大きいほど、インタラクティビティに与える影響は大きくなります。このガイドでは、その理由と対処方法について説明します。

ウェブページを作成するときに、そのページにドキュメント オブジェクト モデル(DOM)が存在することになります。DOM はページの HTML 構造を表し、JavaScript と CSS がページの構造とコンテンツにアクセスできるようにします。

ただし、DOM のサイズは、ブラウザがページを迅速かつ効率的にレンダリングする能力に影響します。一般的に、DOM が大きいほど、最初にページをレンダリングし、その後ページ ライフサイクルの後半でレンダリングを更新するコストが高くなります。

これは、DOM を変更または更新する操作によって、ページの迅速な応答能力に影響する高負荷のレイアウト処理がトリガーされる、非常に大きな DOM を持つページで問題になります。レイアウトに時間がかかると、ページのInteraction to Next Paint(INP)に影響する可能性があります。ページがユーザー操作に迅速に応答するには、DOM サイズを必要最小限に抑えることが重要です。

ページの DOM が大きすぎる場合

Lighthouse によると、1,400 ノードを超えると、ページの DOM サイズが大きすぎます。Lighthouse では、ページの DOM が 800 ノードを超えると警告が表示されます。たとえば、次の HTML があるとします。

<ul>
  <li>List item one.</li>
  <li>List item two.</li>
  <li>List item three.</li>
</ul>

上記のコードには、4 つの DOM 要素(<ul> 要素とその 3 つの <li> 子要素)があります。ウェブページには、これよりはるかに多くのノードがあるはずです。そのため、DOM サイズを抑えるためにできることを理解し、ページの DOM を可能な限り小さくした後でレンダリング作業を最適化するための他の戦略も理解することが重要です。

DOM が大きい場合、ページのパフォーマンスにどのような影響がありますか?

大きな DOM は、次のような方法でページのパフォーマンスに影響します。

  1. ページの最初のレンダリング中。CSS がページに適用されると、DOM に似た構造である CSS オブジェクト モデル(CSSOM)が作成されます。CSS セレクタの特定性が高くなると、CSSOM は複雑になり、ウェブページを画面に描画するために必要なレイアウト、スタイル設定、合成、ペイントの実行に時間がかかります。この追加作業により、ページの読み込みの早い段階で発生するインタラクションのレイテンシが増加します。
  2. 要素の挿入や削除、DOM のコンテンツやスタイルの変更によって DOM が変更されると、その更新をレンダリングするために必要な作業により、レイアウト、スタイル設定、合成、ペイントの作業に非常に高いコストが発生する可能性があります。ページの最初のレンダリングと同様に、操作の結果として HTML 要素が DOM に挿入されたときは、CSS セレクタの特異性が高まることで、レンダリング作業が増加する可能性があります。
  3. JavaScript が DOM をクエリすると、DOM 要素への参照がメモリに保存されます。たとえば、document.querySelectorAll を呼び出してページ上のすべての <div> 要素を選択すると、結果として大量の DOM 要素が返された場合、メモリコストが大幅に増加する可能性があります。
Chrome DevTools のパフォーマンス パネルで、レンダリング作業の過剰によって長時間のタスクが発生している様子のスクリーンショット。長いタスクの呼び出しスタックには、ページスタイルの再計算とプリペイントにかなりの時間が費やされていることが示されています。
Chrome DevTools のパフォーマンス プロファイラに表示される長いタスク。表示されている長いタスクは、JavaScript で DOM 要素を大きな DOM に挿入することによって生じています。

これらはすべてインタラクティビティに影響しますが、上記のリストで 2 番目の項目が特に重要です。インタラクションによって DOM が変更されると、多くの処理が開始され、ページの INP が低下する可能性があります。

DOM サイズの測定方法

DOM のサイズはいくつかの方法で測定できます。最初の方法は Lighthouse を使用します。監査を実行すると、現在のページの DOM に関する統計情報が [診断] の見出しの [過大な DOM サイズの回避] 監査に表示されます。このセクションでは、DOM 要素の合計数、最も子要素が多い DOM 要素、および最も深い DOM 要素を確認できます。

より簡単な方法として、あらゆる主要なブラウザでデベロッパー ツールの JavaScript コンソールを使用する方法があります。DOM 内の HTML 要素の合計数を取得するには、ページの読み込み後にコンソールで次のコードを使用します。

document.querySelectorAll('*').length;

DOM サイズの更新をリアルタイムで確認するには、パフォーマンス モニタリング ツールを使用することもできます。このツールを使用すると、レイアウトとスタイル設定のオペレーション(およびその他のパフォーマンス関連の要素)を現在の DOM サイズと関連付けることができます。

Chrome DevTools のパフォーマンス モニターのスクリーンショット。左の図は、ページ パフォーマンスのさまざまな側面をページのライフサイクル期間中に継続的にモニタリングできます。このスクリーンショットでは、DOM ノードの数、1 秒あたりのレイアウト数、セクションごとのスタイル再計算がアクティブにモニタリングされています。
Chrome DevTools のパフォーマンス モニター。このビューでは、ページの現在の DOM ノード数、1 秒あたりのレイアウト オペレーション数、スタイルの再計算数がグラフに表示されます。

DOM のサイズが Lighthouse の DOM サイズの警告しきい値に近づいている場合、または完全に失敗した場合、次のステップとして、DOM のサイズを縮小して、ユーザーの操作に対するページの機能を改善し、ウェブサイトの INP を改善する方法を検討します。

インタラクションの影響を受ける DOM 要素の数を測定する方法

ラボでページの DOM のサイズに関連している可能性がある遅いインタラクションをプロファイリングしている場合は、プロファイラで [Recalculate Style] というラベルの付いたアクティビティを選択し、下部パネルのコンテキスト データを確認することで、影響を受けた DOM 要素の数を把握できます。

Chrome DevTools のパフォーマンス パネルで、選択されたスタイル再計算アクティビティのスクリーンショット。上部のインタラクション トラックにはクリック インタラクションが表示され、作業の大部分はスタイルの再計算と事前ペイント処理に費やされています。下部のパネルには、選択したアクティビティの詳細が表示されます。この例では、2,547 個の DOM 要素が影響を受けています。
スタイルの再計算の結果として DOM 内で影響を受ける要素の数を観察します。インタラクション トラックのインタラクションの網掛け部分は、インタラクションの所要時間が 200 ミリ秒を超えた部分を表します。これは、INP の「良好」な基準です。

上のスクリーンショットで、作業のスタイル再計算を選択すると、影響を受ける要素の数が表示されます。上のスクリーンショットは、DOM 要素が多いページでのレンダリング作業に対する DOM サイズの影響の極端なケースを示していますが、この診断情報は、操作に対する次のフレームの描画にかかる時間の制限要因が DOM のサイズになっているかどうかを判断するのに役立ちます。

DOM サイズを小さくするにはどうすればよいですか?

ウェブサイトの HTML を監査して不要なマークアップがないかどうかを調べるだけでなく、DOM のサイズを小さくする主な方法は DOM の深さを減らすことです。DOM が不必要に深くなっている可能性があることを示すシグナルとして、ブラウザのデベロッパー ツールの [要素] タブに次のようなマークアップが表示されていることがあります。

<div>
  <div>
    <div>
      <div>
        <!-- Contents -->
      </div>
    </div>
  </div>
</div>

このようなパターンが見つかった場合は、DOM 構造をフラット化して単純化できます。そうすることで、DOM 要素の数が減り、ページのスタイルをシンプルにできます。

DOM の深さは、使用しているフレームワークの問題である可能性もあります。特に、コンポーネント ベースのフレームワーク(JSX に依存するフレームワークなど)では、親コンテナに複数のコンポーネントをネストする必要があります。

ただし、多くのフレームワークでは、フラグメントと呼ばれるものを使用することで、コンポーネントのネストを回避できます。フラグメントを機能として提供するコンポーネント ベースのフレームワークとしては、次のようなものがあります(これらに限定されません)。

任意のフレームワークでフラグメントを使用することで、DOM の深さを軽減できます。DOM 構造のフラット化がスタイル設定に与える影響に懸念がある場合は、Flexboxgrid など、より新しい(そして高速な)レイアウト モードを使用すると効果的な場合もあります。

検討すべきその他の戦略

苦労して DOM ツリーをフラット化し、不要な HTML 要素を削除して DOM をできる限り小さくする場合でも、DOM はかなり大きくなる可能性があり、ユーザーの操作に応じて変化するため、レンダリング作業が多くなってしまうことがあります。このような状況に直面した場合は、レンダリング作業を制限するために検討できる他の戦略もあります。

追加的なアプローチを検討する

初回レンダリング時には、ページの大部分がユーザーに表示されない場合があります。これは、起動時に DOM の該当部分を省略して HTML を遅延読み込みできる機会となり、ユーザーがページの初めは隠していた部分を必要とする部分をユーザーが操作したときに追加できる可能性があります。

このアプローチは、初期読み込み時だけでなく、その後も役立ちます。最初のページ読み込みでは、事前にレンダリングする作業が少なくなります。つまり、最初の HTML ペイロードが軽くなり、レンダリングが速くなります。これにより、その重要な時期のインタラクションは、メインスレッドのアテンションに対する競合が少なくて、実行される機会が増えます。

ページの多くの部分が読み込み時に最初は非表示になっている場合は、再レンダリング作業をトリガーする他の操作も高速化できます。ただし、他のインタラクションによって DOM がさらに増えていくと、ページのライフサイクルを通じて DOM が拡大するにつれてレンダリング作業が増加します。

徐々に DOM に追加するのは難しく、それにもトレードオフがあります。この方法では、ユーザーの操作に応じてページに追加する HTML に入力するデータを取得するために、ネットワーク リクエストを行うことになります。実行中のネットワーク リクエストは INP にはカウントされませんが、認識されるレイテンシが増加する可能性があります。可能であれば、データを取得中であることを示す読み込みスピナーなどのインジケーターを表示して、ユーザーが何かが発生していることを認識できるようにします。

CSS セレクタの複雑さを制限する

ブラウザは CSS のセレクタを解析する際、DOM ツリーを走査して、そのセレクタが現在のレイアウトにどのように適用されるか、また適用されているかを把握する必要があります。これらのセレクタが複雑になるほど、ブラウザはページの最初のレンダリングを実行するだけでなく、操作の結果としてページが変更された場合にスタイルの再計算とレイアウト作業も増加します。

content-visibility プロパティを使用する

CSS には content-visibility プロパティが用意されています。このプロパティを使用すると、画面外の DOM 要素をゆっくりとレンダリングできます。要素がビューポートに近づくと、オンデマンドでレンダリングされます。content-visibility のメリットは、最初のページのレンダリングでレンダリング作業を大幅に削減できるだけでなく、ユーザー操作の結果としてページ DOM が変更されたときに、画面外要素のレンダリング作業をスキップできることです。

詳しくは、

まとめ

ウェブサイトの INP を最適化する良い方法として、DOM のサイズを厳密に必要なものに限定する方法があります。これにより、DOM が更新されたときにブラウザがレイアウトとレンダリングの処理を行うのにかかる時間を短縮できます。DOM サイズを大幅に削減できない場合でも、CSS 包含や content-visibility CSS プロパティなど、レンダリング作業を DOM サブツリーに分離するための手法があります。

どのような方法であれ、レンダリング作業を最小限に抑えられる環境を作り、操作に対するページのレンダリング作業を減らすことで、ユーザーが操作した際のウェブサイトの応答性が向上します。つまり、ウェブサイトの INP が低下し、ユーザー エクスペリエンスが向上します。