つかびーの技術日記

情報系修士卒のWeb系技術日記です。現在のフォーカス分野はアドテクです。

JavaScriptで動的にscriptタグを差し込んだり、実行する方法

   

こんにちは、@s_tsukaです。今回は珍しくJavaScriptネタでいこうと思います。

自分のJS力はそれほど高くないですし、ブラウザやW3Cの仕様に詳しくないですが、scriptタグはハマりポイントだと思うので、書いておきます。(といっても今回のは一般的ではないです)

今回は生のJSを扱います。(最近はAngularや、Reactが流行っていますが、未だに生JSのニーズもあるんですよ)

結論から言うと、単一scriptタグならappendChild、複雑なタグならdocument.write + iframeです。

scriptタグ

このページにたどり着いた読者には説明不要かと思いますが・・・

scriptタグをHTMLファイル上に記述し、ブラウザに読み込ませると、そのscriptが起動します。例えばこんな感じで。

このようなHTMLを開くと画面上にブラウザのダイアログが出現して1と表示されます。

ブラウザはHTMLをDLすると上からparseを開始します。parseを行いつつ、DOM構築を行いますので、通常scriptタグはDOM構築をブロックしないようにbodyの最下部に記述します。ですが、jsファイルをDLしないとページが動作しない、あるいはなるべく早くJSを動かしたい、という場合はheadタグ内に<script src=”http://foo.js…(略) というように記述します。

ブロック考慮=body最下部
早めにDLしておきたい=head内

という感じですね。

動的にscriptタグを生成、追加する方法

通常であればscriptタグはHTML上に直接記述するか、あるいは何らかのテンプレートエンジンによって(scriptタグを含む)HTMLを生成し、それをブラウザへ返却するか、scriptのsrc属性を使って外部ファイルを読み込むと思います。

つまり、(一度ページが描画された後で)動的にscriptタグを生成して、実行する、などはあまりないと思います。(一部では常識的に使われますが)

前述の動的でないケースであれば特に問題はありませんが、場合によってはそのブラウザにDLさせたHTMLからscriptを起動し、そのJavaScriptコードによって、scriptタグをHTML上に挿入する、(そしてそのscriptを起動)ということを行うかと思います。

文章だと難しいですが、イメージだとこんな感じです。id=abcの部分にelementを挿入したい、という感じです。

ちなみにこれはダメな例です。正しく動きません。

意図はわかっていただけるかと思います。動的にscriptタグを挿入して、実行したいのです。

こういう場合はappendChildメソッドを利用します。script elementを生成し、これをappendChildするとうまく動きます。

実際に実行すると123が出力されると思います。

余談ですが、この静的に記述されている部分のscriptをheadの部分に記述するとscriptが動くタイミングではまだDOM parseができておらず、id 123が見つからなくてエラーになります。なのでbodyの下の方に書いています。

動的にscriptタグを含むような複雑なDOMを生成、追加する方法

上記のscriptタグが1つだけの場合は簡単ですが、scriptタグが複数あったり、divタグの中にscriptタグがあったり、そもそもそれらの構造が一定じゃなくてどうDOM構築すりゃいいんだ・・・的な状況になった場合、一気に難しくなります。

例えば上記の例を真似て、以下のコードを動かしてみます。

これは動的にdivを生成して、appendChildしています。みなさんは既にappendChildでscriptが動くことを知っています・・・が!残念ながらこの場合はscriptは動きません。

これに対応する方法は2つあります。(実際はもう少し他にもあると思いますが)

  • appendするタグの中身を解析して、scriptがあった場合、逐一appendChildで1つずつ挿入する(構造を壊さないように注意しつつ)
  • document.writeを使ってHTML片を出力する

appendの方はdivのparseなんてしてられるか!という感じですね。

document.writeの場合は、そのscriptタグの場所にdocumentを書く、という挙動になる上に、document.writeはブロッキングしますので、画面描画が一気に遅くなります。

なかなか良い方法は無いのですが、

「一時的にiframeを作成し、そこにdocument.writeでHTMLを書き出し、実行させる」という方法が良いかと思います。

例えばこんな風に。

 

iframeは不可視なので表示されず、scriptを実行することができました。

ちなみにこの例は簡略化しています。もし「id=abcの部分にdivを挿入したいのであって、iframeは挿入したくない!」という人がいたら、適宜elementの移動などで調整してください。

iframeにはonLoadを付けることができますので、iframe内のscriptなどの実行完了を待って何かを行うことも可能です。

本来はこういうコードは書きたくない・・・と思う人もいるかと思いますが、諸事情により書かなくてはならないときもあります。そんなときに今回のような方法で実現すると良いかと思います。

以上です。

 - JavaScript