应使用 <table>,因其语义正确、可访问性好、支持表头关联与现代CSS控制;仅当需移动端彻底重构布局时才考虑<div>+语义属性。

用 <table> 还是 <div>?先看语义和可访问性
定价表格本质是「对比型数据」,不是装饰性布局。用 <table> 是语义正确且对屏幕阅读器友好的选择;强行用 <div> + CSS Grid/Flex 做“伪表格”,反而要手动补 role="table"、aria-label 等一堆可访问性属性,得不偿失。
除非你明确需要响应式断点下彻底重构列结构(比如移动端把横向对比变成纵向堆叠),否则别为了“听说 table 不灵活”就弃用它。
-
<table>天然支持列宽自适应、表头关联(scope="col")、排序/筛选 JS 插件接入 - 现代浏览器对
<table>的 CSS 控制力足够强,border-collapse、sticky表头、colgroup都可用 - 用
<div>模拟时,Ctrl+F搜索价格容易失效,打印预览也常错乱
表头怎么写才不踩坑:避免 <th> 嵌套错位
常见错误是把功能描述(如“无限存储”)直接塞进 <th>,但没区分「行标题」和「列标题」。定价表通常有两维:左侧功能项(行)、顶部方案名(列)。
正确结构必须用 scope 明确归属:
立即学习“前端免费学习笔记(深入)”;
<thead>
<tr>
<th></th> <!-- 空单元格,不设 scope -->
<th scope="col">基础版</th>
<th scope="col">专业版</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">存储空间</th>
<td>10 GB</td>
<td>100 GB</td>
</tr>
</tbody>- 顶部方案名用
scope="col",让辅助工具知道这是列标题 - 左侧功能项用
scope="row",否则 NVDA/JAWS 读屏时会漏读上下文 - 不要用
<th>包裹价格数字——价格是数据,属于<td>;只有标题才用<th>
响应式折叠:用 @media 改变 display,别删 DOM
移动端窄屏下强行横向滚动体验极差。可行做法是用媒体查询把表格“竖过来”,每项方案变成一个卡片块,但保留原始语义结构。
关键不是隐藏列,而是重排视觉流:
@media (max-width: 768px) {
.pricing-table { display: block; }
.pricing-table thead,
.pricing-table tbody,
.pricing-table tr { display: block; }
.pricing-table th,
.pricing-table td { display: block; }
.pricing-table th:first-child,
.pricing-table td:first-child { font-weight: bold; }
}- 不使用
display: none隐藏列——这会让屏幕阅读器跳过整列内容 - 用
display: block+flex或grid重新组织,但保持<th>/<td>标签层级不变 - 如果方案超过 3 个,移动端建议加锚点跳转,避免长页面无导航
价格高亮和状态标记:用 data- 属性驱动样式,别硬编码颜色
“推荐”“热门”标签或价格栏背景色,容易写成固定 class(如 class="popular"),结果换主题时全要改 HTML。更可持续的做法是用语义化属性:
<tr data-tier="popular"> <th scope="row">专业版</th> <td data-price="29">$29/mo</td> <td>✓</td> </tr>
-
data-tier可用于 CSS 选择器:[data-tier="popular"] { border-left: 4px solid #3b82f6; } -
data-price方便后续 JS 计算总价、做货币切换,也利于 SEO 提取结构化数据 - 避免在 HTML 里写内联 style:
style="background:#e0f2fe"—— 这类样式应由 CSS class 统一控制
最易被忽略的是价格单位的语义处理:<span aria-hidden="true">$</span><span class="visually-hidden">dollar</span>29,否则读屏器可能把 “$29” 读成“美元二十九”而非“二十九美元”。



















