[英]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都會導致下一行退出表格的右側。
基本上我希望它包裝並放下下一個可用的地方。 換句話說,動態地對表進行分配。
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);
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;
我認為你的替代解決方案正在走上正軌,應該驗證的兩個角落案例
width
(藍色單元格被渲染出界限) 我想出了以下算法,這與你的第二個解決方案非常相似
N
行和width
列的值N
將每當需要被分配 cell
i
和j
是矩陣中第一個空格的行和列,然后我們需要占用以下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
元素上設置固定高度來修復它
<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>
如果你有足夠的內容,那么就不需要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.