先记住这个答案
不会创建。position: relative 加 z-index: auto 的元素,只是在当前层叠上下文里被归到「定位元素」这一层,按 DOM 顺序画在普通元素上面,它自己不成为一个独立的层。它的子元素如果写了 z-index,会直接到外面那个层叠上下文里和其他元素比较。一旦把 z-index 改成整数,哪怕是 0,这个元素就创建了新的层叠上下文:它和它的所有后代被当成一个整体参与外层排序,子元素的 z-index 只在内部有效。
auto不建层叠上下文,0会auto时后代可参与外层比较- 创建层叠上下文后,子元素
z-index再大也出不去 - 透明度与变换也可建层叠上下文
从一个被遮住的角标说起
页面顶部有一个固定层级的导航栏,下面的卡片右上角有个角标,需要叠到导航栏上面。
auto 和 0 只差一个值,结果完全相反HTML<style>
.nav { position: relative; z-index: 5; }
.card { position: relative; z-index: auto; } /* 改成 0 试试 */
.badge { position: absolute; top: -20px; z-index: 10; }
</style>
<div class="nav">导航栏</div>
<div class="card">
<span class="badge">NEW</span>
</div>
<!-- .card 为 auto:角标 10 和导航栏 5 直接比较,角标在上 -->
<!-- .card 为 0:卡片整体是 0,小于导航栏的 5,角标被压在导航栏下面 -->.card 是 auto 时它不成层,角标的 z-index: 10 和导航栏的 5 在同一个上下文里比较,10 大,所以角标在上面。
.card 改成 z-index: 0 后,卡片自成一层,外面看来卡片整体的层级就是 0。角标的 10 只在卡片内部有效,出不去,整个卡片连同角标都在导航栏下面。这就是「子元素 z-index: 9999 也没用」的典型原因。
为什么是「定位元素加数值」才算
z-index 最早只对定位元素生效,position: static 的普通元素写了也会被忽略。所以规范里创建层叠上下文最经典的条件就是:position 为 relative 或 absolute,并且 z-index 不是 auto。
auto的含义auto是z-index的初始值,意思是「跟随父级上下文,不单独建层」。整数值的意思是「在父级上下文里占据这个层级,并且我自己是一个新的上下文」。0 虽然在数值上和默认位置一样,但它是一个明确的整数,所以也会建层。 后来规范加了很多条件,不依赖定位也会创建层叠上下文,所以「定位元素加数值才会」这句话是历史上的核心规则,但不是全部。
不靠定位也会创建层叠上下文的情况
排查层级问题时,只盯着 z-index 常常找不到原因,因为下面这些属性也会默默创建层叠上下文。
- 常见的触发条件
position: fixed和position: sticky不写z-index也会创建。opacity小于 1、transform不为none、filter不为none、mix-blend-mode不为normal、isolation: isolate、will-change指定了上面这些属性,也都会创建。flex 和 grid 的子项不需要定位,写了整数z-index也会创建。 实际项目里最容易踩的是动画。给卡片加了transform做悬停放大,卡片就成了层叠上下文,里面的下拉菜单从此被下一张卡片盖住。
容易答错的地方
- 以为
z-index: auto就等于z-index: 0 - 对兄弟元素来说,两者画的位置确实一样,都在定位元素那一层按 DOM 顺序排。区别在子元素:
auto放子元素出去比较,0把子元素关在里面。 - 以为子元素
z-index写得足够大就能盖住一切 - 子元素的
z-index只在它所在的层叠上下文里有效。只要某个祖先创建了层叠上下文,并且这个祖先整体比别人低,子元素写多少都没用。要么调整祖先的层级,要么用 Portal 把弹层渲染到body下。 - 以为给普通元素写
z-index也能创建层叠上下文 position: static的块元素写z-index不生效,也不会创建层叠上下文。flex 和 grid 的子项例外。
面试官还会怎么问?
想让组件内部的层级不影响外部,最干净的写法是什么?
给组件根元素加 isolation: isolate。它唯一的作用就是创建层叠上下文,不需要定位,也不需要随便挑一个 z-index 数值,组件内部的 z-index 就再也漏不出去。
在 DevTools 里怎么找是谁创建了层叠上下文?
Chrome 的 Layers 面板能看到合成层,但层叠上下文和合成层不完全是一回事。更直接的办法是从出问题的元素往上逐个看祖先的计算样式,检查 z-index、opacity、transform、filter、position: fixed 这些属性。
position: absolute 配 z-index: auto 呢?
和 relative 一样,不会创建层叠上下文。relative 和 absolute 在这件事上没有区别,fixed 和 sticky 才会无条件创建。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。