外部 JavaScript を読み込む方法

JavaScript を利用して外部の JavaScript を読み込む方法は、いろいろ考えられます(ref: JavaScriptな日々 | 外部JavaScriptファイルの動的・静的ロードの方法)。今回は、表示までに時間がかかるスクリプト(おそらく document.write が原因)のせいで、ページ全体の表示が遅くなる、という問題にしぼって考えてみます。

問題のページ

次のようなページがあって、heavy.js の表示が遅く、その後に続く <div></div> がなかなか出てこない、という状況。これを何とか解消したいので「あーだこーだ」いうことにします。

<html>
  <head></head>
  <body>
    <div id="heavi_div"><!-- ここが重い -->
      <script type="text/javascript" src="http://example.com/js/heavy.js"></script>
    </div>
    <div></div>
    <div></div>
    <div></div>
  </body>
</html>

小粋空間さんの場合

小粋空間: BlogPeople 等のリンクリストによる表示の遅延を解消する(その1:JavaScript編)で、そのものズバリの方法が書いてあります。──「その2」が wktk なのですが、今のところまだ書かれていませんね。

<html>
  <head>
    <style type="text/css">
      #dummy_div {
      display: none;
      }
    </style>
  </head>
  <body>
    <div id="heavi_div"></div>
    <div></div>
    <div></div>
    <div></div>
    <div id="dummy_div"><!-- ここは表示されない -->
      <script type="text/javascript" src="http://example.com/js/heavy.js"></script>
    </div>
    <script type="text/javascript">
      if(document.getElementById('dummy_div')){
      document.getElementById('dummy_div').innerHTML = document.getElementById('heavi_div').innerHTML;
      };
    </script>
  </body>
</html>

(おそらく)ダミーの div ブロックを作って、その中身が書き込まれたら、実際に表示したい div の中にコピーする──ということでしょうか(ツッコミ待ち)。

なるほど、逆転の発想というか、えっと JavaScript のお作法的には妥当な方法なのでしょうか?

mootools の場合

mootools はご存じの通り、$() (ドル関数)が使えるので、document.getElementById() の記述が端折れます(たぶん、それ以外にも $() を使う意味はあるのだろう)。

さらに reference:element.js - mootools を見ると replaceWith() という、今回の用途にぴったりな物があります。

これは $('hoge').replaceWith('fuga'); と書くと、id="hoge"無くなって元々 id="hoge" があった場所は id="fuga" に置き換わります。

ということで、上記の例はこのように、少しシンプルになりました。

<html>
  <head>
    <script type="text/javascript" src="moo.js"></script>
  </head>
  <body>
    <div id="heavi_div"></div>
    <div></div>
    <div></div>
    <div></div>
    <div id="dummy_div"><!-- ここはページ読み込み後に消滅 -->
      <script type="text/javascript" src="http://example.com/js/heavy.js"></script>
    </div>
    <script type="text/javascript">
      if ($('dummy_div')) {
      $('dummy_div').replaceWith('heavy_div');
      };
    </script>
  </body>
</html>

prototype.js の場合

今回の用途には replaceWith() が便利っぽいので Prototype JavaScript Framework で同じ物がないか探してみたところ、どうも、無いようです(よね?)。

その代わり(では無いけど)、 Insertion オブジェクト があります(Ref: prototype.js v1.4.0 の使い方)。

たとえば Insertin.Top() を使うと、要素下の最初の子供として HTML を挿入できるようです。何スか、その呪文? なるほどー。

こげな感じ?

<html>
  <head>
    <script type="text/javascript" src="prototype.js"></script>
  </head>
  <body>
    <div id="heavi_div"></div><!-- この中に直接出力される -->
    <div></div>
    <div></div>
    <div></div>
    <script type="text/javascript">
      window.onload = function() {
      new Insertin.Top('heavy_div', '<script type="text/javascript" src="http://example.com/js/heavy.js"></script>');
      };
    </script>
  </body>
</html>

──なにか、JavaScript を冒涜するような使い方をしていないだろうか。script の中に script があるのが気持ち悪い感じ。あと、たぶん、moo.js でも同じことができるでしょう。

2006-11-22T09:41:52+09:00 追記

すいまセーン…… ボク(また)ウソついてまーした……。上記のコードはウソです。ビタイチ動きません。

どうも、自分が考えていた以上に「JavaScript から外部 JavaScript を動的に読み込む」のは難しいようです。document.writeで書き出したり、suVene さんの指摘通り、inner.html を使った方がいいですね……。

jQuery の場合

さてはて、いままでのコードで if ($('dummy_div')) {...} の部分は、要するに <div id="dummy_div"></div> が読み込まれた場合に {} 内を処理する、という意味です。

それなら、「ページ読み込み後」という意味の window.onload でも問題はなさそうです。しかしこの場合は、状況によってはいつまでもスクリプトの表示が始まらないことがあります。

jQuery にはそのあたりを考えてか $(function(){}) という物があり、HTMLドキュメントの準備が出来た段階で 実行させることが出来ます、とのこと(ref: jQuery 開発者向けメモ)。

たぶん、もっとエレガントなコードになると思いますが、prepend() を使って、とりあえずこんな感じで。

2006-11-22T09:48:28+09:00 追記

──ということで(上記の追記を参照)、下記のコードも嘘っぱちです。jQuery らしいコードで、動くように書き換えようと頑張りましたが、オレニハマダムリダ……。

<html>
  <head>
    <script type="text/javascript" src="prototype.js"></script>
  </head>
  <body>
    <div id="heavi_div"></div><!-- この中に出力される -->
    <div></div>
    <div></div>
    <div></div>
    <script type="text/javascript">
      $(function(){
      $("heavy_div").prepend('<script type="text/javascript" src="http://example.com/js/heavy.js"></script>');
      });
    </script>
  </body>
</html>

オチの時間

さて、今回のお話で面白い点はどこかというと──

  • シロートがなんかごちゃごちゃやってる
  • たぶんおかしなコードを書いてる
  • もっと高度なコード(寒)が書けるはず

──というところ(だけ)ではなく、

「(表示が)重いスクリプトの表示を改善するために、(読み込みが)重いスクリプトを読み込む」

という、本末転倒なところです。

なので、万が一にも自分が書いたコードがちゃんとした物だったりして、億が一にも「ぼくも使ってみよう」と思う方がいらっしゃったら、「早まらないでー!」というか、自分の用途に合っているかどうか、ご検討のほどを。

たぶん、表示遅延の回避だけに prototype.js を導入というのは「おとうさんが年賀状作成のために『Adobe Illustlator CS2』を購入」みたいな話かと(それもまた一興?)。

前向きな解決策

小粋空間の記事で「その1:JavaScript編」と書いてあるところから推測すると、「その2」(があった場合)は、おそらく CSS でゴニョっとやるのでは。HTML の「下の方」にスクリプトを読み込むようにして、position: absolute で「上の方」にレイアウトする、という方法。

他に考えられるのは <object></object> で読み込む方法でしょうか(IE が足を引っ張るんだよなぁ……)。

みんなは、どう?(無責任に問題提起)

[2] このページの一番上へ戻る