必须用 article 包裹每条通知,time 元素 datetime 属性需带时区的 ISO 8601 格式,未读状态须显式写入 aria-label,操作按钮必须为原生 button 并携带 data-msg-id。

消息列表必须用 article 包裹每条通知,不能用 div 或 li
用 div 堆叠消息项会导致屏幕阅读器无法识别独立内容单元,键盘焦点跳过、滚动定位偏移、无障碍测试直接失败。浏览器对 article 有原生语义支持,它代表“可独立分发、复用、引用的内容”,正好匹配单条通知的业务含义。
常见错误包括:
- 套用旧教程写成
<ul><li class="notification">...</li></ul>——li仅适用于列表项,不是语义化内容容器 - 为“省事”用
<div id="msg-123">—— 缺失语义,aria-label和time标签在其中效力大打折扣 - 嵌套多层
div模拟卡片结构 —— DOM 层级膨胀,CSS 选择器易失控,JS 查询querySelector('time')可能命中错误节点
正确写法只有一行核心:<article id="msg-123">,所有子元素(h3、time、button)都挂载其下。
time 元素的 datetime 属性必须带时区且为 ISO 8601 格式
后端返回 "2026-05-20 14:30:00" 这种无时区字符串,前端用 new Date("2026-05-20 14:30:00") 解析,在 Chrome 可能按本地时区处理,在 Safari 可能当 UTC 处理——同一时间戳,在不同设备上解析出的时间差可能达 8 小时。
立即学习“前端免费学习笔记(深入)”;
必须要求后端输出带时区的完整 ISO 时间,例如:
-
2026-05-20T14:30:00+08:00(中国标准时间) -
2026-05-20T06:30:00Z(UTC)
若后端暂不支持,前端补救方案是手动拼接时区:new Date(`${isoDate}+08:00`),但不如源头修正可靠。别用 toLocaleString() 渲染后再反向解析——中文文本不可靠,正则提取极易崩溃。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
未读状态必须显式写进 aria-label,不能只靠 CSS 类名
只加 class="unread" 再配红点图标,对屏幕阅读器完全不可见;用户刷新页面后,JS 状态丢失,未读数归零;多标签页场景下,一个页标记已读,其他页仍显示未读——全因状态没落地到可序列化的 HTML 属性中。
正确做法是把语义和状态一起塞进属性里:
-
aria-label="未读:订单 #8829 已发货"—— 屏幕阅读器可读,服务端可校验,刷新不丢 - 标记已读后,同步更新该属性为
aria-label="已读:订单 #8829 已发货",而非仅删 class - 批量操作时,通过
document.querySelectorAll('[aria-label^="未读:"]')可靠获取未读 ID 列表,比查 class 稳定得多
注意:不要混用 aria-label 和 aria-live,后者用于动态区域播报,此处只需静态声明。
操作按钮必须是 button,且带 data-msg-id
用 span onclick="markRead(id)" 或 div role="button" 模拟按钮,会导致键盘用户无法 Tab 聚焦、Enter 触发,违反 WCAG 2.1。浏览器对原生 button 有完整的可访问性支持。
每个操作按钮必须满足:
- 类型明确:
<button type="button">标记已读</button>(避免触发表单提交) - 绑定消息 ID:
data-msg-id="msg-123",这是 JS 收集操作目标的唯一可信依据 - 禁用状态需同步:
disabled属性 +aria-busy="true",防止重复点击 - 请求成功后才更新 DOM:
fetch('/api/messages/read', { method: 'POST', body: JSON.stringify({ id: 'msg-123' }) })成功,再改aria-label和 class
容易忽略的一点:关闭通知面板前,检查是否有 pending 的 fetch 请求,若有,应保留 loading 状态并禁用所有按钮,否则用户关掉面板,请求还在后台跑,结果无人知晓。


















