考虑到rowpan和colspan,如何从一维数组创建动态html表? [英] How would one create a dynamic html table from a one dimensional array, taking into account rowspan and colspan?

查看:105
本文介绍了考虑到rowpan和colspan,如何从一维数组创建动态html表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  {值:"ABC",colspan:1,rowspan:2},//等等 

还有一个名为 width 的属性,该属性将是动态的,代表列数.

我相信下面的代码很接近,并且可以处理非行跨度"数据-但是,我在不考虑表超过列数的情况下就如何计算单元格跨度而陷入困境.

我觉得我需要一个步进器",每次有行跨时它都会向上和向下计数,但是我无法正确计算数学.

此刻,任何行跨度都会导致下一行退出表的右侧.

基本上,我希望将每个包装都放在下一个可用位置.换句话说,动态地组合表格.

第1轮-无法正常运行

  • 一个单元格可能会在已经被占用的地方渲染(蓝色单元格试图占据红色单元格占用的空间)

我想出了以下算法,它与您的第二个解决方案非常相似

  • 创建一个由 N 行和 width 列组成的矩阵, N 的值将在需要时分配
  • 对于输入中的每个单元格
    • 从矩阵的第一行开始从左向右移动,以尝试找到空白空间,请注意,如果当前行中没有空白空间,则这是分配新行的地方
    • i j 为矩阵中第一个空白空间的行和列,那么我们需要占用以下 i + cell.rowspace乘以 j + cell.colspace cells,在实现中,我使用cell的索引
    • 如果通过任何方式 cell 试图占据一个超出范围的单元,则会引发错误
    • 如果通过任何方式 cell 试图占据矩阵中已经保存了一些值的单元格,则会引发错误

实现如下

 类矩阵{构造函数(宽度){this.width =宽度this.data = []}set(i,j,d){if(j> = width)throw Error(`set out of bounds index($ {i},$ {j})`)var值= this.get(i,j)如果(值!==未定义)抛出错误(单元格($ {i},$ {j})被$ {value}`占据)this.data [i] [j] = d}get(i,j){this.data [i] = this.data [i] ||数组(this.width)返回this.data [i] [j]}findNextEmpty(i,j){而(true){如果(this.get(i,j)===未定义){返回[i,j]}j + = 1如果(j === this.width){我+ = 1j = 0}}}fromData(data){让我= 0令j = 0data.forEach((meta,metaIndex)=> {[i,j] = this.findNextEmpty(i,j)对于(var ci = i; ci< i + meta.rowspan; ci + = 1){对于(var cj = j; cj< j + meta.colspan; cj + = 1){this.set(ci,cj,metaIndex)}}})返回this.data}}尝试 {const table = new Matrix(width).fromData(input)} catch(err){//输入无效} 

演示


更新:用户在评论中发布了一个案例,该案例似乎无法很好地显示,上面的算法适用于这种情况,即使标记看起来也很好,但是在此表中看起来像是一行是用高度等于0的高度渲染的,我敢肯定有很多方法可以解决此问题,我通过在 table tr 元素

上设置了固定的高度来解决此问题

演示解决了< tr> 的问题用高度= 0渲染

I need to construct a html table from a one dimensional array which, for abstractions sake, has the following format:

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

There is also a property called width which will be dynamic and represent the number of columns.

The code below, I believe is close, and can handle "non-rowspan" data - but I am getting tripped up on how to account for cells spanning, without the table exceeding the column count.

I feel like I need a "stepper" which counts up and down everytime there is a rowspan, but I can't get the maths correct.

At the moment, any rowspan causes the next row to exit the right of the table.

Essentially I would like it to wrap and drop each one in the next available spot. In otherwords assmeble the table dynamically.

Round 1 - Not working

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);

Round 2 - Improved, but assumes input is valid

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(''));

Edit - Bad input

An example of bad input would be splitting cells:

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;

解决方案

I think you were on the right track with your alternative solution, the two corner cases that should be validated are

  • a cell might be rendered out of bounds e.g. when a cell's start position + its colspan is bigger than the width allowed (the blue cell is rendered out of bounds)

  • a cell might be rendered in a place already occupied (the blue cell tries to occupy a space taken by the red cell)

I came up with the following algorithm which is very similar to your second solution

  • Create a matrix of N rows and width columns, the value of N will be allocated whenever needed
  • For each cell in your input
    • Move from left to right starting from the first row of the matrix trying to find an empty space, note that this is where the allocation of new rows occur if there wasn't an empty space in the current row
    • Let i and j be the row and column of the first empty space in the matrix, then we need to occupy the following i + cell.rowspace times j + cell.colspace cells, In the implementation I use the index of cell
    • If by any means cell tries to occupy an out of bound cell throw an error
    • If by any means cell tries to occupy a cell in the matrix which already has some value saved throw an error

The implementation looks as follows

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


Update: A user has posted a case in the comments which seemed not to render fine, the algorithm above works for this case, even the markup looks fine however it seems like a row in this table was rendered with a height equal to zero, I'm sure there are a lot of ways to fix this, I fixed it by setting a fixed height over the table tr elements

Demo fixing the problem where a <tr> was rendered with a height = 0

这篇关于考虑到rowpan和colspan,如何从一维数组创建动态html表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆