autofocus 在 <dialog> 中默认不生效,因为 HTML 规范限定其仅在页面初始加载时触发,不响应 showModal() 等后续显示操作;需手动调用 .focus() 并确保按钮可聚焦、已渲染且 dialog 已激活。

autofocus 在 <dialog> 中默认不生效的原因
<dialog> 是模态容器,打开时浏览器会将焦点重置到 dialog 自身(或其第一个可聚焦子元素),而忽略内部元素上已声明的 autofocus 属性。这不是 bug,而是规范行为:HTML 标准规定 autofocus 仅在页面初始加载时触发,不响应后续 DOM 插入或显示切换。
这意味着即使你写 <button autofocus>确认</button>,调用 dialog.showModal() 后焦点也不会落到它身上。
手动聚焦按钮是唯一可靠方式
必须在 dialog.showModal() 调用后,显式调用 .focus()。注意两点:一是确保按钮已渲染且可聚焦(不在 disabled 或 hidden 状态),二是避免在 dialog 尚未完全激活时调用(某些浏览器有微小延迟)。
推荐做法是用 setTimeout 微任务或监听 dialog 的 focusin 事件:
const dialog = document.getElementById('myDialog');
const confirmBtn = dialog.querySelector('[data-action="confirm"]');
dialog.showModal();
// 确保 focus 发生在 dialog 激活之后
setTimeout(() => confirmBtn.focus(), 0);
更稳妥的写法是监听 dialog 自身获得焦点后的首次 focusin:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
dialog.addEventListener('focusin', function handler() {
confirmBtn.focus();
dialog.removeEventListener('focusin', handler);
}, { once: true });
按钮需满足可聚焦前提
<button> 默认可聚焦,但以下情况会导致 .focus() 失效或静默忽略:
- 按钮被设为
disabled —— 移除 disabled 或改用 tabindex="-1" + CSS 控制视觉禁用
- 按钮父级有
inert 属性(如某些 dialog polyfill 添加)—— 检查并移除
- 按钮在
display: none 或 visibility: hidden 状态下尝试聚焦 —— 确保 dialog 已显示再调用
- 按钮没有
tabindex 且不是原生可聚焦元素(比如用 <div role="button">)—— 改用 <button> 或显式加 tabindex="0"
无障碍与键盘导航的实际影响
自动聚焦按钮不只是为了方便鼠标用户,更是屏幕阅读器流式操作的关键起点。若未正确聚焦,用户按 Tab 可能从页面其他位置开始导航,跳过 dialog 内容,甚至意外关闭 dialog(尤其当 Esc 关闭逻辑未绑定到 dialog 本身时)。
disabled —— 移除 disabled 或改用 tabindex="-1" + CSS 控制视觉禁用inert 属性(如某些 dialog polyfill 添加)—— 检查并移除display: none 或 visibility: hidden 状态下尝试聚焦 —— 确保 dialog 已显示再调用tabindex 且不是原生可聚焦元素(比如用 <div role="button">)—— 改用 <button> 或显式加 tabindex="0"
Tab 可能从页面其他位置开始导航,跳过 dialog 内容,甚至意外关闭 dialog(尤其当 Esc 关闭逻辑未绑定到 dialog 本身时)。
务必同时设置 dialog.setAttribute('aria-modal', 'true') 和 dialog.setAttribute('role', 'dialog'),并在打开时用 document.body.setAttribute('inert', '')(或等效方案)阻止背景交互 —— 否则即使按钮聚焦了,键盘仍可能穿透到背后元素。
立即学习“前端免费学习笔记(深入)”;
实际中最容易被忽略的是:dialog 打开后,焦点虽落在按钮上,但若用户立刻按Enter,而按钮又没绑定 click 事件(只绑了 keydown),就会无响应。确保绑定的是 click,而不是只依赖键盘事件模拟。


















