直接给主内容区加inert、弹窗置于其外层是唯一能同时禁用鼠标、键盘和屏幕阅读器的原生方案;不能给body加inert,否则弹窗交互与可访问性也被切断,且必须用.inert=true/false控制,不可用setattribute。

直接给主内容区加 inert,弹窗本身必须放在它外面——这是唯一能同时锁住鼠标、键盘和屏幕阅读器的原生方案,且不破坏布局或滚动。
为什么不能给 加 inert
给 加 inert 会把弹窗也一起禁用,导致用户点不了关闭按钮、Tab 不进输入框、读屏器读不到弹窗内容。真实 DOM 结构必须是:
<div id="app"> <main inert id="main-content"></main><dialog open></dialog> </div>
-
<main></main>是你要冻结的区域,inert只作用于它及其所有子元素 -
<dialog></dialog>(或任何 toast/loading/浮层)必须在<main></main>外层,否则一并被切断交互与可访问性树 - 滚动容器如果是
<main></main>自身,加inert后仍可滚动(inert不影响滚动行为),但若滚动靠外层或,就得确保它们没被意外冻结
inert 必须用 .inert = true/false 控制,不能用 setAttribute
动态开关时,JS 写法错一点就完全失效:
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
- ✅ 正确:
document.getElementById('main-content').inert = isModalOpen—— 立即退出焦点、阻断事件冒泡、更新可访问性树 - ❌ 无效:
el.setAttribute('inert', '')、el.toggleAttribute('inert')、el.inert = 'true'—— 浏览器完全无视,DOM 属性存在但行为无变化 - ⚠️ 注意:如果模态状态由框架(如 React/Vue)驱动,SSR 输出的 HTML 必须初始带
inert属性,否则 hydration 时会报 “prop mismatch”
旧浏览器兜底不是可选项,而是必做项
Chrome 111+、Firefox 121+、Safari 18.0+ 原生支持;Safari 17、旧 Edge、部分安卓 WebView 完全不识别 inert 属性,且 polyfill(如 @webcomponents/inert)仅模拟部分行为,无法 100% 补齐键盘焦点流重定向和 AT 树移除。
- 检测方式:
if ('inert' in HTMLElement.prototype) - 不支持时,至少做三件事:
element.setAttribute('aria-hidden', 'true')、element.tabIndex = -1、递归对所有可聚焦子元素(button、a[href]、input、[tabindex])设tabIndex = -1并监听focusin强制重定向焦点 - 别依赖
pointer-events: none单独撑场——它挡不住 Tab 进入、Enter 触发 click、或 JS 主动调用.focus()
真正容易被忽略的是:inert 不是“视觉禁用”,也不是“事件拦截”,它是让浏览器彻底“看不见”那一块 DOM 子树的交互存在性。一旦结构放错、JS 赋值写错、或旧环境没兜底,键盘用户就会卡在不可见的 input 上,读屏器会报出“可聚焦但不可操作”的矛盾语义——这些都不是样式问题,而是 DOM 层级的语义断裂。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










