visibility: hidden 保留文档流位置且可被子元素覆盖,display: none 彻底移除渲染并阻止子元素显示;前者不触发重排,后者会。
关键判断:如果需要保留元素在文档流中的位置(比如防止布局跳动、保持其他元素相对位置不变),就用 visibility: hidden;如果要彻底移除渲染并释放空间,必须选 display: none。
常见错误现象是误以为两者都能“隐藏但不影响布局”——其实只有 visibility 满足这点。而 display: none 会让元素完全退出渲染树,其子元素也无法通过 visibility: visible

visibility: hidden 元素仍占据空间,可响应 pointer-events: none 外的事件(但默认不响应点击)visibility: visible 能覆盖继承来的 hidden,这是它和 display 最显著的行为差异visibility 配合 opacity 更安全,避免 display 导致的重排(reflow)visibility 是可继承属性,这意味着父元素设为 hidden 后,所有子元素默认也隐藏——但子元素能用 visible 主动“翻盘”,这点常被忽略。
合法值只有三个:visible、hidden、collapse(仅对表格相关元素有效,普通 div/span 用 collapse 等同于 hidden)。
visibility: hidden → 元素不可见,但保留尺寸、位置、事件绑定能力(除非显式禁用)visibility: visible → 显式恢复可见性,常用于子元素“自救”visibility: collapse → 表格行/列会像 display: none 一样收缩,但非表格元素无此效果不是所有“隐藏”都该用 visibility,它最适用那些需要视觉暂退但逻辑上仍需锚定位置的场景。
opacity 实现淡入淡出aria-hidden="false")注意:它不能替代 display 来做条件渲染,比如 React/Vue 中控制组件是否挂载——那是框架层的结构决策,visibility 只管样式层。
很多人以为 visibility: hidden 后元素就“消失”了,点不到也传不了事件。但事实是:它依然在渲染树里,事件照常捕获和冒泡,只是视觉不可见。
典型问题:一个 visibility: hidden 的按钮,如果没加 pointer-events: none,用户仍可能误点触发事件。
button.hidden {
visibility: hidden;
pointer-events: none; /* 必须手动加这一行 */
}另一个坑是动画性能:单独用 visibility 切换无法触发 GPU 加速,建议搭配 opacity 或 transform 使用;纯 visibility 切换只触发重绘(repaint),不触发重排(reflow),这点比 display 轻量,但也意味着它无法参与 CSS 动画的 transition(因为 visibility 不是可过渡属性)。