javascript式HTML記法その2

  element = Builder.node('div',{id:'ghosttrain'},[
    Builder.node('div',{className:'controls',style:'font-size:11px'},[
      Builder.node('h1','Ghost Train'),
      "testtext", 2, 3, 4,
      Builder.node('ul',[
        Builder.node('li',{className:'active', onclick:'test()'},'Record')
      ]),
    ]),
  ]);

creates (without newlines):

<div id="ghosttrain">
  <div class="controls" style="font-size:11px">
    <h1>Ghost Train</h1>
    testtext234
    <ul>
      <li class="active" onclick="test()">Record</li>
    </ul>
  </div>
</div>

Builder.dump()を呼ぶと、そこでDIV関数やH1関数が定義されて、Shortcutsモードとでもいうべきものになる。(こんな機能があったのか!)

  Builder.dump();
  element = DIV({id:'ghosttrain'},[
	       DIV({className:'controls',style:'font-size: 11px'},[
          H1('Ghost Train'),
		 "testtext", 2, 3, 4,
		 UL([LI({className:'active',onclick:'test()'},'Record')]),]),]);

ブラケット[...]でくくるのがちょっと気に入らない。
だから一工夫する。

  'a big blockquote br b center code div em form h1 h2 h3 h4 h5 h6 hr img iframe
  input i li ol option pre p script select small span strong style sub sup table
  tbody td textarea tr ul u'.split(' ').each(function(e) {
    window['$' + e] = function() {
      if (arguments[0]) {
	if (arguments[0].nodeType) return Builder.node(e,$A(arguments));
	if (arguments[1]) return Builder.node(e,arguments[0],$A(arguments).slice(1));
	return Builder.node(e,arguments[0]);
      };
      return Builder.node(e);
    }
  });

と、こう書けるようになる。

  element = $div({id:'ghosttrain'},
                 $div({className:'controls',style:'font-size: 11px'},
	          $h1('Ghost Train'),
	             "testtext", 2, 3, 4,
	             $ul($li({className:'active',onclick:'test()'},'Record'))));

以下、苦労話。(前提知識として、Builder.node('tagname',...)はElementオブジェクトを返す。)

  • javascriptでは、argumentsという配列のようなオブジェクトに関数に渡された引数が入っている。なんだよ配列のようなって。$A(arguments)で配列に変換した。
    • javascriptに、OCamlHaskellのパターンマッチングが導入される日が来ることを、私は信じて疑わない。
  • arguments[0].nodeType は、最初は arguments[0] instanceof Element と書いてた。これはFirefoxでは動くけど、IEでは動かなかったから、elzrのコードを参考にした。
  • Builderのユニットテストのコード(scriptaculous-js-1.7.0/test/unit/builder_test.html)を眺めると、こんな振る舞いがassertされていることがわかる。このセマンティクスはなかなかだ。ここで、FOO,BARはString,Number,Arrayのいずれかにしておこう。
Builder.node('div')  ==  <div></div>
Builder.node('div',{id: 'hoge'}) == <div id=hoge></div>
Builder.node('div',{id: 'hoge'},FOO,BAR) == <div id=hoge>foobar</div> --(*1)
Builder.node('div',{id: 'hoge'},[FOO,BAR]) == <div id=hoge>foobar</div> (*2)
Builder.node('div',FOO,BAR) == <div>foobar</div>   --(*3)
Builder.node('div',[FOO,BAR]) == <div>foobar</div>  --(*4)

さて、HTMLの構造を書くためにBuilder.node(...)の入れ子を書きたいわけだが、
残念、(*1)や(*3)のFOO,BARにBulder.node(...)をあてはめることはできない。(*2)や(*4)のにはできる。つまりArrayの中ならできるわけだ。
気持ちわるい。(*1)や(*3)でも書けるべきだ。
今回改善したのはそれ。さっきの arguments[0].nodeType 云々はその判断に必要だった。