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

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

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

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

DOM が非常に大きいページでは、DOM を変更または更新する操作によって、ページの迅速な応答能力に影響するコストの高いレイアウト作業がトリガーされると、問題が発生します。レイアウトの負荷が高いと、ページの Interaction to Next Paint(INP)に影響する可能性があります。ユーザー インタラクションにページが迅速に応答するようにするには、DOM のサイズを必要最小限に抑えることが重要です。

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

Lighthouse によると、ページの DOM サイズが 1,400 ノードを超えると、過剰になります。ページの DOM が 800 ノードを超えると、Lighthouse は警告をスローし始めます。次の 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 が不必要に深い可能性があることを示す兆候の一つは、ブラウザのデベロッパー ツールの [要素] タブに次のようなマークアップが表示されている場合です。

<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 が変更されたときに、オフスクリーン要素のレンダリング作業をスキップすることにもあります。

まとめ

DOM サイズを本当に必要なものだけに縮小することは、ウェブサイトの INP を最適化するうえで有効な方法です。これにより、DOM が更新されたときにブラウザがレイアウトとレンダリングの作業を行うのにかかる時間を短縮できます。DOM サイズを大幅に縮小できない場合でも、CSS コンテインメントや content-visibility CSS プロパティなど、レンダリング作業を DOM サブツリーに分離する手法はいくつかあります。

どのような方法であれ、レンダリング作業を最小限に抑える環境を構築し、インタラクションに対するページのレンダリング作業量を減らすことで、ユーザーがウェブサイトを操作したときに、よりレスポンスが速く感じられるようになります。つまり、ウェブサイトの INP が低くなり、ユーザー エクスペリエンスが向上します。