JavaScriptで初期化処理を書くとき、DOMContentLoadedとloadのどちらを使うべきか迷う方は多いです。しかも両者は似て見えても、発火する条件も順番も異なります。ここを曖昧なままにすると、要素取得に失敗したり、画像サイズが読めなかったり、不要に表示を待ってしまったりします。
本記事では、DOMContentLoadedとloadの違い、発火順、使い分けを実務目線で整理します。deferやasync、window.onloadとの関係、よくある失敗例までまとめて理解できる内容です。
DOMContentLoadedとloadの違いと順番をまず結論から理解する
最初に結論を整理すると、DOMContentLoadedはHTMLの解析完了後に発火し、loadはページ全体の読み込み完了後に発火します。そのため、通常の流れではDOMContentLoadedのほうが先、loadのほうが後です。
ここでいうページ全体には、画像、スタイルシート、iframeなどの外部リソースも含まれます。一方でDOMContentLoadedは、DOMツリーの構築が終わった時点で発火するため、画面上の要素を取得してイベントを設定する用途に向いています。
この違いを知らずにloadへ初期化処理を寄せると、必要以上に実行が遅れ、体感速度を落とす原因になります。逆に、画像サイズの取得やレイアウト確定後の処理をDOMContentLoadedで実行すると、期待した値が取れないことがあります。
つまり重要なのは、どちらが優れているかではなく、何の完了を待ちたいのかで選ぶことです。
要点
DOMContentLoadedはDOMが使えるタイミング、loadは外部リソースも含めて読み込みが終わったタイミングです。
発火順は一般的にDOMContentLoaded → loadです。
DOMContentLoadedとは何か
DOMContentLoadedは、ブラウザがHTML文書を解析し、DOMツリーの構築を完了した段階で発火するイベントです。実務では、ボタンやフォーム、メニューなどの要素に対してイベントリスナーを付ける初期化処理でよく使われます。
この時点では、少なくともHTML要素は取得できるため、document.querySelectorなどを安全に実行しやすくなります。ページの見た目を整えるための画像や一部の外部リソースがまだ読み込み中でも、DOM操作自体は開始できるのが大きな利点です。
ただし、すべてのスクリプトが完全に無関係というわけではありません。defer付きスクリプトの実行タイミングや、CSSとの関係によってはDOMContentLoadedの発火が想像より遅れることもあります。したがって、単純にHTML解析だけを待つ軽いイベントと理解しつつ、スクリプト配置との関係も合わせて把握することが重要です。
loadとは何か
loadは、ウィンドウ全体の読み込みが完了したときに発火するイベントです。ここでの読み込み完了には、HTMLだけでなく、画像、スタイルシート、埋め込みフレームなどの外部リソースも含まれます。
そのため、画像の実サイズを前提にした処理、背景画像を含めた表示完了後の計測、外部リソース依存のUI調整などではloadが適しています。
一方で、単にクリックイベントを設定するだけの処理をloadまで待たせる必要は通常ありません。必要以上に待機が発生し、ユーザーがすでに操作可能な状態であるにもかかわらず、スクリプトの有効化が遅れる可能性があります。
つまりloadは万能な開始点ではなく、全リソース完了が条件として本当に必要な場面で使うイベントと考えるのが適切です。
発火順はどうなるのか
基本的な発火順は、HTML解析が完了してDOMContentLoadedが発火し、その後に外部リソースの読み込みが完了してloadが発火します。したがって、通常はDOMContentLoadedのほうが先です。
この順番を理解しておくと、初期化処理を段階的に分けやすくなります。たとえば、DOM要素の取得やイベント設定はDOMContentLoadedで行い、画像サイズ依存の調整だけをloadで追加実行する設計ができます。
なお、ページ構成やスクリプトの読み込み方法によって、DOMContentLoaded自体が遅くなることはありますが、だからといってloadより後になるわけではありません。順番の本質は変わらず、DOM構築完了が先、ページ全体完了が後です。
発火タイミングの仕組みを理解して混同を防ぐ
DOMContentLoadedとloadを正しく使い分けるには、ブラウザがページをどの順序で処理しているかを押さえる必要があります。HTMLを上から解析し、必要に応じてCSSやJavaScript、画像などの取得を進め、DOMを構築し、最終的にページ全体のロード完了へ進みます。
この流れのどこでイベントが発火するのかを理解すれば、なぜ要素は取得できるのに画像サイズは未確定なのか、なぜscriptタグの置き場所で動作が変わるのかが自然に見えてきます。
| 項目 | DOMContentLoaded | load |
|---|---|---|
| 待つ対象 | DOM構築完了 | ページ全体の読み込み完了 |
| 画像の読込完了 | 未保証 | 保証されやすい |
| 主な用途 | DOM操作、イベント設定 | 画像計測、最終レイアウト依存処理 |
| 発火順 | 先 | 後 |
HTML解析とDOM構築の関係
ブラウザはHTMLを先頭から読み進めながら、要素を解釈してDOMツリーを作ります。DOMとは、JavaScriptから文書構造を扱うための内部表現です。つまり、DOMが完成していなければ、後ろに書かれた要素はまだ取得できません。
DOMContentLoadedはこのDOM構築完了を起点にするため、HTML要素へのアクセスに適しています。scriptタグをhead内に置いた場合に、DOM未構築で要素取得に失敗する問題を回避する目的でも使われます。
ただし、解析の途中で通常のscriptに遭遇すると、ブラウザはそのスクリプトを実行するために解析を一時停止します。これがDOMContentLoadedの到達時刻に影響します。発火条件そのものは単純でも、到達までの道筋にはスクリプト配置が関わる点を押さえることが大切です。
外部リソース読み込みとloadの関係
loadが遅くなる主な理由は、外部リソースを待つからです。代表例は画像ですが、スタイルシートや埋め込み要素なども影響します。特に画像が多いページや重い埋め込みがあるページでは、DOMContentLoadedからloadまでに体感できる差が出ることがあります。
この差があるからこそ、単純なDOM初期化をloadに置くのは非効率です。一方で、読み込み完了後でなければ確定しない情報を扱う場面ではloadが不可欠です。
実務では、DOMが使える処理は早く始める、リソース依存処理だけ後に回すという分離が保守性と表示速度の両面で有効です。
deferとasyncが順番に与える影響
scriptタグのdeferとasyncは、DOMContentLoadedとの関係で特に重要です。defer付きスクリプトはHTML解析を止めずに取得され、解析完了後に文書順で実行されます。そして一般に、deferスクリプトの実行後にDOMContentLoadedが発火します。
一方のasyncは、取得完了しだい実行されるため、文書順もDOM構築との足並みもそろいません。そのため、DOM依存の初期化処理をasyncスクリプトに直接置くと、タイミングが不安定になることがあります。
最近のフロントエンドではdeferの利用が非常に相性がよく、ページ下部にscriptを置く代替としても有効です。順番を安定させたいならdefer、独立した処理ならasyncという考え方で整理すると理解しやすいです。
DOMContentLoadedとloadの使い分けを実務で判断する方法
迷ったときは、処理が依存している対象を確認すれば判断できます。対象がDOM要素ならDOMContentLoaded、画像や外部読込後の状態ならloadです。実際の現場では、ほとんどの初期化処理はDOMContentLoadedまたはdeferで十分です。
loadを選ぶべきなのは、待つ理由が明確なケースに限ると考えると設計しやすくなります。
DOMContentLoadedを使うべきケース
フォームの入力補助、モーダルの開閉、アコーディオン、タブ切り替え、メニューのイベント設定など、HTML要素が存在すれば開始できる処理はDOMContentLoaded向きです。
また、複数のコンポーネントを初期化する共通処理でも、画像完了まで待つ必要がないなら早めに実行するほうがユーザー体験はよくなります。
bodyの閉じタグ直前にscriptを置く構成ならDOMContentLoadedなしでも動くことがありますが、再利用性や配置変更への耐性を考えると、明確にタイミングを定義しておく価値は高いです。
loadを使うべきケース
画像のnaturalWidthや描画後サイズを使う処理、フォントや外部読込後の最終配置を前提にした調整、画面全体の完全表示後に行う計測などはloadが向いています。
たとえばヒーロー画像の高さを基準にする演出や、すべての埋め込み完了後に実行したい計算では、DOMContentLoadedだと値が不完全な場合があります。
ただし、個別の画像だけを待ちたい場合に、ページ全体のloadを使うのは待ちすぎになることがあります。その場合は対象要素単位での読込イベントを検討したほうが効率的です。
迷ったときの判断基準
判断に迷う場合は、次の観点で切り分けると整理しやすいです。
- 要素を取得してイベントを付けるだけならDOMContentLoaded
- 画像や埋め込みの完了が必要ならload
- script配置を安定させたいならdeferも検討
- 待機時間を増やしたくないならloadを安易に使わない
特に初心者が混同しやすいのは、動くからloadにしてしまうケースです。動作の安定性だけでなく、実行の早さと必要条件の最小化まで考えると、より適切な選択ができるようになります。
よくある記述例と失敗パターンを整理する
イベントの意味を理解していても、実装時には細かな失敗が起こります。代表例は、window.onloadとaddEventListenerの違いを見落とすこと、スクリプトの配置とイベント待機を二重にしてしまうこと、asyncでDOM依存コードを先走らせることです。
ここでは、誤解しやすい点をまとめて整理します。
window.onloadとaddEventListenerの違い
loadイベントを書く際に、window.onloadへ直接関数を代入する方法と、addEventListenerでloadを登録する方法があります。実務では後者のほうが安全です。なぜなら、window.onloadへの代入は後から上書きされる可能性があるからです。
複数の処理を共存させたい場合、addEventListenerなら追加登録できます。DOMContentLoadedについても同様に、documentへaddEventListenerで登録する形が扱いやすいです。
保守性を重視するなら、イベント登録方法そのものも使い分けの一部として意識すると、後からコードが増えても崩れにくくなります。
scriptタグの位置だけで解決できる場合
scriptタグをbody末尾に置けば、その時点で上部のDOM要素は概ね構築済みです。そのため、単純なDOM取得だけならDOMContentLoadedを書かなくても動くことがあります。
ただし、構成変更でscriptの位置が変わる、テンプレート分割で読み込み順がずれる、モジュール化で読み込み方法が変わると、前提が崩れることがあります。
小規模ページでは簡潔さが利点になりますが、長期運用や複数人開発では、イベントタイミングを明示したほうが意図が伝わりやすいです。見た目に動くことと、設計として明快であることは別だと考えると判断しやすくなります。
よくある誤解と回避策
よくある誤解は、DOMContentLoadedなら何でも安全、loadなら必ず最適、という二択で考えてしまうことです。実際には、処理対象ごとに必要な待機条件が違います。
また、deferを使っているのにさらにDOMContentLoadedを過剰に重ねると、意図せず初期化が複雑になることがあります。逆にasyncでは順番保証が弱いため、DOM依存処理を慎重に扱う必要があります。
回避のコツ
処理ごとに、何が利用可能である必要があるのかを先に決めます。
その条件がDOMならDOMContentLoaded、全体完了ならloadという順で選べば、過不足のない設計になります。
まとめ
DOMContentLoadedとloadの違いは、待っている対象の違いです。DOMContentLoadedはDOM構築完了時、loadは画像やスタイルシートなどを含むページ全体の読み込み完了時に発火します。したがって順番は、通常DOMContentLoadedが先、loadが後です。
実務では、イベント設定やDOM操作はDOMContentLoaded、画像サイズや最終レイアウト依存の処理はloadと使い分けるのが基本です。
さらに、deferやasync、scriptタグの配置によって体感や挙動が変わるため、イベント名だけでなく読み込み戦略まで含めて考えることが大切です。
迷ったら、何の完了を待ちたいのかを基準に判断してください。それだけで、DOMContentLoadedとloadの違いと順番はかなり明確に整理できます。
コメント