お久しぶりです。寺岡です。
たまにはRuby以外の記事も書いてみようと思ったので、
知っておきたいJavaScriptのエスケープ処理に関する小ネタを紹介します。
encodeURI, decodeURI は使わない
JavaScriptでURLエンコーディングを行う場合に、encodeURI, decodeURIを使用してはいけません。
これらの関数は「:」や「/」などの、URL上で意味を持つ記号は処理してくれません。
encodeURIComponent, decodeURIComponentを使いましょう。
実例で見てみます。
「あいうえお」という文字列を対象としている場合は結果は同じですが、
「http://」という文字列を対象とした場合、結果が異なることがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var value; // ------------------------- // ひらがなのURLエンコード // ------------------------- value = "あいうえお"; // どちらも「http://example.com/%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A/hoge」になる console.log("http://example.com/" + encodeURI(value) + "/hoge"); // console.log("http://example.com/" + encodeURIComponent(value) + "/hoge"); // ------------------------- // ひらがなのURLデコード // ------------------------- value = encodeURIComponent("あいうえお"); // どちらも「あいうえお」になる console.log(decodeURI(value)); console.log(decodeURIComponent(value)); // ------------------------- // URL記号のURLエンコード // ------------------------- value = "http://"; // 「http://example.com/http:///hoge」になる。エンコードされていない。 console.log("http://example.com/" + encodeURI(value) + "/hoge"); // 「http://example.com/http%3A%2F%2F/hoge」になる。OK! console.log("http://example.com/" + encodeURIComponent(value) + "/hoge"); // ------------------------- // URL記号のURLデコード // ------------------------- value = encodeURIComponent("http://"); // 「http%3A%2F%2F」になる。デコードされていない。 console.log(decodeURI(value)); // 「http://」になる。OK! console.log(decodeURIComponent(value)); |
encodeURIやdecodeURIを利用しないと上手くいかないという場合はプログラムの構造を間違えている可能性が非常に高いので、一度見直してみると良いと思います。
HTMLをエスケープしたい
JavaScriptでDOMを使ってHTMLを組み立てるのは面倒です。
1 2 3 4 5 6 7 8 9 |
// DOMを使ってbodyに<div attr1="hoge">あいうえお</div>を追加する var attr1 = "hoge"; var content = "あいうえお"; var body = document.getElementsByTagName('body')[0]; var div = document.createElement('div'); div.setAttribute("attr1", attr1); div.innerText(content); body.appendChild(div); |
こんな時はinnerHTMLやjQueryを使って一気に組み立ててしまいたくなります。
1 2 3 4 5 6 7 8 |
// 文字列で組み立ててbodyに<div attr1="hoge">あいうえお</div>を追加する var attr1 = "hoge"; var content = "あいうえお"; # innerHTML body.innerHTML = '<div attr1="' + attr1 + '">' + content + '</div>'; # jQuery $('body').html('<div attr1="' + attr1 + '">' + content + '</div>'); |
しかし、attr1やcontentにタグに使われる記号である「<>"'」などがきた場合、HTMLを壊してしまいます。
これを回避するために、タグに埋め込む文字列はHTMLの実体参照にエスケープする必要があります。
PHPでいうhtmlspecialcharsですね。
残念ながらJavaScriptにはこの動作を行う組み込み関数がありません。
ですが、以下のコードで実装することは出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// HTMLエスケープ function escapeHTML(html) { var elem = document.createElement('div'); elem.appendChild(document.createTextNode(html)); return elem.innerHTML; } // HTMLエスケープ - jQueryを使うと一行で書ける function escapeHTML2(html) { return jQuery('<div>').text(html).html(); } // どちらも 「<a>」 になる console.log(escapeHTML('<a>')); console.log(escapeHTML2('<a>')); |
二つの関数のどちらもやっていることは同じで、以下の処理を行っています。
- ダミーのdiv要素を作成
- エスケープしたい文字をタグ内の文字列に設定
- div要素内のHTMLを取り出す
正規表現で置換することもできますが、この方法だとJavaScriptエンジンに
処理を任せられるのでスマートだと思います。
2013/4/18 追記
こちらの記事にあるように、この方法ではタグ内のテキストのエスケープには有効ですが、タグの属性値では「"」「'」がエスケープされないため危険です。
属性値の場合は正規表現などで置換するか、JavaScriptからDOMの属性に値を設定するべきでしょう。
Comments
このHTMLエスケープの方法ですとJavaScriptがブラウザ上で実行されている時にしか使うのは難しいですね
DOMが操作出来ることが前提になりますね。
試したことはありませんが、jsdom を使えばいけるかもしれません。
HTML のエスケープ処理ですが、こちらの Qiita の記事によると TextNode を使った方法では ' や " がエスケープされないみたいです。