inert仅控制逻辑层冻结,不影响样式;视觉反馈需css配合,如滤镜或遮罩;二者缺一不可,且dom结构须确保浮层不在inert容器内。

inert 本身不提供视觉效果,CSS 需单独处理灰度/遮罩
inert 属性只负责逻辑层冻结:它让元素及其子树不可聚焦、不可点击、不响应事件、不进入可访问性树——但完全不影响渲染样式。页面看起来一模一样,只是“点不动、Tab 不进、读屏器跳过”。要实现用户感知的“已冻结”视觉反馈,必须靠 CSS 配合。
常见做法是给 inert 区域加一层半透明遮罩或整体降饱和度:
-
main[inert]{ opacity: 0.7; pointer-events: none; } —— 注意:这里pointer-events: none是冗余的(inert 已拦截鼠标),但能防止某些 polyfill 场景下漏事件 -
main[inert]::before{ content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.4); z-index: 999; } —— 仅当main是全屏容器时可用,否则需用绝对定位适配其边界 - 更稳妥的是用滤镜:
main[inert]{ filter: grayscale(30%) contrast(85%); },不影响布局,且对滚动容器友好
为什么不能只靠 CSS 实现真正的“冻结”
只加 opacity 或 filter 而不设 inert,会立刻暴露三大缺陷:
- 键盘仍可 Tab 进入
button、input等元素,焦点卡住后无法自然离开 - 屏幕阅读器仍会读出内容,且可能报“可聚焦但不可用”,违反 WCAG
- JS 仍能调用
.focus()、.click(),或监听到冒泡上来的click事件(除非你额外stopPropagation)
换句话说:CSS 只管“看起来像锁了”,inert 才管“真的锁死了”。二者缺一不可,且职责分明。
inert + CSS 组合时 DOM 结构必须正确
视觉遮罩或滤镜生效的前提,是你没把浮层(如 <dialog></dialog>、<div class="toast">)嵌套在 inert 容器里。否则它们也会被灰度化、被遮罩盖住——用户看不见弹窗,也点不了关闭按钮。
<ul>
<li>✅ 正确结构:<code><main inert></main><dialog open></dialog><div class="toast"></div>
<main inert><dialog open></dialog></main> —— dialog 会被 inert 彻底屏蔽dialog 和 toast 渲染在 main 外层(比如用 createPortal 或 Teleport)移动端虚拟键盘弹出时的视觉错位风险
在 iOS 或部分安卓 WebView 中,若 inert 区域包含可滚动容器(如 main 内有 overflow-y: auto),虚拟键盘弹出会触发 viewport 缩放或滚动偏移,导致 inert 元素的遮罩层位置错乱、滤镜失效、甚至焦点丢失。
- 解决方案不是禁用 inert,而是监听
virtualkeyboard事件(Chrome 108+、Safari 16.4+): -
virtualKeyboard.addEventListener('geometrychange', () => { document.getElementById('main-content').inert = isModalOpen; });—— 强制重置 inert 状态,触发浏览器重新计算焦点与渲染边界 - 旧版浏览器兜底:检测到
inert不支持时,改用aria-hidden="true"+tabIndex="-1"+pointer-events: none,并手动管理焦点重定向
真正容易被忽略的是:inert 的“冻结”是语义级的,不是视觉级的;你看到的灰度或遮罩,只是给健全用户一个提示信号,而 inert 本身才是那个让键盘、鼠标、读屏器全部同步失能的开关。两者一旦脱节,无障碍就断了。











