Hatena::ブログ(Diary)

ヒルズで働く@robarioの技ログ このページをアンテナに追加 RSSフィード

2007年09月23日

[]JavaScriptで外部ライブラリを読み込むためのスクリプトをCodeRepos.orgに上げた。

Sjaxを使わないJavaScript Loader - ヒルズで働く@robarioの技ログJavaScriptから外部JavaScriptを読み込む方法 - ヒルズで働く@robarioの技ログ の改良版です。この二つの記事は忘れてもらって結構です。

前振りとか

ライブラリを読み込みたい(><)

「script要素をappendChildする」でやると読み込み完了を待ってくれないので「setTimeoutで3000ms後に本処理」とか、嫌だ。

そこで「指定したプロパティが存在するか」を監視するスクリプトloader.js.incを書きました。no titleに置いてあります。

no title

no title(loader.js.incの圧縮版)

ライブラリ読み込み方あれこれ

  • <script>タグをHTMLに書く
  • <script>タグをdocument.writeする
    • ×documentがcloseされていると使えないので、onloadイベント以降やブックマークレットでは使えない
    • ×UIスレッドが停止する
  • script要素をappendChildする
    • ×読み込み完了したタイミングを検出できない
  • Ajaxで読み込んでeval
    • ×外部ドメインのライブラリが読み込めない
    • △(工夫次第で)後続スクリプトは、確実に読み込み完了していることを期待して良い
  • Sjaxで読み込んでeval
    • ×外部ドメインのライブラリが読み込めない
    • ×UIスレッドが停止する


それらの欠点を踏まえて作ったのがloader.js.inc。

  • loader.js.inc
    • ○onloadイベント以降でもブックマークレットでも使える。
    • ○外部ドメインのライブラリが読み込める
    • △(多少のタイムラグはあるが)後続スクリプトは、確実に読み込み完了していることを期待して良い
    • ○UIスレッドは停止しない

使い方とか

(function(){    //
...             // ローダ(loader.js.incもしくはloader-min.js.incを挿入)
})              //

([     //
...    // ライブラリ(記述方法は後述)
])     //

(function() {    //
...              // コールバック
})               //
();    // ←コールバックへの引数
  • 末尾はブックマークレットなどで良く使われるイディオム(function(){})();の形式になります。つまり、「元のスクリプトを一切変更せずに、直前にコードを挿入するだけで良い」のがポイントです。ブックマークレットでも簡単に外部ライブラリを利用できるようになります。
  • 『コールバック』および『コールバックへの引数』は省略可能です。省略した場合、ライブラリの読み込みのみが行なわれます。

『ライブラリ』の記述方法

例えばjQueryを読み込む場合、以下のように書きます。

(function(){    //
...             // ローダ(loader.js.incもしくはloader-min.js.incを挿入)
})              //
([
     {'jQuery':'http://example.com/js/jquery.js'}
])
(function() {
    alert(jQuery);
})();

こうすると

1. jquery.js を読み込む

2. typeof jQuery が 'undefined' でなくなるまで待つ

3. コールバックを呼び出す。

という順番で処理が進みます。コールバックが呼ばれた時点で、確実に jQuery が定義されていることになります。


次に、 jQuery.iUtil を読み込んでみます。 jQuery.iUtil は jQuery に依存しているので、以下のように書きます。

([
     {'jQuery':'http://example.com/js/jquery.js'},
     {'jQuery.iUtil':'http://example.com/js/iutil.js'}
])

こうすると

1. jquery.js を読み込む

2. typeof jQuery が 'undefined' でなくなるまで待つ

3. iutil.js を読み込む

4. typeof jQuery.iUtil が 'undefined' でなくなるまで待つ

という順番で処理が進みます。iutil.js を読み込む際には確実に jQuery が定義されていることになります。


次に、jQuery.iDrag と jQuery.iDrop も読み込んでみます。

これら2つは jQuery にのみ依存しています。互いに依存しておらず、jQuery.iUtil にも依存していません。

つまり、jQueryを除く3つは並列に読み込んでも問題無いということです。

この場合は以下のように書きます。

([
     {'jQuery':'http://example.com/js/jquery.js'},
     {'jQuery.iUtil':'http://example.com/js/iutil.js',
      'jQuery.iDrag':'http://example.com/js/idrag.js',
      'jQuery.iDrop':'http://example.com/js/idrop.js'}
])

すると

1. jquery.js を読み込む

2. typeof jQuery が 'undefined' でなくなるまで待つ

3. iutil.js, idrag.js, idrop.js を*同時に*読み込む

4. typeof jQuery.iUtil, typeof jQuery.iDrag, typeof jQuery.iDrop の*全て*が 'undefined' でなくなるまで待つ

という順番で処理が進みます。

jQuery.iSort は、 jQuery.iUtil, jQuery.iDrag, jQuery.iDrop の3つに依存してるため、配列の次の要素として以下のように書きます。

([
     {'jQuery':'http://example.com/js/jquery.js'},
     {'jQuery.iUtil':'http://example.com/js/iutil.js',
      'jQuery.iDrag':'http://example.com/js/idrag.js',
      'jQuery.iDrop':'http://example.com/js/idrop.js'},
     {'jQuery.iSort':'http://example.com/js/isortables.js'}
])

isortables.js の読み込みは、 jQuery.iUtil, jQuery.iDrag, jQuery.iDrop が全て定義された後に始まります。



プロパティの定義を行なわないスクリプトは、連想配列の代わりに文字列でURIのみを書きます。

([
     {'jQuery':'http://example.com/js/jquery.js'},
     {'jQuery.iUtil':'http://example.com/js/iutil.js',
      'jQuery.iDrag':'http://example.com/js/idrag.js',
      'jQuery.iDrop':'http://example.com/js/idrop.js'},
     {'jQuery.iSort':'http://example.com/js/isortables.js'},
     'http://example.com/main1.js',
     'http://example.com/main2.js'
])

読み込み完了を待つ術が無いため、 main2.js は main1.js の読み込み如何に関わらず読み込みを開始してしまいます。

つまり、main1.js と main2.js は同時に読み込まれます。



なお「プロパティの定義を行なわない」のに「読み込み完了を待ちたい」というスクリプトには対応していません。

(チェック方法を定義できるようにしたらいけるかも?プロパティ名を指定するところにfunctionを渡せるようにするとか)


その他

  • Firefox2とIE6で動作を確認しています。
  • 一切グローバルを汚染しません。
  • 以前とチェック方法が変わったのは、jQuery.iUtilなどをチェックする場合にwindow['jQuery.iUtil']になってしまって上手く行かなかったから。
  • 以前の記事を読むと「IEでコンテキストがwindowじゃない時、評価をしてくれないからsetTimeoutしなきゃいけない」と書いてあるけど、ホントかどうか良く分からんからやってない。
  • 名前が決まりません。
  • 本記事中に出てきたように、プロパティ名を指定するところにfunctionを渡せるようにするとかやってみるかも。
  • Perlか何かで、ローダのアップデートや、指定ライブラリの自動挿入・除去をするスクリプトを作りたい。
  • 変なことしてる人、
window['42'] = 'the Answer to (the Great Question of ) life the universe and everything';

なんていうスクリプトを読み込みたい場合、

{'42':'http://example.com/js/42.js'}

と書くと

typeof 42 == 'number'

になってしまって読み込みを開始してくれないので、

{'window["42"]':'http://example.com/js/42.js'}

などとしてください。

wikkywikky 2007/11/08 01:57 はじめまして、JSの動的読み込みについて検索中にこちらを発見しました。
読み込みが確実に行われるのが良いですね。
こちらのローダーを利用させていただきたいと思うのですが、ライセンス等はどんな感じになっているのでしょうか?
お忙しいところすみませんが、よろしくお願いします。

holidays-lholidays-l 2007/11/08 11:49 はじめまして!ライセンスはページ左上プロフィールにもありますが「ここに公開してあるものはコードでも資料でも何でもかんでも好き勝手に使ってください(’=’ライセンス2.0」となっております^^。リンクもお礼も著作者表示も不要でーす。

wikkywikky 2007/11/09 01:45 holidays-lさん、ご回答ありがとうございます!
思いっきりプロフィール部分に書いてありましたね。お手数をおかけしてしまいすみませんでした(^^;
JSの読み込み部分がすっきりしそうです。ありがたく利用させていただきます。

modkamodka 2009/03/07 11:44 すみません、エントリを移動したときにトラックバックを二重に送ってしまいました。
読み込みのチェックの仕方とか無駄のない綺麗なコードとか、凄すぎてため息がでそうになりました。

holidays-lholidays-l 2009/03/09 12:47 いえいえ、普段そんなにトラックバックもらえない(悲)ので、がんがんやってもらって大丈夫ですよー。参考にして頂けて嬉しいです。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証