登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

HTML inert 属性怎样让模态框外内容不可交互

来源:17golang原创

时间:2026-11-03 13:47:29 199浏览 收藏

模态框打开时,背景区域不能只靠半透明遮罩“看起来不能点”。更稳妥的做法是把背景根节点设为 inert,让它及其后代不能获得焦点、响应点击,也不会继续出现在辅助技术的交互路径中。关闭模态框时再把 HTMLElement.inert 恢复为 false。

官方资料:https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/inert

如果页面使用原生 并通过 showModal() 打开,浏览器会让对话框外的文档内容自动处于 inert 状态;如果是自定义遮罩层,就需要自己把背景容器切换为 inert。

先分清遮罩层和 inert 状态

遮罩层只负责视觉反馈,例如压暗背景、阻止鼠标落到背景上的一部分区域;它不会自动改变键盘 Tab 顺序,也不会让背景按钮从辅助技术的可访问树中消失。inert 是交互状态:元素和它的 flat tree 后代不能聚焦、不能触发点击事件,通常也不能被页面查找、选中文本或通过辅助技术操作。

模态框前景与 inert 背景区域的静态边界关系说明图
图1:模态框前景保持可操作,背景容器及其后代进入 inert 状态;这是静态结构说明图,不是浏览器截图或运行证据。

因此,先把“用户看不见”与“用户不能交互”分开处理:遮罩负责颜色和层级,inert 负责交互边界,模态框自身负责焦点进入与退出。

把背景内容包成一个可切换的根节点

自定义模态框最容易维护的结构,是让正文和导航共用一个背景根节点,弹窗放在它的外层。这样只需要切换一个属性,不必逐个给按钮加 disabled。

账户设置

这里的关键不是 role 本身,而是页面背景拥有稳定的 DOM 边界。后续 JavaScript 可以统一写入 page-shell.inert,弹窗关闭后再恢复。

用 HTMLElement.inert 切换模态框外内容

HTMLElement.inert 是布尔属性:设为 true 就等价于给元素增加 inert 状态,设为 false 则移除这个元素自身的显式 inert 状态。下面的示例还保存了打开前的焦点,关闭后把焦点还给触发按钮。

const pageShell = document.querySelector('#page-shell')
const modal = document.querySelector('#edit-modal')
const openButton = document.querySelector('#open-modal')
const closeButton = document.querySelector('#close-modal')

let lastFocusedElement = null

function openModal() {
  // 记录触发控件,关闭时把键盘焦点还给它。
  lastFocusedElement = document.activeElement
  pageShell.inert = true
  modal.hidden = false

  // 等弹窗显示后再聚焦内部控件,避免 hidden 状态拦截 focus。
  closeButton.focus()
}

function closeModal() {
  // 先移除背景的 inert 状态,再隐藏弹窗。
  pageShell.inert = false
  modal.hidden = true

  // 只有仍在当前文档中的元素才适合恢复焦点。
  if (lastFocusedElement instanceof HTMLElement && lastFocusedElement.isConnected) {
    lastFocusedElement.focus()
  }
}

openButton.addEventListener('click', openModal)
closeButton.addEventListener('click', closeModal)

这段代码只解决“背景是否能交互”和“打开前焦点如何回来”两个问题。若要实现完整的焦点循环、Escape 关闭和滚动锁定,还要根据模态框组件的实际需求继续设计。

dialog.showModal() 与手动 inert 有什么区别

原生

通过 showModal() 进入模态状态时,对话框外的文档内容会自动变成 inert;这适合愿意采用原生对话框语义的页面。自定义 div 弹窗不会自动获得这个行为,必须手动切换背景容器。

const dialog = document.querySelector('#profile-dialog')
const openDialog = document.querySelector('#open-dialog')
const closeDialog = document.querySelector('#close-dialog')

openDialog.addEventListener('click', () => {
  // showModal() 进入原生模态状态,外部文档会被浏览器隔离。
  dialog.showModal()
})

closeDialog.addEventListener('click', () => {
  // close() 退出模态状态,浏览器恢复外部文档的交互。
  dialog.close()
})

要注意两个边界:第一,显式给对话框自身加 inert 会让它也不能操作;第二,父元素继承的 inert 状态不能靠子元素设置 inert = false 抵消。需要解除时,应从真正带有 inert 的祖先节点开始恢复。

原生 dialog showModal 与自定义弹窗手动 inert 的状态边界说明图
图2:原生 dialog 模态状态与自定义弹窗手动切换背景 inert 的对照关系;这是静态选型说明图。

给 inert 状态补上可见提示

inert 本身不会自动改变颜色或透明度。若背景内容被禁止操作却仍然看起来完全正常,用户可能不知道为什么按钮暂时不能用。可以用属性选择器补充视觉提示,但不要把视觉样式当作 inert 的替代品。

/* 只提示被 inert 的背景区域,交互限制仍由 inert 属性负责。 */
[inert] {
  opacity: 0.55;
  filter: saturate(0.7);
}

/* 弹窗打开时禁止背景滚动,避免视觉焦点和页面位置脱节。 */
body:has(#edit-modal:not([hidden])) {
  overflow: hidden;
}

如果浏览器兼容目标不支持 :has(),可以在打开和关闭时给 body 增删一个 class。视觉提示、滚动处理和 inert 的职责要分开,便于后续更换组件。

disabled、aria-hidden 与 inert 不能互相替代

手段主要作用适用边界
inert冻结元素及后代的交互、聚焦和辅助技术访问整块背景、暂时不可操作的区域
disabled禁用单个表单控件按钮、输入框等控件自身不可用
aria-hidden改变辅助技术的语义可见性不能单独阻止真实点击或键盘聚焦
遮罩 CSS表达层级、暗化背景和视觉状态不能单独完成交互隔离

常见错误是只写 aria-hidden="true",却留下可聚焦的链接和按钮;或者只把背景盖住,却没有把键盘焦点移进弹窗。实践中通常是 inert + 明确焦点管理 + 视觉提示 组合使用。

一份收尾清单

  • 自定义弹窗先保证背景有统一根节点,再切换 HTMLElement.inert。
  • 打开时保存触发元素,关闭时确认它仍在文档中再恢复焦点。
  • 原生 dialog.showModal() 已经提供模态外部隔离,不要重复给错误的父节点加 inert。
  • 不要用 aria-hidden 代替真实的交互禁用,也不要用透明度代替 inert。
  • 父级继承的 inert 不能由子级简单设置为 false,恢复状态时要回到实际控制节点。

一句话总结:遮罩解决“看起来被挡住”,inert 解决“确实不能交互”,而焦点管理解决“用户接下来从哪里继续操作”。把三者拆开设计,模态框的键盘和无障碍行为会更稳定。

相关问题

给一个按钮设置 inert 可以代替 disabled 吗?

不建议。单个表单控件的语义状态优先使用 disabled;inert 更适合冻结一整块内容及其后代。

inert 会自动显示灰色或半透明效果吗?

不会。它主要改变交互和可访问性状态,视觉变化要由页面自己的 CSS 明确表达。

dialog.showModal() 还需要手动给背景加 inert 吗?

正常原生模态对话框不需要再手动冻结整个文档;只有自定义弹窗、特殊嵌套结构或额外的业务区域才需要单独设计。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>