table结构不规范直接导致excel导出质量问题。必须显式写出thead/tbody,th需带scope属性,caption长度≤31字符,避免td内嵌元素;隐藏行应使用hidden属性而非display:none;禁止非法嵌套及colspan/rowspan越界或跨区合并。

为什么 table 结构会影响 Excel 导出质量
导出时出现列错位、空行、表头丢失或中文乱码,往往不是库的问题,而是原始 table 的 DOM 结构不规范。SheetJS(xlsx)等库会忠实解析 HTML 树,<thead> 缺失、<code><tbody> 被省略、<code><tr> 嵌套在 <code><div> 里,都会导致 <code>XLSX.utils.table_to_book() 提取的行列结构错乱。更隐蔽的是:浏览器自动补全的 tbody 在 JS 中不可见,但导出时会被补上——你没写的,库替你写了,结果多出空行。
必须保留的语义化标签和属性
不是“建议用”,而是导出逻辑依赖这些节点做分片解析:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
<thead> 和 <code><tbody> 必须显式写出,不能靠浏览器自动包裹;否则 <code>table_to_book()无法区分表头与数据,所有行都当普通行处理<th> 只用于表头单元格,且需配 <code>scope="col"或scope="row",否则 SheetJS 无法识别列维度,导出后 Excel 里筛选按钮失效-
<caption></caption>会被映射为工作表标题(sheet name),但仅当内容纯文本且长度 ≤ 31 字符才安全;超长或含特殊字符会触发Invalid sheet name错误 - 避免在
<td> 内嵌 <code><div>、<code><span></span>或带style的元素——它们的内容可能被截断或转为空字符串导出前要主动过滤的 DOM 干扰项
用户搜索后隐藏的行、操作列按钮、合计行、分页器,如果只是用
display: none隐藏,table_to_book()照样导出。它不读 CSS,只读 HTML 结构。- 把隐藏逻辑从
row.style.display = "none"改成row.hidden = true,然后导出前调用table.querySelectorAll("tr:not([hidden])")构造干净副本 - 移除带特定 class 的行,比如
table.querySelectorAll("tr.search-input, tr.total-row").forEach(el => el.remove()) - 检查是否有空
<tr></tr>或只有<td></td>的行——它们会导出为 Excel 里的空白行,影响后续公式引用范围 - 确保所有
<td> 和 <code><th> 都在同一个层级的 <code><tr> 下,不要出现 <code><tr><td></td></tr> <tr><td>... 这种非法嵌套 <h3> <code>colspan和rowspan的实际限制SheetJS 支持合并单元格,但前提是 HTML 中的
colspan/rowspan属性值合法且无重叠。一旦出现跨<thead> 和 <code><tbody> 的合并,或两个相邻 <code><td> 的 <code>colspan相加超过表格总列数,导出就会静默失败——Excel 打开报“发现不可读内容”,点“是”后合并丢失、数据偏移。- 导出前用 JS 校验:遍历所有
td,累加colspan值,确认每行总跨度等于列数(可用table.rows[0].cells.length作基准) - 避免在
<thead> 里用 <code>rowspan跨多行,SheetJS 对其解析不稳定;如需多级表头,改用两行<tr> + <code>colspan拆分 -
rowspan值大于 1 的单元格,其下方对应位置的<td> 必须真实存在(哪怕内容为空),否则导出后该列数据整体下移 真正卡住导出效果的,从来不是库的 API 调用是否正确,而是你传给它的那个 <code>table元素本身有没有“说人话”。结构松散、语义模糊、隐藏逻辑混杂的表格,再强的库也救不回来。
- 导出前用 JS 校验:遍历所有
- 把隐藏逻辑从










