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

标签 javascript jquery html arrays

我需要从一个一维数组构造一个 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 时允许(蓝色单元格呈现出边界)

out of bounds

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

cell occupied

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

  • 创建 N 的矩阵行和 width列,N 的值将在需要时分配
  • 对于每个 cell在你的输入中
    • 从矩阵的第一行开始从左向右移动,试图找到一个空白空间,请注意,如果当前行中没有空白空间,则会在此处分配新行
    • ij是矩阵中第一个空位的行和列,那么我们需要占用下面的i + cell.rowspacej + 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/

相关文章:

javascript - 如果当前组件发生更改,则停止加载新的路由/组件

javascript - rest API- 如何格式化 JSON 值

php - 如何在 htaccess 中调用 $_GET 变量和解析 url 时回显文本?

javascript - 将下拉列表触发的显示/隐藏更改为 li JS

javascript - 使用该选项的 id 获取 select2 的选定选项

javascript - Rails Assets 管道 : Return digest version when non-digest requested

jquery - 根据多个复选框过滤表行

javascript - 如何从 Laravel 5 中的 AJAX 调用返回 View ?

javascript - 单击时显示和隐藏选项卡内容

javascript - 无法使用 Twilio Video 禁用 localParticipant.audioTracks