12

我有一个如下所示的 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"]
}

它应该像这个小提琴一样被编译成 HTMLhttp://jsfiddle.net/RwdWq/一样编译成 HTML

这是我写的代码

 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);
                    }
                }
        }

目前它的工作方式是这样的http://jsfiddle.net/RwdWq/3/

如果数据很大,我该如何解决这个问题并且页面有时会死掉。帮我提高性能,我想检查一下没有桌子的可能性。

4

5 回答 5

15

我知道如何解决你的问题。有不止一种解决方案。你的代码很长,我所有的解决方案都需要重写你写的几乎所有东西,所以我只放最重要的部分。先到先。

1. 你的 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]
    ]
}

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

2.json解析算法

您从一些函数 render 开始,它可能是 imho 命名空间或构造函数。然后你创建了很多小函数,然后是算法的looooong部分。

尝试将您的算法拆分为更多功能,并将每个功能设置为一些要点,例如:

function parseJson() { ... } 

function parseHeader() { ... } 

function parseRows() { ... } 

function renderColumnsHeaders() { ... }

function renderRow() { ... }

您也可以将其作为对象。然后您可以轻松添加各种功能,如排序、过滤或其他内容。

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);

这样的代码几乎是专业的;)易于维护,易于扩展,易于从中制作插件;)

3.提高表格渲染性能

为什么表格渲染这么慢?好吧,这可能不是因为表格,而是因为您试图立即包含大型html。使用 javascript 制作 DOM 元素或直接将 DOM 元素写入 html 都没有关系。如果有很多元素,渲染它总是需要一段时间。

不幸的是,html 渲染是同步的。这意味着一切都被冻结,直到你的功能没有完成。它与例如动画或 ajax 的工作方式不同(它被称为“异步 JavaScript 和 XML”)。

您拥有的第一个选项是逐步使用 ajax 和加载表。为一次可以加载的行数设置一些限制。然后: * 调用 ajax * 从 ajax 获取 json * 解析它 * 获取行并渲染它们 * 重复 然后你的浏览器不会冻结。渲染仍将是同步的,但在两次渲染调用之间会有“窗口”,因此各种事件和其他事情都在这里完成。只是很多短尖峰不会像一个长尖峰一样冻结您的浏览器。

另一种选择是根据用户位置加载数据。用户无法一次查看所有数据。这与第一个选项非常相似,称为“无限滚动”。您已经可以在 facebook、google 图片上看到...有 jquery 插件。

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

编辑:

没有桌子的可能性

可以使用 div 元素或者一些列表而不是表格。我不推荐它。表格用于大数据,浏览器的开发人员正在尽最大努力提高表格的性能。也许你听说过或读过一些关于表格速度慢的猜测。他们很慢,但这些时间已经过去了。所以在这种情况下坚持使用表格。

于 2013-06-26T13:15:50.993 回答
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>

JS在这里摆弄。

于 2013-06-30T12:52:29.463 回答
2

更新 1

我确实尝试了John Resig的微模板引擎,发现它太麻烦了,所以我觉得下划线是解决这个问题的一个不错的选择。

更新 2

请参阅:https ://stackoverflow.com/questions/17298292/building-the-table-html-from-json-data-with-js-template/17353525#17353525

这是一个适用于您的数据的小提琴,在另一个答案中更新,但在此处为该问题的未来读者提供(并且stackoverflow不允许答案包含没有代码的小提琴,所以我也在这里粘贴):

使用下划线和一点点 jQuery,但如果您还没有使用 jQuery(易于替换),可能不足以保证它:

http://jsfiddle.net/Wzpde/8/

<!-- BEGIN: Underscore Template Definition. -->
<script type="text/template" class="template">    
    <!-- collect some info, better to do outside of temmplate,
         but to keep with the idea of doing it all in a template -->
    <% 
    //////
    // Collect the header data into the hdrs variable
    ////

    var colsUnderSubCols = data["Columns under subColumns"]
    var hdrs = [[]]; // Initialize the hdrs array as an array with one array element.

    // Walk through each column header
    _.each(data["Column Headers"], function(tophdr, tind){ 
        var n=1;

        // First take the first row of headers, since they are specified in a different format
        // than subsequent headers (they are specified as strings, not arrays).

        hdrs[0].push(tophdr.slice(0, 1));

        // Next, take the subsequent arrays that represent the rest of the sub-columns.
        // Note that these do not include the final columns, as those are also specified
        // differently due to the awkward JSON structure and are tacked on at the very end.

        var otherhdrs = tophdr.slice(1, tophdr.length);

        // walk through each of the array-based sub-columns

        for(var m=0; m<otherhdrs.length; m++){

            // As we are building the header columns by name, we need to iterate over the
            // over the number of headers that will be in the row representing this depth
            // of sub-column, so we aggregate the multiplicative value of each:

            n *= otherhdrs[m].length;

            // using the final number of columns for this depth, we can determine the 
            // column name by the remainder of our iteration index divided by the length
            // of unique column names at our current column header depth.  

            for(var i=0; i<n; i++){

                // Initialize this container of headers for this depth if it is not 
                // yet initialized:

                if(!hdrs[m+1]) hdrs[m+1] = [];

                // i represents the iteration index into the total number of column headers
                // at the current column header depth, and otherheaders[m] is the array of
                // unique column header names at this depth, so taking the modulo allows us
                // the correct column name:

                hdrs[m+1].push(otherhdrs[m][i%otherhdrs[m].length]);
            }
        }
    });

    // Finally, the last set of column is not specified like either the first column or the 
    // sub-columns in the JSON structure provided, so we tack those on last.
    // They can be tacked on by iterating over the last sub-header array with a nested 
    // iteration of these final column names:

    var finalhdrs = [];
    for(var i=0; i<hdrs[hdrs.length-1].length; i++){
        for(var j=0; j<colsUnderSubCols.length; j++){
            finalhdrs.push(colsUnderSubCols[j]);
        }
    }

    // Push them as the last set of header names:

    hdrs.push(finalhdrs);

    //////
    // Collect group rowspan information into a groupspan variable
    ////

    var dft = data["Data for Table"];
    groupspan = [];

    // Each row is going to have some <td>'s applied, but it depends on whether or not
    // we previously saw the group name, so that can be done as a look-behind so-to-speak:

    for(var m=1; m<dft.length; m++){

        //Initialize the look-behind groupspan arrays if not previously initialized:
        // This is certainly an opportunity for some optimization.  But
        // as it is can provide a baseline for functionality testing:

        if(! groupspan[m-1]){ 
            groupspan[m-1] = [];
            _.each(dft[m-1].name,function(item,ind){ groupspan[m-1][ind] = 1; });
        }

        //Initialize the groupspan arrays if not previously initialized:
        // This is certainly an opportunity for some optimization.  But
        // as it is can provide a baseline for functionality testing:

        if(! groupspan[m]){
            groupspan[m] = [];
            _.each(dft[m].name,function(item,ind){ groupspan[m][ind] = 1; });
        }

        //Now that initialization of the look-behind goupspan info and the
        // groupspan info has taken place, we can walk through them and
        // build out some information that lets the rowspans be built later.

        //Provided a 3-deep group configuration and 4 rows of data, we wind up
        // with a structure like the following:
        //   [ [ sp1, sp2, sp3 ], [ sp4, sp5, sp6 ], [ sp7, sp8, sp9 ], [ sp10, sp11, sp12 ] ]
        // If an sp* above is == 0, then we skip putting in a <td> 
        // Otherwise, we prepend a <td> with the rowspan set to the sp* variable
        // So essentially, we are collecting whether or not a <td> should be prepended
        // to the row, and if so,  what its rowspan should be:

        for(var n=0; n<dft[m].name.length; n++){
            if(!!dft[m-1] && dft[m].name[n] === dft[m-1].name[n]){
                groupspan[m-1][n]++;
                groupspan[m][n] = 0;
            }else{
                break;
            }
        }
    }

    %>

<table>
    <% 
    //////
    // Now we can build out the table using the information we've gathered above:
    ////

    // For each header in the hdrs variable populated above:

    for(var i=0; i<hdrs.length; i++){
    %>
    <tr>
        <%

        // Be sure to put the group headers first, before the column headers:

        if(i===0){
            _.each( data["row group headers"], function( hdr ){ 
            %>
                <td rowspan="<%- hdrs.length %>" >
                     <%- hdr %>
                </td>
            <% 
            });
        }

        // Now put the column headers in:

        _.each( hdrs[i], function( hdr ){ %>
            <td colspan="<%- hdrs[hdrs.length-1].length/hdrs[i].length%>">
                <%- hdr %>
            </td>
        <% }); %>
    </tr>
    <%
    } 

    // Done with group and column headers, now for each data row
    // (but not the group titles, which get injected into the rows below
    //  with appropriate rowspan):

    for(var d=0; d<dft.length; d++){
        var dftdata = dft[d].data;
    %>
        <tr>
            <%

            // Using the groupspan information detailed above,
            // we can layer in the group titles with the appropriate
            // number of <td>'s and appropriate rowspan attributes:

            for(var i=0; i<groupspan[d].length;i++){
                if(groupspan[d][i] === 0){ 
                    continue;
                }else{
            %>
                <td rowspan="<%- groupspan[d][i] %>" >
                    <%- dft[d].name[i] %>
                </td>
            <%
                }
            }

            // All of the heavy lifting is done, now we can put the data into the table:

            _.each( dftdata, function( item ){  
                for(var di=0; di<data["Columns under subColumns"].length; di++){ 
                %>
                <td>
                    <%- item[di] %>
                </td>
                <%
                }
                }); %>
        </tr>
    <%
    }
    %>

</table>
</script>

具有嵌套列和组的数据:

var data = { "Column Headers" : [ // Hierarchy is not limited to two levels, it is n level
    [  "Column1" , ["Column1's SubColumn 1", "Column1's SubColumn 2", "Column1's SubColumn 3"], ["abc", "Hello", "A"], ["t1","t2"] ], 
    [  "Column2" , ["Column2's SubColumn 1", "Column2's SubColumn 2", "Column2's SubColumn 3"], ["abc", "Hello", "A"], ["t1","t2"] ],
    [  "Column3" , ["Column3's SubColumn 1", "Column3's SubColumn 2", "Column3's SubColumn 3"], ["abc", "Hello", "A"], ["t1","t2"] ]  
],
"Columns under subColumns" : ["I am column 1", "I am column 2"],
"Data for Table" :[
    { "name": ["Group 1","Sub Group A", "SubSub1"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 1","Sub Group B", "SubSub2"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 2","Sub Group A", "SubSub3"], "data" : [[0,1],[1,2],[45,20],[0,1],[1,2],[45,20]]},
    { "name": ["Group 2","Sub Group B", "SubSub4"], "data" : [[0,1],[1,2],[45,20],[0,86],[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 1", "Sub group Header 2"]
}

_.templateSettings.variable = "data";

// Grab the HTML out of our template tag and pre-compile it.
var template = _.template($( "script.template" ).html()); 
// Render the underscore template and inject it into the DOM
$("body").prepend(template(data));

更新 3

此处的新小提琴修复了组问题:

http://jsfiddle.net/Ju7xz/6/

于 2013-06-27T22:08:12.293 回答
1

我确实从个人基准测试和许多在线可用的测试用例中知道 DOM 操作正在影响代码的性能(您正在创建一个 DOM 以及在数据的每个节点上执行大约三个其他 DOM 操作。)

此外,在较小程度上,在 JavaScript 中不可变的许多字符串的处理/连接是此代码中的一个因素。例如,一次创建一个包含 1,000 个数字的数组并使用 将它们写出.join(' ') 可使写入前一百万个整数的工作的性能提高约 40 倍(与一次写出一个整数相比)。

请参阅我上面的评论,如果您认为我尝试解决这个问题值得,请告诉我。(像这样的问题对我来说很有趣,但如果你已经对现有的答案基本满意的话,还不够有趣。)

于 2013-06-27T23:33:35.357 回答
-2

例如,您可以使用 jQuery 遍历函数来遍历您的 JSON,或者使用纯 javascript,然后您可以使用EJS
作为 EJS 的替代品,您可以使用PURE,它完全不同,但看起来更强大。

于 2013-06-27T16:22:02.093 回答