JavaScriptで動的にWebページの内容を書き換える場合に、文字列のHTMLエスケープをしたい時がある。具体的には、「& " ' < >」の5つを変換したい。
PHPならhtmlSpecialChars()、Railsならh()でよいが、JavaScriptにはHTMLエスケープ用の関数が用意されていないので自分で書く必要がある。
せっかくなので、Twitterでつぶやける文字数以内で書くことを目指してみる。
で、こうなった。
function h(s){return s.replace(/[&"'<>]/g,function(m){return"&"+["amp","quot","#039","lt","gt"]["&\"'<>".indexOf(m)]+";"})}
123文字。
意外と余裕だ。
クォーテーションのエスケープをしないでいいなら(HTML要素の属性値のHTMLエスケープに使わないなら)、こんな方法もある。
function h(s,p){p=document.createElement("p");p.innerText=p.textContent=s;return p.innerHTML}
93文字。
(第2引数のpは変数宣言を避けて文字数を減らすためのダミーの引数なので、実際に使うときには第1引数のみ渡せばよい。)
innerTextはFirefoxでは使えないしtextContentはIEでは使えないので、両方に対応させるために両方書いている。
でも、innerTextには細かい問題ある(参考:
DOM の textContent と innerText について - フリーフォーム フリークアウト)ようなので、環境依存を避けて丁寧にやりたいなら、少し長くなるけどdocument.createTextNode()を使った方が無難かも?
function h(s,d,p){d=document;p=d.createElement("p");p.appendChild(d.createTextNode(s));return p.innerHTML}
106文字。
でもでも、それなら最初の正規表現を使ったやり方のほうが短い。
function h(s){return s.replace(/[&<>]/g,function(m){return{"&":"&","<":"<",">":">"}[m]})}
99文字。
(2011/06/22 追記)
はてなブックマークやTwitterで教えてもらったので追記。
hatestさん
そうですね。用途によってはパラメータのチェックとか型変換とかすべきかも。
ひなしさんfunction h(s){return s.replace(/[&<>"']/g,function(m){return "&#"+m.charCodeAt(0)+';'})} の88文字でよくない?
なるほど。こっちの方がスマートかつ短い。
(ところでTopsyの検索にひっかからないのは何故?)
uuuさん最近はreturn s.replace(/["'&><]/g, function(m){return "&#"+m .charCodeAt(0).toString(16) + ";"})みたいのがマイブーム
こちらもcharCodeAt()。16進数なので"&#"のところは"&#x"の書き間違いと思われる。16進数にするメリットとかあるのかな?
ということでcharCodeAt()を使って書き直してみる。もうエスケープ対象文字の判別とか無しで、全てユニコード化してしまおう。
function h(s,i,t,r){for(i=t="";r=s.charCodeAt(i);i++)t+="&#"+r+";";return t}
76文字!
charCodeAt()を使っているのでJavaScript1.2未対応ブラウザ(IE3とか)では動かないだろう。あと文字コードによっては問題ある?
Null文字が入っていると変換の途中で終わってしまうが、
NULL文字は非SGML文字らしいので良しとしよう。
インクリメントに使う「i」を空文字("")で初期化するのは気持ちが悪いがとりあえず動くみたい。JavaScriptの仕様としてはどうなんだろう。微妙なところか?(参考:
JavaScript講座 : インクリメント/1加算)
(追記終わり)
関連:
floatingdays: JavaScriptで HTMLエスケープ