表格结构必须按“属性-商品”二维展开,即<thead>放属性名、<tbody>每行对应一个商品;需用语义化<th scope="row">和<th scope="col">明确行列关系,配合CSS Grid重排视觉流但保留表格语义,同时规范空值表达与移动端属性列冻结。

表格结构要按“属性-商品”二维展开,不是“商品-属性”
商品对比本质是横向比参数、纵向列维度,所以 <thead> 必须放属性名(如“价格”“续航”“重量”),<tbody> 每行对应一个商品。反着做——把商品名放第一列、属性当表头——会导致响应式错乱、语义混乱,且无法用 CSS 选择器统一控制属性列样式。
常见错误是先写好商品列表再硬套表格,结果 <tr> 里堆满 <td> 而没区分维度。正确做法:先确定对比维度(即属性字段),再为每个商品生成一列 <th> 或 <td>,让每列代表一个商品,每行代表一个可比项。
- 属性字段必须提前收敛,避免动态插入导致列宽抖动
- 首列固定为属性名,用
<th scope="row">标明语义 - 商品列标题用
<th scope="col">,方便屏幕阅读器识别 - 不要用
colspan合并属性行,会破坏行列对应关系
用 CSS Grid 替代 table 布局更可控,但语义不能丢
纯 <table> 在移动端容易横向溢出,强行加 overflow-x: auto 又割裂体验。实际项目中,更推荐用语义化 <table> 结构 + CSS Grid 重排视觉流:保留 <table> 的 DOM 结构和 ARIA 支持,用 display: grid 重新定义 <tr> 和 <td> 的布局流向。
关键不是放弃表格标签,而是不让它承担布局职责。Grid 容易踩的坑是忽略可访问性:如果用 grid-template-areas 打乱 DOM 顺序,屏幕阅读器仍按 HTML 顺序读取,会造成认知错位。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 保持
<table>的原始 HTML 结构不变 - 只对
<tbody>和<tr>应用display: grid,列数由商品数量决定 - 用
grid-auto-flow: column让每行变成横向滚动单元 - 属性行(
<thead>)保持display: contents避免干扰 Grid 流
“暂无数据”和空值要区分渲染,不能全塞
对比表里常出现某商品缺失某参数(如“不支持无线充电”),和该参数本身未采集(如“待确认”)。前者是明确否定,后者是信息缺失。混用 或留空会导致用户误判,也影响后续 JS 判断。
建议统一用语义化占位符:— 表示不支持/不适用,? 表示未知/未填写,并为两者加不同 class 方便样式隔离。避免用颜色单独表达状态——色盲用户无法识别,必须配合文字或图标。
- 禁止用透明文字或
visibility: hidden隐藏空值,会破坏可访问性 - JS 动态填充时,检查
value === nullvsvalue === undefined,处理逻辑不同 - 服务端返回空字符串时,前端应主动转为
?,而非原样渲染
移动端竖屏下必须锁定属性列,否则对比失效
横向滚动时,如果属性名跟着一起滚走,用户根本不知道当前看到的是哪一项参数。这不是 UI 美观问题,是功能缺陷——对比失去坐标参照。
解决方案不是冻结整个第一列(那样会挤压商品内容),而是仅冻结 <th scope="row"> 对应的单元格,并确保其 position: sticky 的父容器有明确高度和 overflow-x: auto。容易被忽略的是:sticky 生效需要父元素有非 static 的定位上下文,且不能和 transform 共存。
- 给
<tbody>加position: relative,避免 sticky 脱离预期容器 - 属性列
<th>设置left: 0; z-index: 1,防止被其他列遮盖 - 测试时用真机滑动,模拟器可能不触发 sticky 行为
- iOS Safari 对 sticky 支持较弱,需加
-webkit-overflow-scrolling: touch


















