【问题标题】:Javascript Create complex table header from jsonJavascript从json创建复杂的表头
【发布时间】:2014-12-12 15:39:46
【问题描述】:

我想用 colspan 创建一个 html 表头,但我迷路了; 我有这个json:

var metadata = [{
"colIndex": 0,
    "colType": "String",
    "colName": "PM"
}, {
"colIndex": 1,
    "colType": "String",
    "colName": "PROD"
}, {
"colIndex": 2,
    "colType": "Numeric",
    "colName": "NR/M1"
}, {
"colIndex": 3,
    "colType": "Numeric",
    "colName": "NR/M2"
}, {
"colIndex": 4,
    "colType": "Numeric",
    "colName": "NR/M3"
}, {
"colIndex": 5,
    "colType": "Numeric",
    "colName": "NR/M4"
}, {
"colIndex": 6,
    "colType": "Numeric",
    "colName": "NR/M5"
}, {
"colIndex": 7,
    "colType": "Numeric",
    "colName": "NR ART/M6"
}, {
"colIndex": 8,
    "colType": "Numeric",
    "colName": "NR ART/M1"
}, {
"colIndex": 9,
    "colType": "Numeric",
    "colName": "NR ART/M2"
}, {
"colIndex": 10,
    "colType": "Numeric",
    "colName": "NR ART/M3"
}, {
"colIndex": 11,
    "colType": "Numeric",
    "colName": "NR ART/MX"
}];

表格标题将主要基于拆分“colType”:“Numeric”

+------+-------+-----+--------+-----+-----+-----+-----+------+----+----+----+
|      |       |               NR               |           NR ART          |
+ PAM  | PROD  +-----+--------+-----+-----+-----+-----+------+----+----+----+
|              | M1   | M2    | M3  | M4  | M5  | M6  | M1  | M2  | M3 | MX |
+------+-------+-----+--------+-----+-----+-----+-----+------+----+----+----+

首先我尝试在 colType 为数字的地方拆分 colName

var arr = [], a;
$.each(metadata, function (i, v) {
  arr.push(v.colName.split("/"));
  a = v.colName.split("/").length;
});

接下来,我有独特的父母(?),但我能用这个做什么? 我想我必须在层次结构中遍历这个数组(父 - 子,然后构造 html 标题)。

数组是动态的。

有什么建议吗?请和谢谢。

【问题讨论】:

  • 我的回答有帮助吗?

标签: javascript jquery html json


【解决方案1】:

下面的代码应该可以完成工作,请检查

var arr = {}, a;
$.each(metadata, function (i, v) {
     arr[v.colName.split("/")[0]] = arr[v.colName.split("/")[0]].push(v.colName.split("/")[1]) || [] ;       
});

为了显示它

for(t1 in arr){
    if(t1.length > 0){
          //has children element
    }
    else{
        //has only 1 element
    }
}

【讨论】:

    【解决方案2】:

    使这复杂化的一件事是您的表中需要colspanrowspan。因此,您需要根据 colName 属性对对象进行组计数。

    这是一个粗略的示例,说明如何做到这一点。解释在代码 cmets 中。 (此示例使用colspan 的组计数,但rowspan 是硬编码的。您也可以对rowspan 进行组计数。

    我在这个例子中使用 jQuery 只是为了创建和添加表格。核心逻辑是纯 Javascript

    片段

    var metadata = [{"colIndex": 0, "colType": "String", "colName": "PM"}, {"colIndex": 1, "colType": "String", "colName": "PROD"}, {"colIndex": 2, "colType": "Numeric", "colName": "NR/M1"}, {"colIndex": 3, "colType": "Numeric", "colName": "NR/M2"}, {"colIndex": 4, "colType": "Numeric", "colName": "NR/M3"}, {"colIndex": 5, "colType": "Numeric", "colName": "NR/M4"}, {"colIndex": 6, "colType": "Numeric", "colName": "NR/M5"}, {"colIndex": 7, "colType": "Numeric",    "colName": "NR ART/M6"}, {"colIndex": 8, "colType": "Numeric", "colName": "NR ART/M1"}, {"colIndex": 9, "colType": "Numeric", "colName": "NR ART/M2"}, {"colIndex": 10, "colType": "Numeric", "colName": "NR ART/M3"}, {"colIndex": 11, "colType": "Numeric", "colName": "NR ART/MX"}];
    
    // First filtering objects which will form the headers
    var rowHeaders = metadata.filter(function(obj) { return obj.colType == "String"; });
    var colHeaders = metadata.filter(function(obj) { return obj.colType == "Numeric"; });
    
    // Now we need the grouping on colName with value before slash.
    // These will form the group header. Count will determine the colspan.
    var colHeadersMain = {};
    metadata.forEach(function(obj) { 
        if (obj.colType == "String") { return } // Only for Numeric colType
        // Check if the key has been stored in the colHeadersMain
        if (obj.colName.split('/')[0] in colHeadersMain) {
            // If yes, then increment the value of the key for object colHeadersMain
            colHeadersMain[obj.colName.split('/')[0]]++;
        } else {
            // If not, reset the value
            colHeadersMain[obj.colName.split('/')[0]] = 1;
        }
    });
    
    // Variables to hold jQuery elements
    var $table = $("<table>"), $thead = $("<thead>"), 
        $row1 = $("<tr>"), $row2 = $("<tr>");
    
    // Iterate row headers and form the first row
    // Hard-coding the rowspan. 
    // ToDo: Maintain count, similar to column headers.
    rowHeaders.forEach(function(obj, idx) {
        var $th = $("<th rowspan='2'>");
        $th.text(obj.colName);
        $row1.append($th);
    });
    
    // Iterate col grouping headers and continue the row
    // colpsan is the group count that we created earlier
    for (var key in colHeadersMain) {
        var $th = $("<th colspan='" + colHeadersMain[key] + "'>");
        $th.text(key);
        $row1.append($th);    
    }
    
    // Add the first header row to thead
    $thead.append($row1); 
    
    // Iterate next line of col headers 
    colHeaders.forEach(function(obj, idx) {
        var $th = $("<th>");
        $th.text(obj.colName.split('/')[1]);
        $row2.append($th);    
    });
    
    // Add to the thead
    $thead.append($row2); 
    
    // Add to the table
    $table.append($thead);
    
    // Add to the page / element
    $("#wrap").append($table);
    
    // Done.
    table, td, th { border: 1px solid gray; border-collapse: collapse; }
    th, td { padding: 4px; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="wrap"></div>

    【讨论】:

    • 谢谢。你的代码,结果就像@pete 代码,但几乎是我想要的。请参阅我在皮特答案中的评论。
    • @DGA:唯一硬编码的是rowspan2,也可以像colpsan 一样动态化。没有其他东西是硬编码的。请更详细地说明您真正追求的是什么。
    • 1.该数组始终有两个 colType(字符串和数字)。 2. 我的示例数组的 colName 格式为 "colName": " NR/M4"(由“/”结果 2 个元素分割)所以我将在两个级别上有数字标题,但是如果我有 "colName": "NR/M4/OUT" 怎么办?当我用“/”分割时,标题将有 3 个级别(行)。我是$row1 = $("&lt;tr&gt;"), $row2 = $("&lt;tr&gt;");。这些行也必须是动态的,因为在这种新情况下,我需要创建$row1 = $("&lt;tr&gt;"), $row2 = $("&lt;tr&gt;");$row3 = $("&lt;tr&gt;");。当然,这可以从obj.colName.split('/').length 创建。对不起我的英语不好。
    • 是的。这变得复杂了。也许递归解析可能有效,我不太确定。
    【解决方案3】:

    由于您没有在帖子中标记 jQuery,因此这是一个普通的 JavaScript 解决方案(尽管您确实在帖子的代码中使用了 $,这可能表示 jQuery)。

    下面的代码在应用于metadata 时会将其解析为更有利于您执行操作的格式:

    var columns = [];
    metadata.forEach(function(column, index, array) { // parse into a more friendly format for making columns
        var col = {}, // will be the column
            parts = column.colName.split('/'), // you're splitting on the column name in your post
            match = null, // for lookup, leaving null for falsy test later
            i = 0; // iterator / index
        col.name = parts[0]; // store the name
        col.type = column.colType; // and type
        col.index = column.colIndex; // and index, if you want to use it later
        col.subColumns = []; // make an empty array for any subcolumns that may exist so we can concat later
        if (parts.length > 1) {
            col.subColumns.push({ // subcolumn, push it into the array
                "name": parts[1],
                "index": col.index
            });
        }
        for (i = 0; i < columns.length; i += 1) {
            if (columns[i].name === col.name) { // [parent] column already exists
                match = i; // store the index
                break; // exit the loop
            }
        }
        if (match) { // index found
            columns[i].subColumns = columns[i].subColumns.concat(col.subColumns); // concat the subcolumns
        } else {
            columns.push(col); // did not exist, add it to the array
        }
    });
    

    完成后,循环两次columns 变量以生成两行(假设变量为theadtrth 元素)。

    columns.forEach(function(column, index, array) { // loop through once to make "parent" headers
        th = document.createElement('th'); // create th element
        th.colSpan = column.subColumns.length; // set a colSpan as necessary
        if (!column.subColumns.length) { //  if there are no subcolumns, make the header span two rows
            th.rowSpan = 2;
        }
        th.innerText = column.name; // set the header text
        tr.appendChild(th); // add to row
    });
    
    columns.forEach(function(column, index, array) { // loop through a second time to get the child headers
        column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers
            th = document.createElement('th'); // create th
            th.innerText = subColumn.name; // set the header text
            tr.appendChild(th); // add to row
        });
    });
    

    这是在行动:

    var metadata = [{
            "colIndex": 0,
            "colType": "String",
            "colName": "PM"
        }, {
            "colIndex": 1,
            "colType": "String",
            "colName": "PROD"
        }, {
            "colIndex": 2,
            "colType": "Numeric",
            "colName": "NR/M1"
        }, {
            "colIndex": 3,
            "colType": "Numeric",
            "colName": "NR/M2"
        }, {
            "colIndex": 4,
            "colType": "Numeric",
            "colName": "NR/M3"
        }, {
            "colIndex": 5,
            "colType": "Numeric",
            "colName": "NR/M4"
        }, {
            "colIndex": 6,
            "colType": "Numeric",
            "colName": "NR/M5"
        }, {
            "colIndex": 7,
            "colType": "Numeric",
            "colName": "NR ART/M6"
        }, {
            "colIndex": 8,
            "colType": "Numeric",
            "colName": "NR ART/M1"
        }, {
            "colIndex": 9,
            "colType": "Numeric",
            "colName": "NR ART/M2"
        }, {
            "colIndex": 10,
            "colType": "Numeric",
            "colName": "NR ART/M3"
        }, {
            "colIndex": 11,
            "colType": "Numeric",
            "colName": "NR ART/MX"
        }],
        columns = [],
        thead = document.getElementById('dynamic'), // cache this DOM lookup
        tr = document.createElement('tr'),
        th; // placeholder for th elements that will be created
    metadata.forEach(function(column, index, array) { // parse into a more friendly format for making columns
        var col = {}, // will be the column
            parts = column.colName.split('/'), // you're splitting on the column name in your post
            match = null, // for lookup, leaving null for falsy test later
            i = 0; // iterator / index
        col.name = parts[0]; // store the name
        col.type = column.colType; // and type
        col.index = column.colIndex; // and index, if you want to use it later
        col.subColumns = []; // make an empty array for any subcolumns that may exist so we can concat later
        if (parts.length > 1) {
            col.subColumns.push({ // subcolumn, push it into the array
                "name": parts[1],
                "index": col.index
            });
        }
        for (i = 0; i < columns.length; i += 1) {
            if (columns[i].name === col.name) { // [parent] column already exists
                match = i; // store the index
                break; // exit the loop
            }
        }
        if (match) { // index found
            columns[i].subColumns = columns[i].subColumns.concat(col.subColumns); // concat the subcolumns
        } else {
            columns.push(col); // did not exist, add it to the array
        }
    });
    columns.forEach(function(column, index, array) { // loop through once to make "parent" headers
        th = document.createElement('th'); // create th element
        th.colSpan = column.subColumns.length; // set a colSpan as necessary
        if (!column.subColumns.length) { //  if there are no subcolumns, make the header span two rows
            th.rowSpan = 2;
        }
        th.innerText = column.name; // set the header text
        tr.appendChild(th); // add to row
    });
    thead.appendChild(tr); // add row to thead
    tr = document.createElement('tr'); // reset row
    columns.forEach(function(column, index, array) { // loop through a second time to get the child headers
        column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers
            th = document.createElement('th'); // create th
            th.innerText = subColumn.name; // set the header text
            tr.appendChild(th); // add to row
        });
    });
    thead.appendChild(tr); // add row to thead
    th {border: 1px solid black;}
    <table>
        <thead id="dynamic"></thead>
    </table>

    jQuery,如果你使用它,可以让这个语法更“糖”一点(主要是在制作行和标题单元格,见下文),但原理是一样的。

    columns.forEach(function(column, index, array) { // loop through once to make "parent" headers
        th.clone().attr({
            "colSpan": column.subColumns.length,
            "rowSpan": column.subColumns.length === 0 ? 2 : 1
        }).text(column.name).appendTo(tr);
    });
    tr.appendTo(thead);
    tr = $('<tr />');
    columns.forEach(function(column, index, array) { // loop through a second time to get the child headers
        column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers
            th.clone().text(subColumn.name).appendTo(tr);
        });
    });
    tr.appendTo(thead);
    

    【讨论】:

    • 谢谢。您的代码就像@abhitalks 代码一样,但几乎是我想要的。对于这个数组(有 2 个级别),可以对级别进行硬编码,但我说这个数组是动态的。是我的错,因为我没有指定它是水平的还是垂直的。水平是可以的,因为表格是为所有列的前两个级别(我的数组)创建的。我会研究你的代码来学习和适应我的需要。
    【解决方案4】:

    几个小时后,我找到了问题的答案。 在第一部分中,我创建了 tr 和 th(th 非唯一)

    var a = [], l = 0;
    for (var i = 0; i < metadata.length; i++) {
       var d = {
        "colName": metadata[i].colName.split("/"),
            "colType": metadata[i].colType
       };
       a.push(d);
       if (metadata[i].colType == "Numeric") {
          trl = metadata[i].colName.split("/").length;
       }
    }
    for (var l = 0; l < trl; l++) {
       var tr = $("<tr/>").appendTo("thead");
    }
    
    for (var j = 0; j < a.length; j++) {
       if (a[j].colType == "String") {
         $("<th/>").addClass("string").attr("rowspan", l).html(a[j].colName).appendTo("thead tr:nth(0)");
       } else {
          for (var k = 0; k < a[j].colName.length; k++) {
              $("<th/>").addClass("numeric").html(a[j].colName[k]).appendTo("thead tr:nth(" + k + ")");
          }
      }
    }
    

    此部分改编自here。 现在我把桌子收拾干净了,生活就是独一无二的。

    $('table tr').each(function () {
      var tr = this;
      var counter = 0;
      var strLookupText = '';
    
      $('th.numeric', tr).each(function (index, value) {
        var td = $(this);
        if ((td.text() == strLookupText) || (td.text() == "")) {
            counter++;
            td.prev().attr('colspan', '' + parseInt(counter + 1, 10) + '').css({
                textAlign: 'center'
            });
            td.remove();
        } else {
            counter = 0;
        }
        strLookupText = td.text();
       });
    });
    

    代码有点乱,但已经晚了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-28
      • 2020-12-18
      • 1970-01-01
      • 2018-12-12
      • 1970-01-01
      • 2018-01-31
      • 2021-10-27
      相关资源
      最近更新 更多