从对象数据生成表

时间:2013-06-19 10:00:02

标签: javascript dom

我有一个类似下面的对象:

var obj = {
   a : {
      x : 1,
      y : 2,
      z : 3
   },
   b : {
      x : 1,
      y : 2,
      z : 3
   }
}

有了它,我想生成下表。格式如下所示

http://jsfiddle.net/gD87t/

我正在尝试从对象获取元素并尝试追加但与rowSpan值混淆

var tr = document.createElement('tr');
for(var i in obj){
   var td = document.createElement('td');
   td.rowSpan = ? // Here I am getting confused.
}

模板引擎可以解决我的问题吗? 做这个的最好方式是什么?

7 个答案:

答案 0 :(得分:4)

这是使用纯js中的递归函数执行此操作的一种方法:

function addObjectToTable(table, obj, tr) {
  var rows = 0;
  for (key in obj) {
    if (tr == null) {
      tr = document.createElement('tr');
      table.appendChild(tr);
    }  

    var td = document.createElement('td');
    td.textContent = key;
    tr.appendChild(td);

    var value = obj[key];
    if (typeof value != 'object') {
      var td = document.createElement('td');
      td.textContent = value;
      tr.appendChild(td);
      rows += 1;
    }
    else {
      var subrows = addObjectToTable(table, value, tr);
      td.setAttribute('rowspan',subrows);
      rows += subrows;
    }

    tr = null;
  }
  return rows;
}

这将被称为:

var table = document.createElement('table');
addObjectToTable(table,obj);
document.body.appendChild(table);

请注意,首次调用时, tr 参数为null,因为我们始终必须在顶层创建一个新行。当递归调用函数时, tr 参数从上层传入,因为较低级别最初会添加到其父对象的行中。

该函数返回添加的行数,因此当重新调用它时,调用者将知道将 rowspan 值设置为的内容。

Fiddle link

答案 1 :(得分:3)

我找不到处理循环的答案,我也决定在没有任何不必要的DOM写入的情况下这样做。此外,我没有按照OP请求的确切标记,因为我觉得嵌套表对于像这样的递归操作更方便 - 并且提供接近相同的视觉目的。

所以,这是我创建的函数:

function dataToTable (data) {
    var storage = [];
    return (function buildTable (data) {
        var table = '<table><tbody>';
        var name, value;
        // Add the object/array to storage for cirular detection.
        storage.push(data);
        for (name in data) {
            value = data[name];
            table += '<tr><td>' + name + '</td><td>';
            // If the value is an object we've put in storage (circular)
            if (storage.indexOf(value) !== -1) {
                table += '<em>Circular</em>';
            } else if (typeof value === 'object') {
                table += buildTable(value);
            } else {
                table += value;
            }
            table += '</td></tr>';
        }
        return table + '</tbody></table>';
    }(data));
}

以下是我用来测试的对象:

var obj = {
   a : {
      x : 1,
      y : 2,
      z : 3
   },
   b : {
      x : 1,
      y : 2,
       z : {
           test1: 0,
           test2: {
               test3: 1,
               test4: ['a','b','c']
           }
       }
   }
};
obj.c = obj;
obj.b.z.test2.test4.push(obj.a);

该函数将此对象转换为HTML表。你用桌子做什么取决于你。在我的小提琴上,我使用DOM将表添加到DIV(document.getElementById)。

http://jsfiddle.net/5RhXF/1/

我希望你能清楚地看到我的实施。

UPDATE ::

我决定在jQuery库上测试它,它工作正常!除了,函数打印为toString值没有良好的文本格式..这是有道理的,但不是很有帮助。所以,我认为这是一个很好的,简单的方法来查看框架/库的API和什么不是。因此,我为函数的语法高亮添加了美化,并在表生成器中添加了函数的类型检查,以及快速类来摆脱美化框周围的边框(因为表格单元格上已经有边框) 。如果有人对为源读/调试设计的版本感兴趣,那么这就是小提琴:

http://jsfiddle.net/5RhXF/7/

答案 2 :(得分:2)

更新:如果你不需要空单元格解决方案可以(查看小提琴http://jsfiddle.net/gD87t/11/

示例对象:

var obj = {
   a : {
      x : 1,
      y : 2,
       z : { 
           c : 4,
           d : 5
       }
   },
   b : {
      x : 1,
      y : 2,
      z : 3
   }
}

构建表的例程:

function merge(rows , inner) {
    inner.reduce(function (i, p) {
      rows.push(i)  
    })
}

function getRows(o) {
    var rows = []
    if (typeof o == 'object') {
       for (var k in o) {                        
           var innerRows = getRows(o[k])
           , firstCell = $('<td />')
                     .text(k)
                     .attr('rowspan',innerRows.length)           
           innerRows[0].prepend(firstCell)            
           rows = rows.concat(innerRows)        
    }
    } else {
       var tr = $('<tr />')
       , td = $('<td />').text(o)
       tr.append(td)
       rows.push(tr)
    }
    return rows
}

function buildTable(o, $t) {    
    var rows = getRows(o)    
    $t.append(rows)    
}

buildTable(obj, $('#table2'))

答案 3 :(得分:1)

rowspan的值是内部对象中的属性数。您可以使用Object.keys函数获取对象上的键列表,然后使用其length属性来确定有多少属性:

for(var i in obj){
   var td = document.createElement('td');
   td.rowSpan = Object.keys(obj[i]).length;
}

答案 4 :(得分:1)

var table = document.createElement('table');
var i, j;
var row, cell;
for(i in obj) {
    if(obj.hasOwnProperty(i)) {
        var row = document.createElement('tr');
        var cell = document.createElement('td');
        cell.rowSpan = Object.keys(obj[i]).length;
        cell.innerText = i;
        row.appendChild(cell);
        for(j in obj[i]) {
            if(obj[i].hasOwnProperty(j)) {
                cell = document.createElement('td');
                cell.innerText = j;
                row.appendChild(cell);
                cell = document.createElement('td');
                cell.innerText = obj[i][j];
                row.appendChild(cell);
                table.appendChild(row);
                row = document.createElement('tr');
            }
        }
    }
}
document.body.appendChild(table);

当然,它在jQuery中看起来不那么冗长。但看起来你想在普通的DOM中做到这一点。

See it woking

答案 5 :(得分:0)

好吧,这真的很棘手,但我认为已经完成了。顺便说一句,我仍然建议无表解决方案。您可以查看工作代码here

var obj = {
   a : {
      x : 1,
      y : 2,
       z : {c:1, d:3}
   },
   b : {
      x : 1,
      y : 2,
      z : 3
   }
}
var table = document.createElement('table');
function createTable (o, parentCells) {
    for (var key in o) {

        var row = document.createElement('tr');

        var cell = document.createElement('td');
        cell.rowSpan = 1;
        cell.innerText = key;
        if (typeof o[key] !== "object") {
           var cellv = document.createElement('td');
           cellv.innerText = o[key];
           row.appendChild(cell);        
           row.appendChild(cellv);
           table.appendChild(row);
        }
        else {

           for (var i = 0; i < parentCells.length; i++) {
              parentCells[i].rowSpan += Object.keys(o[key]).length;
           }

           cell.rowSpan += Object.keys(o[key]).length;

            var newParentCells = new Array(parentCells);
            newParentCells.push(cell);
            row.appendChild(cell);
            table.appendChild(row);
           createTable(o[key], newParentCells);
        }


    }
}
createTable(obj, []);
document.body.appendChild(table);

答案 6 :(得分:0)

如果您希望像在问题中一样使用document.createElement,最简单的方法可能是这样的:

function generateTable(o) {
    var table, tr, td, i, j, l, keys;

    table = document.createElement('table')

    for(i in o){
        tr = document.createElement('tr');
        table.appendChild(tr);
        td = document.createElement('td');
        keys = Object.keys(o[i]);
        td.rowSpan = keys.length;
        td.textContent = i;
        tr.appendChild(td);
        x=0;

        for(j=0;j<keys.length;j++) {
            if(j) {
                tr = document.createElement('tr');
                table.appendChild(tr);            
            }
            td = document.createElement('td');
            td.textContent = keys[j];
            tr.appendChild(td);
            td = document.createElement('td');
            td.textContent =o[i][keys[j]];
            tr.appendChild(td);
        }
    }
    return table;
}

CAVEAT:Object.keys在旧浏览器中不可用,因此您需要这样的polyfill:

(摘自MOZILLA DEVELOPER NETWORK

if (!Object.keys) {
  Object.keys = (function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length;

    return function (obj) {
      if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');

      var result = [];

      for (var prop in obj) {
        if (hasOwnProperty.call(obj, prop)) result.push(prop);
      }

      if (hasDontEnumBug) {
        for (var i=0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
        }
      }
      return result;
    };
  })();
}

另外,有一个更简单的polyfill,可以很好地覆盖大多数情况:

(取自Token Posts

if (!Object.keys) Object.keys = function(o) {
  if (o !== Object(o))
    throw new TypeError('Object.keys called on a non-object');
  var k=[],p;
  for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
  return k;
}

你可以在这里看到它的一个小提琴:http://jsfiddle.net/uyJv2/