htmlエスケープについて

こういうhtmlがあった時に、

<input type="text" name="txt" id="txt">
<span id="spn"></span>

txtの内容をspnに移動するには、

document.getElementById('spn').innerHTML = document.getElementById('txt').value;

とすればいける。
ただし、上記では不十分。innerHTMLに代入する場合はhtmlEscape処理が必要となる。というわけでこんな感じ。

document.getElementById('spn').innerHTML = htmlEscape(document.getElementById('txt').value);

htmlEscape関数は前のエントリで作ったこれが使えそう。

var htmlEscape = (function(){
  var map = {"<":"&lt;", ">":"&gt;", "&":"&amp;", "'":"&#39;", "\"":"&quot;", " ":"&nbsp;"};
  var replaceStr = function(s){ return map[s]; };
  return function(str) { return str.replace(/<|>|&|'|"|\s/g, replaceStr); };
})();

これをしないと例えばIEでtxtのところに「A&B」と入力して移動させると画面には「A」としか表示されない。「&」が実体参照の先頭文字という解釈をしているためにおこっている模様。ただし実体参照は「&」で始まって「;」で終わるはずだから「A&B」を実体参照として解釈するのは本来ならおかしいはずなんだけど。ちなみに「A&あ」だとそのまま表示されます。「&」のあとにマルチバイト文字がくるということは実体参照ではないのでそのまま表示するっていうロジックが走ってそう。バグなんちゃうか。firefoxだと「A&B」も「A&あ」もそのまま表示されます。
けど、こんなんを自作しなくてもjQueryを使ってこう書けば、内部でやってくれるから気にせず使える。

$('#spn').txt($('#txt').val());

jQueryを使って選択状態にする

以下のようなhtmlがあったときに、

<div>
  <input type="text" name="txt"><br>
  <input type="checkbox" name="chk" value="true"><br>
  <input type="radio" name="rdo" value="false">false
  <input type="radio" name="rdo" value="true">true<br>
  <select name="sel">
    <option value="false">false</option>
    <option value="true">true</option>
  </select><br>
</div>

<button onclick="setTrue();">click!</button>

とりあえずボタンを押したら全ての入力要素に「true」を設定するというsetTrue関数を実行する。
ただし、チェックボックスであればtrueの方がチェックされた状態に、ラジオボタンも同じくtrueの方が選択状態に、プルダウンであればtrueが選択された状態になって欲しい。こんなjavascriptを書いた。

function setTrue() {
  $("div :input").each(function(){
      $(this).val("true");
  });
}

これで、テキストフィールドはtrueと入力され、プルダウン項目は選択された状態になった。しかしながらチェックボックスラジオボタンはならず。
というわけでこんな風に書かないといけないっぽい。

function setTrue() {
  $("div :input").each(function(){
    if($(this).val() == "true" && ($(this).is(":checkbox") || $(this).is(":radio"))) {
      $(this).attr("checked", "checked");
    }else{
      $(this).val("true");
    }
  });
}

うーむ。おしいなぁ。。

HTMLエスケープ関数

いいライブラリがあるのかもしれないけど、今回のプロジェクトでは使えないので、自作。

function htmlEscape(str) {
  return str.replace(/<|>|&|'|"|\s/g, function(s){
    var map = {"<":"&lt;", ">":"&gt;", "&":"&amp;", "'":"&#39;", "\"":"&quot;", " ":"&nbsp;"};
    return map[s];
  });
}

上のだとすっきり書けるけど、無名関数辺りの最適化がどのように行われるか不明。なので、下のようにクロージャにして初期化を確実に1回に抑える方がより安心。

var htmlEscape = (function(){
  var map = {"<":"&lt;", ">":"&gt;", "&":"&amp;", "'":"&#39;", "\"":"&quot;", " ":"&nbsp;"};
  var replaceStr = function(s){ return map[s]; };
  return function(str) { return str.replace(/<|>|&|'|"|\s/g, replaceStr); };
})();

配列の再・初期化

var ary = [];
ary.push('a');
ary.push('b');
ary.push('c');

ary = [];

「a」「b」「c」を追加した後、再度新たな配列をaryに代入する事で再初期化される。
けど新たに生成するのも気が引けるし、前のインスタンスをGC頼みにするってのもなぁ。
・・・と思っていたら。なんとこれで、初期化と同じになるみたい。

ary.length = 0;

eval

今日初めてJavaを組んでいる時に、JRubyもしくはJava6のVM上でjavascriptが動くやつが使いたいと思った。理由はタイトルにあるように、Javaでevalが使いたい!と思ったから。eval相当を自分で組むのは嫌だ〜。

for-in文とif-in文のおさらい

これらはオブジェクトに含まれるプロパティを調べる時に役立ちます。for-inはよく使うんだけど、if-inは久々に使うことになったので、思い出すのに時間がかかってしまいました。というわけでいつものように忘れないためにメモ。

var obj = {name : "hatena", showMessahge : function(){ alert("hello"); }};

こんなオブジェクトがあったときに、このobjがもつプロパティ一覧を見たいときは次のようにする。

for(var property in obj) {
  console.log(property); // firebugのコンソールで表示
}

一覧表示ではなく、特定のプロパティを持っているかどうかを調べる時は次のようにする。

if("showMessahge" in obj) {
  console.log("含む");
}else{
  console.log("含まない");
}

知っておくとデバッグ時に役に立つことがあると思います。

文字列連結のアルゴリズム

http://blog.livedoor.jp/dankogai/archives/51172176.html
こんな方法思いつきもしませんでした!
単にn回繰り返すんではなくて、1ビットずつシフト演算するからlogN回の繰り返しで実現されている、と。すごー。