$(document).ready()は本当に必要だったのかDOM読み込みの仕組みから解説

$(document).ready()はなぜ使われていたのか

jQueryを触ったことがある人なら、ほぼ確実に見たことがあるのがこれです。

$(document).ready(function(){
  $("#menu").hide();
});

あるいは省略形です。

$(function(){
  $("#menu").hide();
});

現在のJavaScriptではほとんど見かけません。
では、これは単なる「昔の書き方」だったのでしょうか。

実は違います。
当時は、これを書かないとJavaScriptが“正しく動かない”可能性が高かったのです。

その理由は「DOMの読み込みタイミング」にあります。

HTMLとJavaScriptは同時に動いていない

Webページは、ブラウザが上から順にHTMLを読み込んで構築します。
JavaScriptはその途中で実行されます。

たとえば次のHTMLを見てください。

<html>
  <head>
    <script>
      document.getElementById("title").textContent = "変更";
    </script>
  </head>
  <body>
    <h1 id="title">Hello</h1>
  </body>
</html>

このコードはエラーになります。
理由は単純で、scriptが実行された時点でまだ<h1>が存在していないからです。

ブラウザは

1. <head>を読む
2. scriptを実行する
3. その後に<body>を読む

という順序で処理します。

つまりJavaScriptの問題ではなく、
DOMがまだ完成していないタイミングでDOM操作をしているのが原因です。

DOMが完成する瞬間 ― DOMContentLoaded

ブラウザはHTMLをすべて解析し、DOMツリーを構築した瞬間にイベントを発火します。
それが「DOMContentLoaded」です。

document.addEventListener("DOMContentLoaded", function(){
  document.getElementById("title").textContent = "変更";
});

これが現代の標準的な書き方です。
DOMが構築された後に処理が実行されるため、安全に要素へアクセスできます。

では、なぜ昔はこれが使われなかったのでしょうか。

問題はブラウザ対応だった

2000年代、最大の障害はInternet Explorerでした。
古いIEはDOMContentLoadedイベントを正しくサポートしていませんでした。

つまり

  • Firefoxでは動く
  • IEでは動かない

という状況です。

ここで登場したのがjQueryの$(document).ready()です。

$(document).ready()の本当の役割

jQueryのreadyは単なるイベントではありません。
内部では複数の検出方法を組み合わせて「DOM完成」を判定していました。

代表的なものは以下です。

  • DOMContentLoaded
  • onreadystatechange
  • doScroll hack(IE独自)
  • loadイベントのフォールバック

つまり$(document).ready()は

「どのブラウザでもDOMが使えるタイミング」を保証する仕組み

でした。

開発者はDOMの状態を気にせず、次のように書けました。

$(function(){
  $("#title").text("変更");
});

これが普及した最大の理由です。

window.onloadとの違い

よく混同されるのがこれです。

window.onload = function(){
  // 処理
};

window.onloadは「ページのすべてが読み込まれた後」に実行されます。
ここで言う「すべて」には画像も含まれます。

つまり、

イベント 発火タイミング
DOMContentLoaded HTML解析完了(DOM使用可能)
window.onload 画像・CSS・外部ファイルも含め完全読込

画像が多いページではwindow.onloadは数秒遅れることもありました。
UI初期化を待たされるため、体感速度が悪化します。

jQueryのreadyは
「操作できる最速のタイミング」で実行されるのが重要でした。

本当に必要だったのか

結論として、当時は必要でした。

理由は次の3つです。

  • DOMContentLoadedが統一されていなかった
  • scriptの実行タイミング制御が難しかった
  • IE対応が必須だった

つまりベストプラクティスではなく、
安全にJavaScriptを書くための前提条件でした。

現在はどうなったか

現在は次の書き方が可能です。

<script defer src="app.js"></script>

defer属性により、HTML解析後にスクリプトが実行されます。
そのためreadyは不要になりました。

あるいは次でも十分です。

document.addEventListener("DOMContentLoaded", init);

ブラウザの互換性問題が解消されたためです。

注意点

ただし1つだけ注意があります。
モジュールスクリプトです。

<script type="module" src="app.js"></script>

moduleは自動的にdefer扱いになります。
readyを書くと逆に冗長になる場合があります。

「昔の習慣」で書くと、
処理が二重に実行されるバグの原因になることがあります。

まとめ

$(document).ready()は古い書き方ではありますが、意味のないものではありません。

  • DOM未完成問題を回避した
  • ブラウザ差を吸収した
  • UI初期化を最速化した

当時のWeb開発では「書いた方がいい」ではなく
書かないと壊れる可能性があったコードでした。

そして現在、私たちが気にせずDOMを扱えるのは、
ブラウザがようやくjQueryの前提に追いついたからだと言えます。