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 を適用すると、CSS オブジェクト モデル(CSSOM)と呼ばれる DOM に似た構造が作成されます。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 サイズはいくつかの方法で測定できます。1 つ目の方法は 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 が変更されたときに画面外要素のレンダリング作業をスキップできるという利点もあります。

おわりに

DOM サイズを必要最小限に抑えることは、ウェブサイトの INP を最適化するうえで効果的です。こうすることで、DOM の更新時にブラウザがレイアウトやレンダリングを行うのにかかる時間を削減できます。DOM サイズを効果的に縮小できない場合でも、CSS コンテインメントや content-visibility CSS プロパティなど、レンダリング処理を DOM サブツリーに分離するための手法があります。

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

Unsplash より(Louis Reed 作成)ヒーロー画像。