display: none 彻底移除元素并触发重排,visibility: hidden 仅隐藏但保留布局位置且只触发重绘;前者不可继承、子元素不可见,后者可继承且子元素可设为可见;两者均阻止鼠标事件,但仅后者支持 focus()。
关键区别在于:display: none 让元素彻底退出文档流,不占空间;visibility: hidden 只让元素“不可见”,仍占据原有布局位置。
这是最直接影响渲染行为的差异:
display: none:元素从渲染树中移除,父容器会重新计算尺寸和子元素位置(触发 重排(reflow))visibility: hidden:元素保留在渲染树中,仅跳过绘制阶段(只触发 重绘(repaint),不重排)
visibility: hidden 性能通常更好(尤其在动画或列表折叠场景)getComputedStyle(el).display 或 getComputedStyle(el).visibility 可验证当前状态继承性与事件响应行为不同:
visibility: hidden 是可继承的 —— 子元素默认也隐藏,但可通过设置 visibility: visible 单独显示子元素display: none 不可继承,且子元素无论如何设置 display 都不会显示(父级已脱离渲染树)click、hover),但 visibility: hidden 元素仍可被 focus()(若本身可聚焦),而 display: none 元素无法获得焦点很多 bug 源于混淆二者语义:
visibility: hidden 实现“抽屉菜单”会导致空白占位,用户滚动时看到大片空隙display: none 控制表单字段显隐,再用 querySelector 查找时可能返回 null(因为 DOM 节点还在,但渲染树里没了)display: none 默认忽略该元素;visibility: hidden 仍可能被读出(需配合 aria-hidden="true")display 属性做过渡(CSS transition 对 display 无效),但可以对 visibility + opacity 组合做渐隐/* 安全的渐隐方案示例 */
.fade-out {
visibility: hidden;
opacity: 0;
transition: opacity 0.3s, visibility 0.3s;
}
.fade-in {
visibility: visible;
opacity: 1;
}有些细节必须动手测:比如 offsetHeight 在 display: none 下为 0,而 visibility: hidden 下仍返回真实值。别只看视觉表现。