复杂筛选器需满足键盘可聚焦操作、屏幕阅读器精准播报状态、使用原生语义控件、aria-live精准控制、多条件状态显性暴露及与表格强耦合同步。

复杂筛选器不是加个 aria-label 就能过无障碍检测的。它必须让键盘用户能聚焦、操作、感知状态变化,同时让屏幕阅读器准确播报“你在筛选什么”“当前筛选了哪些值”“点击后会发生什么”。否则,残障用户会卡在输入框里反复按 Tab,却不知道下一步该点哪个按钮、选中了几个条件、筛选是否已生效。
筛选控件必须用原生语义,别套 role="button" 或 role="checkbox"
很多项目把 <div class="filter-item"><span>价格</span><input type="range"></div> 改成 <div role="checkbox" aria-checked="false">,以为这就可访问了——其实彻底破坏了语义。屏幕阅读器会把它当成一个孤立的复选框,但里面没有 <input type="checkbox">,也没有 <label> 关联,键盘无法空格切换,焦点管理也全乱了。
- 所有筛选项优先用原生表单控件:
<input type="checkbox">、<input type="radio">、<select>、<input type="range">,它们自带焦点、键盘交互和状态同步 - 如果视觉上不能用原生样式(比如自定义多选标签),就用
<button>+aria-pressed模拟开关,而不是role="checkbox" - 禁用
<div role="button">包裹整个筛选项——它会覆盖原生语义,且不响应Enter/Space,必须手动监听keydown才能补救
aria-live 必须精准控制,避免重复播报或静默失效
筛选器提交后 DOM 动态更新 <tbody>,但屏幕阅读器不会自动读新数据。很多人直接给容器加 aria-live="polite",结果每次筛选都读出全部表格行,或者因为防抖延迟导致只读“正在加载”,用户根本不知道筛选是否成功。
- 只对明确变化的内容区域设
aria-live,比如筛选结果计数器:<span aria-live="polite" aria-atomic="true">共匹配 12 条记录</span> - 避免在
<table>或<tbody>上设aria-live——它会尝试读整个结构,性能差且语义混乱 - 筛选触发后,若焦点仍在原输入框,需手动
element.focus()到结果摘要区,确保屏幕阅读器从有意义的位置开始播报
多条件组合筛选必须暴露当前状态,且支持清除与重置
用户勾选了“城市=北京”“价格区间=500–2000”“状态=启用”,但界面上只有几个带删除图标的标签。键盘用户 tab 过去,听到的是“删除按钮”,完全不知道这个按钮删的是哪个条件;屏幕阅读器也不会主动告诉用户“当前已启用 3 个筛选条件”。
立即学习“前端免费学习笔记(深入)”;
- 每个筛选条件标签必须有
aria-label,例如:<button aria-label="移除城市筛选:北京">×</button> - 提供全局状态摘要区,放在筛选区域顶部或底部,用
aria-live="polite"+aria-atomic="true"更新,如:<div aria-live="polite" aria-atomic="true">已启用:城市(北京)、价格(500–2000)、状态(启用)</div> - “清除所有”按钮必须明确关联到全部筛选控件,用
aria-controls指向筛选容器 ID,例如:<button aria-controls="filter-panel">清除所有</button>
最常被忽略的是:筛选器不是独立模块,它和表格/列表是强耦合的。你改了筛选逻辑,但没同步更新表格的 aria-sort 状态、没保留 <tbody> 内原有编辑态输入框的焦点、没在筛选后恢复滚动位置——这些细节叠加起来,会让键盘用户觉得“页面卡住了”。



















