从JSON数据构建表格布局(代码改进)

Exc*_*ion 12 javascript

我有一个像下面的JSON对象,我正在使用下面的包装函数将JSON转换为HTML

从JSON检索的部分:

var data = { "Column Headers" : [ // Hierarchy is not limited to two levels, it is n level
    [  "Column1" , ["Column1's SubColumn 1", "Column1's SubColumn 2"] ], 
    [  "Column2" , ["Column2's SubColumn 1", "Column1's SubColumn 2"] ],
    [  "Column3" , ["Column3's SubColumn 1", "Column1's SubColumn 2"] ]  
],
"Columns under subColumns" : ["I am column 1", "I am column 2"],
"Data for Table" :[
    { "name": ["Group 1","Sub Group 1"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 1","Sub Group 2"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 2","Sub Group 1"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 2","Sub Group 2"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]}
], // here the hierarchy is not limited to two sub groups.. it could be any number..
"row group headers" : ["Group 1 Header", "Sub group Header"]
}
Run Code Online (Sandbox Code Playgroud)

它应该编译成HTML,就像在这个小提琴http://jsfiddle.net/RwdWq/

这是我写的代码

 var render = function(data){
    var formattedData = {};
    function returnRowsData( obj ) {
      return obj["Data for Table"];
    }
    function returnColsData(obj) {
      return obj["Column Headers"];
    }
    function rowLabels(obj) {
      return obj["row group headers"];
    }
    function bottomColLabels(obj) {
      return obj["Columns under subColumns"];
    }
    function simplifyCols(obj) {
      var reform = {
        table : {}
      }, bottomLabs = bottomColLabels(data);
      var y = 0;
      for(var i = 0, l = obj.length; i < l; i++){
        var key = obj[i];
        key.push(bottomLabs);
        for (var j = 0, m = key.length; j < m; j++) {
           var colspan = 1;
           for (var k = j + 1; k < m; k++) {
              colspan *= key[k].length; 
           }
           reform.table[j] = reform.table[j] || [];
           if (j == 0) {
            y += colspan;
           }
           reform.table[j].push({
             span : colspan,
             data : key[j]
           });
          }
        }
        reform.count = y;
        return reform;
     }
     var formatted = simplifyCols( returnColsData( data[0]) ) || {};
     var cols = formatted.table;
     //console.log(cols);
     formattedData.cols = cols;
     var keys = Object.keys(cols).sort(function(a, b){
         return a - b;
     });
     var table = document.createElement('table');
     for (var i = 0, l = keys.length - 1; i < l; i++) {
        var keyData = cols[keys[i]], tr = document.createElement('tr');
        if (i == 0) {
            var rLs = rowLabels(data[0]);
            for (var rL = 0; rL < rLs.length; rL++) {
              var td = document.createElement('th');
              td.innerHTML = rLs[rL];
                td.rowSpan = keys.length;
                td.className = "rowLabel";
                tr.appendChild(td);
            }
        }
        table.appendChild(tr);
        for (var j = 0, m = keyData.length; j < m; j++) {
            var eleData = keyData[j].data;
            if(eleData instanceof Array){
             for (var k = 0, n = eleData.length; k < n; k++) {
               var td = document.createElement('td');
               td.innerHTML = eleData[k];
               td.colSpan = keyData[j].span;
               td.className = "colHeaders";
               tr.appendChild(td);
              }
             }else{
               var td = document.createElement('td');
               td.innerHTML = keyData[j].data;
               td.colSpan = keyData[j].span;
               td.className = "colHeaders";
               tr.appendChild(td);
             }
        }
        table.appendChild(tr);
     }
     var tr = document.createElement('tr');
     var noOfbottomLabs = formatted.count ?  formatted.count / bottomLabs.length : bottomLabs.length;
                for (var i = 1; i <= noOfbottomLabs; i++) {
                    for (var j = 0; j < bottomLabs.length; j++) {
                        var td = document.createElement('td');
                        td.innerHTML = bottomLabs[j];
                        td.className = "bottomLabs";
                        tr.appendChild(td);
                    }
                }
                table.appendChild(tr);
                function setToValue(obj, value, path) {
                    var parent = obj;
                    for (var i = 0; i < path.length - 1; i += 1) {
                        parent[path[i]] = parent[path[i]] || {}
                        parent = parent[path[i]];
                    }
                    parent[path[path.length-1]] = value;
                }   
                var rowsData = returnRowsData(data), tempRows = {}, tempArr = {};
                for (var i = 0, l = rowsData.length; i < l ; i++) {
                    var names = rowsData[i].name, _data  = rowsData[i].data;
                    setToValue(tempRows, _data, names);
                }
                var similiar = {};
                for (var ele = 0, lent = rowsData.length; ele < lent; ele++) {
                    var curD = rowsData[ele], tr = document.createElement('tr');
                    for (var i = 0; i < curD.name.length; i++) {
                        var td = document.createElement('td');
                        td.innerHTML = curD.name[i] || "-";
                        td.setAttribute('val', curD.name[i]);
                        td.className = "rowHeader";
                        similiar[curD.name[i]] = 0;
                        tr.appendChild(td);
                    }
                    var merg = [];
                    merg = [].concat.apply( merg, curD.data);
                    for (var i = 0; i < merg.length; i++) {
                        var td = document.createElement('td');
                        td.innerHTML = merg[i] || "-";
                        td.className = "tdData";
                        tr.appendChild(td);
                    }
                    table.appendChild(tr);
                    console.log(merg);
                }
                document.body.appendChild(table);
                for (var text in similiar) {
                    var elements = document.querySelectorAll('[val="' + text + '"]');
                    elements[0].rowSpan = elements.length;
                    for (var j = 1; j < elements.length; j++) {
                        var v = elements[j];
                        v.parentNode.removeChild(v);
                    }
                }
        }
Run Code Online (Sandbox Code Playgroud)

目前它正在像这样工作http://jsfiddle.net/RwdWq/3/

如果数据很大,我怎样才能解决这个问题,并且页面有时会死掉.帮助我提高性能,我想检查没有表格的可能性.

Ent*_*ack 15

我知道如何解决你的问题.还有一个解决方案.你的代码很长,我的所有解决方案都要求重写你写的几乎所有东西,所以我会把这里最重要的部分放在这里.首先是第一个.

你的json结构很糟糕.我建议你改一点.这是我的:

编辑:正如Bergi所说,我的结构没有精确定义标题中单元格的顺序.你的这种方式更好.

var data = {

    "Column Headers" : {
        "Column1" : { 
            "Column1's SubColumn 1" : ["I am column 1", "I am column 2"], 
            "Column1's SubColumn 2" : ["I am column 1", "I am column 2"]
        },
        "Column2" : {
            "Column2's SubColumn 1": ["I am column 1", "I am column 2"], 
            "Column2's SubColumn 2" : ["I am column 1", "I am column 2"]
        },
        "Column3" : {
            "Column3's SubColumn 1": ["I am column 1", "I am column 2"], 
            "Column3's SubColumn 2": ["I am column 1", "I am column 2"]
        } 
    },

    "Row Headers" : {
        "Column1" : ["Column1's SubColumn 1", "Column1's SubColumn 2"],
        "Column2" : ["Column2's SubColumn 1", "Column1's SubColumn 2"],
        "Column3" : ["Column3's SubColumn 1", "Column1's SubColumn 2"] 
    },

    "Data for Table" : [
    [0, 1, 1, 2, 45, 20, 0, 1, 1, 2, 45, 20],
    [0, 1, 1, 2, 45, 20, 0, 1, 1, 2, 45, 20],
    [0, 1, 1, 2, 45, 20, 0, 1, 1, 2, 45, 20],
    [0, 1, 1, 2, 45, 20, 0, 1, 1, 2, 45, 20]
    ]
}
Run Code Online (Sandbox Code Playgroud)

它仍然不完美,但至少它是人类可读的.

2. Json解析算法

你开始使用一些函数渲染,它可能是imho命名空间或构造函数.然后你创建了很多小函数,然后将它们放在算法的各个部分之后.

尝试将算法拆分为更多函数,并将每个函数设置为一些主要点:

function parseJson() { ... } 

function parseHeader() { ... } 

function parseRows() { ... } 

function renderColumnsHeaders() { ... }

function renderRow() { ... }
Run Code Online (Sandbox Code Playgroud)

你也可以把它作为一个对象.然后,您可以轻松添加各种功能,如排序,过滤或其他东西.

var MyDataTable = (function () { // this is namespace

    var MyDataTable = function(json) { // this is object constructor, it will probably just loads data
        this.data = json;
    }

    // these functions are visible only inside namespace
    function parseJson() { ... } 

    function parseHeader() { ... } 

    function parseRows() { ... } 

    function renderColumnsHeaders() { ... }

    function renderRow() { ... }

    MyDataTable.prototype.renderTable = function (where) { ... } // this will render whole table

    MyDataTable.prototype.filterTableBy = function () { ... } // filter that you can implement later


return MyDataTable; // returns constructor
})();


var data = { ... } // some data

var firstTable = new MyDataTable(data);
firstTable.renderTable(someDivOrAnyElementYouWant);
Run Code Online (Sandbox Code Playgroud)

这样的代码是almoast profi;)易于维护,易于扩展,易于从中插件;)

3.提高表呈现性能

为什么表格渲染这么慢?嗯,这可能是不是因为表的,但因为大的HTML,您正试图在包括一次.如果你使用javascript来制作DOM元素,或者你将DOM元素直接写入html,这无关紧要.如果有很多元素,渲染它总是需要一段时间.

不幸的是,html渲染是同步的.这意味着一切都被冻结,直到你的功能没有完成.它不像动画或ajax那样工作(它被称为'异步JavaScript和XML'用于共振).

您拥有的第一个选项是逐步使用ajax和load table.设置一次可以加载的行数限制.然后:*调用ajax*从ajax获取json*解析它*获取行并渲染它们*重复然后你的浏览器不会冻结.渲染仍然是同步的,但两个渲染调用之间会有"窗口",所以这里会完成各种事件和其他事情.简单地说,很多短尖峰不会像一个长尖峰一样冻结你的浏览器.

另一种选择是根据用户位置加载数据.用户无法一次查看所有数据.这与第一个选项非常相似,它被称为"无限滚动".你可能已经在facebook上看到了谷歌图片...这里有jquery插件.

最后一个选项是使用setIntervalsetTimeout本机javascript函数.除了你不使用ajax之外,这种技术也适用于许多较短的尖峰.您首先在JavaScript中加载所有数据.然后使用这些函数逐步渲染它们.由于这些函数是异步的,因此效果很好.但它需要更深入的了解.我有异步调用的例子.有些div会像彩虹那样改变颜色,但它不是动画:http: //windkiller.g6.cz/async/async-html-render.html

编辑:

可靠性使它没有桌子

可以使用div元素或者某些列表而不是表格.我不推荐它.这里有大数据表,浏览器开发人员正在尽最大努力提高表的性能.也许你听过或读过一些表格很慢的猜测.它们很慢,但这些时间都消失了.所以在这种情况下坚持使用表格.

  • 你的JSON结构更糟糕 - 它不能确定`Column 1`是否在`Column 2`之前.JSON对象是**无序的**键值对! (5认同)

小智 3

这是我的基于 jQuery 的解决方案:

<!DOCTYPE html>
<html>
    <head>
      <title>json2html</title>
      <style>
        #target     { display: none }
        td          { text-align: center; border: 1px solid gray; }
        .colHeaders { }
        .rowLabel   { }
        .bottomLabs { }
        .rowHeader  { }
        .tdData     { }
      </style>
      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
      <script type="text/javascript">
      var json2html = (function () {
          var json2html = function(json) {
              this.data = json;
              this.rgh  = json["row group headers"];
              this.ch   = json["Column Headers"];
              this.dft  = json["Data for Table"];
              this.cus  = json["Columns under subColumns"];
              this.sc   = this.cus.length;

              var depth = 0;
              for (var i in this.ch) {
                  depth = Math.max(depth, this.ch[i].length);
              }
              this.depth = depth;
          }

          function repeat(pattern, count) {
              var result = pattern;
              while (--count > 0) {
                  result += pattern;
              }
              return result;
          }

          function join(data, td) {
              try {
                  return td + data.join('</td>' + td) + '</td>';
              } catch (e) {
                  return td + data + '</td>';
              }
          }

          function renderSubHeader(data, index, sc) {
              var html = '';
              $.each(data, function() {
                  var cs = sc;
                  for (var i = index + 1; i < this.length; i++) {
                      cs *= this[i].length;
                  }
                  var value = (typeof this[index] != 'undefined') ? this[index] : '';
                  var cell = join(value, '<td class="colHeaders"' + ((cs > 1) ? ' colspan="'+cs+'">' : '>'));
                  if (index > 1) {
                      for (var i = index - 1; i > 0; i--) {
                          html += repeat(cell, this[i].length);
                      }
                  } else {
                      html += cell;
                  }
              });
              return(html);
          }

          function renderHeader(data) {
              var html = '<tr>';
              html += join(data.rgh, '<th rowspan="'+(data.depth + 1)+'" class="rowLabel">');
              html += renderSubHeader(data.ch, 0, data.sc);
              html += '</tr>';
              for (var index = 1; index < data.depth; index++) {
                  html += '<tr>';
                  html += renderSubHeader(data.ch, index, data.sc);
                  html += '</tr>';
              };
              return html;
          }

          function renderColHeader(data) {
              var html = '<tr>';
              $.each(data.dft[0].data, function(index) {
                  html += join(data.cus, '<td class="bottomLabs">');
              });
              return html+'</tr>';
          }

          function renderData(data) {
              var html = '';
              $.each(data.dft, function(nr) {
                  html += '<tr>';
                  html += join(this.name, '<td class="rowHeader">');
                  $.each(this.data, function() {
                      html += join(this, '<td class="tdData">');
                  });
                  html += '</tr>';
              });
              return html;
          }

          function mergeCells(cells, attr) {
              var rs = 1;
              var old = null;
              cells.each(function() {
                  if (old == null) {
                      old = $(this);
                      rs = 1;
                  } else {
                      if ($(this).text() == old.text()) {
                          rs++;
                          $(this).remove();
                      } else {
                          if (rs > 1) {
                              old.attr(attr, rs);
                              rs = 1;
                          }
                          old = $(this);
                      }
                  }
              });
              if (rs > 1) {
                  old.attr(attr, rs);
              }
          }

          json2html.prototype.renderTable = function(thead, tbody) {
              var startTime = new Date();
              thead.html(
                  renderHeader(this) + 
                  renderColHeader(this)
              );
              tbody.html(renderData(this));
              for (var i = this.rgh.length; i > 0; i--) {
                  mergeCells($('td:nth-child('+i+')', tbody), 'rowspan');
              };
              var endTime = new Date();
              console.log('renderTable('+this.dft.length+' rows): ' + (endTime - startTime) + 'ms');
          }

          return json2html;
      })();

      //==================================================================================================

      var data1 = {
          "Column Headers": [
              ["Column1", ["Column1's SubColumn 1", "Column1's SubColumn 2"] ], 
              ["Column2", ["Column2's SubColumn 1", "Column1's SubColumn 2"] ],
              ["Column3", ["Column3's SubColumn 1", "Column1's SubColumn 2"] ]
          ],
          "Columns under subColumns": [
              "I am column 1", 
              "I am column 2"
          ],
          "Data for Table": [
              { "name": ["Group 1","Sub Group 1"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] },
              { "name": ["Group 1","Sub Group 2"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] },
              { "name": ["Group 1","Sub Group 2"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] },
              { "name": ["Group 1","Sub Group 2"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] },
              { "name": ["Group 2","Sub Group 1"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] },
              { "name": ["Group 2","Sub Group 2"], "data": [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]] }
          ],
          "row group headers": [
              "Group 1 Header", 
              "Sub group Header"
          ]
      };

      var data2 = { 
          "Column Headers" : [
              [ "Column1", ["Column1's SubColumn 1", "Column1's SubColumn 2", "Column1's SubColumn 3"], ["abc", "Hello1"] ], 
              [ "Column2", ["Column2's SubColumn 1", "Column2's SubColumn 2", "Column2's SubColumn 3"], ["def", "Hello2"] ],
              [ "Column3", ["Column3's SubColumn 1", "Column3's SubColumn 2", "Column3's SubColumn 3"], ["ghi", "Hello3"] ]  
          ],
          "Columns under subColumns": [
              "I am column 1", 
              "I am column 2"
          ],
          "Data for Table": [
              { "name": ["Group 1","Sub Group 1", "abc"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 1","Sub Group 1", "def"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 2","Sub Group 1", "ghi"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 2","Sub Group 2", "jkl"], "data": [[0,1],[1,2],[45,20],[0,86],[1,2],[45,20],[0,1],[1,2],[45,20],[0,86],[1,2],[45,20],[0,1],[1,2],[45,20],[0,86],[1,2],[45,20]] }
          ], 
          "row group headers": [
              "Group 1 Header", 
              "Sub group Header",
              "abc"
          ]
      };

      var data3 = { 
          "Column Headers" : [
              [ "Column1", ["Column1's SubColumn 1", "Column1's SubColumn 2", "Column1's SubColumn 3"], ["abc", "Hello1"] ], 
              [ "Column2", ["Column2's SubColumn 1", "Column2's SubColumn 2", "Column2's SubColumn 3"] ],
              [ "Column3", ["Column3's SubColumn 1", "Column3's SubColumn 2", "Column3's SubColumn 3"], ["ghi"] ]  
          ],
          "Columns under subColumns": [
              "I am column 1", 
              "I am column 2"
          ],
          "Data for Table": [
              { "name": ["Group 1"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 1"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 2"], "data": [[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20],[0,1],[1,2],[45,20],[0, 1],[1,2],[45,20]] },
              { "name": ["Group 2"], "data": [[0,1],[1,2],[45,20],[0,86],[1,2],[45,20],[0,1],[1,2],[45,20],[0,86],[1,2],[45,20]] }
          ], 
          "row group headers": [
              "Group 1 Header", 
          ]
      };

      $(function () {
          var data = [data1, data2, data3];
          $('#dataset').change(function() {
              $('#target').hide();
              if (this.value != '') {
                  $('#target thead').empty();
                  $('#target tbody').empty();
                  var html = new json2html(data[this.value]);
                  html.renderTable($('#target thead'), $('#target tbody'));
                  $('#target').fadeIn('slow');
              }
          });
      });
      </script>    
    </head>
    <body>
      <label for="dataset">Choose a dataset</label>
      <select id="dataset">
          <option value=""></option>
          <option value="0">Data 1</option>
          <option value="1">Data 2</option>
          <option value="2">Data 3</option>
      </select>          
      <table id="target">
        <thead>
        </thead>
        <tbody>
        </tbody>
      </table>
    </body>
Run Code Online (Sandbox Code Playgroud)

JS在这里摆弄。