考虑到rowspan和colspan,如何从一维数组创建动态html表?

Chr*_*ris 14 html javascript arrays jquery

我需要从一维数组构造一个html表,为了抽象,它具有以下格式:

{ value: "ABC", colspan: 1, rowspan: 2 }, // etc
Run Code Online (Sandbox Code Playgroud)

还有一个名为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);
Run Code Online (Sandbox Code Playgroud)

第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(''));
Run Code Online (Sandbox Code Playgroud)

编辑 - 输入错误

错误输入的一个例子是拆分单元格:

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;
Run Code Online (Sandbox Code Playgroud)

Mau*_*ppe 6

我认为你的替代解决方案正在走上正轨,应该验证的两个角落案例

  • 一个单元格可能被渲染出界限,例如当一个单元格的起始位置+它的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
}
Run Code Online (Sandbox Code Playgroud)

演示


更新:用户在评论中发布了一个似乎没有渲染的案例,上面的算法适用于这种情况,即使标记看起来很好但是看起来这个表中的一行被渲染为高度等于零,我我确定有很多方法可以解决这个问题,我通过在table tr元素上设置一个固定的高度来修复它

演示修复<tr>高度= 0渲染的问题