JavaScriptのデータ構造を元にHTMLを生成

JavaScript で HTML を動的に作成することはよくやりますが、下記のように直球な方法のは中々面倒くさい。

```js
var sHtml = "<table><thead><tr><th>" + sHoge + "</th></tr></thead></table>";
```

いろんな便利な方法があるのでしょうが、ググッても私がイメージしてたようなのは中々見つからなかったので、自分で作れないか試してみました。

```js
var gh = {
    gen: null,
    /*
        データ構造を元に HTML 生成。
        引数は 文字列 or 数値 or ハッシュ 想定。
        ハッシュは、'tag', 'inward' が予約KEYで、それ以外はアトリビュートとされる。
        'inward' は 文字列 or 数値 or 配列 想定で、配列には引数と同じものを入れることが出来る。
        以下はテーブル作成例。
        var h = {
            tag: 'table',
            class: 'table table-bordered',
            inward: [
                {
                    tag: 'thead',
                    inward: [
                        {
                            tag: 'tr',
                            inward: [
                                {
                                    tag: 'th',
                                    inward: 'title1'
                                },
                                {
                                    tag: 'th',
                                    inward: 'title2'
                                },
                            ]
                        }
                    ]
                },
                {
                    tag: 'tbody',
                    inward: [
                        {
                            tag: 'tr',
                            inward: [
                                {
                                    tag: 'td',
                                    inward: 'value1'
                                },
                                {
                                    tag: 'td',
                                    inward: [
                                        'value2',
                                        { tag: 'br' },
                                        { tag: 'b', inward: 'fugafuga' }
                                    ]
                                },
                            ]
                        }
                    ]
                },
            ]
        };
        var sHtml = gh.generateHtml(h);
    */
    generateHtml: function(h) {
        if (typeof h == 'string' || typeof h == 'number') {
            return h;
        }
        var sInward  = null;
        var sTag     = null;
        var aAttr    = [];
        for (var key in h) {
            if (key == 'tag') {
                sTag    = h[key];
            }
            else if (key == 'inward') {
                if (typeof h[key] == 'string' || typeof h[key] == 'number') {
                    sInward = h[key];
                }
                else {
                    sInward = '';
                    for (var i = 0; i < h[key].length; i ++) {
                        sInward += arguments.callee(h[key][i]);
                    }
                }
            }
            else {
                if (h[key] == null) {
                    aAttr.push(key);
                } else {
                    aAttr.push(key + '="' + h[key] + '"');
                }
            }
        }
        if (sTag == null) {
            return '';
        }
        var sTagS = '<' + sTag + (aAttr.length == 0 ? '' : ' ' + aAttr.join(' ')) + '>';
        return (sInward == null) ? sTagS : sTagS + sInward + '</' + sTag + '>';
    }
};
gh.gen = {
    /* hBase をベースとして、a の要素（ハッシュ）を加えたもので HTML 生成して a に対応した配列として返す */
    unite: function (hBase, a) {
        var aR  = [];
        for (var i = 0; i < a.length; i ++) {
            var h = {};
            for (var key in hBase) {
                h[key] = hBase[key];
            }
            for (var key in a[i]) {
                h[key] = a[i][key];
            }
            aR.push(gh.generateHtml(h));
        }
        return aR;
    },
    /* 配列から tr,td 生成 */
    array2tr: function (a) {
        var hTr = { tag: 'tr', inward: [] };
        for (var i = 0; i < a.length; i ++) {
            hTr.inward.push({
                tag: 'td',
                inward: a[i]
            });
        }
        return gh.generateHtml(hTr);
    },
    /* a 生成 */
    a: function (h) {
        h.tag   = 'a';
        return gh.generateHtml(h);
    },
    /* option 生成。inward は文字列想定。value 省略時は inward が入る */
    option: function (h) {
        h.tag   = 'option';
        if (! 'value' in h) {
            h.value = h.inward;
        }
        return gh.generateHtml(h);
    }
};
```

コメントにもありますが、入れ子になったデータ構造を渡すとHTML文字列を作成します。
img タグを作る場合とかはこんな感じでしょうか。

```js
var sHtml = gh.generateHtml({ tag: 'img', src: 'hoge.png' });
```

unite は下記のように一部のデータ構造が同じものをいっぱい作るときを想定しています。

```js
var a = [
    { name: 'hoge', value: 123 },
    { name: 'fuga', value: 456 }
];
var sHtml = gh.gen.unite({ tag: 'input', type: 'hidden' }, a).join("");
```

これでも多少は便利よく使えるかとは思うのですが、もっといい感じのがあるはず…とも思うので、知っていたら教えてくださいね。

##追記 2014-08-04
dankogai さんから下記を紹介していただきました。
便利すぎる…。のでこっち使ったほうが良いです。
[json2dom() & dom2json()](http://blog.livedoor.jp/dankogai/archives/51065972.html)