jsを用いて動的にscriptを読み込む際の注意点

javascriptを用いて動的にscriptを読み込む際、ハマってしまったのでメモしておきます。

やりたいこと

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="script_with_jquery.js"></script>
</head>

上記のように、head内でscriptを読み込むサイトがあるとします。
ページによっては使用しないスクリプトがあるので、これらのスクリプトをjavascriptで動的に読み込むように改良したいというわけ。

ポイントは2つのjsファイルに依存関係があること。
今回は、script_with_jquery.jsというファイルはjquery.min.jsに依存している。

大元のコード

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="script_with_jquery.js"></script>
</head>

これは正しく動作します。
しっかりjqueryを読み込んだ後に、script_with_jquery.jsを読み込んでいるので大丈夫です。

動きそうで動かないコード

<head>
    <script>
    var head = document.getElementsByTagName('head')[0];
    var script1 = document.createElement("script");
    script1.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js';
    head.appendChild( script1 );
    var script2 = document.createElement("script");
    script2.src = 'script_with_jquery.js';
    head.appendChild( script2 );
    </script>
</head>

私は、上記のようなコードを書いて、期待通りに動かなくて頭を悩ませました。
一見動きそうに見えませんか?(運が良ければ動きます。)

javascriptにて、scriptを動的に生成しています。
依存関係を守るため、先にjqueryの方をappendChildして、その後、script_with_jquery.jsをappendChildしています。

なぜ動かないかというと、、、

javascriptを用いて動的に読み込む際に非同期通信が発生する

はい。というわけです。落ち着いて考えてみれば当然のことなのですが。。。

つまり、javascriptを用いてscriptをappendChildした場合、そのscriptが完全に読み込まれる前に次の行の処理に移行してしまう。

ということは、順番通りにappendChildしても、その順番通りにscriptが読み込まれるとは限らないということです。(運よくjavascriptのダウンロードと展開が次の行のjavascriptよりも先に完了すれば、正常に動作してしまいます。)

ではどうするか?(動くコード)

そこで、以下のように改良します。

<head>
    <script>
    var head = document.getElementsByTagName('head')[0]; 
    var script1 = 'https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js';
    var script2 = 'script_with_jquery.js';
    var script_src_list = [ script1, script2 ];
    var script_idx = 0;
    function execute_scripts() {
        var script = document.createElement("script");
        script.src = script_src_list[ script_idx ];
        head.appendChild( script );
        if ( ++script_idx < script_src_list.length ) {
            script.onload = execute_scripts;
        }
    }
    execute_scripts();
    </script>
</head>

javascriptにて生成したscriptに、onload関数を指定します。

onload関数で、1つ目のscriptがロードされたら、2つ目のscriptを読み込みを実行する。という動作を実現しています。

これで、無事依存関係を崩さずにscriptを動的に読み込むことが可能となりました。

これで頭を悩ませてる人、私だけでしょうか・・・?
以上、jsを用いて動的にscriptを読み込む際の注意点についてでした。