要了解每个元素何时绘制,需要知道其堆叠上下文和在该堆叠上下文中的层叠级别(由z-index
定义)。还需要知道该元素是否建立堆叠上下文。设置z-index
将会触发这一点:
对于定位框,z-index属性指定:
- 当前堆叠上下文中框的层叠顺序。
- 框是否建立新的堆叠上下文
取值的含义如下:
<integer>
生成的框在当前堆叠上下文中的层叠级别为此整数。该框还会建立新的堆叠上下文。
有两件重要的事情需要知道:绘制顺序和堆叠上下文。如果参考规范,可以找到元素如何以及何时被绘制。
- 具有负z-index(不包括0)的定位后代形成的堆叠上下文以z-index顺序(最负的先)然后树顺序排列。
所有定位、不透明或变换后代,按树顺序分为以下类别:
- 所有具有'z-index: auto'或'z-index: 0'的定位后代,按树顺序。
- 具有大于或等于1的z-index的定位后代形成的堆叠上下文以z-index顺序(最小的先)然后树顺序排列。
从中可以清楚地看出,首先在第3步绘制带有负数z-index
的元素,然后在第8步中绘制z-index
等于0的元素,最后在第9步中绘制具有正数z-index
的元素,这是合理的。我们还可以在规范的另一部分中读到:
每个框都属于一个堆叠上下文。给定堆叠上下文中的每个框都有一个整数堆叠级别,它是相对于同一堆叠上下文中的其他框在z轴上的位置。具有更高堆叠级别的框始终位于堆叠级别较低的框前面。框可以具有负堆叠级别。在堆叠上下文中具有相同堆叠级别的框根据文档树顺序自下而上堆叠。
auto
当前堆栈上下文中生成的框的堆栈级别为0。除非它是根元素,否则该框不会建立新的堆栈上下文。
现在我们拥有所有信息来更好地理解每种情况。如果父元素具有与auto
不同的z-index
值,则它将创建一个堆叠上下文,因此子元素将被绘制在其z-index
(负或正)内部。子元素的z-index
将仅告诉我们以父元素为基础的绘制顺序(这涵盖了您的第二个点)。
现在,如果只有子元素具有正z-index
,并且我们在父元素上没有设置任何内容,则考虑绘制顺序,子元素将在步骤(9)后绘制,父元素将在步骤(8)中绘制。将父元素上移的唯一逻辑方法是增加z-index
,但这样做将使我们陷入前面的情况,其中父元素将建立一个堆叠上下文,子元素将属于它。
当子元素设置正z-index
时,没有办法使父元素位于其上方。如果我们在父元素上设置与auto
不同的z-index
(正或负),也没有办法使父元素位于子元素之上。1
唯一可以让子元素位于其父元素下方的情况是在子元素上设置负z-index
并保持父元素为z-index: auto
,这样就不会创建堆叠上下文,并按照绘制顺序先绘制子元素。
除了z-index
,还有其他属性创建堆叠上下文。如果您遇到预期的堆叠顺序,则需要考虑这些属性,以查看是否创建了堆叠上下文。
从以上内容中我们可以得出一些重要的结论:
- 堆叠上下文可以包含在其他堆叠上下文中,并共同创建一个堆叠上下文层级。
- 每个堆叠上下文完全独立于其兄弟:只有后代元素在堆叠时才会被考虑。
- 每个堆叠上下文都是自包含的:在元素内容堆叠后,整个元素将被视为父堆叠上下文的堆叠顺序。 ref
1: 如果考虑使用3D变换,则存在某些hacky方式。
例如,一个元素会在其父元素下面,即使父元素已指定z-index。
.box {
position:relative;
z-index:0;
height:80px;
background:blue;
transform-style: preserve-3d;
}
.box > div {
margin:0 50px;
height:100px;
background:red;
z-index:-1;
transform:translateZ(-1px);
}
<div class="box">
<div></div>
</div>
另一个示例,我们可以在另一个堆叠上下文中的两个元素之间放置一个元素:
.box {
position: relative;
transform-style: preserve-3d;
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>
需要注意的是,使用这种技巧可能会产生一些副作用,因为transform-style
、perspective
和transform
将影响position:absolute/fixed
元素。相关链接:为什么在父元素上应用CSS过滤器会破坏子元素的定位?