4

我有一个像下面这样的对象:

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.
}

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

4

7 回答 7

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参数为空,因为我们总是必须在顶层创建一个新行。但是,当函数被递归调用时,tr参数是从上层传入的,因为下层最初将添加到其父对象的行中。

该函数返回添加的行数,因此当它被递归调用时,调用者将知道将rowspan值设置为什么。

小提琴链接

于 2013-06-23T17:44:36.620 回答
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/

我希望你会发现我的实现很清楚。

更新::

我决定在 jQuery 库上对此进行测试,它成功了!除了,这些函数作为它们的 toString 值打印,没有好的文本格式。这是有道理的,但不是很有帮助。所以,我认为这是一种很好且简单的方法来查看框架/库的 API 等等。因此,我为函数的语法高亮添加了 prettify,还在表格生成器中添加了函数的类型检查,以及一个快速类来消除美化框周围的边框(因为表格单元格上已经有边框了) . 如果有人对为源代码阅读/调试而设计的版本感兴趣,这里是小提琴:

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

于 2013-06-23T15:47:36.750 回答
2

UPDATED: if you don't need empty cells solution could be (check fiddle http://jsfiddle.net/gD87t/11/)

Example object :

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

And routine to build table:

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'))
于 2013-06-22T13:14:55.497 回答
1

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

for(var i in obj){
   var td = document.createElement('td');
   td.rowSpan = Object.keys(obj[i]).length;
}
于 2013-06-19T10:28:29.713 回答
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 中做这件事。

看到它在醒来

于 2013-06-22T12:42:51.710 回答
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 开发者网络

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 可以很好地涵盖大多数情况:

(取自令牌帖子

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/

于 2013-06-26T21:21:41.203 回答
0

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

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);
于 2013-06-22T18:23:01.600 回答