読者です 読者をやめる 読者になる 読者になる

scriptタグの中にJavaScriptを書く正しい方法

HTML JavaScript

XHTMLでCDATA区間を使う。以上。

経緯

HTML4のscriptタグでJavaScriptをちょろっと書く時、中にHTMLタグがあるとダメという話が発端である。例えば、以下のようなのがダメだ。

<script type="text/javascript">
document.write('<p><a href="http://www.hikoboshi.org">hikoboshi.org</a></p>');
</script>

ちなみに、ブラウザで動かせば多分動く。(試してないけど。)じゃあ何がダメかというと、HTMLの仕様的に。ちなみにこういう書き方をすると、多分HTML4でもXHTML1でもダメだと思われ。

そんな話をきっかけに、いろいろ調べた。

何故ダメか

まずはHTML4のDTDを調べる。(別にstrictを選ばなくても良いんだけどとりあえず。)

<!ELEMENT SCRIPT - - %Script;          -- script statements -->

%Scriptを追っかける。

<!ENTITY % Script "CDATA" -- script expression -->

つーわけで、scriptタグの中はCDATAだ。CDATAって何なのかというとこれ。

Although the STYLE and SCRIPT elements use CDATA for their data model, for these elements, CDATA must be handled differently by user agents. Markup and entities must be treated as raw text and passed to the application as is.

要はただのテキストってことである。タグとかの記号を書いても(つまり<とか"とかを実体参照を用いずに使っても)そのものとして認識される。

だが、そうしてただの、何の制約もないテキストにすると、scriptタグの終わりが認識できないということになる。それを避けるためにCDATAの終わりが設定されていて、ここではETAGO(</)となっている。

The first occurrence of the character sequence "</" (end-tag open delimiter) is treated as terminating the end of the element's content. In valid documents, this would be the end tag for the element.

で。さっきのを見直してみる。

<script type="text/javascript">
document.write('<p><a href="http://www.hikoboshi.org">hikoboshi.org</a></p>');
</script>

CDATAの終わりは『oshi.org</a』ここの</だ。これはscriptの終了でなければならなくて、エラーとなる。

というわけで、scriptタグの中のJavaScriptに、HTMLタグ(正確には閉じタグ)を書くことが出来ない。よく、JavaScriptでいろいろ書くときは別ファイルにして…みたいな話があるけれど、こういう理由があった。

とはいえ、たった一行これを書くだけに1ファイル?とも思う。何か良い方法はないものか。

XHTMLも調べた

あまり期待していなかったが、XHTMLなら(ちょっと設計が新しいから)何か良い手だてがあるかも?と思い、調べてみた。

同様にDTDとドキュメント。

<!ELEMENT script (#PCDATA)>

In XHTML, the script and style elements are declared as having #PCDATA content. As a result, < and & will be treated as the start of markup, and entities such as < and & will be recognized as entity references by the XML processor to < and & respectively. Wrapping the content of the script or style element within a CDATA marked section avoids the expansion of these entities.

script要素の内容が#PCDATAになっているので、中のタグとかがきちんと解釈される。(ETAGO以外は)何を書いても良かったHTML4に比べると一見不親切になったように見えるけれど、そうではない。自分でCDATA区間を指定すれば良い。

<script type="text/javascript">
<![CDATA[
document.write('<p><a href="http://www.hikoboshi.org">hikoboshi.org</a></p>');
]]>
</script>

この場合、区間の終わりは]]>で指定されるので、</でうっかり終わったりしない。まぁ、逆に]]>は書けないのだけれど、</よりは出会う可能性は低い。

CDATA区間を明示するのが面倒な場面も往々にしてありそうだけれど、どちらかと言うとXHTMLの方が穴がない。きちんと進化しているのだなぁと思う。

HTML4ではscriptタグの中にJavaScriptを書けない?

というわけではない。タグが書けないだけだ。prototype.jsのEvent.observeでイベントをちょろっとセットするだけとかだったらまずタグは書かないし、DOMで要素を作ってappendChildでも良い。

でも、document.writeでマークアップされた何かを書くことは難しい、かも。innerHTMLとかもそう。何か良い方法知っていたら教えてください。実は困ってます、これ(ぉぃ

追記

コメント欄にて、バックスラッシュでエスケープするのを教えてもらった。一瞬、CDATAにそういう仕様が?と思ったのだけれど、参照先を見に行ったら理由が分かった。この場合は、JavaScript側のエスケープ記号を使う、ということらしい。要は</と書かなければ良く、scriptの中はそのままの形で(例えばJavaScriptエンジンに)渡されるので、それを解釈するJavaScript側で問題ない文字列を挟む。なるほど、JavaScript側の実装を利用するというのは、全く考えつかなかった。賢いなぁ。