焦点卡住主因是表单元素缺失tabindex、未处理enter事件或错误隐藏提示;应确保可交互元素有tabindex="0"、用hidden+aria-hidden替代display:none、提交按钮须为,并关闭干扰插件。

用 NVDA + Firefox 测试时,为什么焦点一进表单就“卡住”?
常见现象是:按 Tab 进入输入框后,NVDA 停在那儿不继续读,甚至反复播报同一句;或者按 Enter 提交没反应,但鼠标点却正常。这基本不是 NVDA 问题,而是你的表单元素缺失 tabindex、没处理 Enter 键事件,或用了 display: none 隐藏错误提示但没加 aria-hidden="true"。
实操建议:
- 确保所有可交互元素(包括自定义按钮、下拉触发器)都有
tabindex="0",且没被tabindex="-1"意外禁用 - 避免用
display: none控制表单状态——改用hidden属性 +aria-hidden="true"同步控制 - 提交按钮必须是
<button type="submit"></button>,而非<div onclick="submit()">,否则 NVDA 不识别其语义和键盘行为 <li>测试前关闭浏览器插件(尤其某些“无障碍增强”类插件),它们常劫持焦点逻辑,干扰真实体验</li> <h3>动态加载内容后 NVDA 不播报,该怎么补救?</h3> <p>NVDA 默认不会监听 DOM 变化,所以异步返回的错误提示、搜索建议、加载中状态,它大概率静默跳过。这不是“NVDA 不行”,是你没告诉它“这儿有新东西要读”。</p> <p>实操建议:</p> <ul> <li>错误提示区域加 <code>role="alert"(中断式,适合表单校验失败)或aria-live="polite"(非中断式,适合搜索建议) - 不要把
aria-live放在display: none容器里——它必须是渲染在 DOM 中、且未被aria-hidden="true"掩盖的元素 - 如果用 JS 动态插入提示,确保插入后立即触发一次
focus()到该区域(仅限role="alert"),或调用element.setAttribute('aria-live', 'assertive')强制刷新 - 验证方式:在 NVDA 开启状态下,手动触发一次异步操作,听是否播报;若无声,打开开发者工具检查该元素是否在可访问性树中可见(Chrome DevTools 的 Accessibility Inspector 也能辅助判断)
为什么 NVDA 读不出“用户名”而只说“编辑文本”?
这是最典型的标签缺失问题。NVDA 不会根据视觉位置、placeholder 或上一行文字推测字段用途,它只认明确绑定的 label 或 ARIA 名称。
实操建议:
- 首选显式关联:
<label for="username">用户名</label><input id="username"> - 次选隐式包裹:
<label>用户名<input type="text"></label>(注意不能跨<div> 等块级元素) <li>禁用 placeholder 当标签——<code>placeholder默认不被 NVDA 读取,且失去焦点后信息即消失 - 避免混用:
<label for="x">邮箱</label>+<input id="x" aria-label="Email Address">,不同读屏对优先级处理不一致,NVDA 读aria-label,JAWS 可能忽略 - 用 NVDA 的“浏览模式”(默认开启)按
Insert + F7打开元素列表,看nav是否出现在“Landmarks”里;若无,说明语义未被识别 - 检查菜单容器是否用了
<nav aria-label="主菜单"></nav>或内部有<h2>主菜单</h2>,否则 NVDA 不知道这是个独立导航区 - 每个菜单项必须是
<a></a>或带role="menuitem"的元素,并支持Enter/Space激活、→/←方向键切换 - 如果菜单是“汉堡按钮”展开的,确保展开后焦点落到第一个菜单项,且关闭时焦点回到按钮本身——这点 NVDA 用户极其依赖,漏掉就会迷失上下文
测试时发现 NVDA 跳过了整个导航菜单,怎么定位原因?
导航菜单被跳过,通常不是“NVDA 设置问题”,而是结构语义断裂:要么没用 <nav></nav> 包裹,要么用了 role="navigation" 却没配 aria-label 或标题,要么菜单项是 <div> 模拟但没设 <code>role="menuitem" 和键盘支持。
实操建议:
aria-expanded”,但它会在你点击按钮后沉默两秒,然后继续读上一个标题——那个沉默,就是你该回去查状态同步逻辑的地方。











