display: none 彻底移除元素,不占空间、不触发事件、不被读屏器识别;visibility: hidden 仅视觉隐藏但保留布局、可测尺寸、子元素可单独显示。

display: none 会彻底移除元素,不占空间、不触发事件、不被读屏器识别
设置 display: none 后,元素从渲染树中被完全剔除:它没有尺寸、不参与布局计算、后续兄弟元素立刻上移填补空位。JavaScript 中调用 getBoundingClientRect() 或读取 offsetHeight 都返回 0。display: none 的元素无法获得焦点、不响应任何鼠标/键盘事件,屏幕阅读器默认跳过它——这对表单步骤切换、权限控制类隐藏很合适。
常见误用:
- 在需要保留布局结构的 hover 动画容器上用了
display: none,导致鼠标移入前内容“突然上跳” - 隐藏含
<input>的表单区域却用display: none,结果该字段提交时被浏览器直接忽略(表单序列化时跳过) - 用
element.style.display = ""恢复显示,但空字符串可能继承父级或 UA 默认值(比如inline),而非你预期的block
visibility: hidden 只视觉隐藏,仍占位、可测尺寸、子元素能“穿透”显示
visibility: hidden 不影响文档流:元素盒模型照常存在,margin/padding/height 全部生效,周围元素不动。JS 仍能准确读取它的几何信息,getBoundingClientRect() 返回真实值。它还支持继承——父设 visibility: hidden,子默认也隐藏;但子显式设 visibility: visible 就能单独显示(display 没这能力)。
关键风险点:
立即学习“前端免费学习笔记(深入)”;
- 按钮或链接设了
visibility: hidden,用户仍可用 Tab 键聚焦并回车触发,造成逻辑漏洞 - 表格中对
<tr> 用 <code>visibility: hidden,部分浏览器下<tbody> 高度异常塌缩<li>没配 <code>pointer-events: none,鼠标悬停/点击仍会捕获事件(只是用户看不到反馈)
动画与性能:visibility 可过渡,display 不行
display 是非连续 CSS 属性,不能参与 transition 或 @keyframes。想实现淡出效果,硬切 display: none 会导致生硬跳变。而 visibility 可以和 opacity 配合做平滑过渡:
/* 正确组合 */
.hidden {
visibility: hidden;
opacity: 0;
transition: opacity 0.2s;
}
.showing {
visibility: visible;
opacity: 1;
}这样既避免 layout thrashing(visibility 切换只触发重绘),又保证动画结束时元素不可交互(visibility: hidden 阻断焦点和事件)。注意:别用 opacity: 0 单独替代二者——它仍响应事件、参与 z-index 层叠、影响可访问性。
媒体查询与选择器权重冲突时,display 优先级更高
当一个元素同时被多个规则设置 display 和 visibility,display: none 会直接让 visibility 失效——因为元素已不在渲染树里。例如:
@media (max-width: 768px) {
.nav { visibility: hidden; }
}
.layout .nav { display: block; } /* 这条权重更高,覆盖 media 查询,但 nav 仍可见 */如果反过来:
.layout .nav { display: none; }
@media (max-width: 768px) {
.nav { visibility: visible; } /* 无效:display: none 已让元素消失 */
}所以别指望靠 visibility “救活”被 display: none 干掉的元素。真正要响应式切换,统一用一种机制,或用 JS 控制 class 切换更可控。
实际调试时最省事的判断方式:打开 DevTools → Elements 面板选中目标元素 → 看右侧 Computed 栏里 display 和 visibility 的最终值;再切到 Layout 面板勾选 “Show layout shifts”,看隐藏前后是否引发周围元素位置变动——动了,就是 display 在起作用;没动,基本是 visibility 或 opacity。


















