纯前端实现可访问通知中心:优先级、已读状态与批量操作
应用里的通知中心经常同时承担三类信息:需要立即处理的故障、稍后查看的业务消息、以及只用于确认操作成功的短暂提示。把所有内容都塞进同一个下拉框,会让重要通知被淹没,也会让键盘和读屏用户难以判断哪些信息真正需要处理。
一、先区分瞬时提示和持久通知
“保存成功”更适合用短暂 toast 或页面内状态反馈;需要稍后处理、可标记已读或能追溯的消息,才进入通知中心。持久通知应有唯一 ID、类型、创建时间、已读状态和可选的操作入口。不要用页面文本临时拼出一个无法更新的消息对象。
const notice = {
id: 'notice-1024',
level: 'warning',
createdAt: Date.now(),
readAt: null,
action: { label: '查看任务', href: '/tasks/1024' },
};
二、优先级不能只靠颜色
警告和错误可以使用不同视觉样式,但必须同时提供文字、图标名称或可访问标签。红色并不等于“紧急”,也不能成为唯一区分方式。通知列表中以类型文字或 aria-label 标出“错误”“提醒”“信息”,颜色只负责辅助扫描。
高优先级消息不应无限弹出打断用户。可以固定到列表顶部、显示数量徽标,并在用户当前正在处理高风险表单时延后播报。需要立即读出的错误使用 alert,普通到达消息使用 polite 的 live region。
三、未读计数要来自同一份状态
角标数字、列表已读样式和“全部已读”按钮必须由同一份数据推导。不要在一个组件里单独加一,在另一个组件里减一。标记已读时先更新本地乐观状态,再提交服务端;失败时恢复并给出明确提示。
function unreadCount(items) {
return items.reduce((count, item) => count + (item.readAt ? 0 : 1), 0);
}
如果服务端支持游标或版本号,更新时带上版本,避免两个标签页同时标记已读后互相覆盖。跨标签页同步可以监听 storage 事件,也可以使用 BroadcastChannel;同步的是状态变化,不是完整 DOM。
四、批量操作需要清楚的范围
“全部已读”和“全部删除”是不同风险等级。全部已读可以带撤销,删除应明确数量并要求确认。批量操作前让用户知道当前筛选范围:是全部通知,还是当前标签、当前搜索结果。界面文案写清“将当前筛选的 12 条标为已读”,比只写“全部已读”更可靠。
操作完成后要把焦点留在稳定位置。删除多条后原按钮可能消失,可以把焦点移到通知列表标题或下一条记录,并通过 live region 播报“已删除 3 条通知”。
五、列表导航与详情
通知列表可以是普通的链接列表,也可以使用 listbox。若选择 listbox,方向键移动只是视觉高亮,Enter 才打开详情;如果使用普通链接,Tab 顺序必须可预测。不要同时给同一项绑定方向键和浏览器默认滚动,导致移动端或输入法冲突。
每个通知的可访问名称应包含标题、类型和已读状态,避免读屏时只听到重复的“查看详情”。操作按钮要和通知标题区分开,例如“查看任务通知:部署失败”。
六、空状态与失败状态
没有通知时,明确说明“当前没有新通知”,而不是渲染一个空白面板。加载失败要保留重试入口;部分加载成功时,不要把整个列表清空。通知服务不可用不应阻塞应用主流程,但要让用户知道更早的消息可能没有显示。
七、持久化与隐私
通知可能包含项目名、错误详情或用户信息。离线缓存前应检查内容敏感度,避免把一次性验证码、访问令牌或完整错误堆栈落进本地存储。退出登录时清理该用户的本地通知状态,防止下一个账号看到残留内容。
八、测试重点
覆盖未读计数增减、全部已读、按筛选批量操作、撤销删除、跨标签同步、服务端失败回滚、空列表、长标题换行和键盘操作。再用读屏检查新消息只播报必要摘要、不重复读出整段正文。通知中心的价值在于帮助用户快速判断,而不是制造更多需要处理的提示。
九、小结
通知中心应把瞬时反馈与持久消息分开,用统一状态管理未读计数,用文字而非颜色表达优先级,并让批量操作范围清楚、可撤销、可播报。只要焦点、失败回滚和隐私边界处理到位,通知越多也不会让界面失去秩序。
- 点赞
- 收藏
- 关注作者
评论(0)