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

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

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

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

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

ページの DOM が大きすぎるのはどのような場合ですか?

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

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

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

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

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

  1. ページの最初のレンダリング中。CSS がページに適用されると、DOM に似た構造である CSS オブジェクト モデル(CSSOM)が作成されます。CSS セレクタの特定性が高くなると、CSSOM は複雑になり、ウェブページを画面に描画するために必要なレイアウト、スタイル設定、合成、ペイントの実行に時間がかかります。この追加作業により、ページの読み込みの早い段階で発生するインタラクションのレイテンシが増加します。
  2. 要素の挿入や削除、DOM の内容やスタイルの変更によって DOM が変更されると、その更新をレンダリングするために必要な作業により、レイアウト、スタイル設定、合成、ペイントの作業に非常に高いコストが発生する可能性があります。ページの初期レンダリングの場合と同様に、CSS セレクタの特定度を高めると、インタラクションの結果として HTML 要素が DOM に挿入されるときにレンダリング作業が増える可能性があります。
  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 が不必要に深い可能性があることを示す兆候の 1 つは、ブラウザのデベロッパー ツールの [要素] タブに次のようなマークアップが表示されている場合です。

<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 が低下し、ユーザー エクスペリエンスが向上します。