我需要从一维数组构造一个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;
答案 0 :(得分:6)
我认为你的替代解决方案走在正确的轨道上,应该验证的两个角落案例是
width
时(蓝色单元格被渲染出界限)我提出了以下算法,这与你的第二个解决方案非常相似
N
行和width
列的矩阵,N
的值将在需要时分配cell
i
和j
成为矩阵中第一个空白空间的行和列,然后我们需要占用以下i + cell.rowspace
次j + cell.colspace
个单元格,In实现我使用单元格索引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>
was rendered with a height = 0 答案 1 :(得分:3)
这是问题的直接解决方案。
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}
无效。
答案 2 :(得分:1)
这就是v0.0.1,它处理任何输入数据并构造HTML文本,就像在这种情况下一样,提供有意义的输入数据,因为垂直和水平跨越的单元格不相交或者colspan赢了“ t超出了提供的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>');