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

如何用HTML、CSS、原生JS实现可调整行列尺寸的100%宽度表格?

100%宽度可拖拽调整列宽/行高表格的最优实现方案

问题描述

需要创建宽度为100%的表格,支持拖拽调整列宽与行高。当前使用的原生JS代码在自适应内容宽度的表格上效果正常,但在100%宽度表格中存在问题:拖拽第二列向左时,第三列会被拉动调整,甚至出现列重叠情况。

现有代码的核心问题

现有实现只处理了向右拖拽时当前列与下一列的宽度分配,完全忽略了100%宽度表格的总宽度约束:向左拖拽缩小当前列时,未同步调整下一列宽度来维持表格总宽度,导致列宽总和失衡,最终出现重叠。


最优实现方案:修复原生JS适配100%宽度表格

以下是针对100%宽度表格优化后的完整实现,解决列重叠问题,同时保证交互流畅:

完整代码示例

JavaScript

const tables = document.getElementsByTagName("table");
for (let i = 0; i < tables.length; i++) {
  resizableGrid(tables[i]);
}

function resizableGrid(table) {
  const row = table.getElementsByTagName("tr")[0];
  const cols = row ? row.children : undefined;
  if (!cols || cols.length === 0) return;

  table.style.overflow = "hidden";
  table.style.tableLayout = "fixed"; // 关键:启用固定布局,强制使用设置的列宽

  // 初始化列宽:基于表格100%宽度均分,排除边框占用
  const tableWidth = table.offsetWidth;
  const totalBorder = parseInt(getStyleVal(table, "border-left-width")) * 2 + 
                      (cols.length - 1) * parseInt(getStyleVal(cols[0], "border-left-width"));
  const availableWidth = tableWidth - totalBorder;
  const initColWidth = availableWidth / cols.length;

  for (let i = 0; i < cols.length; i++) {
    cols[i].style.width = `${initColWidth}px`;
    cols[i].style.minWidth = "50px"; // 设置最小列宽,避免缩至消失
    cols[i].style.position = "relative";
    const div = createDiv(table.offsetHeight);
    cols[i].appendChild(div);
    setListeners(div);
  }

  function setListeners(div) {
    let pageX, curCol, nxtCol, curColWidth, nxtColWidth;

    div.addEventListener("mousedown", function (e) {
      e.preventDefault(); // 防止拖拽时选中文本
      curCol = e.target.parentElement;
      nxtCol = curCol.nextElementSibling;
      pageX = e.pageX;

      curColWidth = parseFloat(curCol.style.width);
      if (nxtCol) nxtColWidth = parseFloat(nxtCol.style.width);
    });

    div.addEventListener("mouseover", function (e) {
      e.target.style.borderRight = "2px solid #0000ff";
    });

    div.addEventListener("mouseout", function (e) {
      e.target.style.borderRight = "";
    });

    document.addEventListener("mousemove", function (e) {
      if (!curCol || !nxtCol) return;

      const diffX = e.pageX - pageX;
      // 计算新宽度,确保不低于最小限制
      const newCurWidth = Math.max(parseFloat(curCol.style.minWidth), curColWidth + diffX);
      const newNxtWidth = Math.max(parseFloat(nxtCol.style.minWidth), nxtColWidth - diffX);

      // 仅当两者都满足最小宽度时更新,避免布局错乱
      if (newCurWidth >= parseFloat(curCol.style.minWidth) && newNxtWidth >= parseFloat(nxtCol.style.minWidth)) {
        curCol.style.width = `${newCurWidth}px`;
        nxtCol.style.width = `${newNxtWidth}px`;
        pageX = e.pageX; // 更新基准点,消除累积误差
      }
    });

    document.addEventListener("mouseup", function () {
      // 重置状态,避免残留影响
      curCol = undefined;
      nxtCol = undefined;
      pageX = undefined;
      nxtColWidth = undefined;
      curColWidth = undefined;
    });
  }

  function createDiv(height) {
    const div = document.createElement("div");
    div.style.top = 0;
    div.style.right = 0;
    div.style.width = "8px";
    div.style.position = "absolute";
    div.style.cursor = "col-resize";
    div.style.userSelect = "none";
    div.style.height = `${height}px`;
    div.style.zIndex = 10; // 确保拖拽层在最上层,避免被内容遮挡
    return div;
  }

  function getStyleVal(elm, css) {
    return window.getComputedStyle(elm, null).getPropertyValue(css);
  }
}

CSS

body {
  font-family: sans-serif;
  margin: 20px;
}
table {
  border-collapse: collapse;
  width: 100%;
  border: 1px solid lightgray;
}
td, th {
  text-align: center;
  border-left: 1px solid lightgray;
  padding: 0.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap; /* 避免内容撑开列宽 */
}

HTML

<table>
  <caption>100%宽度可拖拽列宽表格</caption>
  <thead>
    <tr><th>列A</th><th>列B</th><th>列C</th><th>列D</th></tr>
  </thead>
  <tbody>
    <tr><td>内容A1</td><td>内容B1</td><td>内容C1</td><td>内容D1</td></tr>
    <tr><td>内容A2</td><td>内容B2</td><td>内容C2</td><td>内容D2</td></tr>
    <tr><td>内容A3</td><td>内容B3</td><td>内容C3</td><td>内容D3</td></tr>
  </tbody>
  <tfoot>
    <tr><th>列A</th><th>列B</th><th>列C</th><th>列D</th></tr>
  </tfoot>
</table>

关键改进点

  1. 启用table-layout: fixed:强制表格使用设置的列宽,而非自适应内容,这是100%宽度表格可控列宽的核心。
  2. 初始化均分列宽:根据表格总宽度计算初始列宽,确保初始状态下总宽度为100%。
  3. 完善拖拽逻辑:
    • 支持向左/向右拖拽,保证当前列与下一列的宽度总和不变,维持表格总宽度。
    • 设置列宽最小值,避免列被缩至消失或重叠。
    • 更新拖拽基准点,消除累积误差导致的布局偏移。
  4. 增强交互体验:增大拖拽区域宽度,提升拖拽层层级,添加文本溢出处理避免内容撑开列宽。

行高拖拽扩展

若需支持行高拖拽,逻辑与列宽类似:

  • 在每行底部添加横向拖拽条。
  • 监听鼠标事件,调整当前行(或当前行+下一行)的高度,并设置最小行高限制。

内容的提问来源于stack exchange,提问作者D'arganatan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:34:57