HTMLページを読み込むと同時に実行される JavaScript、いつも定番の雛形を使って書いているのだけれど、ふと立ち止まって考えてみました。

 

 

はじめに結論から

  • <script> タグ内のスクリプトは、タグより前にある HTML 要素にアクセスできる
  • jQuery.readyDOMContentLoaded は HTML が読み込みと解析が完了した時点でイベントが発生しますが、まだ画面に描画される前です。
  • load イベントは、スタイルシートや画像といったページに従属するリソースを含んだ、ページ全体が読み込まれた時に発生します。

 

<script> タグ内のスクリプトは、タグより前にある HTML 要素にアクセスできる

裏を返して言えば、<script> タグより後に出現する HTML 要素にアクセスすることはできません。

例えば、下の例は実行すると正しく動作しますが、

<html>
    <body>
        <div id="someid"></div>
        <script>
            document.querySelector('#someid').innerHTML = "Hello, guys!";
        </script>
    </body>
</html>

 

次の例は、エラーになります。

<html>
    <body>
        <script>
            document.querySelector('#someid').innerHTML = "Hello, guys!";
            /* これはエラーになる */
        </script>
        <div id="someid"></div>
    </body>
</html>

つまり、<script> タグ内のスクリプトが実行される時点では、まだ <div id="someid></div> は読み込まれていないからです。

これは、例えば <head></head> にスクリプトを配置した場合も同様です。

ようするに、</body> タグ直前にスクリプトを設置することで、ページ内の要素にアクセスできるようになります。

 

HTML の DOM 解析が完了した時に実行する

DOMContentLoaded イベント、または jQuery の jQuery.ready を使用することで HTML が全て読み込まれて、DOM の解析が終わった段階でスクリプトを実行することができます。

DOMContentLoaded イベントを使用した例は次のとおりです。

<html>
    <body>
        <script>
            window.addEventListener('DOMContentLoaded', function() {
                document.querySelector("#someid").innerHTML = "Hello, guys!";
            });
        </script>
        <div id="someid"></div>
    </body>
</html>

 

DOMContentLoaded イベントはスクリプトが実行される以前に発生する可能性があるため、リスナーを追加する前に確認するように実装すべきです。

function doSomething() {
    // do something
}

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

 

次にjQuery の場合は、

<html>
    <body>
        <script>
            jQuery.ready(function() {
                $("#someid").innerHTML = "Hello, guys!";
            });
        </script>
        <div id="someid"></div>
    </body>
</html>

jQuery は他にも次の様に書くことができます1

$(function() {
    $("#someid").innerHTML = "Hello, guys!";
});

 

ページに従属する画像やスタイルシートといったリソースの読込完了時に実行する場合

先程の DOMContentLoaded イベントや、jQuery.ready を使用した場合には、ページに従属したスタイルシートや画像、フレームといった要素はまだ読み込まれていないため、これらに関連する要素にアクセスするには load イベントを使用します。

load イベントは、スタイルシートや画像といった関連するリソースを全て含んだページ全体が読み込まれたときに発生します。これは、リソースの読み込みが完了されるのを待つことなく、DOM が読み込まれると同時に起動される DOMContentLoaded とは対照的です。
—— MDN Window: load event

window.addEventListener('load', function() {
    // do something.
});

あるいは、

window.onload = function() {
    // do something.
}

あるいは <body> タグの onload プロパティに指定する方法もあります。

<head>
    <script>
        function doSomething() {
            // do something.
        }
    </script>
</head>
<body onload="doSomething()">
    :
    :
</body>

 

特定の画像がロードされたときに実行する

特定のリソースに個別に load イベントを使用することもできます。

そのリソースの要素自身にアクセスできる必要があるため、DOMContentLoaded イベントか jQuery.ready のイベントハンドラ内にコードを記述します。

document.querySelector('img.some-image').addEventListener('load', function() {
    // do something
});

もし画像のロードに失敗した場合は、イベントは発生しません ので、エラー発生時にも処理が必要な場合、error イベントにハンドラを登録します。

テスト

ここで、実際にページを用意して、簡単なテストをしてみようと思います。

次のようなHTML (jstiming.html) があるとします。

<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<script type="text/javascript" src="./jstiming.js"></script>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<title>JavaScript 実行タイミングのテスト</title>
</head>
<body>
  <div id="imgbox">
    <img id="img" src="任意の画像noURL">
  </div>
</body>
</html>

このHTMLに以下のようにスクリプトを埋め込みます。

<head>
    :
    <script>
    window.onload = function() {
        var h1 = $('#img').height();
        console.log("window.onload: height = " + h1);
    }

    function doSomething() {
        var h2 = $('#img').height();
        console.log("DOMContentLoaded: height = " + h2);
    }

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

    window.addEventListener('load', function() {
        var h3 = $("#img").height();
        console.log("window.addEventListener(load): " + h3);
    });
    </script>
    :
</head>
<body>
    <div id="imgbox">
        <img id="img" src="任意の画像noURL">
    </div>
    <script>
        var h4 = $('#img').height();
        console.log("body bottom: h = " + h4);
    </script>
</body>

さらに、このHTMLページと同一のディレクトリに次の JavaScript ファイル (jstiming.js) を設置します。

$(function() {
    var h3 = $("#img").height();
    console.log("external file: h = " + h3);
});

これを私の環境 (Firefox) で実行すると次の様な順番で表示されました。

body bottom: h = 0
external file: h = 0
DOMContentLoaded: height = 0
window.onload: height = 450
window.addEventListener(load): 450

この JavaScript では、HTML にある <img> タグの高さ (height) を取得して表示させています。

<body> の最下部に設置したスクリプトはもちろんですが、DOMContentLoaded$(handler) では画像が読み込まれていないので高さは 0 ですが、load イベント発生時のスクリプトでは、高さが取得できています。


  1. jQuery 3.0 からはこの書き方 $(handler) が推奨されるみたいですね。 


 
記事を共有する
カテゴリー: javascriptweb

zaturendo

中小企業社内SE。

0件のコメント

コメントを残す

メールアドレスが公開されることはありません。