display: none 彻底移除元素,visibility: hidden 和 opacity: 0 保留占位;前者禁用交互与可访问性,后两者需额外处理焦点和事件;仅 opacity 支持平滑过渡动画。

visibility: hidden、display: none 和 opacity: 0 都能让元素“看不见”,但它们对 DOM 结构、布局、事件、可访问性的影响完全不同——选错一个,就可能让表单提交失败、键盘导航卡死、动画卡顿或屏幕阅读器误读。
元素是否还在文档流里?
这是最根本的分水岭:
-
display: none:元素彻底退出渲染树,offsetWidth和offsetHeight立即为0,父容器高度塌缩,兄弟元素自动上移;getBoundingClientRect()返回全0值 -
visibility: hidden和opacity: 0:元素仍在文档流中,占位不变,父容器高度不塌,兄弟元素位置不动
典型场景:折叠面板收起时若用 display: none,下方内容会突然上跳;用 visibility: hidden 或 opacity: 0 就能避免这种“抖动”。
子元素能不能自己“钻出来”?
这个行为差异在嵌套结构中极易踩坑:
-
display: none下,无论子元素怎么设display: block或visibility: visible,一律不可见 -
visibility: hidden是继承属性,但子元素可显式覆盖:visibility: visible就能单独显示 -
opacity: 0也继承,但子元素设opacity: 1并不能“变回来”——因为父级透明度会层叠计算(最终仍是0 × 1 = 0)
常见错误:给弹窗容器加 visibility: hidden,结果忘了里面按钮仍能被 tab 键聚焦,用户按回车就意外触发。
交互和可访问性表现为何天差地别?
表面看不见,背后能力却大不相同:
-
display: none:不响应任何事件,不进form.elements,不出现在FormData中,屏幕阅读器完全跳过 -
visibility: hidden:默认不响应鼠标事件,但可被tab键聚焦(除非加tabindex="-1"),屏幕阅读器仍可能读取(需配aria-hidden="true") -
opacity: 0:默认仍响应所有事件(点击、hover、focus),键盘可聚焦,屏幕阅读器照常播报——它只是“隐形”,不是“隐藏”
关键补救:用 opacity: 0 做淡出时,必须同步加 pointer-events: none 和 aria-hidden="true",否则用户点空、读屏器念错都是高概率问题。
能不能配合 transition 做动画?
浏览器对这三个属性的动画支持截然不同:
-
display不支持过渡:写transition: display 0.3s完全无效,切换是瞬时的 -
visibility只对visible → hidden有延迟隐藏效果;hidden → visible是立即生效,无法淡入 -
opacity支持完整过渡,是唯一适合做渐隐/渐显的选项,但必须搭配pointer-events: none控制交互时机
实用组合:先设 visibility: hidden; opacity: 0,再用 transition: opacity 0.2s + visibility: visible 触发淡入——这样既避免点击穿透,又保证动画平滑。
真正难的不是记住三者区别,而是每次写 opacity: 0 时都记得补 pointer-events: none,每次用 visibility: hidden 都检查键盘焦点是否还卡在里面,以及永远不把 display: none 当作“临时藏一下”的快捷方式——它本质是“删掉再重建”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











