opacity小于1(如0.99)强制创建层叠上下文,使子元素z-index仅在父内生效,无法覆盖外部元素;opacity:1则不触发该行为,这是CSS规范明确要求的临界点。

opacity 小于 1 会强制创建新的层叠上下文(stacking context),导致子元素的 z-index 失去跨父级比较能力——它只能跟同个父容器下的兄弟比高低,无法盖过外部其他定位元素。
为什么 opacity: 0.99 和 opacity: 1 行为天差地别?
这不是浏览器 bug,而是 CSS 规范明确要求:只要 opacity 计算值严格小于 1(哪怕 0.999),就必须创建新层叠上下文。而 opacity: 1 不触发该行为。这个临界点非常敏感,且不可缓动或渐变绕过。
- Chrome DevTools 的 «Computed» 面板里搜
Stacking context,显示Yes就确认被隔离了 - 临时注释掉父级
opacity,若遮挡立刻消失,就是它 - 动画中哪怕只有一帧算出
opacity: 0.99(比如从 1 → 0.99 → 0 的过渡),那一帧就会动态创建/销毁上下文,引发层级抖动
常见误判场景:弹窗按钮被导航栏压住
典型表现是:给弹窗外层加了 opacity: 0.95 做淡入,内部按钮设了 z-index: 9999,结果还是被页面顶部 position: fixed 导航栏盖住。
- 根本原因:弹窗外层已创建独立层叠上下文,按钮的
z-index只在弹窗内部有效 - 导航栏和弹窗不在同一上下文中,
z-index数值再大也无意义 - Vue/React 中用 class 切换控制显隐时,若一个 class 含
opacity: 0.99、另一个是opacity: 1,切换瞬间就可能触发上下文重置
怎么避免或绕过这个限制?
不要依赖 opacity 控制交互层级——它只管视觉透明度,不负责事件权或层叠域划分。
- 如需淡入效果,优先用
opacity: 1 → opacity: 0全程整数端点,避开 (0,1) 开区间 - 必须半透明+高交互(如 Tooltip 遮罩层),把可点击目标元素用 JS 移到
document.body下,脱离原上下文 - 纯视觉装饰层(背景蒙版、粒子动效)务必加
pointer-events: none,否则opacity: 0.1的元素仍会拦截点击 - 排查时重点盯
transform、filter、will-change等同样会触发上下文的属性,它们常和opacity联动出现
真正难处理的不是单个 opacity,而是多个隐式触发条件叠加(比如 opacity: 0.99 + transform: translateZ(0) + will-change: opacity),这种组合会让层叠结构变得极难追踪。DevTools 的 «Layers» 面板比 «Elements» 更能直观暴露嵌套层级。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











