我需要从一个一维数组构造一个 html 表,为了抽象起见,它具有以下格式:
{ value: "ABC", colspan: 1, rowspan: 2 }, // etc
还有一个名为 width
的属性,它将是动态的并表示列数。
我认为下面的代码很接近,并且可以处理“非行跨度”数据 - 但我对如何在表格不超过列数的情况下计算单元格跨越感到困惑。
我觉得我需要一个“步进器”,每当有一个行跨度时它就会向上和向下计数,但我无法正确计算。
此刻,任何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
在你的输入中- 从矩阵的第一行开始从左向右移动,试图找到一个空白空间,请注意,如果当前行中没有空白空间,则会在此处分配新行
- 让
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
}
Demo
更新:用户在评论中发布了一个案例,该案例似乎无法正常呈现,上面的算法适用于此案例,甚至标记看起来也不错,但它看起来像是此表中的一行以等于零的高度呈现,我敢肯定有很多方法可以解决这个问题,我通过在 table tr
上设置固定高度来修复它元素
Demo fixing the problem where a <tr>
was rendered with a height = 0
关于javascript - 考虑到 rowspan 和 colspan,如何从一维数组创建动态 html 表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37189200/