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さん
引数sがStringであることをチェックしてないから、{replace:function(){return "<script>"}}とかいうオブジェクト渡されたら終わるぞそうですね。用途によってはパラメータのチェックとか型変換とかすべきかも。
ひなしさん
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エスケープ
0 件のコメント:
コメントを投稿