cancel事件无法被preventDefault阻止,唯一可靠拦截方式是在cancel事件中调用showModal()重新打开对话框,并确保dialog已连接且手动恢复焦点。

cancel事件不能被阻止,但可以拦截关闭行为
按 Esc 键触发的是 cancel 事件,不是可 preventDefault() 的普通事件。浏览器强制执行该行为:只要 dialog 处于 showModal() 状态,Esc 就一定会触发 cancel 并关闭对话框——你无法用 e.preventDefault() 或 e.stopImmediatePropagation() 拦住它。
真正有效的拦截方式:监听 cancel 事件后立即 reopen
既然关不了,就“关了再开”。在 cancel 事件回调里调用 showModal(),视觉上用户感觉没关,实际是瞬间重开。这是目前唯一可靠、无障碍友好的做法。
- 必须确保 dialog 已连接(
dialog.isConnected === true),否则showModal()会静默失败 - 避免在
cancel里直接调用showModal()后又立刻close(),这会导致状态抖动甚至焦点丢失 - 如果需要条件性禁用 Esc(比如表单校验中),应在
cancel回调里先做判断:if (shouldPreventClose) { dialog.showModal(); }
为什么不能用 pointer-events: none 或 focus trap 拦 Esc
dialog::backdrop { pointer-events: none; } 只影响鼠标,对键盘无作用;而手动 trap focus 并拦截 keydown 事件(如监听 Escape)属于破坏性干预——它会让屏幕阅读器失去模态语义,也违反 WCAG 2.1 中关于模态框可访问性的要求。原生 dialog 的 Esc 行为是辅助技术依赖的契约,绕过它等于放弃可访问性。
容易被忽略的副作用:reopen 后焦点不会自动回到 dialog
调用 showModal() 重开后,焦点默认落在第一个可聚焦子元素上。但如果之前焦点在 input 里,reopen 后它可能失焦。务必在 cancel 回调的 showModal() 之后加一句 dialog.focus() 或显式 input.focus(),否则键盘用户会卡在页面空白处。
立即学习“前端免费学习笔记(深入)”;



















