题干与适用场景
一个管理后台需要确认删除操作,并在对话框内填写原因。产品希望部分场景必须点击明确按钮才能关闭,部分场景允许 Escape 或点击外部关闭。请使用原生 dialog 元素设计关闭策略,并确保键盘、触摸和表单校验路径都可用。
MDN 将 closedby 分为 none、closerequest 和 any:它们分别限制为开发者机制、平台关闭请求加开发者机制、或三种方式全部允许。模态对话框用 showModal(),非模态对话框用 show()。
面试官考察点
观察候选人能否把业务风险映射到关闭策略,区分 modal 与 popover,理解 Escape、light dismiss、close()、requestClose()、method=dialog、焦点初始位置和表单校验的交互。
回答前需要澄清的问题
- 删除确认是否允许误触关闭,未保存输入是否必须保留?
- Escape 和点击外部是否等价于取消,还是需要二次确认?
- 表单验证失败时,关闭按钮是否应绕过校验?
- 是否需要在不支持
closedby的浏览器中提供一致体验? - 对话框关闭后,焦点应返回哪个触发控件?
30 秒回答框架
“危险操作默认使用 showModal() 和 closedby="closerequest" 或 none,保留明确的取消/确认按钮;若是非破坏性提示,可使用 any 或 popover="auto" 的轻量关闭。表单用 method="dialog" 管理结果,取消按钮按需要加 formnovalidate。打开时把焦点放到标题、首个输入或取消按钮,关闭后恢复触发器焦点。检测能力并在旧浏览器回退到显式按钮和手动事件处理。”
分步骤深入解答
第一步:选择 modal 或 non-modal
showModal() 将其他页面内容置为 inert,适合删除确认和必须完成的任务;show() 不阻塞页面,适合辅助面板。不要仅切换 open 属性模拟 modal,因为焦点和背景交互语义不会自动建立。
第二步:把风险映射到 closedby
closedby="none" 只允许开发者指定的关闭机制,适合不可逆确认;closerequest 允许 Escape 或平台关闭请求,也保留按钮;any 额外允许点击外部的 light dismiss,适合低风险信息面板。
<dialog id="delete-dialog" closedby="closerequest">
<form method="dialog">
<button value="cancel">取消</button>
<button value="confirm">删除</button>
</form>
</dialog>第三步:处理 Escape、request-close 与 close
Escape 或平台手势属于 close request;requestClose() 可触发请求关闭流程,应用可以拦截并确认;close() 直接关闭并可设置 returnValue。不要把 Escape 静默当作已确认删除。
第四步:管理焦点和恢复位置
打开 modal 后将焦点放在最合适的输入、取消按钮或标题;不要给 dialog 元素添加 tabindex。关闭时把焦点还给触发按钮,若触发器已移除则选择稳定的替代位置。
第五步:处理 form method=dialog
method="dialog" 会关闭对话框并保存表单控件状态,不会提交网络请求;按钮的 value 进入 returnValue。若取消按钮应绕过必填校验,使用 formnovalidate 或显式 close(),避免用户被困在表单中。
第六步:区分 dialog 与 Popover
Popover 是非模态 top layer;popover="auto" 支持 light dismiss,manual 不会自动关闭且可同时显示多个。不要把 popover 当作删除确认的 modal 替代品,也不要把 modal 的 inert 期待套在非模态元素上。
第七步:能力检测与降级
检测 HTMLDialogElement、showModal 和 closedby 相关能力;不支持时提供显式按钮、手动 Escape 处理、焦点管理和背景阻塞。降级路径仍需保留相同的取消与确认语义。
第八步:验证关闭与可访问性
测试键盘 Tab、Escape、外部点击、触摸、表单验证失败、重复打开、多个 modal、浏览器返回手势和旧浏览器。记录关闭原因(confirm、cancel、escape、light-dismiss),并用屏幕阅读器验证名称、状态和焦点移动。
高质量示范回答
“删除确认使用 showModal(),危险场景默认 closedby="closerequest",最严格时用 none,保留明确取消按钮。低风险提示才使用 any 或 popover="auto"。表单通过 method="dialog" 返回结果,取消按钮按需绕过校验。打开时设置初始焦点,关闭后恢复触发器焦点;旧浏览器检测能力并回退到显式按钮、Escape 和手动焦点管理。测试所有关闭来源和辅助技术。”
常见错误
- 所有 dialog 都用 any → 删除操作可能被误触关闭 → 按风险选择 closedby。
- 给 dialog 加 tabindex → 焦点语义错误 → 聚焦内部可交互元素或标题。
- 把 close 当 requestClose → 跳过确认逻辑 → 区分请求关闭与直接关闭。
- form method=dialog 遇到校验失败无法取消 → 用户被困住 → 取消按钮使用 formnovalidate 或 close。
- 用 popover 代替 modal → 背景仍可交互 → 确认需要 inert 时使用 showModal。
- 只测鼠标 → 键盘和触摸用户无法退出 → 覆盖 Escape、Tab、外部点击和屏幕阅读器。
追问及应对
追问一:closerequest 与 none 的核心差异是什么?
closerequest 允许 Escape 或平台关闭请求,none 只允许开发者指定机制;不可逆操作通常需要 none 或额外确认。
追问二:为什么 showModal 比 open 属性可靠?
它建立模态状态、背景 inert 和浏览器默认焦点行为;单独设置 open 只显示非模态对话框。
追问三:Escape 关闭时 returnValue 会怎样?
除非应用主动设置,否则 Escape 不等同于某个提交按钮的 value;关闭原因应单独记录为 escape 或 request-close。
追问四:如何让取消按钮绕过必填校验?
对取消按钮使用 formnovalidate,或在事件中调用 close("cancel");确认按钮仍保留正常校验。
追问五:旧浏览器如何降级?
提供显式打开/关闭按钮,手动管理 Escape、焦点陷阱、背景阻塞和关闭原因;能力恢复后再使用原生属性,契约测试保持一致。