超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

深入理解层叠上下文(stacking context)和 z-index

在CSS的盒模型中,所有的元素除了可以再屏幕上自由摆放外,还可以沿着 z轴 进行摆放,特别是当元素为止互相重叠的时候,了解z轴的摆放顺序非常重要。使用 z-index 可以改变元素在 z轴 的上下层级,但是 z-index 具体是如何影响元素的层级呢?为什么有时候它并不生效。本文详细总结一下这些问题。

层叠上下文(stacking context)

元素被绘制到画布上的层级顺序用层叠上下文来描述。当前层叠上下文可以包含子层叠上下文,当元素的内容发生层叠后,整个该元素将会在父层叠上下文中按顺序进行层叠。每个层叠上下文完全独立于它的兄弟元素,在其他层叠环境中的元素不会影响当前层叠环境。

每个盒元素都属于一个层叠上下文。给定层叠上下文中的每个定位元素都具有一个整数的层叠层级,具有更大堆栈级别的元素盒子总是在具有较低堆栈级别的盒子的前面(上面)。盒子可能具有负层叠级别。层叠上下文中具有相同堆栈级别的框根据文档树出现的顺序层叠在一起。

根元素形成根层叠上下文,是所有层叠上下文的根层叠上下文。注意,层叠上下文不一定与包含块相关,也就是说并不是每个元素都能形成一个层叠上下文。文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 根元素 (HTML)
  • z-index 值不为 auto 的 绝对/相对定位
  • position 值为 fixed|sticky
  • 一个 z-index 值不为 auto 的 flex 项目 (flex item),即:父元素 display: flex|inline-flex
  • opacity 属性值小于 1 的元素
  • transform 属性值不为 “none”的元素
  • mix-blend-mode 属性值不为 normal 的元素
  • filter | perspective | clip-path |mask / mask-image / mask-border 值不为 none 的元素
  • perspective 值不为 none 的元素
  • isolation 属性被设置为 isolate 的元素
  • will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling 属性被设置 touch的元素

其中,加粗几条是经常见到和使用的css属性。

层叠上下文的顺序

实际上确定页面上所有元素(包括边框,背景,文本节点等)的堆叠顺序是非常复杂的,本文对其进行了一些简化,将不常用和不常出现的内容去掉,更详细顺序的请参考 css规范 Appendix E. Elaborate description of Stacking Contexts

以下是层叠顺序的基本规则(从底层向顶层排列):

  1. 堆叠上下文的根元素
  2. 具有负 z-index 值的定位元素(position 不为 static),其中较高 z-index 值的堆叠在较低值的前面,具有相同 z-index 值的元素根据HTML中的出现顺序进行堆叠)
  3. 非定位元素(不存在 position 或 position 为 static
  4. 定位元素具有自动的 z-index 值( z-indexauto 或者 为 0
  5. 浮动块元素(指定 float
  6. 定位元素具有正 z-index 值(较高的值堆叠在较低值之前;具有相同值的元素根据HTML中的外观进行堆叠)

层叠位置示例如下:

See the Pen Stacking context by tcatche (@tcatche) on CodePen.

如果在某些场合下,为元素指定 z-index 并不生效,这个时候需要查看下是否其上层是否形成了堆栈上下文,如果形成了,那么无论设置多大的数值都不生效:

如下示例,按照预想,red 元素设置了 z-index,应该在 green 元素上面,但是并不起作用:

See the Pen Z-index Not work by tcatche (@tcatche) on CodePen.

出现这一个问题的原因在于,示例中指定了每个颜色块的父元素top 元素一个透明度属性 opacity: 0.99; 如果不使用该属性,元素的层级大概以认为如下(注意,这里只是为了说明,并不代表元素的真实层级):

<div class="top top1"><!-- top1: 1 -->
<span class="red"><!-- red:4 --></span>
</div>
<div class="top top2"><!-- top2: 2 -->
<span class="green"><!-- 3 --><span>
</div>

当使用该属性将 top 元素变成了层叠上下文元素,在确定位置的时候首先定位 top1 和 top2,确定 top2 的层级在 top1 之上。而后确定 top1 和 top2 内部元素的层级,而 top1 内的子元素指定的 z-index 仅影响其在 top1 中的层级,并无法改变 top1 在更上的层级,因此不起作用。

此时,可以认为元素的层级大概如下:

<div class="top top1"><!-- top1: 1 -->
<span class="red"><!-- red: 1.1 --></span>
</div>
<div class="top top2"><!-- top2: 2 -->
<span class="green"><!-- green: 2.1 --><span>
</div>

参考