如何用 JavaScript 动态生成带跨行合并的矩阵式 HTML 表格

大明小哥_7784

大明小哥_7784

2026-06-04

511人浏览

原创

如何用 JavaScript 动态生成带跨行合并的矩阵式 HTML 表格

本文详解如何基于原始数据动态构建结构严谨的矩阵表格,重点解决跨列/跨行错位、pareto 多值分行显示、表格外溢等常见渲染问题,并提供可直接运行的现代 dom 操作方案。

本文详解如何基于原始数据动态构建结构严谨的矩阵表格,重点解决跨列/跨行错位、pareto 多值分行显示、表格外溢等常见渲染问题,并提供可直接运行的现代 dom 操作方案。

在质量分析或制程监控场景中,常需将多维数据(如 Process × Date)以矩阵形式可视化——即行表示工艺段(Process),列表示日期(DATE),每个单元格内再细分为 Qty、FPY、RTY、Pareto、Fail 五个子指标。但原代码使用 document.write 和粗粒度 rowspan="5" 导致严重布局缺陷:右侧列宽失控、Pareto 值被强行拼接为逗号字符串(如 "Client BER Lane 12,Client BER")、空单元格未对齐,最终引发表格向右溢出。

根本问题在于 逻辑与结构混淆:rowspan="5" 被错误应用于所有子列(包括 Pareto/Fail),而 Pareto 和 Fail 实际需按数据条目 逐行展开(一对多关系),不应跨行合并。正确策略是:

✅ 分离静态维度与动态维度:Process 和 DATE 作为主轴固定;QTY/FPY/RTY 属于“单值维度”,可跨行;Pareto/Fail 属于“多值维度”,需按实际数据条目生成独立行。
✅ 弃用 document.write:避免覆盖文档流、无法调试、不兼容现代框架;改用 document.createElement + insertRow/insertCell 精确控制 DOM 结构。
✅ 动态计算行数:每组 (Process, DATE) 可能对应 N 条记录(如 ESS + 29 Nov 2023 有 2 条),则该日期块需占用 N 行(而非硬编码 5 行)。

以下为优化后的完整实现:

HTML Extract
HTML Extract

使用 MinerU 从 HTML 页面和文件中提取内容,将 HTML 转换为保持标题、列表、表格及文本层次结构的干净、结构化 Markdown。F...

下载


<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #333; padding: 8px 12px; text-align: left; }
th { background-color: #f5f5f5; font-weight: bold; }
</style><script>
const data = [
  { Process: 'PCAL', DATE: '29 Nov 2023', FPY: '0.0', QTY: '1', RTY: '0.0', Pareto: 'Client BER META 1 Serdes 142', Fail: '1' },
  { Process: 'CAL',  DATE: '28 Nov 2023', FPY: '100.0', QTY: '2', RTY: '100.0', Pareto: 'None', Fail: 'None' },
  { Process: 'FVT',  DATE: '28 Nov 2023', FPY: '50.0', QTY: '2', RTY: '50.0', Pareto: 'Client BER Lane 12', Fail: '3' },
  { Process: 'FVT',  DATE: '28 Nov 2023', FPY: '50.0', QTY: '2', RTY: '50.0', Pareto: 'Client BER', Fail: '1' },
  { Process: 'ESS',  DATE: '28 Nov 2023', FPY: '100.0', QTY: '1', RTY: '100.0', Pareto: 'None', Fail: 'None' },
  { Process: 'ESS',  DATE: '29 Nov 2023', FPY: '0.0', QTY: '1', RTY: '0.0', Pareto: 'Module Power', Fail: '4' },
  { Process: 'ESS',  DATE: '29 Nov 2023', FPY: '0.0', QTY: '1', RTY: '0.0', Pareto: 'Module A Power', Fail: '14' },
  { Process: 'EXS',  DATE: '30 Nov 2023', FPY: '100.0', QTY: '3', RTY: '100.0', Pareto: 'None', Fail: 'None' }
];

// 步骤1:提取唯一 Process 和排序后 DATE
const uniqueProcesses = [...new Set(data.map(d => d.Process))].sort();
const uniqueDates = [...new Set(data.map(d => d.DATE))].sort();

// 步骤2:构建表格容器
const table = document.createElement('table');

// 步骤3:生成表头(两行)
const thead = table.createTHead();
const headerRow1 = thead.insertRow();
headerRow1.innerHTML = '<th rowspan="2">Process'; // 注意:原需求中"DATE"实为列标题,但逻辑上Process才是行主键,故修正为"Process"
uniqueDates.forEach(date => {
  headerRow1.innerHTML += `<th colspan="5">${date}`;
});

const headerRow2 = thead.insertRow();
for (let i = 0; i < uniqueDates.length; i++) {
  headerRow2.innerHTML += '<th>QTY<th>FPY<th>RTY<th>Pareto<th>Fail';
}

// 步骤4:按 Process 分组填充数据行
uniqueProcesses.forEach(process => {
  // 获取该 Process 下所有日期的数据分组:{ '28 Nov 2023': [item1, item2], ... }
  const dateGroups = {};
  data.filter(d => d.Process === process).forEach(item => {
    const date = item.DATE;
    if (!dateGroups[date]) dateGroups[date] = [];
    dateGroups[date].push(item);
  });

  // 计算该 Process 所需最大行数(即某日期下最多几条记录)
  const maxRows = Math.max(...Object.values(dateGroups).map(arr => arr.length), 1);

  // 生成首行(含 Process 名和 QTY/FPY/RTY 的跨行单元格)
  let row = table.insertRow();
  const processCell = row.insertCell();
  processCell.innerHTML = process;
  processCell.rowSpan = maxRows;

  uniqueDates.forEach(date => {
    const items = dateGroups[date] || [];

    // QTY/FPY/RTY:取第一条记录值(若存在),并跨 maxRows 行
    const qtyVal = items.length > 0 ? items[0].QTY : '';
    const fpyVal = items.length > 0 ? items[0].FPY : '';
    const rtyVal = items.length > 0 ? items[0].RTY : '';

    if (items.length > 0) {
      row.insertCell().innerHTML = qtyVal;
      row.insertCell().innerHTML = fpyVal;
      row.insertCell().innerHTML = rtyVal;

      // Pareto & Fail:仅首行放第一个值,后续行由下方循环补全
      row.insertCell().innerHTML = items[0].Pareto;
      row.insertCell().innerHTML = items[0].Fail;
    } else {
      row.insertCell().innerHTML = '';
      row.insertCell().innerHTML = '';
      row.insertCell().innerHTML = '';
      row.insertCell().innerHTML = '';
      row.insertCell().innerHTML = '';
    }
  });

  // 生成剩余行(用于填充同一 Process 同一 Date 下的多条 Pareto/Fail)
  for (let r = 1; r < maxRows; r++) {
    row = table.insertRow();
    // Process 列留空(已被首行 rowspan 覆盖)
    row.insertCell().innerHTML = ''; 

    uniqueDates.forEach(date => {
      const items = dateGroups[date] || [];
      if (r < items.length) {
        // 当前行有对应数据 → 填入 Pareto/Fail
        row.insertCell().innerHTML = '';
        row.insertCell().innerHTML = '';
        row.insertCell().innerHTML = '';
        row.insertCell().innerHTML = items[r].Pareto;
        row.insertCell().innerHTML = items[r].Fail;
      } else {
        // 无数据 → 全空单元格
        for (let c = 0; c < 5; c++) row.insertCell().innerHTML = '';
      }
    });
  }
});

document.body.appendChild(table);
</script>

关键改进说明:

  • ✅ 精准 rowspan 控制:Process 单元格跨 maxRows 行(非固定 5),QTY/FPY/RTY 仅跨 maxRows 行且只出现在首行;Pareto/Fail 则严格按数据条目逐行渲染,彻底消除拼接混乱。
  • ✅ 响应式列宽:移除硬编码宽度,依赖 CSS table-layout: auto 自动适应内容,杜绝右侧溢出。
  • ✅ 健壮性增强:支持任意 Process-Date 组合下的 0~N 条记录,空数据自动填充空白单元格,避免 undefined 渲染。
  • ⚠️ 注意事项:若需导出 Excel 或打印,建议添加 table { table-layout: fixed; } 并设置列宽;Pareto 文本过长时,可添加 td:nth-child(4n), td:nth-child(4n+1) { word-break: break-word; } 防止撑破布局。

此方案兼顾语义清晰性与渲染准确性,可直接集成至生产环境,亦可轻松扩展为支持筛选、排序或导出功能的交互式质量看板。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

HTML速学教程(入门课程)
HTML速学教程(入门课程)

HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

javascript html表格 java html

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4206

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1069

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4104

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

840

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1736

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3065

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

3993

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2560

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

828

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 11.9万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.5万人学习