首页 > 解决方案 > 如何通过 JavaScript/jQuery 以编程方式创建不规则表格?

问题描述

有一个合并了一些行的excel,我们需要创建一个与它在excel中显示的布局相同的表,并用数据库中的数据填充。

布局如下所示:

表格

我尝试使用以下代码:

for (let group of group_list) {
      var table = document.createElement("table")
      table.setAttribute("id","summaryTbl");
      $('#main').append(table);
      var row = table.insertRow(0);
      row.insertCell(0).outerHTML = "<th>Column 1</th>";
      row.insertCell(1).outerHTML = "<th>Column 2</th>";
      row.insertCell(2).outerHTML = "<th>Column 3</th>";
      row.insertCell(3).outerHTML = "<th>Column 4</th>";
      $('#main').append('</thead>');

但不知何故,它总是显示“tbody”而不是“thead”:

控制台日志

除了以下代码:

var table = document.createElement("table")
table.setAttribute("id","summaryTbl");
var row = table.insertRow(0);
row.insertCell(0).outerHTML = "<th>Column 1</th>";
        ...
$('#summaryTbl tr:last').after('<tr><td>'+"Test 1"+'</td>');
        ...
$('#summaryTbl').append('<tr><td>'+ "Test 2"+'</td>');

有没有更好的方法来生成这样一个不规则的表格?使用一些 JavaScript 库还是仅仅通过 for-loop 和 if-else 条件?

标签: javascriptjquerydom

解决方案


我已经看过您的帖子并尝试将其作为自己的练习,我不妨分享一下结果。不完全相同,因为在您的图片中,“1.1.1.1.x”填充到左侧,“2.3.x.1”比其他行小,我没有做这些棘手的案例。

const values = ["1.1.1.1.1", "1.1.1.1.2", "1.1.1.1.3", "1.1.1.1.4", "1.1.1.1.5", "1.2.1.1.1", "2.1.1.1.1", "2.2.1.1.1", "2.2.2.1.1", "2.2.3.1.1", "2.3.1.1", "2.3.2.1", "2.3.3.1", "2.3.4.1"]

// create a nested object from values
// 1.2.3 becomes {1:{2:{3:}}}
const transformedValues = (() => {
  const output = {}
  values.forEach(el => {
    let tmp = output
    el.split(".").forEach(val => {
      if (!tmp[val]) {
        tmp[val] = {}
      }
      tmp = tmp[val]
    })
  })
  return output
})()
// find the object max depth, for the column count
const maxDepth = (function rec(obj, depth = 0){
  return Math.max(depth, ...Object.values(obj).map(val => rec(val, depth + 1)))
})(transformedValues)

// header name generator (more than 26 and you'll go into non alphanumeric)
let headerASCII = "A".charCodeAt(0)
const getNextHeader = () => String.fromCharCode(headerASCII++)

// create the table
const table = document.createElement("table")

/*
@param {string} val the text of the created cell
@param {Object} next the childs of the object
@param {HTMLRowElement | null} row the last created
@param {boolean} newRow if we need to create a new row
@param {boolean} newHeader if we need to create a new thead
*/
function createTable(val, next, row, newRow = false, newHeader = false) {
  // create new header
  if (newHeader) {
    const head = document.createElement("thead")
    // with depth - 1 cells (first one takes 2 columns)
    for (let i = 0; i < maxDepth - 1; i++) {
      let th  = document.createElement("th")
      th.textContent = getNextHeader()
      if(i === 0) {th.colSpan = 2}
      head.appendChild(th)
    }
    table.appendChild(head)
  }
  // create new row
  if (newRow) {
    row = document.createElement("tr")
    table.appendChild(row)
  }
  // new cell
  const cell = document.createElement("td")
  cell.textContent = val
  // span on enought rows to align with every child
  cell.rowSpan = (function childCount(obj) {return Object.values(obj).reduce((acc, el) => acc + childCount(el), 0) || 1})(next)
  row.appendChild(cell)
  
  // recurcive call
  Object.entries(next).forEach(([key, value], i) => {
    createTable(`${val}.${key}`, value, row, i !== 0)
  })
}
// first call
Object.entries(transformedValues).forEach(([key, value]) => {
  createTable(key, value, null, true, true)
})

document.body.append(table)
table {
  border-collapse: collapse;
}

table th, table td {
  border: 2px solid black;
}

table th {
  background-color: cyan;
}


推荐阅读