jQueryでreadyメソッドはDOMが完全にロードされたタイミングでコードを実行するように実装されていました。このメソッドはすべてのDOM要素が利用可能になった時点で所定の関数を実行するので、要素へ確実にアクセスしたり操作したりできます。
jQuery 3.0がリリースされるまでは、次のように無名関数で使うのが一般的でした。
$(document).ready(function() {
// Handler for .ready() called.
});
jQuery 3.0での「ready()」メソッドの変更
jQuery 3.0のリリースまでは、readyメソッドを呼び出す方法はいくつかありました。
- document要素に対して:$(document).ready(handler);
- 空の要素に対して:$().ready(handler);
- 直接(つまり特定の要素に対してではなく):$(handler);
上に挙げた3つの方法は機能的に同じです。どの要素に対して呼び出されたかにかかわらず、指定されたハンドラーはDOMが完全にロードされたタイミングで呼び出されます。言い換えれば、呼び出しがimage要素$("img")に対してでもdocument要素に対してでも、特定の要素がロードされたタイミングでコールバックが発生するわけではありません。むしろ呼び出しはDOM全体のロードが完了した時点で実行されます。
jQuery 3.0では$(handler);以外の構文は推奨されていません。理由は公式に次のように説明されています。
(要素の)選択は.ready()メソッドの反応と無関係であるため、非効率的で、メソッドの反応について誤解を招きかねないからです。
ReadyイベントとLoadイベントの違い
readyイベントは、DOMが完全にロードされ要素に安全にアクセスできるようになったタイミングで発生します。一方、loadイベントは、DOMとすべてのアセットのロードが完了したタイミングで発生します。
loadイベントは次のように使用できます。
$(window).on("load", function(){
// Handler when all assets (including images) are loaded
});
このコードではDOMとの相互作用の準備完了だけでなく、画像の完全なロードも待ちます(画像のサイズによっては時間がかかる場合があります)。
通常のDOM操作では、おそらくloadイベントを使う必要はないでしょう。しかし、たとえばアセットのロード完了までローディングスピナーを表示しておきたいとき、画像のサイズに従ってなにか計算したいときなどは、loadイベントの使用が正解、という場合もあります。
jQueryの「.ready()」メソッドはおそらく不要
readyメソッドを使えば、すべてのDOM要素を確実に安全に操作できるようになった時点でコードが実行されます。とはいえreadyメソッドの使用にどのような意義があるというのでしょうか? HTMLドキュメントの<head>セクション内に書かれたJavaScriptコードを実行する場合でも、コードは確かにブラウザーが後続の要素(たとえば<body>要素)のロードを完了したタイミングで実行されるのです。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>.ready() tutorial</title>
<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script>
$(function(){ // .ready() callback, is only executed when the DOM is fully loaded
var length = $("p").length;
// The following will log 1 to the console, as the paragraph exists.
// This is the evidence that this method is only called when the
// DOM is fully loaded
console.log(length);
});
</script>
</head>
<body>
<p>I'm the content of this website</p>
</body>
</html>
JavaScriptを<body>内の最終部分として実行するなら、おそらくready()メソッド内に記述する必要はありません。なぜなら、操作・アクセスすることになるすべての要素はすでにロードされているからです。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>.ready() tutorial</title>
</head>
<body>
<p>I'm the content of this website</p>
<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script>
var length = $("p").length;
// The following will log 1 to the console, as the paragraph exists.
console.log(length);
</script>
</body>
</html>
「ready()」メソッドに代わる素のJavaScript
モダンブラウザー(IEではバージョン9以上)においては、DOMContentLoadedイベントについて次のようにリッスンできます。
document.addEventListener("DOMContentLoaded", function(){
// Handler when the DOM is fully loaded
});
ただし、イベントがすでに発生してしまっているとコールバックは実行されないことに注意してください。コールバックをいつも確実に実行するには、jQueryは次のようにドキュメントのreadyStateをチェックし(こちらを参照)、readyStateがcomplete状態になるとすぐにコールバックを実行します。
var callback = function(){
// Handler when the DOM is fully loaded
};
if (
document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
この手法がすでに実装されたdomReadyライブラリーもインクルードできます。
古いバージョンのInternet Explorer
IE8以下の場合、次のようにonreadystatechangeイベントを使ってドキュメントのreadyStateを検出できます。
document.attachEvent("onreadystatechange", function(){
// check if the DOM is fully loaded
if(document.readyState === "complete"){
// remove the listener, to make sure it isn't fired in future
document.detachEvent("onreadystatechange", arguments.callee);
// The actual handler...
}
});
あるいはloadイベントも使用でき、jQueryと同様に、この方法ならどのようなブラウザーにも通用します。すべてのアセットがロードされるまで待つことになるので、いくらか遅延も生じます。注意点として、この方法の場合も上で説明したようにreadyStateをチェックし、すでにイベントが発生していたとしてもコールバックを確実に実行する必要があります。
最後に
readyメソッドの代わりになる素のJavaScriptを探しているなら、DOMContentLoadedイベントを導入できます。システム要件にIE9未満(のブラウザー)が含まれている場合、onreadystatechangeイベントを使います。
プロジェクトでjQueryを使っているなら、ビルトインのready関数を安全に導入できますが、先に述べたように、要素に対して(非推奨の方法で)ready()メソッドを使うこと(たとえば$(document).ready()とすること)は避けてください。
最後に、多くの場合ここで紹介した手法は必要ないかもしれないということを忘れないでください。JavaScriptを終了タグ</body>の直前に持ってくるだけで、DOMのロード完了を確実にできます!
※本記事はMev-Rael、Tim Severienが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Quick Tip: Replace jQuery’s Ready() with Plain JavaScript)
[翻訳:新岡祐佳子/編集:Livit]