必须使用语义化而非实现crm客户列表,因其准确表达二维结构化数据,保障可访问性、seo和交互扩展性,并通过/、scope属性、button控件及响应式css确保小屏可用与无障碍合规。

CRM客户列表页面不是“能显示就行”,而是必须兼顾语义正确性、可访问性、后续交互扩展性,以及小屏下的可用性。用 div 堆出个“看起来像表格”的列表,上线快,但改筛选、加导出、适配读屏器时会卡死。
为什么必须用 <table> 而不是 <code><div> 列表
<p>客户列表本质是二维结构化数据(姓名/电话/状态/操作),<code><table> 是唯一被 HTML 规范明确定义用于此场景的语义化标签。绕开它会直接带来三类硬伤:
<ul>
<li>屏幕阅读器无法识别“这是客户列表”,每行只读作孤立段落,丢失“第3行:张三,已激活”这类上下文</li>
<li>搜索引擎无法索引“共显示 42 条客户记录”,<code>class="customer-item" 对爬虫毫无意义
div 并模拟行列关系,代码冗长且易错<thead> 和 <code><tbody> 不是可选项
<p>只写 <code><tr><td> 是无效语义——浏览器和辅助工具根本不知道哪行是标题、哪行是数据。必须显式包裹:
<ul><li><code><thead> 内只能用 <code><th>(不能用 <code><td>),并加 <code>scope="col",例如 <th scope="col">客户姓名</th>,让读屏器明确告知“这一列叫客户姓名”
<tbody> 是 JS 操作的数据主容器,删除某行、批量启用/禁用,都应以它为父节点调用 <code>querySelectorAll('tr')
border="1";用 CSS 控制边框,但务必保留 border-collapse: collapse 防止单元格缝隙错位操作按钮必须是 <button></button>,且带 data- 属性
“编辑”“删除”不是文字标签,是可触发行为的控件。写成 <span class="btn-delete">删除</span> 会切断键盘导航与读屏支持:
- Tab 键无法聚焦该元素,键盘用户完全不可操作
- 读屏器只朗读“删除”,不会附加“按钮”语境,用户无法判断是否可点击
- 移动端触摸目标默认无 padding,极易误触
- 正确写法:
<button type="button" data-customer-id="872">删除</button>,ID 存在data-属性里,比从文本中innerText.match(/\d+/)更可靠、更安全
小屏下 <table> 必须降级为卡片布局
<p>在 <code>max-width: 768px 下强制横向滚动表格,等于放弃移动用户体验。CSS 降级方案要干净:
- 用媒体查询隐藏
<thead>(<code>display: none),避免重复信息干扰
- 给
<tbody> 设 <code>display: block,内部每个 <tr> 改为 <code>display: block; margin-bottom: 1rem
- 每行内用
<div class="card-field">
<strong>姓名:</strong>李四</div> 模拟字段标签+值,保持信息可读性
- 不推荐 JS 动态重写 DOM 结构——响应式应由 CSS 驱动,更轻量、更稳定
真正难的不是写出能显示客户的 HTML,而是在第一行 <table> 就决定好未来半年要不要加导出、要不要接读屏、要不要上无障碍审计——这些约束都在语义和结构里,不在样式里。</table>
<thead>(<code>display: none),避免重复信息干扰
<tbody> 设 <code>display: block,内部每个 <tr> 改为 <code>display: block; margin-bottom: 1rem
<div class="card-field">
<strong>姓名:</strong>李四</div> 模拟字段标签+值,保持信息可读性真正难的不是写出能显示客户的 HTML,而是在第一行 <table> 就决定好未来半年要不要加导出、要不要接读屏、要不要上无障碍审计——这些约束都在语义和结构里,不在样式里。</table>











