搜索结果列表需用role="list"显式声明,配合aria-label动态数量、aria-live="polite"绑定容器、每项含可聚焦操作入口、搜索框设aria-controls并强制焦点迁移至结果区。

搜索结果列表必须用 role="list" 显式声明
屏幕阅读器不会自动把 <div> 或 <section> 当作列表处理,哪怕里面全是 <li>。不加 role="list",NVDA、VoiceOver 可能跳过“共 X 项”提示,用户无法感知结果是集合结构。
正确写法是给包裹容器加上该 role:
<div role="list" aria-label="搜索结果,共12项"> <div role="listitem">…</div> <div role="listitem">…</div> </div>
-
aria-label值建议动态插入实际数量(如搜索结果,共<span id="result-count">0</span>项),并在 JS 更新后调用aria-live区域同步播报 - 避免嵌套
role="list":比如在结果项内部再用<ul>,会导致语义混乱;内部结构用普通语义标签即可 - 若用原生
<ol>/<ul>,无需额外加role,但需确保每个<li>内容可聚焦且有明确操作目标(如链接或按钮)
aria-live="polite" 必须绑定到结果容器本身
用户输入关键词后,页面异步渲染新结果,屏幕阅读器默认不会主动读出变化。只靠 aria-live 区域“存在”不够——它得和 DOM 更新节点严格一致。
错误做法:<div aria-live="polite"></div> 放在别处,JS 把结果 innerHTML 写进另一个容器;这样 live 区域没内容变化,完全静默。
立即学习“前端免费学习笔记(深入)”;
- 把
aria-live="polite"直接加在结果列表的父容器上(即上面role="list"的那个<div>) - 更新时用
textContent或innerHTML替换整个子内容,不要只 append 新listitem—— 否则部分读屏器(如 JAWS 2022)可能漏读新增项 - 数量变化强烈建议单独用
<span aria-live="assertive">同步播报,例如“找到 7 条结果”,避免 polite 模式被其他通知淹没
每个结果项必须含可聚焦、语义明确的操作入口
无障碍导航依赖键盘 Tab 流,如果结果项只是静态 <div>,用户卡在列表外,无法进入单条查看。更糟的是,有些实现用 onclick 绑定 <div>,却没设 tabindex="0" 或角色。
- 每条结果至少包含一个
<a href>或<button type="button">,且文本内容能独立说明目标(避免“点击查看”这种无上下文表述) - 若用
<button>触发详情弹窗,必须加aria-expanded和aria-controls关联对应区域 - 禁止仅靠
onKeyPress监听Enter而忽略Space:按钮行为需同时响应两个键,否则键盘用户操作失败
全局搜索框的 aria-controls 与焦点管理不能省略
搜索框和结果列表物理位置可能相距很远(比如顶部搜索栏 + 中部结果区),不建立显式关联,屏幕阅读器无法理解二者关系,也无法在提交后自动将焦点移入结果。
- 搜索
<input>上必须设aria-controls="results-container-id",ID 与结果容器一致 - 表单提交或搜索触发后,JS 必须执行
document.getElementById('results-container-id').focus()—— 即使容器本身不可聚焦,也要确保其第一个可聚焦子元素(如首条结果的链接)获得焦点 - 若结果为空,仍要让焦点落在空状态容器,并用
aria-live="assertive"播报“未找到匹配结果”,而不是让焦点悬空或回退到搜索框
aria-controls 的 ID 绑定和焦点强制迁移这两步。很多团队测了读屏播报、也加了 role,但用户按回车后焦点还停在输入框里,根本进不到结果——问题就出在这两个联动动作没做实。



















