动态插入的元素若不显式管理焦点,会默认停留在原处造成脱节
别名: 动态焦点 · 插入后焦点 · focus after insertion
概念解释
提交之后页面插入了一条错误、点了「添加一项」列表多出一行、筛选结果替换了整块内容——这些时刻,浏览器默认不移动焦点。焦点仍停在刚才按下的按钮上,新内容出现在别处。键盘用户和下一个该处理的对象已经不在同一处,这叫插入后的焦点脱节。
播报新内容和解脱节不是一回事。实时区域可以让阅读器开口,焦点仍可能留在旧按钮上;下一步 Tab 走的还是旧位置旁边,不是刚出现的字段。
机制
用户代理把焦点当作「用户上一次明确选中的节点」。DOM 插入、Ajax 替换、路由切视图,都不是一次明确的用户选中,所以焦点节点若还在文档里,就原地不动;若被销毁,焦点往往掉到 body。无论哪种,空间模型和序列模型都断了:看得见的人要在新块和旧按钮之间来回找,看不见的人听到「出错了」却按 Tab 走进无关区域。
该不该把焦点移过去,取决于插入的是什么。阻断性错误、新打开的对话、用户刚请求创建的字段,应当成为焦点。后台「已保存」提示不应当抢焦点。缺的是这条显式决策——不是浏览器忘了帮忙,是产品没有把「工作对象换了」写成一次焦点移动。
怎么研究
选三类插入:表单校验错误、列表末尾新增项、无刷新的整块结果替换。触发后立刻问:焦点在哪个节点、下一个 Tab 落到哪、屏幕阅读器是否开口。三问分开记,不要用「有提示」代替「焦点已到」。
对照:同样操作在整页刷新的旧站点上,焦点通常会落到新页顶部或第一个字段——那是导航重建,不是插入。单页应用用插入冒充导航时,脱节最严重。
边界
聊天室、日志、协作光标这类持续流入的内容,若每条都抢焦点,用户无法打字。这类流用实时区域通知即可,不要移焦点。用户正在输入时插入的非阻断提示,也不得夺走输入框。相反,支付失败、权限拒绝、会话将过期,插入后若不把焦点移到可操作的那条消息,键盘用户可能在已经失效的提交按钮上反复按 Enter。
怎么落地
- 插入阻断性消息或用户刚请求的新字段时,把焦点移到该消息或字段;不要只改 DOM。
- 节点被销毁前,先把焦点送到仍然存在、且与任务相关的节点,避免落到
body。 - 非阻断的「已保存」不要抢焦点;用实时区域通知即可。
- 验证:提交一个会失败的表单,手不离开键盘。焦点若仍停在提交按钮,而错误出现在屏幕另一头,脱节已经发生。