[jQuery] 外部JavaScriptファイル読込みに関する考察 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

前回の「GO2WEB20のTwitterバッチをAmebloにつけてみた。と、IE対策方法 」に端を発してjQueryを使って外部のJavaScriptのソースを読み込む際の様々なやり方の検証をしてみる。



append()を使ってscriptタグを追加する


jQueryのappend()を使ってタグを追加できますのでこれでscriptタグを追加してみます。


<script>
$(document).ready(function(){

    var script_tag     = document.createElement("script");
    script_tag.type    = "text/javascript";
    script_tag.src     = "hoge.js";
    script_tag.charset = "utf-8";

    $("body").append(script_tag);

})
</script>


function foo() {
    alert('hello');
}

foo();


これは、上手く動作します。

ただ、FireBugなんかでHTMLを見てみると、scriptタグが追加(表示)されていません。


- FireBugで見てみても追加したscriptタグは表示されない
A Day In The Boy&#39;s Life-jQuery-append1


これは、jQueryがhtmlに挿入後に即時にscriptタグを削除する仕様のようで、append()以外でもprepend()やbefore()、after()でも同様のようです。


参考: jQuery の挙動を解読する(32):jQuery.clean() メソッド解読──jQuery解読(49) @ anything from here


しかし実際には、append実行時には即時に読み込んだJavaScriptが実行されてます。

ってことは、先にappendしたscript内で定義した関数なんかは呼び出し可能なんでしょうか。

タグ自体がHTML要素内で削除されているので、なんだか呼び出しできない気がします。

ということで、下記のようにJavaScriptを分けて各パターンで実行してみます。


<script type="text/javascript" src="foo.js"></script>
<script>
  foo();
</script>

同じことをするために2つのscriptタグを生成してbodyにappendしてみます。


<script>
$(document).ready(function(){

    var script_tag1     = document.createElement("script");
    script_tag1.type    = "text/javascript";
    script_tag1.src     = "foo.js";
    script_tag1.charset = "utf-8";

    $("body").append(script_tag1);

    var script_tag2     = document.createElement("script");
    script_tag2.type    = "text/javascript";
    script_tag2.charset = "utf-8";
    script_tag2.text    = "foo();";

    $("body").append(script_tag2);

})
</script>


これも動いた!

HTML要素からタグが削除されるていても、先行して読み込んだJavaScript内の関数を呼び出すことは可能なようです。

FireBugのHTML要素では見えないんですが、DOMとしては関数fooが存在していることがわかります。


A Day In The Boy&#39;s Life-jQuery-append2



直接scriptタグを書いてappendToしてみる


書き方としては、さっきの逆になる。


<script>
$(document).ready(function(){

    $("<script type='text/javascript' charset='utf-8' src='foo.js'></scri" + "pt>").appendTo("body");
    $("<script type='text/javascript' charset='utf-8'>foo();</scri" + "pt>").appendTo("body");

})
</script>

これも問題なく動作します。
scriptタグを分割せずにそのまま書いてしまうと「unterminated string litera」と言うことで構文エラーになってしまいます

ですので、scriptの閉じタグを適当なところで分割し、文字列として挿入することで回避するようにします。


参考: Firefox下でのunterminated string literalエラーを回避する @ OKの日記


また、scriptタグがFireBug上で表示されないのはappndの時と同様です。

しかし、先行してappendToしたスクリプト内の関数は、後からapendToしたスクリプト内から実行することは可能です。


こちらの書き方は、単純なJavaScriptなら良いですが、ソースが長くなるとかなり見づらくなってしまいますので適用範囲が限られてくるかもしれません。



$.getScriptを利用して外部JavaScriptをロードする


$.getScriptではHTTPリクエストを通じて外部のJavaScriptファイルを呼び出すことができます。

また、対象のJavaScriptファイルの読込みが成功した場合に実行する関数(コールバック関数)を定義できるため、シンプルで見やすいソースを書くことができます。


- $.getScriptを利用して外部JSファイルをロードし、ロード成功後に関数を実行する

<script>
$(document).ready(function(){
    $.getScript("foo.js", function(){
      foo();
    });
})
</script>


他の書き方より随分とシンプルですね。

foo.jsファイルのロードが正常に完了すると、コールバック関数として第2引数のfunction()内が実行されます。

ですので、ロードするJavaScript内で定義された関数を実行したかったり、その中で定義している変数を書き換えたければコールバック関数内で設定することで、別のJavaScriptをロードする必要がありません。


しかも、$.getScriptを使った場合、JavaScriptの実行を他のスクリプトより遅延して実行させることができます

今までの書き方を複合させて、下記のように書いてみます。

スクリプトの実行順序がわかりやすいように、引数にスクリプト名を渡し、それをalertで表示させるようにしています。

ちなみにfoo1.jsからfoo3.jsまでは中身が全く同じJavaScriptファイルです。


<script>
$(document).ready(function(){

    $.getScript("foo1.js", function(){
      foo('script1');
    });

    $("<script type='text/javascript' src='foo2.js'></scri" + "pt>").appendTo("body");
    $("<script type='text/javascript' charset='utf-8'>foo('script2');</scri" + "pt>").appendTo("body");

    var script_tag1     = document.createElement("script");
    script_tag1.type    = "text/javascript";
    script_tag1.src     = "foo3.js";
    script_tag1.charset = "utf-8";

    $(script_tag1).appendTo("body");

    var script_tag2     = document.createElement("script");
    script_tag2.type    = "text/javascript";
    script_tag2.charset = "utf-8";
    script_tag2.text    = "foo('script3');";

    $("body").append(script_tag2);
})
</script>

これを実行してみると「script2 → script3 → script1」の順でalertが表示されます

スクリプトのロード自体が遅れるのかと思いましたが、そういうわけではなくFireBugで見てみるとロード自体は上から順にされています。


- FireBugで状況をみると$.getScriptでロードしたJSファイルが最初に読み込まれている
A Day In The Boy&#39;s Life-FireBug-コンソール


コールバック関数の実行自体が遅延されるのかとも考えましたが、そういうわけではなく各JSファイルで直接alertを出すようにして読み込んでみても$.getScriptでロードしたスクリプトが一番最後に実行されました。


JavaScriptの動的ロードというのは一般的ですが、その中でも実行順序に変化をもたせたいという場合に、使えるかもしれません。