You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义better-table元素rows属性配置行数与实际渲染行数缺失一行问题排查

解决better-table自定义元素行数缺失一行的问题

我帮你找到了问题的根源:你的addMultiRow方法是通过tbody.childNodes来统计和获取行元素,但你在tbody里添加了一个scrollbar div,这个div会被算在childNodes里,导致行计数和索引匹配出错。当你设置rows=10时,代码实际上只生成了9行tr,因为第一个childNodes位置被div占用了,循环逻辑没有处理这个额外元素的干扰。

具体问题点拆解

  1. 移除多余行的逻辑:你用body.childNodes.length -1来代表当前行数,这个计算依赖div始终是tbody的第一个子元素,而且错误地把div的存在当成了“一行”的抵消,逻辑本身就不准确。
  2. 添加/更新行的循环:你用body.childNodes[i]来获取第i行,但因为div的存在,i=0对应的是div而不是tr,导致循环到最后时少生成了一行。

解决方案

核心思路是直接针对tr元素进行统计和操作,完全忽略tbody里的其他元素,这样不管你在tbody里放多少额外元素(比如那个scrollbar div),都不会影响行的计数和生成。

需要修改两个部分的代码:

1. 修复addMultiRow方法中的行处理逻辑

把依赖childNodes的计数改成直接查询tr元素:

addMultiRow(body, dataCell = ['null']) {
  // 获取当前所有tr元素
  const currentRows = Array.from(body.querySelectorAll('tr'));
  
  // Remove unnecessary rows
  while (currentRows.length > dataCell.length) {
    const lastRow = currentRows.pop();
    lastRow.remove();
  }
  
  // Add or update rows
  for (let i = 0; i < dataCell.length; i++) {
    let row = currentRows[i];
    if (row) {
      this.setCell(row, this.#colsLength, dataCell[i]);
    } else {
      // Add missing rows
      this.addRow(body, dataCell[i]);
    }
  }
}

2. 修复rows属性的数组生成逻辑

原来的new Array(len).fill(new Array(len).fill('1'))会导致所有行共享同一个数组引用,是一个潜在bug,改成动态生成每个行的独立数组:

case 'rows':
  len = parseInt(newValue);
  if (!isNaN(len) && newValue != oldValue) {
    // 为每一行生成独立的数组,避免引用共享
    const rows = Array.from({length: len}, () => new Array(this.#colsLength).fill('1'));
    this.addMultiRow(this.tbody, rows);
  }
  break;

修改后的完整JavaScript代码

CSS和HTML部分无需修改,以下是修复后的完整JS代码:

'use strict';
class betterTable extends HTMLElement {
  #colsLength;
  constructor() {
    super();
    this.#colsLength = 0;
    /* TABLE */
    this.table = document.createElement('table');
    /* HEADER */
    this.thead = document.createElement('thead');
    this.table.appendChild(this.thead);
    this.theadRow = document.createElement('tr');
    this.thead.appendChild(this.theadRow);
    /* BODY */
    this.tbody = document.createElement('tbody');
    this.table.appendChild(this.tbody);
    this.scrollbar = document.createElement('div');
    this.tbody.appendChild(this.scrollbar);
  }
  connectedCallback() {
    this.render();
  }
  render() {
    this.appendChild(this.table);
  }
  setColumn(colsName = ['null']) {
    this.setCell(this.theadRow, colsName.length, colsName);
    this.#colsLength = colsName.length;
    this.cols = colsName.length;
  }
  setCell(row, len = this.#colsLength, cellText = new Array(this.#colsLength)) {
    // Remove unnecessary cells
    while (row.childNodes.length > len) row.lastChild.remove();
    // Add missing cells
    for (let i = 0; len > row.childNodes.length; i++) {
      const cell = row.parentNode ? row.parentNode.tagName === 'THEAD' ? document.createElement('th') : document.createElement('td') : document.createElement('td');
      row.appendChild(cell);
    }
    for (let i = 0; i < row.childNodes.length; i++) {
      if (typeof cellText[i] === 'string') {
        if (row.childNodes[i].innerText != cellText[i]) {
          row.childNodes[i].innerText = cellText[i];
        }
      } else if (cellText[i] instanceof Element) {
        row.childNodes[i].appendChild = cellText[i];
      } else {
        if (row.childNodes[i].innerText != 'null') {
          row.childNodes[i].innerText = 'null';
        }
      }
    }
  }
  addRow(body, dataCell) {
    const row = document.createElement('tr');
    this.setCell(row, this.#colsLength, dataCell);
    body.appendChild(row);
  }
  addMultiRow(body, dataCell = ['null']) {
    // 获取当前所有tr元素
    const currentRows = Array.from(body.querySelectorAll('tr'));
    
    // Remove unnecessary rows
    while (currentRows.length > dataCell.length) {
      const lastRow = currentRows.pop();
      lastRow.remove();
    }
    
    // Add or update rows
    for (let i = 0; i < dataCell.length; i++) {
      let row = currentRows[i];
      if (row) {
        this.setCell(row, this.#colsLength, dataCell[i]);
      } else {
        // Add missing rows
        this.addRow(body, dataCell[i]);
      }
    }
  }
  /* ///////////// Attributes ///////////// */
  static get observedAttributes() {
    return ['cols', 'rows'];
  }
  attributeChangedCallback(name, oldValue, newValue) {
    let len;
    switch (name) {
      case 'cols':
        len = parseInt(newValue);
        if (!isNaN(len) && this.#colsLength !== len && newValue != oldValue) {
          this.setColumn(new Array(len));
        }
        break;
      case 'rows':
        len = parseInt(newValue);
        if (!isNaN(len) && newValue != oldValue) {
          // 为每一行生成独立的数组,避免引用共享
          const rows = Array.from({length: len}, () => new Array(this.#colsLength).fill('1'));
          this.addMultiRow(this.tbody, rows);
        }
        break;
      default:
        break;
    }
  }
  /* // GETTER & SETTER // */
  get length() {
    return this.#colsLength;
  }
  get cols() {
    return this.getAttribute('cols');
  }
  get rows() {
    return this.getAttribute('rows');
  }
  set cols(num) {
    this.setAttribute('cols', num);
  }
  set rows(num) {
    this.setAttribute('rows', num);
  }
}
customElements.define('better-table', betterTable);

现在你设置rows='10'时,就能正确生成10行tr了,同时tbody里的scrollbar div也能保留。

内容的提问来源于stack exchange,提问作者DDHost

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:47:34