知ってた? jQueryのready()メソッドはもう書かなくていいらしい

2016/11/08

Julian Motz

133

Articles in this issue reproduced from SitePoint
Copyright © 2016, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2016, KADOKAWA ASCII Research Laboratories, Inc. Japanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

jQueryのコードといえば、$(document).ready(function() {}で書くのがお決まり。でもそのコードの書き方はもう古いかもしれません。

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をチェックし(こちらを参照)、readyStatecomplete状態になるとすぐにコールバックを実行します。

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-RaelTim Severienが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。

(原文:Quick Tip: Replace jQuery’s Ready() with Plain JavaScript

[翻訳:新岡祐佳子/編集:Livit

Copyright © 2016, Julian Motz All Rights Reserved.

Julian Motz

Julian Motz

ソフトウェア開発者・メディアデザイナーで、現在はドイツのベルリンに住んでいます。Web・ハイブリッドアプリケーションに力を入れていて、オープンソース開発の愛好者です。

Loading...