日期:2014-05-16  浏览次数:20454 次

我的动态javascript对象到DOM元素的生成器

?

06年曾经写过一个将 object 转化为 DOM 元素的生成器,后来换工作之后一段时间不写js,代码就找不到了。但是最近因工作需要,所以重新写了一个。不过现在似乎很多js代码库都实现了该技术了,不新鲜了。

?

代码的原理很简单,就是一个简单的js对象,根据其中的 nodeName字段值 调用 document.createElement() 生成DOM元素,然后将js对象其余的字段值都设置到新生成的 DOM元素中。先看一下demo代码,用于生成一个表格,每个单元格中存放一个checkbox:

?

?

    var table = {nodeName: "table", className: "selection", children:[]};
    var ctrls;
    for(var i = 0; i < infos.length; ++i)
    {
        if( i % 5 == 0 )
        {
            var row = {nodeName: "tr", children:[]};
            ctrls = row.children;
            table.children.push(row);
        }
        ctrls.push({nodeName: "td", className: "select_project", children:[
            {nodeName:"input", "id": "proj_" + infos[i].id, name:"selproj", "type": "radio", value:infos[i].id, onclick: fnOnProjectCheck}
            , {nodeName: "label", "htmlFor": "proj_" + infos[i].id, innerHTML: infos[i].lname}
        ]});
    }

    containor.innerHTML = "";
    generateElements(containor, table);

?

?如果用直接的字符串拼装,看起来就不会那么清爽了。我在使用过程中最觉得方便的是创建了一个几百行的表格,因为表格很长,所以需要每隔20行插入一行表头,用这个技术实在是简化好多重复工作。

?

??要使用很简单,就是调用 generateElements ,并传入两个参数,一个是容器DOM对象,一个是要创建的节点描述信息。描述信息对象是一个纯js对象,其中:

?

  • 必须包含 nodeName 或者 tagName 字段,值为 要创建的DOM对象的 nodeName 值,比如 "table","div"等
  • 其他属性会被直接赋予给DOM对象的属性中
  • 如果包含 children 属性,则该属性对应的值将会作为子元素创建
generateElements(parentDomElement, options)的参数和使用组合描述如下:
  • parentDomElement:可选,存放生成节点的容器对象
    • 参数存在,则自动将生成的元素直接加入到容器对象中,然后将创建的元素同时返回给调用者
    • 如果省略,则仅仅帮助创建DOM元素,然后返回给调用者
  • options:根据内容创建DOM元素
    • 类型为数组:则每一个数组元素都是一个option对象,最终生成一组DOM元素
    • 类型为对象:参考 option 对象的定义,生成一个DOM元素

?

option对象字段定义:

?

  • nodeName 或 tagName:必须,为DOM对象对应的tag值
  • children:子元素定义
    • 类型为数组:则每一个数组元素都是一个 option 对象,表示包含多个子元素
    • 类型为对象:参考 option 对象定义,表示包含单个子元素
  • 其他属性:直接复制到DOM对象中,不支持递归嵌套复制,比如 style.backgroundColor这种形式就无法复制到DOM对应的style属性的backgroundColor中去
其他话就不多说了,上代码:

?

// return element that generated
function generateElements(parentElem, info)
{
    if( parentElem == null ) return null;
    if( info == null )
    {
        if( parentElem.ownerDocument == document ) return null;
        info = parentElem;  // means single argument
        parentElem = null;
    }

    if( info instanceof Array )
    {
        var elems = [];
        for(var i = 0; i < info.length; ++i)
            elems.push(generateElements(parentElem, info[i]));
        return elems;
    }
    else
    {
        var pName = parentElem? parentElem.nodeName.toLowerCase(): "";
        var nodeName = (info.nodeName||info.tagName).toLowerCase();

        var fnCreate = function(p, n){return document.createElement(n);};
        var fnAppend = function(p, el){p.appendChild(el);};

        if( "table" == pName )
        {
            fnAppend = empty;
            if( "thead" == nodeName )
                fnCreate = function(p, n){ return p.createTHead(); };
            else if( "tfoot" == nodeName )
                fnCreate = function(p, n){ return p.createTFoot(); };
            else if( "tr" == nodeName )
                fnCreate = function(p, n){
                    return p.insertRow(-1);
                };
            else
                fnCreate = empty;
        }
        else if( indexOfArray(["tbody", "thead", "tfoot"], pName) != -1 )
        {
            fnAppend = empty;
            if( "tr" == nodeName )
                fnCreate = function(p, n){
                    return p.insertRow(-1);
                };
            else
                fnCreate = empty;
        }
        else if( "tr" == pName )
        {
            if( "td" == nodeName )
            {