层叠上下文和z-index

渲染过程和层叠顺序

浏览器将HTML解析为DOM的同时还创建了另一个树形结构,渲染树(render tree)。渲染树代表了每个元素的视觉样式和位置,同时决定浏览器绘制元素的顺序。

  • 通常情况下(没有使用定位),元素在HTML里出现的顺序决定了绘制顺序。后绘制的元素会出现在先绘制的元素前面。
  • 定位元素时,这种行为会改变。浏览器会先绘制所有非定位的元素,然后绘制定位元素。默认情况下,所有的定位元素会出现在非定位元素前面。

使用z-index控制层叠顺序

z-index属性的值可以是任意整数(正负都行)。z表示的是笛卡儿x-y-z坐标系里的深度方向。拥有较高z-index的元素出现在拥有较低z-index的元素前面。拥有负数z-index的元素出现在静态元素后面。

z-index的行为很好理解,但是使用它时要注意两个小陷阱。

  • z-index只在定位元素上生效,不能用它控制静态元素。
  • 给一个定位元素加上z-index可以创建层叠上下文。

层叠上下文

一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素。其中一个元素会作为层叠上下文的根,比如给一个定位元素加上z-index的时候,它就变成了一个新的层叠上下文的根。所有后代元素就是这个层叠上下文的一部分。

所有层叠上下文内的元素会按照以下顺序,从后到前叠放:

  • 层叠上下文的根
  • z-index为负的定位元素(及其子元素)
  • 非定位元素
  • z-indexauto的定位元素(及其子元素)
  • z-index为正的定位元素(及其子元素)

举个例子

下面这个例子可以用来理解层叠上下文,nested在第一个盒子的层叠上下文中,就算设置了很高的z-index,也会被第二个盒子遮挡。因为第一个盒子形成的层叠上下文在第二个盒子后面。

<body>
  <div class="box one positioned">
    one
    <div class="absolute">nested</div>
  </div>
  <div class="box two positioned">two
  </div>

</body>
body {
  margin: 40px;
}

.box {
  display: inline-block;
  width: 200px;
  line-height: 200px;
  text-align: center;
  border: 2px solid black;
  background-color: #ea5;
  margin-left: -60px;
  vertical-align: top;
}

.one { margin-left: 0; }
.two { margin-top: 30px; }


.positioned {        (以下5行)每个定位的盒子都创建了一个层叠上下文,z-index为1
  position: relative;               
  background-color: #5ae;           
  z-index: 1;                      
}                                  

.absolute {
  position: absolute;
  top: 1em;
  right: 1em;
  height: 2em;
  background-color: #fff;
  border: 2px dashed #888;              
  z-index: 100;       ←---- z-index只控制元素在它所处层叠上下文内的层叠顺序
  line-height: initial;
  padding: 1em;
}

表现结果

扩展到实际应用中的场景是,在已经打开的弹窗中再打开一个弹窗,就算第一个弹窗中的某个元素设置了很高的z-index,也还是会被第二个(后绘制)的弹窗挡住。