您需要了解两件重要的事情:绘制顺序和堆叠上下文。如果您参考the specification,您可以找到绘制元素的方式和时间。
-
堆叠上下文由 具有负 z 索引(不包括 0)按 z 索引顺序(最负值在前)然后是树顺序的定位后代形成。
- 所有已定位、不透明或变换的后代,按树形顺序排列,属于以下类别:
- 所有具有“z-index:auto”或“z-index:0”的定位后代,按树顺序排列。
-
堆叠上下文由 z-indices 大于或等于 1 按 z-index 顺序(从小到大)然后是树顺序的定位后代形成。
从这里可以清楚地看出,我们首先在步骤 (3) 绘制带有负值 z-index 的元素,然后在步骤 (8) 绘制 z-index 等于 0 的元素,最后在步骤 (8) 绘制带有正值 z-index 的元素(9),这是合乎逻辑的。我们也可以在the specification的另一部分阅读:
每个盒子都属于一个堆叠上下文。给定堆叠上下文中的每个盒子都有一个整数堆叠级别,这是它在 z 轴上相对于同一堆叠上下文中的其他盒子的位置。具有较高堆栈级别的框总是在具有较低堆栈级别的框之前格式化。盒子可能有负的堆栈级别。在堆叠上下文中具有相同堆叠级别的框根据文档树顺序从下到上堆叠。
还有
建立本地堆叠上下文的元素会生成一个具有两个堆叠级别的框:一个用于它创建的堆叠上下文(始终为 0),另一个用于它所属的堆叠上下文(由 z-index 属性给出) .
要了解每个元素何时被绘制,您需要了解其堆叠上下文以及此堆叠上下文(由z-index 定义)内的其堆叠级别。您还需要知道该元素是否建立了堆叠上下文。这是棘手的部分,因为设置 z-index 会这样做:
对于定位框,z-index 属性指定:
- 框在当前堆叠上下文中的堆叠级别。
- 框是否建立堆叠上下文
值的含义如下:
<integer>
这个整数是当前堆栈上下文中生成的盒子的堆栈级别。该框还建立了一个新的堆叠上下文。
auto
生成的盒子在当前堆叠上下文中的堆叠层级为0。盒子不会建立新的堆叠上下文,除非它是根元素。
现在我们掌握了所有信息,可以更好地了解每个案例。如果父元素的z-index 值不是auto,那么它将创建一个堆叠上下文,因此子元素将被绘制在其z-index 是(负或正)的内部。子元素的z-index 将简单地告诉我们父元素内的绘画顺序(这涵盖了您的第二点)。
现在,如果只有子元素有一个正的z-index 并且我们在父元素上没有设置任何内容,那么考虑到绘制顺序,子元素将在后面绘制(在步骤(9)中),父元素在步骤( 8)。上面绘制父元素的唯一合乎逻辑的方法是增加z-index,但是这样做会使我们陷入之前的情况,即父元素将建立一个堆叠上下文,子元素将属于它。
在为子元素设置 正 z-index 时,无法让父元素高于子元素。此外,如果我们将z-index 设置为不同于auto(正或负)的父元素,则无法将父元素置于子元素之上。1
我们可以在其父元素下方有一个子元素的唯一情况是在子元素上设置一个负数z-index并将父元素保持在z-index: auto,因此这不会创建堆叠上下文并遵循绘画顺序孩子会先被画出来。
除了z-index,还有other properties that create a stacking context。如果您遇到预期的堆叠顺序,您还需要考虑这些属性,以查看是否创建了堆叠上下文。
我们可以从上面得出的一些重要事实:
- 堆叠上下文可以包含在其他堆叠上下文中,并共同创建堆叠上下文的层次结构。
- 每个堆叠上下文完全独立于其兄弟元素:处理堆叠时仅考虑后代元素。
- 每个堆叠上下文都是自包含的:在元素的内容被堆叠之后,整个元素被认为是父堆叠上下文的堆叠顺序。 ref
1:如果我们考虑使用 3D 变换,会有一些 hacky 方法。
一个元素位于其父元素之下的示例,即使该元素指定了z-index。
.box {
position:relative;
z-index:0;
height:80px;
background:blue;
transform-style: preserve-3d; /* This is important */
}
.box > div {
margin:0 50px;
height:100px;
background:red;
z-index:-1; /* this will do nothing */
transform:translateZ(-1px); /* this will do the magic */
}
<div class="box">
<div></div>
</div>
另一个例子,我们可以在另一个堆叠上下文中的两个元素之间放置一个元素:
.box {
position: relative;
z-index: 0;
height: 80px;
background: blue;
}
.box>div {
margin: 0 50px;
height: 100px;
background: red;
z-index: 5;
transform: translateZ(2px);
}
.outside {
height: 50px;
background: green;
margin: -10px 40px;
transform: translateZ(1px);
}
body {
transform-style: preserve-3d;
}
<div class="box">
<div></div>
</div>
<div class="outside"></div>
我们也可以有一些疯狂的堆叠顺序,如下所示:
.box {
width: 100px;
height: 100px;
position: absolute;
}
body {
transform-style: preserve-3d;
}
<div class="box" style="top:100px;left:50px;background:red;"></div>
<div class="box" style="top: 50px;left: 115px;background:blue;"></div>
<div class="box" style="top: 101px;left: 170px;background:green;"></div>
<div class="box" style="top: 175px;left: 115px;background:purple;transform: rotateY(-1deg);"></div>
我们应该注意,使用这种 hack 可能会产生一些副作用,因为transform-style、perspective 和transform 会影响position:absolute/fixed 元素。相关:CSS-Filter on parent breaks child positioning