簡體   English   中英

考慮到rowspan和colspan,如何從一維數組創建動態html表?

[英]How would one create a dynamic html table from a one dimensional array, taking into account rowspan and colspan?

我需要從一維數組構造一個html表,為了抽象,它具有以下格式:

{ value: "ABC", colspan: 1, rowspan: 2 }, // etc

還有一個名為width的屬性,它將是動態的並表示列數。

下面的代碼,我認為是接近的,並且可以處理“非rowpan”數據 - 但我正在惹惱如何考慮跨越單元格,而不會超出列數。

我覺得我需要一個“步進器”,每當有一個行程時,它就會上下計數,但我無法正確計算數學。

目前,任何rowspan都會導致下一行退出表格的右側。

基本上我希望它包裝並放下下一個可用的地方。 換句話說,動態地對表進行分配。

第1輪 - 不工作

http://jsbin.com/zopoxaqato/edit?js,console,output

const input = [
  { value: "a1", colspan: 1, rowspan: 1 },
  { value: "a2", colspan: 1, rowspan: 1 },
  { value: "a3", colspan: 1, rowspan: 3 },

  { value: "b1", colspan: 1, rowspan: 1 },
  { value: "b2", colspan: 1, rowspan: 1 },

  { value: "c1", colspan: 1, rowspan: 1 },
  { value: "c2", colspan: 1, rowspan: 2 },

  { value: "d1", colspan: 1, rowspan: 1 },
  { value: "d3", colspan: 1, rowspan: 1 },

  { value: "e1", colspan: 1, rowspan: 1 },
  { value: "e2", colspan: 2, rowspan: 1 },
];
const width = 3;



const trs = [];
let tds = [];
let rowSpanOffset = 0;

// Loops over entries
input.forEach((cell, index) => {

  // Stock standard td
  tds.push(`<td colspan="${cell.colspan}" rowspan="${cell.rowspan}">${cell.value}</td>`);


  // New row time
  if(index % width === width - 1 || rowSpanOffset < 0) {
    trs.push("<tr>" + tds.join('') + "</tr>");
    // Reset for next row
    tds = [];    
  }

});


const leTable = "<table class='table'>"+trs.join('')+"</table>";


$("body").append(leTable);

第2輪 - 改進,但假設輸入有效

http://jsbin.com/solesiyuro/edit?js,output

const input = [
  { value: "a1", colspan: 1, rowspan: 1 }, // 1
  { value: "a2", colspan: 1, rowspan: 1 }, // 2
  { value: "a3", colspan: 1, rowspan: 3 }, // 3

  { value: "b1", colspan: 1, rowspan: 1 }, // 1
  { value: "b2", colspan: 1, rowspan: 1 }, // 1

  { value: "c1", colspan: 1, rowspan: 1 }, // 1
  { value: "c2", colspan: 1, rowspan: 2 }, // 2

  { value: "d1", colspan: 1, rowspan: 1 }, // 1
  { value: "d3", colspan: 1, rowspan: 1 }, // 1

  { value: "e1", colspan: 1, rowspan: 1 }, // 1
  { value: "e2", colspan: 1, rowspan: 1 }, // 2
];
const width = 3;

const totalCellCount = _.reduce(input, (sum, c) => sum + c.colspan * c.rowspan, 0);
const grid = _.chunk(_.fill(new Array(totalCellCount), -1), width);

_.each(input, cell => {
  let start = [-1, -1];

  outerLoop: 
  for(let y = 0; y < grid.length; y++) {
      for(let x = 0; x < width; x++) {
          if(grid[y][x] === -1) {
            start = [x, y];
            break outerLoop;
          }
      }    
  }

  for(let y = 0; y < cell.rowspan; y++) {
      for(let x = 0; x < cell.colspan; x++) {
         grid[start[1] + y][start[0] + x] = null;        
      }    
  }
  grid[start[1]][start[0]] = cell;        

});

let trs = [];
let tds = [];

for(let y = 0; y < grid.length; y++) {
  for(let x = 0; x < grid[y].length; x++) {
    const cell = grid[y][x];
    if(cell) {
      const value = cell.value;
      tds.push('<td colspan="'+cell.colspan+'" rowspan="'+cell.rowspan+'">'+cell.value+'</td>');
    }
  }    
  trs.push('<tr>'+tds.join('')+'</tr>');
  tds = [];
}


$(".table").append(trs.join(''));

編輯 - 輸入錯誤

錯誤輸入的一個例子是拆分單元格:

const input = [
  { value: "a1", colspan: 1, rowspan: 1 },
  { value: "a2", colspan: 1, rowspan: 2 },
  { value: "a3", colspan: 1, rowspan: 1 },

  { value: "b1", colspan: 3, rowspan: 1 },

];
const width = 3;

我認為你的替代解決方案正在走上正軌,應該驗證的兩個角落案例

  • 一個單元格可能被渲染出界限,例如當一個單元格的起始位置+它的colspan大於允許的width (藍色單元格被渲染出界限)

出界

  • 一個單元格可能會在已占用的位置渲染(藍色單元格試圖占據紅色單元格占用的空間)

細胞占據

我想出了以下算法,這與你的第二個解決方案非常相似

  • 創建的矩陣N行和width列的值N將每當需要被分配
  • 對於輸入中的每個cell
    • 從矩陣的第一行開始從左向右移動,試圖找到一個空的空格,請注意,如果當前行中沒有空格,則會出現新行的分配
    • ij是矩陣中第一個空格的行和列,然后我們需要占用以下i + cell.rowspace乘以j + cell.colspace單元格,在實現中我使用單元格的索引
    • 如果以任何方式cell試圖占據了結合細胞的拋出一個錯誤
    • 如果通過任何方式cell試圖占據矩陣中已經有一些值保存的單元格拋出錯誤

實現如下

class Matrix {
  constructor(width) {
    this.width = width
    this.data = []
  }

  set(i, j, d) {
    if (j >= width) throw Error(`set was run out of bounds index (${i}, ${j})`)
    var value = this.get(i, j)
    if (value !== undefined) throw Error(`cell (${i}, ${j}) is occupied with ${value}`)
    this.data[i][j] = d
  }

  get(i, j) {
    this.data[i] = this.data[i] || Array(this.width)
    return this.data[i][j]
  }

  findNextEmpty(i, j) {
    while (true) {
      if (this.get(i, j) === undefined) {
        return [i, j]
      }
      j += 1
      if (j === this.width) {
        i += 1
        j = 0
      }
    }
  }

  fromData(data) {
    let i = 0
    let j = 0
    data.forEach((meta, metaIndex) => {
      [i, j] = this.findNextEmpty(i, j)
      for (var ci = i; ci < i + meta.rowspan; ci += 1) {
        for (var cj = j; cj < j + meta.colspan; cj += 1) {
          this.set(ci, cj, metaIndex)
        }
      }
    })
    return this.data
  }  
}

try {
  const table = new Matrix(width).fromData(input)
} catch (err) {
  // the input was invalid
}

演示


更新:用戶在評論中發布了一個似乎沒有渲染的案例上面的算法適用於這種情況 ,即使標記看起來很好但是看起來這個表中的一行被渲染為高度等於零,我我確定有很多方法可以解決這個問題,我通過在table tr元素上設置固定高度來修復它

演示修復高度= 0渲染<tr>的問題

這是問題的直接解決方案。

    function buildTbl() {
        var tbl = document.createElement('table');
        tbl.className = 'tbl';
        var cols = width, tr = null, td = null, i = 0, inp = null, rowspan = [];
        while (inp = input[i]) {
            if (cols >= width) {
                tr = tbl.insertRow(-1);
                cols = 0;
                for (var j = 0, n = rowspan.length; j < n; j++) {
                    if (rowspan[j] > 1) {
                        cols++;
                        rowspan[j]--;
                    }
                }
            }
            td = tr.insertCell(-1);
            td.innerHTML = inp.value;
            if (inp.colspan > 1)
                td.setAttribute('colspan', inp.colspan);
            if (inp.rowspan > 1) {
                td.setAttribute('rowspan', inp.rowspan);
                rowspan.push(inp.rowspan);
            }
            cols += inp.colspan;
            i++;
        }
        document.getElementById('content').appendChild(tbl);
    }

更新:

如果我添加css,那么表將按預期(所需)呈現。

    .tbl{border:solid 1px #ccc}
    .tbl tr{height:20px}
    .tbl td{border:solid 1px #fcc}

生成的HTML:

<table class="tbl">
    <tbody>
        <tr>
            <td>a1</td>
            <td>a2</td>
            <td rowspan="3">a3</td>
        </tr>
        <tr>
            <td rowspan="2">b1</td>
            <td>b2</td>
        </tr>
        <tr>
            <td rowspan="2">c2</td>
        </tr>
        <tr>
            <td>d1</td>
            <td>d3</td>
        </tr>
        <tr>
            <td>e1</td>
            <td colspan="2">e2</td>
        </tr>
    </tbody>
</table>

更新2

如果你有足夠的內容,那么就不需要tr的固定高度。

        const input = [
  { value: "a1 long content long content long content long content long content long content long content ", colspan: 1, rowspan: 1 },
  { value: "a2 long content long content long content long content long content long content", colspan: 1, rowspan: 1 },
  { value: "a3 long content long content long content long content long content long content", colspan: 1, rowspan: 3 },

  { value: "b1 long content long content long content long content long content long content long content long content long content long content", colspan: 1, rowspan: 2 },
  { value: "b2 long content long content long content long content long content long content", colspan: 1, rowspan: 1 },

 // { value: "c1", colspan: 1, rowspan: 1 },
  { value: "c2 long content long content long content long content long content long content long content", colspan: 1, rowspan: 2 },

  { value: "d1 long content long content long content long content long content long content", colspan: 1, rowspan: 1 },
  { value: "d3 long content long content long content long content long content long content", colspan: 1, rowspan: 1 },

  { value: "e1 long content long content long content long content long content", colspan: 1, rowspan: 1 },
  { value: "e2 long content long content long content long content long content long content", colspan: 2, rowspan: 1 },
              ];

CSS:

    .tbl{border:solid 1px #ccc;width:300px}
    /*.tbl tr{height:20px}*/
    .tbl td{border:solid 1px #fcc}

更重要的是, .tbl tr{height:20px}沒有效果。

這就是v0.0.1,它處理任何輸入數據並構造HTML文本,就像在這種情況下一樣,提供有意義的輸入數據,因為垂直和水平跨越的單元格不相交或者colspan不會超出由提供的width值設置的限制。 我還打算稍后開發一個V0.0.2,無論隨機colspan和rowspan值是什么,它都能產生有效的表格布局。 我認為v0.0.1足以滿足您的需求。

我首先開發了一個tableMap ,它在2D數組中構造表的映射。 實際上在客戶端,現在構建DOM表非常簡單。 主單元格被稱為sp為0的額外屬性標記,而跨越的屬性具有sp屬性作為非零值。 實際上,DOM樹在這個2D陣列中很容易獲得。 只需要反向迭代來選擇sp == 0的單元格,構建DOM樹是唯一要做的事情。

但是,由於您要求HTML表,對於服務器端,我更進一步,將tableMap轉換為HTML字符串。

對不起我非正統的縮進風格。 我更喜歡使用箭頭,三元和短路,因此寬屏布局更容易讓我感知代碼。

您可以使用@ repl.it進行播放的代碼

 var input = [ { value: "a1", colspan: 1, rowspan: 1 }, { value: "a2", colspan: 1, rowspan: 1 }, { value: "a3", colspan: 1, rowspan: 3 }, { value: "b1", colspan: 1, rowspan: 1 }, { value: "b2", colspan: 1, rowspan: 1 }, { value: "c1", colspan: 1, rowspan: 1 }, { value: "c2", colspan: 1, rowspan: 2 }, { value: "d1", colspan: 1, rowspan: 1 }, { value: "d3", colspan: 1, rowspan: 1 }, { value: "e1", colspan: 1, rowspan: 1 }, { value: "e2", colspan: 2, rowspan: 1 }, ], width = 3, cellCount = input.reduce((p,c) => p += c.colspan * c.rowspan,0), rowCount = Math.ceil(cellCount/width), rc = {r:0,c:0}, tableMap = input.reduce((t,e) => { var getNextRC = (rc) => {rc.r = rc.c == 2 ? ++rc.r : rc.r; rc.c = ++rc.c%width; return rc}, insertCell = (rc) => { if (!t[rc.r][rc.c]){ for (var c = 0; c < e.colspan; c++) for (var r = 0; r < e.rowspan; r++)t[rc.r+r][rc.c+c] = {"td": e, "sp": r+c}; getNextRC(rc); } else { getNextRC(rc); insertCell(rc); } return rc; }; rc = insertCell(rc); return t;}, new Array(rowCount).fill(true).map(e => new Array(width).fill(false))), tableHTML = tableMap.reduceRight((t,r,i) => { var dt = r.reduceRight((t,d,i) => t = !d.sp ? i > 0 ? '</td><td colspan = "' + d.td.colspan + '" rowspan = "' + d.td.rowspan + '">' + d.td.value + t : '<td colspan = "' + d.td.colspan + '" rowspan = "' + d.td.rowspan + '">' + d.td.value + t : t, '</td>'); t = i > 0 ? '</tr><tr>' + dt + t : '<tr>' + dt + t; return t; }, '</tr>'); document.write("<style>table, th, td {border: 1px solid black;}</style>"); document.write('<table>' + tableHTML + '</table>'); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM