Mach3.laBlog

“readyState” – Alphabetical Advent Calendar 2013

この記事は賞味期限切れです。(更新から1年が経過しています)

“R” は readyState の “R”。

R

readyState

document.readyState は、HTMLのダウンロード・パース状況を取得できるAPIです。 値は “loading” / “interactive” / “complete” などが返され、 それぞれ以下のような状態を表します。

  • loading: ダウンロード中
  • interactive: HTMLのダウンロードとパースが完了
  • complete: サブリソースのダウンロードが完了

また、readyState の状態が移り変わる毎に、readystatechange イベントが発火されます。

document.addEventListener("readystatechange", function(e){
    console.log(document.readyState); // "loading", "interactive", "complete" など
}, false);

DOMContentLoaded

readystatechange と似た働きをもつ物に DOMContentLoaded イベントがありますが、 DOMContentLoaded をサポートしていないブラウザ、例えばIE8以下向けのフォールバックとして readystatechange が使用されたりします。

ごくシンプルに書くと、このような処理をする事になります。

var initialize = function(){
    console.log("ready");
    // アプリケーションの初期化...
};

var domReadyHandler = function(e){
    if(e.type === "DOMContentLoaded"){
        document.removeEventListener("DOMContentLoaded", domReadyHandler);
    } else {
        if(document.readyState !== "complete"){ return; }
        document.detachEvent("onreadystatechange", domReadyHandler);
        window.detachEvent("onload", domReadyHandler);
    }
    initialize();
};

if("addEventListener" in document){
    document.addEventListener("DOMContentLoaded", domReadyHandler, false);
} else if("attachEvent" in document){
    document.attachEvent("onreadystatechange", domReadyHandler);
    window.attachEvent("onload", domReadyHandler);
}

DOMContentLoaded の代替として readystatechange の “complete” のタイミングで処理を行います。 環境の判別に “addEventListener” の有無を使用していますが、 これはたまたま DOMContentLoaded のサポート環境と被っていた為です。 (DOMContentLoaded は document.ondomcontentloaded として参照できない為、イベントのサポートの有無で判別しようとすると冗長になってしまいます)

フレーム内コンテンツなどを考慮するとさらに長いコードになりますが、 下記リンクにて詳細に説明されています。

document 以外での readyState

IE10以下に限っては document 以外にも、img要素やscript要素で readyState が使えます。 従来のIEでは script 要素の onload イベントが使用出来なかった為、 スクリプトローダー等ではこれをフォールバックとして使用していたりします。

例えば、複数のスクリプトを動的・同期的に読み込んで実行したいケースでは、 script 要素の async を false にすれば想定する動きをしてくれます。

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
Set this Boolean attribute to indicate that the browser should, if possible, execute the script asynchronously…

var loadSync = function(resources){
    resources.forEach(function(name){
        var script = document.createElement("script");
        script.async = false;
        script.src = name;
        document.getElementsByTagName("head")[0].appendChild(script);
    });
};

loadSync(["jquery.js", "underscore.js", "backbone.js", "main.js"]);

しかし async 属性はIE8以下ではサポートされていない為、 readystatechange イベントでスクリプトのロード状況を監視し、 読み込みが完了した時点でDOMに挿入する事で再現してみます。

// async がサポートしているかを確認
var supportAsync = "async" in document.createElement("script");

var loadSync = function(resources){
    var nodes = [];
    var count = resources.length;
    var head = document.getElementsByTagName("head")[0];

    var process = function(){
        if(this.readyState === "loaded"){ count--; }
        if(! count){
            nodes.forEach(function(node){
                node.onreadystatechange = null;
                head.appendChild(node);
            });
        }
    };

    var createNode = function(src){
        var node = document.createElement("script");
        if(supportAsync){
            node.async = false;
            node.src = src;
            head.appendChild(node);
        } else {
            node.onreadystatechange = process;
            node.src = src;
        }
        return node;
    };

    // forEach が polyfill で定義されている前提
    resources.forEach(function(name){
        nodes.push(createNode(name));
    });
};

loadSync(["jquery.js", "underscore.js", "backbone.js", "main.js"]);

IE11からは script要素の readyState は使えなくなり、load イベントの使用が推奨されています。

参考資料

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*