display: none 彻底移除元素,不参与布局、不占空间、不响应事件;visibility: hidden 保留布局但隐藏视觉,仍可聚焦和触发事件;opacity: 0 仅透明,仍占空间且响应事件。

display: none 彻底退出渲染流程
浏览器在布局(layout)阶段就跳过该元素:不计算盒模型、不分配空间、不参与文档流。后续兄弟元素会立即上移填补位置,offsetHeight、getBoundingClientRect() 返回全为 0 的对象。
常见错误现象:display: none 元素被 focus() 调用时无反应;用 JavaScript 动态切换它后读取 offsetTop 再设回 block,容易引发 layout thrashing(因反复强制同步布局)。
- 必须搭配
aria-hidden="true"才能确保屏幕阅读器不读取(默认已忽略) - 打印样式表中不会输出
- 父元素设
display: none,子元素无论怎么设置display或visibility都不可见
visibility: hidden 仅跳过绘制(paint),不跳过布局
元素仍保留在文档流中,尺寸、边距、位置全部照常计算。offsetHeight 和 getBoundingClientRect() 返回真实值,滚动锚点、CSS 计数器(如 counter-increment)均不受影响。
容易踩的坑:父元素设 visibility: hidden 后,若子元素显式设 visibility: visible,内容会“透出”——这在模态框遮罩层里极易导致 UI 泄露。
立即学习“前端免费学习笔记(深入)”;
- 默认仍响应
focus(),input可被聚焦但光标不可见 - 支持
transition: visibility 0.2s实现延迟显示(配合opacity或transform更实用) - 不触发重排(reflow),只触发重绘(repaint),性能更温和
pointer-events 和事件穿透不是它们的原生行为
很多人误以为 visibility: hidden 会导致点击穿透到下层元素,其实不然。两者默认都让鼠标事件无法命中目标元素,但事件捕获/冒泡路径是否包含该元素,取决于其是否在渲染树中——display: none 元素根本不在渲染树里,visibility: hidden 元素仍在。
真正决定事件是否穿透的是 pointer-events 属性:
-
pointer-events: none才会让事件直接透传(推荐与visibility: hidden组合使用) -
pointer-events: auto(默认)下,visibility: hidden元素仍可捕获事件,只是用户看不到反馈 -
display: none元素无论如何设置pointer-events,都不会出现在事件路径中
别用 opacity: 0 混淆概念
opacity: 0 是视觉透明,和隐藏逻辑无关。它仍占空间、响应事件、影响 z-index 层叠顺序、触发合成层——这些都会带来意料之外的交互或性能问题。
典型误用场景:用 opacity: 0 替代 display: none 来“隐藏按钮”,结果按钮依然能被点击、挡住下方链接、导致滚动卡顿。
- 需要渐隐动画?用
opacity+transition,但结束后应补上display: none(或用visibility: hidden) - 需要保留语义和可访问性?优先考虑
visibility: hidden+aria-hidden="true" - 需要彻底移除交互与布局影响?
display: none是唯一选择
display 或 visibility 前,得快速判断:你此刻要抹掉的是视觉、是空间、是事件响应能力,还是三者都要。漏判任意一项,后面调试时就会在 DOM 树、无障碍测试、动画表现之间反复横跳。


















