JavaScript の関数に引数としてマルチバイト文字列を渡したい場合、特に href 属性の値として JavaScript の関数を呼ぶ場合に、文字コードと escape 方法の組み合わせがいくつか考えられる。 当然ながら,browser によってサポートしているパターンが違っているはずなので,ごく簡単なサンプルを使用して現象を確認してみた。
ここでは、document の文字コードは EUC-JP, Shift-JIS を使用し,JavaScript に渡す文字列はこれに UTF-8, UCS-2 を加えた。escape 方法は escape なし、URLescape, JavaScript の escape() 関数、JavaScript 内部でのみマルチバイト文字列を使う、という各パターンについて調査した。 また文字列の出力には alert() を用いた。これは出力時に HTML parser の処理が(恐らく)介入せず、JavaScript engine の挙動を判別しやすいと考えたためである。
anchor タグの href 属性に "javascript:func(...)" をセットした。func() は HTML file 中に書かれた client side JavaScript で、その内容は次の通り。
これらが書かれた HTML file をさまざまな OS, browser で読み込み、リンクをクリックした際の挙動をまとめた。動作試験に用いたファイルは次の通り。
OS, browser ごとの挙動を以下にまとめる。 なお、下の表の結果の欄は上記のサンプルの実行結果が左から順に並んでいる。
OS | browser | 結果 (EUC-JP) | 結果 (Shift-JIS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Mac OS 9.2.2 | Netscape 4.76 |
o x u o
x u x x
o u
o o u o
o u x x
o u
| Netscape 6.1
|
o H H o
o x x x
o H
|
o H H o
o u x x
o H
| Netscape 7.02
|
o - o o
- x x U
o H
|
o - o o
- x x U
o H
| Mozilla 1.0rc2
|
o - o o
- x x U
o H
|
o - o o
- x x U
o H
| Mozilla 1.2.1
|
o o o o
x x x U
o H
|
o o o o
x x x U
o H
| Internet Explorer 5.0
|
x x o o
x o o o
o U
|
x x o o
x o o o
o U
| Internet Explorer 5.1.7
|
x x o o
x o o o
o U
|
x x o o
x o o o
o U
| Opera 6.02J
|
o H H o
x x o o
o U
|
o H H o
x x o o
o U
| iCab 2.8
|
o x u o
x u o o
x H
|
o o u o
o u o o
x H
| iCab 2.9.7
|
o x u o
x u o o
x H
|
o o u o
o u o o
x H
| Mac OS X 10.2
| Netscape 7.0
|
o - o o
- x x U
o H
|
o - o o
- x x U
o H
| Netscape 7.1
|
o o o o
x x x U
o H
|
o o o o
- x x U
o H
| Mozilla 1.2.1
|
x o x o
x x x U
o H
|
u o u o
x x x U
o H
| Mozilla 1.4
|
o o o o
x x x U
o H
|
o o o o
x x x U
o H
| Mozilla Firebird 0.6.1
|
o o o o
o o o U
o H
|
o o o o
o o o U
o H
| Camino 0.7.0
|
o - o o
- x x U
o H
|
o - o o
- x x U
o H
| OmniWeb 4.5
|
o x o o
x o o o
o x
|
o x o o
x o o o
o x
| Internet Explorer 5.2.1
|
x x o o
x o o o
o U
|
x x o o
x o o o
o U
| Internet Explorer 5.2.3
|
x x o o
x o o o
o U
|
x x o o
x o o o
o U
| Safari Public Beta
|
o H o U
x o U o
o U
|
o H o o
x o o o
o U
| Safari 1.0
|
o H o o
x o o o
o U
|
o H o o
x o o o
o U
| Opera 6.03
|
o H H o
x x o o
o U
|
o H H o
x x o o
o U
| iCab 2.9.5
|
o x u o
x u o o
x H
|
o o u o
o u o o
x H
| Windows XP Home Edition
| Netscape 7.1
|
o o o o
x x x U
o H
|
o o o o
x x x U
o H
| Mozilla 1.4
|
o o o o
x x x U
o H
|
o o o o
x x x U
o H
| Mozilla Firebird 0.6.1
|
o o o o
o o o U
o H
|
o o o o
o o o U
o H
| Internet Explorer 6.0sp2
|
o x x o
x x o o
o U
|
o x x o
x x o o
o U
| Opera 7.11
|
o H H o
x x o o
o U
|
o H H o
x x o o
o U
| Windows NT 4.0 sp6
| Netscape 6.1
|
o H H o
o x x x
o H
|
o H H o
o u x x
o H
| Internet Explorer 5.5 sp2
|
o x x o
x x o o
o U
|
o x x o
x x o o
o U
| FreeBSD 3.2
| Netscape 4.51
|
o H H o
o x x x
o H
|
o H H o
x x x x
o H
| |
where as:
o: 正しく表示 -: 何も起こらない x: 謎の文字化け u: UTF-8 を EUC-JP/Shift-JIS で表示しようとした文字化け U: %uHHHH style の escape 文字列 H: %HH style の escape 文字列
この結果から、「alert window に日本語を表示する」という単純な処理についても,ブラウザ間で挙動が大きく違っていることが確認できた。
1. の結果,JavaScript の関数に EUC-JP/Shift-JIS 文字列(URLencode 無し)を渡す場合 (1-a) と,\uHHHH Style の Unicode 文字を使った場合 (1-d) には多くのブラウザで正常に表示できることが分かった。これに対して, URL escape した EUC-JP/Shift-JIS 文字列 (1-b) では文字化けするブラウザが増えている。一方で URL escape した UTF-8 文字列を使った 1-c では正常表示するものが 1-b よりは多かった。
HTML 4.01 Specification では href 属性の URI 中の non-ASCII character は本来 UTF-8 にした上で URL escape すべきであると定められている。 一方,JavaScript (ECMA Script) 中では Unicode 文字が使われると定められている (ECMA 262) 。 しかし引数を URL decode する処理と JavaScript への文字列引き渡しのどちらを先行すべきかの規格はないので,URL escape された引数を使用した場合には,JavaScript engine と HTML parser の処理が重なるため,処理順により異なる結果が得られるはずだ。これがブラウザ間の挙動の違いに影響していると考えられる。
1-d の場合の引数は,HTML 的には単なる ascii であると同時に ECMA 262 で定義された Unicode character でもある。このため両者の処理が競合しない,すなわち処理順に依存しない結果が得られるため,(ほぼ)全てのブラウザで正しい表示が得られたのは当然であろう。
なお,Mac 版の IE は 1-a, b のいずれも文字化けを起こすが、1-c, d では正常に表示できた。これは,処理系が完全に Unicode を前提としているためと思われる。
2. では unescape() を使用した。この関数は RFC 1738 準拠のもの(Netscape などで使用)と ECMA-262 (PDF) 準拠のもの(MSIE 4 以降で使用)があり、ブラウザ依存性の原因となる関数の代表的なものである。ちなみに Mozilla の実装では、そこに DOM の escape() も加わって、さらにややこしいことになっているらしい。
それはともかく今回の結果を見ると、案の定ひどい有り様になっている。EUC-JP/Shift-JIS の場合 (2-a) では Netscape が、Unicode の場合 (2-b, c, d) には MSIE が対応できているようだが、unescape() を使った時点で browser 非依存のページ構築はほぼ不可能だと考えるべきだろう。
最後に、3. ではマルチバイト文字列を HTML 内ではなく、JavaScript 内部(<script>...</script>)に持たせてみた。恐らくは HTML としての処理を迂回できるために、多くの browser で表示に成功している。ちなみに、UTF-8 の場合 (3-b) には正しく表示できた browser は存在しなかった(当り前)。
全体として見てみると、汎用性という意味で最も期待できるのが 1-d であるという結果になった。この Unicode と \u を用いる方法は、ECMA 262 で規定されている。Netscape では、JavaScript 1.3 を採用した Navigator 4.06 以降で実装されているらしい。少なくともそれ以降は、\uHHHH を使うのが、どうやら正しい作法のようだ(ECMA-262, 6. Source Text を参照)。 他の例では HTML parser と JavaScript engine とでそれぞれ処理が発生するはずなので、お互いの分担がハッキリしてないのが原因ってとこなんでしょうか。
ちなみに、Safari は Public Beta にしては優秀と言える結果だと思うが、EUC, JIS の場合に "\n" を alert window の中で改行と解釈できなかった。しかし大きな問題ではないので、正式版にはそこそこ期待ができそうだ。IE に代わってバンドルされることになれば、汎 OS 的 script を書くのは楽になるかも知れない。