HTML inert实战:我总算能轻松禁止一个区域的点击和聚焦了

2026-08-20 0 460

如果你写过一个“假模态框” —— 就是用div模拟的那种弹窗,那你一定遇到过这个烦心事:弹窗打开了,背后的按钮还是能用Tab键聚焦,甚至用回车触发点击事件。特别是那些做后台管理的老项目,整个页面全是button和a标签,弹窗一亮相,背景区域的各种控件依然“活跃”得不合时宜。过去我会写一堆JS去手动给背景元素设置tabindex=-1、监听focusin重新拉回焦点,反反复复调试。直到我最近试用了一下HTML的 inert 属性,才发现我原来跟无头苍蝇似的做了两年傻事。

inert是什么?

核心就是一个布尔属性,加在某个元素身上,表示这个元素及其所有子元素都“不可交互”。具体来说:

  • 鼠标点击无效,按钮按下没反应
  • 键盘Tab不会聚焦到该区域内的任何元素
  • 辅助技术(读屏器)也会忽略这块区域
  • 元素本身不会响应焦点,连文本选择都不行

这看起来像把整个区域“冻结”了。但它和display:none不一样,元素依然占据页面空间,依然可见,只是没法操作。

基本用法:像开关灯一样简单

直接写在标签上:

<div inert>
    <button>这个按钮点不动</button>
    <a href="https://example.com" rel="external nofollow" >这个链接也摸不到</a>
</div>

浏览器会帮你屏蔽所有交互事件。但是光这样写死没有太大意义,实际开发中我们要动态地切换这个属性。所以常规用法是配合JS:

// 获取背景区域
const main = document.getElementById('main');
// 禁用
main.inert = true;
// 重新启用
main.inert = false;

没错,它就是一个普通的DOM属性,你可以随时设置。

实战:给一个后台弹窗加上焦点圈禁

我做一个常见的商品列表页面。点击“详情”按钮后,右边滑出一个展示面板,此时我希望面板后面的整个列表区域不可操作。代码如下:

<body>
  <div id="backdrop" style="display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5);"></div>
  <aside id="detailPanel" style="position: fixed; right: 0; top: 0; width: 300px; background: white;">
    <h2>商品详情</h2>
    <p>这里显示商品信息...</p>
    <button id="closePanel">关闭</button>
  </aside>

  <main id="mainList">
    <ul>
      <li>商品1 <button class="view-detail" data-id="1">查看详情</button></li>
      <li>商品2 <button class="view-detail" data-id="2">查看详情</button></li>
    </ul>
  </main>

  <script>
    const main = document.getElementById('mainList');
    const panel = document.getElementById('detailPanel');
    const backdrop = document.getElementById('backdrop');
    const closeBtn = document.getElementById('closePanel');

    document.querySelectorAll('.view-detail').forEach(btn => {
      btn.addEventListener('click', () => {
        // 禁用背景区域
        main.inert = true;
        // 显示遮罩和面板
        backdrop.style.display = 'block';
        panel.style.display = 'block';
        // 焦点移到面板内(虽然inert不会移动焦点,但最好还是主动拉过去)
        closeBtn.focus();
      });
    });

    function close() {
      main.inert = false;
      backdrop.style.display = 'none';
      panel.style.display = 'none';
    }
    closeBtn.addEventListener('click', close);
    backdrop.addEventListener('click', close);
  </script>
</body>

这样设置之后,当面板打开,mainList 里的所有按钮、链接都不能被Tab聚焦,点击更是无效。用户只能在面板和遮罩之间交互。关掉面板后,inert=false,一切恢复正常。你会发现你根本不需要在背景每个元素上设置tabindex=-1,也不用监听focusout事件去判断焦点是否落在了背景区域,只要一个属性,全部搞定。

结合:has()或状态类实现纯CSS切换?

既然之前写过CSS的:has(),有人可能会问:能不能不写JS,直接用css让inert属性生效?不行,因为inert是HTML属性,不是CSS属性。单纯靠CSS是没法让一个元素变得“焦点不可达”的,尽管你可以用pointer-events: none去禁止鼠标点击,但对键盘焦点毫无作用。所以还是得踏实写两行JS去切换inert。

不过如果你用Vue或React,可以直接在模板里绑定::inert="isOpen",比如Vue里:

<main :inert="isPanelOpen">...</main>

这就更简洁了,框架会帮你更新属性。

与 aria-hidden 有什么区别?

很多人容易搞混。简单说:

  • aria-hidden="true" 只是告诉屏幕阅读器“忽略这块内容”,但Tab焦点依然可以进入。你如果在前台弹窗的背景区块上只设置aria-hidden,键盘用户照样能Tab到背后的按钮上去,体验很糟糕。
  • inert 则是真正移出焦点链和交互事件,鼠标键盘全部屏蔽,不管你是读屏器还是普通用户,都无法操作它。

在模态框场景中,最佳组合是:给背景设置inert,同时把面板区域加上role="dialog"aria-modal="true"。这样对辅助技术也完整。

一个额外的使用场景:侧边栏导航

后台系统里有一种抽屉式导航,平时隐藏,点击汉堡菜单后从左滑出,此时内容区应该禁止操作,让用户专注在导航上。这个需求我以前写过一个带遮罩和监听scroll的组件,可麻烦。现在可以直接在内容区加上inert:

// 当导航打开
document.getElementById('page-content').inert = true;
// 关闭后
document.getElementById('page-content').inert = false;

清爽利索。如果有人用Tab扫描页面,焦点会在导航和侧栏之间循环,不会莫名其妙跑到背后去。

注意:inert不能用于自身是焦点目标的元素

如果你要给某个button加inert,那这个button本身是不可聚焦的,所以它的onclick不会触发。但是,它内部如果有子元素(比如span),span也不会被点击到。这是正常的。但有一种情况比较反直觉:如果你对一个拥有contenteditable的div设置inert,那么文本编辑也不能再进行了。我实际用的时候,发现输入框无法输入,这其实正是“禁用”的语义。

支持程度怎么样?

目前Chrome 102+、Firefox 112+、Safari 15.5+都支持了。截至2025年,所有主流浏览器都能用。如果你要兼容IE或者旧版移动浏览器,可以引入一个简单的polyfill,不过现在基本没有这个必要了。

如果不确定,可以写一行判断:

if ('inert' in HTMLElement.prototype) {
  // 浏览器支持
} else {
  // 降级方案
}

实际上,我连判断都没做,因为公司浏览器都已是新版。

几个容易忽略的细节

  • inert 属性生效时,元素的内容也不可选中,所以用户不能复制里面的文字。这个要考虑到,有些场景可能不需要禁止文本复制,那你可以不用inert,改用 pointer-events:nonetabindex="-1" 组合。
  • 当某个元素设置为inert后,它的所有后代都被视为不可交互,哪怕后代上有onclick事件也白搭。
  • 如果父元素是inert,但你希望某个子元素可交互,没有办法“解开”子元素。inert不能被子元素覆盖,只能移除父元素的inert属性。

最后的最后,我改写了公司老代码

我把原来一个用iframe遮罩的老弹窗组件重构了一下,用上了inert。代码量从原来的两百多行缩到了几十行。当然这不是唯一原因,但inert确实帮我省掉了大量焦点管理逻辑。以后再有人问“为什么弹窗后面的按钮还能点”,我就会告诉他:“你试试给背景加个inert属性。” 就这么简单。

如果你也在写弹窗或侧栏,强烈建议用inert试一下,相信你也会把那一堆tabindex和focusout删掉的。

HTML inert实战:我总算能轻松禁止一个区域的点击和聚焦了
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 html HTML inert实战:我总算能轻松禁止一个区域的点击和聚焦了 https://www.taomawang.com/web/html/2578.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务