在没有 CSS 布局的年代里，web 网页都是按照元素类型（块级元素或是行内元素等等），自上而下、从左至右的顺序排列，两两之间互不重叠。原理足够简单，但是无法实现元素的层叠效果。在有了 CSS 布局之后，float、position 来了丰富的表现能力，但是同样带来了复杂的表现效果。CSS 标准中引入了层叠上下文（The stacking context）的概念，来帮助我们理解 HTML 元素发生层叠时的复杂表现。

## 层叠上下文：

如果把我们的显示器屏幕看作一个没有厚度的二维平面，我们的视线垂直穿过这个平面，产生了一条贯穿平面的轴线，这条轴线就是 Z 轴。显示器的屏幕所在那个二维平面，就称为一个层叠上下文（The stacking context）。层叠上下文是一个容器，用来存放 HTML 元素和新的层叠上下文。我们用 Z 轴距离来描述层叠上下文与 HTML 的堆叠顺序，所以 Z 轴距离是没有单位的无量纲量，没有物理上的距离意义。于此同时，每个层叠上下文都会创建一个新的 Z 轴。

简而言之，就是**没有创建自己的层叠上下文的元素，将被父层叠上下文包含。子层叠上下文，也会被父层叠上下文包含。**所有的元素和层叠上下文，都被根元素(HTML)的层叠上下文包含。

**常用**的创建层叠上下文的方式：

- 根元素 (HTML)
- z-index 值不为 "auto"的 绝对/相对定位
- z-index 值不为 "auto"的 flex item
- opacity 属性值不为 1 的元素
- transform 属性值不为 "none"的元素
- position: fixed

## 层叠顺序：

理解了层叠上下文的概念之后，就可以探讨层叠顺序了。
先来看看元素处在同一层叠上下文内的表现：

```HTML
<style>
  .demo {
    position: relative;
    padding-top: 15px;
    width: 100%;
    height: 350px;
    background: #eee;
    border: 1px solid #eee;
    font-size: 12px;
    color: #333;
  }
  .demo div {
    border: 1px dashed #000;
  }
  .demo-0 .float {
    margin: 10px;
    width: 12.5%;
    height: 300px;
    background-color: #ffc;
  }
  .demo-0 .left {
    float: left;
  }
  .demo-0 .right {
    float: right;
  }
  .demo-0 .normal {
    margin-top: 1em;
    height: 100px;
    background-color: #cff;
  }
  .demo-0 .normal + .normal {
    margin: -80px auto;
    width: 25%;
    height: 60px;
    background-color: #ccf;
  }
  .demo-0 .absolute {
    position: absolute;
    top: 115px;
    left: 6.25%;
    width: 62.5%;
    height: 150px;
    background-color: #fcf;
  }
  .demo-0 .relative {
    position: absolute;
    top: 125px;
    right: 6.25%;
    width: 62.5%;
    height: 150px;
    background-color: #ccf;
  }
</style>
<div class="demo demo-0">
  <div class="absolute">div0:绝对定位</div>
  div0 ~ div5表示元素在HTML文档中出现的顺序。
  <div class="float left">div1:向左浮动</div>
  <div class="float right">div2:向右浮动</div>
  <div class="normal">div3:普通流</div>
  <div class="normal">div4:普通流</div>
  <div class="relative">div5:相对定位</div>
</div>
```

<style>
  .demo {
    position: relative;
    padding-top: 15px;
    width: 100%;
    height: 350px;
    background: #eee;
    border: 1px solid #eee;
    font-size: 12px;
    color: #333;
  }
  .demo div {
    border: 1px dashed #000;
  }
  .demo-0 .float {
    margin: 10px;
    width: 12.5%;
    height: 300px;
    background-color: #ffc;
  }
  .demo-0 .left {
    float: left;
  }
  .demo-0 .right {
    float: right;
  }
  .demo-0 .normal {
    margin-top: 1em;
    height: 100px;
    background-color: #cff;
  }
  .demo-0 .normal + .normal {
    margin: -80px auto;
    width: 25%;
    height: 60px;
    background-color: #ccf;
  }
  .demo-0 .absolute {
    position: absolute;
    top: 115px;
    left: 6.25%;
    width: 62.5%;
    height: 150px;
    background-color: #fcf;
  }
  .demo-0 .relative {
    position: absolute;
    top: 125px;
    right: 6.25%;
    width: 62.5%;
    height: 150px;
    background-color: #ccf;
  }
</style>
<div class="demo demo-0">
  <div class="absolute">div0:绝对定位</div>
  div0 ~ div5表示元素在HTML文档中出现的顺序。
  <div class="float left">div1:向左浮动</div>
  <div class="float right">div2:向右浮动</div>
  <div class="normal">div3:普通流</div>
  <div class="normal">div4:普通流</div>
  <div class="relative">div5:相对定位</div>
</div>

通过上面的 demo，我们可以得到如下层叠顺序（从底到顶顺序）：

- 根元素的背景与边框
- 普通流里的块元素，按 HTML 中的出现顺序堆叠
- 浮动块元素
- 普通流里的后代行内元素
- 定位元素按 HTML 中的出现顺序堆叠

了解了同一层叠上下文中的层叠顺序之后，我们就可以探讨一下层叠上下文互相之间的层叠顺序。我们先来看这样一个 demo：

```HTML
<style>
  .demo-1 {
    z-index: 0;
  }
  .demo-1 .z-1 {
    position: absolute;
    z-index: -1;
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    background-color: #fcc;
  }
  .demo-1 .z1 {
    position: absolute;
    z-index: 1;
    top: 50px;
    right: 50%;
    bottom: 50px;
    left: 6.25%;
    background-color: rgba(255, 255, 204, 0.4);
  }
  .demo-1 .z0 {
    position: absolute;
    z-index: 0;
    top: 75px;
    right: 6.25%;
    bottom: 75px;
    left: 18.75%;
    background-color: #cfc;
  }
  .demo-1 .z100 {
    position: absolute;
    z-index: 100;
    top: 75px;
    right: 6.25%;
    bottom: 75px;
    left: 18.75%;
    background-color: #ccf;
  }
  .demo-1 .normal {
    margin-top: 150px;
    width: 37.5%;
    height: 100px;
    background-color: #fff;
  }
  .demo-1 .opacity {
    margin: 200px 0 -300px 12.5%;
    width: 12.5%;
    height: 100px;
    background-color: #ccf;
    opacity: 0.9;
  }
</style>
<div class="demo demo-1">
  <div class="z1">div0: z-index = 1</div>
  <div class="z0">
    div1: z-index = 0
    <div class="z100">div1.1: z-index = 100</div>
  </div>
  <div class="opacity">div2: opacity: 0.9</div>
  <div class="normal">div3:普通流</div>
  <div class="z-1">div4: z-index= -1</div>
</div>
```

<style>
  .demo-1 {
    z-index: 0;
  }
  .demo-1 .z-1 {
    position: absolute;
    z-index: -1;
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    background-color: #fcc;
  }
  .demo-1 .z1 {
    position: absolute;
    z-index: 1;
    top: 50px;
    right: 50%;
    bottom: 50px;
    left: 6.25%;
    background-color: rgba(255, 255, 204, 0.4);
  }
  .demo-1 .z0 {
    position: absolute;
    z-index: 0;
    top: 75px;
    right: 6.25%;
    bottom: 75px;
    left: 18.75%;
    background-color: #cfc;
  }
  .demo-1 .z100 {
    position: absolute;
    z-index: 100;
    top: 75px;
    right: 6.25%;
    bottom: 75px;
    left: 18.75%;
    background-color: #ccf;
  }
  .demo-1 .normal {
    margin-top: 150px;
    width: 37.5%;
    height: 100px;
    background-color: #fff;
  }
  .demo-1 .opacity {
    margin: 200px 0 -300px 12.5%;
    width: 12.5%;
    height: 100px;
    background-color: #ccf;
    opacity: 0.9;
  }
</style>
<div class="demo demo-1">
  <div class="z1">div0: z-index = 1</div>
  <div class="z0">
    div1: z-index = 0
    <div class="z100">div1.1: z-index = 100</div>
  </div>
  <div class="opacity">div2: opacity: 0.9</div>
  <div class="normal">div3:普通流</div>
  <div class="z-1">div4: z-index= -1</div>
</div>

通过上面的 demo，我们可以得到如下层叠顺序（从底到顶顺序）：

- 层叠上下文的背景与边框
- z-index < 0
- 普通流里的块元素
- z-index = 0 与 非 z-index 创建的层叠上下文
- z-index > 0

结合了层叠上下文内与层叠上下文间的层叠顺序之后，我们可以整理出这样的层叠顺序（从底到顶顺序）：

- 层叠上下文的背景与边框
- z-index < 0
- 普通流里的块元素，按 HTML 中的出现顺序堆叠
- 浮动块元素
- 普通流里的后代行内元素
- 定位元素、z-index = 0、非 z-index 创建的层叠上下文，按 HTML 中的出现顺序堆叠
- z-index > 0

## 结语：

在大多数时候（没有创建新的层叠上下文的时候），即使没有掌握 CSS 层叠上下文以及元素层叠顺序，z-index 的表现也能符合预期。我想，这大概就是 CSS 的易学难精的原因：即使你没有掌握它的原理，凭借直觉你也能够在某种程度上使用它。因此，人的惰性会让自己本能的逃避 CSS 复杂的布局原理。不知道我能不能坚持把 CSS 布局原理理解透彻呢？行远自迩，登高自卑。以此自勉！