• DOMContentLoadedとloadはDOM解析完了か画像を含む読込完了かで使い分ける

DOMContentLoadedとloadはDOM解析完了か画像を含む読込完了かで使い分ける

公開日: 執筆:佐藤

HTMLから作られるDOMを操作するだけなら、DOMContentLoadedを使います。画像など、ページ表示に必要な外部資源の読込完了を待つ処理なら、loadを使います。

loadへ処理を集めればよいわけではありません。DOMだけで実行できる処理まで画像の読込を待つと、操作開始が遅れます。反対に、画像の寸法が必要な処理をDOMContentLoadedで始めると、まだ読み込まれていない画像を測る可能性があります。

DOMContentLoadedはDOMを組み立てた後に発生する

HTML5の読み込み手順では、HTMLの解析を終えると文書の状態をinteractiveへ変え、解析終了後に実行するスクリプトを処理してから、DocumentへDOMContentLoadedを発生させます。

その時点では、画像などの外部資源が読み込み中でも構いません。要素の取得、class名の追加、クリック処理の登録など、DOMがあれば始められる処理に向きます。

function prepareMenu() {
  var menu = document.querySelector('.global-menu');

  if (menu !== null) {
    menu.classList.add('is-ready');
  }
}

document.addEventListener('DOMContentLoaded', prepareMenu, false);

これは説明用の仮例です。DOMContentLoadedはDocumentへ登録します。HTML内に対象要素があるかを確認し、見つかった場合だけ操作します。

loadは読込を遅らせる外部資源を待って発生する

HTML5の手順では、DOMContentLoadedの後、文書のloadを遅らせるものがなくなるまで待ちます。その後、文書の状態をcompleteへ変え、Windowへloadを発生させます。

画像を含む表示資源の読込後に寸法を使う処理は、loadへ登録します。

function showPhotoWidth() {
  var photo = document.getElementById('main-photo');
  var output = document.getElementById('photo-width');

  if (photo !== null && output !== null) {
    output.textContent = photo.width;
  }
}

window.addEventListener('load', showPhotoWidth, false);

このコードも説明用の仮例です。画像要素と出力先の両方を変数へ取り、nullでないことを確かめます。

処理 選ぶイベント 理由
要素を取得してclass名を付ける DOMContentLoaded DOMが組み上がれば実行できる
ボタンへクリック処理を登録する DOMContentLoaded 対象要素があれば実行できる
画像の読込後に寸法を測る load 画像の読込完了が条件になる
ページ全体を読込中表示から切り替える load 何を待つかを定義したうえで完了を扱う

待つ対象を「ページ全部」という言葉だけで決めず、処理にDOMだけが必要か、外部資源も必要かを分けます。

document.readyStateでイベントが終わっていないか確認する

イベントを登録するコードが常にHTML解析中に実行されるとは限りません。別のスクリプトが後から読み込む場合や、処理を遅れて呼び出す場合は、登録前にDOMContentLoadedが終わっている可能性があります。

document.readyStateには、loading、interactive、completeの三つの状態があります。HTML5のDocument定義では、解析中をloading、解析済みで外部資源を読み込み中の状態をinteractive、読込完了をcompleteとしています。

DOMを使う初期化処理は、次のように分けられます。

function initializePage() {
  // DOMを使う初期化処理
}

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initializePage, false);
} else {
  initializePage();
}

loadingならイベントを待ち、interactiveまたはcompleteならDOMは解析済みなので、すぐ実行します。これにより、イベント登録が間に合わず、初期化処理が一度も動かない状態を避けられます。

画像を含む読込完了が必要なら、completeかどうかで分けます。

if (document.readyState === 'complete') {
  showPhotoWidth();
} else {
  window.addEventListener('load', showPhotoWidth, false);
}

一つの初期化処理を二つのイベントへ重ねない

同じ関数をDOMContentLoadedとloadの両方へ登録すると、一回だけ行うつもりの処理が二回動きます。イベントを増やす前に、処理を次の二つへ分けます。

  • DOMがあれば実行できる処理
  • 画像などの読込完了が必要な処理

それぞれを別の関数にし、必要なイベントへ一度だけ登録します。対応ブラウザーは仕様書だけでは決まりません。公開前に、対象とするブラウザーでイベントの順序、要素の取得、画像寸法、二重実行がないことを確認します。

まず既存のwindow.onloadへ登録している処理を一つ選んでください。画像や外部資源を使わず、DOMだけで完了する処理なら、DOMContentLoadedへ分けられるかを確認します。

戻る