我们都知道 CSS 中的 z-index 属性可以用来改变定位元素的层叠顺序,但是如果仅仅希望通过不断增大 z-index 数值来达到提高层叠次序的目的就有可能陷入欲求不能的迷惑中,其实关于层叠顺序这背后的学问还真不少。
我们先来看一组案例:

上图所示的两组桌子它们的 dom 结构和定位方式都是一模一样的,但是左边那组 1 号桌的桌号却显示在了 2 号桌的上面。
<style>
.left,.right{
position: relative;
width: 400px;
height: 200px;
}
.table{
position: absolute;
top: 25px;
width: 200px;
height: 150px;
border: 3px solid #ddd;
border-radius: 10px;
text-align: center;
line-height: 150px;
color: #fff;
font-size: 20px;
}
.table1{
left: 25px;
background: #7f8bdf;
}
.table2{
left: 175px;
background: #f59891;
}
.table .number{
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
line-height: 30px;
background: #717477;
border-radius: 50%;
z-index: 2;
}
.right .table{
z-index: 1;
}
</style>
<div class="left">
左
<div class="table table1">
桌
<div class="number">1</div>
</div>
<div class="table table2">
桌
<div class="number">2</div>
</div>
</div>
<div class="right">
右
<div class="table table1">
桌
<div class="number">1</div>
</div>
<div class="table table2">
桌
<div class="number">2</div>
</div>
</div>
通过样式代码我们可以看到仅仅是右边的桌子设置了 z-index 数值就造成了这种差异,这是为什么?我们首先欢迎今天的第一位嘉宾——层叠上下文登场。
层叠上下文(stacking context),是 HTML 元素的三维概念,这些 HTML 元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的 z 轴上延伸, HTML 元素依据其自身属性按照优先级顺序占用层叠上下文的空间。
可以这样理解:我们看到的网页就像是一个桌面, HTML 元素就是桌面上的一张张纸,层叠上下文就像是一个信封可以容纳其它纸张甚至是又一个信封。

当纸张铺开的时候我们可以一眼看到所有,但是一旦他们堆叠在一起就要有一个谁在上谁在下的顺序。

层叠水平(stacking level)决定了同一个层叠上下文中元素在 z 轴上的显示顺序,就像桌子上纸张的顺序、信封里信纸的顺序。
层叠水平只是一个概念,那么我们根据什么来判定元素的层叠水平,有一个著名的示图揭示了规则,这就是层叠顺序(stacking order)。在 CSS3 推出后,这个规则得到了进一步的补充。

规则很清楚,无需进一步解释说明。
注意了注意了!
真的是这样吗?我们验证一下就知道了:
<style>
.floor{
width: 300px;
height: 300px;
background: #aaabc9;
}
.round{
position: absolute;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 50%;
background: #f59891;
color: #fff;
z-index: 1;
}
.right .round{
z-index: -1;
}
</style>
<div class="left">
<div class="floor">地板</div>
<div class="round">
<div class="number">桌</div>
</div>
</div>
<div class="right">
<div class="floor">地板</div>
<div class="round">
<div class="number">桌</div>
</div>
</div>
左右两边,地板都是一个普通的块级元素,桌子分别创建了 z-index 为 1 和-1 的层叠上下文,可它们并没有全部显示在地板上面,右边桌子显然被地板盖住了,这也符合上面图中负 z-index 在 block 元素下面的规则。

因此严谨地说,除了负 z-index 的层叠上下文元素,其余层叠上下文都比普通元素层叠水平高。说了这么多,到底怎么才算创建了层叠上下文元素呢,别急,我们接下来就详细说一说。
目前一共有三种方式来创建层叠上下文:
关于定位元素创建层叠上下文的方式,如果仅仅将 position 设置成了 absolute 或 relative 而没有设置 z-index 则该元素的 z-index 值为 auto ,仍然是一个普通元素,只是层叠水平高些,不会创建层叠上下文,只有设置了具体数值哪怕是 0 才行。至于 fixed 比较特殊,在现代浏览器即便没有设置 z-index 值也会自动创建层叠上下文。同一层叠上下文中, z-index 值越大,层叠水平越高。
可以创建层叠上下文的 CSS3 属性如下:
比较两个非同辈元素的层叠顺序时,要先向上找父元素或者祖先元素,如果两个元素互为兄弟节点的祖先元素中有一个创建了层叠上下文则这个元素在上,若两个祖先元素都创建了层叠上下文则按 7 层层叠顺序和 dom 流顺序判断。如果互为兄弟节点的祖先元素都没有创建层叠上下文则所有祖先元素中最先创建非负 z-index层叠上下文的元素在上。
回归开头的例子,由于左边的桌子只是设置了绝对定位,并没有创建层叠上下文,当父元素没有创建层叠上下文时,其连同子元素处于同一层叠上下文中,按照 7 层层叠顺序决定层叠水平。由于子元素 z-index 值大于 0,因此两个桌码的层叠水平最高。右边桌子都设置了 z-index 数值,各自创建了一个层叠上下文,层叠水平相同,后来者居上,层叠上下文元素的层叠水平会影响子元素,所以 2 号桌整个桌子都会覆盖在 1 号桌的上面。

