12k
All articles

表格而非 Div:用于真实表格数据的简洁 API

使用 HTML table DOM API 及其原生方法构建真实数据表格,可有效规避 XSS 风险,并默认生成语义化、无障碍的标记结构。

OpenReplay Team
OpenReplay Team
表格而非 Div:用于真实表格数据的简洁 API

大多数构建仪表板或管理工具的 JavaScript 开发者都曾在某个时刻与表格渲染较量过。你可能拼接过 HTML 字符串,与 innerHTML 搏斗过,或者看着同事使用嵌套的 div 和 CSS Grid 重建表格。其实有一条更简单的路径自 Web 早期以来一直隐藏在显而易见之处:HTML 表格 DOM API。

HTMLTableElement 接口提供了用于增量创建、读取和修改表格结构的原生方法——无需字符串拼接或完全重新渲染。它将表格视为需要操作的结构化数据,而非需要生成的标记。

核心要点

  • HTMLTableElement 接口提供了 insertRow()insertCell()deleteRow() 等原生方法,用于直接操作表格而无需字符串拼接。
  • 实时 HTMLCollection 对象(rowscells)会自动更新,使增量更改变得简单。
  • 使用表格 API 可避免 innerHTML 固有的 XSS 漏洞,并减少实时更新时的布局抖动。
  • 带有适当 <thead><th><tbody> 的语义化 <table> 元素提供了基于 div 的布局无法匹敌的内置可访问性。

被遗忘的 JavaScript 表格 API

HTML 表格 DOM API 存在于每个 <table> 元素上。它包括 insertRow()insertCell()deleteRow()createTHead() 等方法,以及 rowscells 等实时集合,这些集合会随着表格的变化自动更新。

以下是核心模式:

const table = document.createElement('table')
const row = table.insertRow()
const cell = row.insertCell()
cell.textContent = 'Hello'

无需模板字面量。无需 innerHTML。只需使用专门构建的方法进行直接 DOM 操作。

你可以通过索引访问任何单元格:

console.log(table.rows[0].cells[0]) // <td>Hello</td>

rowscells 属性返回实时 HTMLCollection 对象。删除一行,table.rows.length 会立即更新。这使得增量更新变得简单——当数据到达时添加一行,当数据被删除时移除一行,而无需触及表格的其余部分。

为什么开发者遗忘了 HTMLTableElement

该 API 有一些怪癖。用于追加到末尾的 -1 索引约定感觉很奇怪。没有 insertHeaderCell() 方法——你必须使用 createElement() 手动创建 <th> 元素并追加它们。这些粗糙的边缘,加上完全抽象 DOM 操作的框架的兴起,将该 API 推向了默默无闻。

但这些限制是次要的。表头单元格的解决方法很直接:

const thead = table.createTHead()
const headerRow = thead.insertRow()
const th = document.createElement('th')
th.textContent = 'Name'
headerRow.appendChild(th)

你还可以使用 createTFoot()createCaption(),并访问 table.tBodies 以获取多个主体部分。该 API 涵盖了完整的表格结构。

安全性和性能优势

使用 innerHTML 构建表格会引发 XSS 漏洞。任何未经清理的用户数据都会变成可执行的 HTML。表格 API 完全避开了这一点——textContentinsertCell() 不会解析 HTML。

性能也很重要。使用 API 修改单个单元格不会强制浏览器重新解析和重建整个表格。对于实时更新的仪表板,这种增量方法可以减少不必要的重新解析和回流。

语义化表格和内置可访问性

基于 div 的表格重新实现的错误之处在于:它们丢弃了语义。一个带有适当 <thead><th><tbody> 元素的真正 <table> 为屏幕阅读器和辅助技术提供了导航表格数据所需的一切。你基本上可以免费获得可访问的数据表格。

在表头单元格中添加 scope="col"<caption> 元素就完成了整个图景。不需要 ARIA 网格角色——那些是用于交互式小部件网格的,而非数据表格。语义化表格是显示结构化信息的正确原语。

何时使用原生 API

HTML 表格 DOM API 在原生 JavaScript 环境中表现出色:内部工具、轻量级仪表板、管理面板,或任何你想要直接控制而无需框架开销的地方。

如果你正在使用 React 或 Vue,你已经在使用处理渲染的虚拟 DOM。像 TanStack Table v8 这样的库提供了与框架渲染配合良好的无头表格逻辑。但对于原生 JS 或渐进增强场景,原生 API 仍然是更清晰的选择。

结论

表格从未停止成为表格数据的正确工具。改变的是我们构建它们的方式。HTMLTableElement 接口在原始字符串操作和重度抽象之间提供了一条中间路径——原生、增量且安全。

该 API 是存在的。它在每个浏览器中都有效。它默认生成语义化、可访问的标记。对于在 JavaScript 中处理真实数据的前端开发者来说,它值得重新发现。

常见问题

什么是 HTMLTableElement API,我为什么要使用它?

HTMLTableElement API 是一个原生浏览器接口,用于通过 JavaScript 直接操作表格元素。它提供了 insertRow 和 insertCell 等方法来构建表格,无需字符串拼接。你应该使用它,因为它避免了 XSS 漏洞,支持增量更新而无需完全重新渲染,并且自动生成语义化可访问的标记。

如何使用表格 API 添加表头单元格?

表格 API 缺少专用的 insertHeaderCell 方法。相反,使用 createTHead 创建一个 thead 部分,插入一行,然后使用 document.createElement 手动创建 th 元素并将它们追加到行中。这个解决方法很直接,并让你完全控制表头单元格的属性,如 scope。

我应该在什么时候使用原生表格 API 而不是框架?

在原生 JavaScript 项目、内部工具、轻量级仪表板或渐进增强场景中使用原生表格 API。如果你已经在使用 React 或 Vue,它们的虚拟 DOM 会高效地处理渲染。对于排序和过滤等复杂表格功能,可以考虑使用像 TanStack Table 这样的无头库。

为什么语义化表格比基于 div 的布局更适合表格数据?

使用适当的 thead、th 和 tbody 元素的语义化表格提供了屏幕阅读器自动理解的内置可访问性。基于 div 的布局需要大量的 ARIA 属性来复制此功能,而且往往达不到要求。语义化表格是结构化数据的正确 HTML 原语,并且使其可访问所需的工作量更少。

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.