嗯~一个比较基础但容易混乱的知识点~
首先上一大段文绉绉的w3c规范以表明这是一篇严肃的学术文章(觉得太长可先跳过~):

In CSS 2.1, each box has a position in three dimensions. In addition to their horizontal and vertical positions, boxes lie along a “z-axis” and are formatted one on top of the other. Z-axis positions are particularly relevant when boxes overlap visually.
The order in which the rendering tree is painted onto the canvas is described in terms of stacking contexts. Stacking contexts can contain further stacking contexts. A stacking context is atomic from the point of view of its parent stacking context; boxes in other stacking contexts may not come between any of its boxes.
Each box belongs to one stacking context. Each positioned box in a given stacking context has an integer stack level, which is its position on the z-axis relative other stack levels within the same stacking context. Boxes with greater stack levels are always formatted in front of boxes with lower stack levels. Boxes may have negative stack levels. Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.
——W3C

其实说人话,大致意思就是:
每个盒模型的位置是三维的,除了x轴和y轴,还有一个表示层叠的z轴;
z轴上的位置决定了我们看到的盒模型之间的层叠效果(谁盖住谁)。

上述规范还解释了层叠上下文的特点以及盒模型的层叠级别,下面我们通过栗子慢慢探究。

1. 浮动与行内

eg.1-1/eg.1-2共同结构与样式:

1
2
3
4
5
<div class="div1"></div>
<div class="div2"></div>
<div class="div3"></div>
<div class="div4"></div>
<div class="div5"></div>

1
2
3
4
5
6
div{border: 1px #000 dashed;height:200px;width:300px;text-align: center;}
.div1{background-color: #FF4363;margin:0 0;}
.div2{background-color: #FC9D99;margin:-170px 20px;}
.div3{background-color: #F9CCAD;margin:-160px 40px;}
.div4{background-color: #C7C7A8;margin:-150px 60px;}
.div5{background-color: #84AF9B;margin:-140px -10px;}

eg.1-1:常规流中非定位非行内元素的层叠情况
层叠情况

结论: 常规流中非定位非行内的元素根据html顺序,按照“后来居上”的规则层叠。


eg.1-2:定位元素/行内元素/浮动元素之间的层叠关系
zindex

结论: 层叠顺序如下(高➡低):
z-index为auto的定位元素;
常规流内行内非定位元素;
非定位的浮动元素;
常规流内非行内非定位元素;
z-index为负的定位元素。

2. 为定位元素设置z-index

首先,你必须了解以下两点:

  • 每个定位元素都有一个整型的层叠级别(stack level);
  • z-index属性只对定位元素有效。

eg.2-1/eg.2-2共同结构与样式:

1
2
3
4
5
<div class="div1" style="position:relative"></div>
<div class="div2" style="position:relative"></div>
<div class="div3" style="position:relative"></div>
<div class="div4" style="position:relative"></div>
<div class="div5" style="position:relative"></div>

1
2
3
4
5
6
div{border: 1px #000 dashed;height:200px;width:300px;text-align: center;}
.div1{background-color: #FF4363;margin:0 0;}
.div2{background-color: #FC9D99;margin:-170px 20px;}
.div3{background-color: #F9CCAD;margin:-160px 40px;}
.div4{background-color: #C7C7A8;margin:-150px 60px;}
.div5{background-color: #84AF9B;margin:-140px -10px;}

eg.2-1: 5个定位元素在未设置z-index时的层叠情况
z-index

结论: z-index为auto的定位元素根据html顺序,按照“后来居上”的规则层叠。


eg.2-2: 5个定位元素设置不同z-index时的层叠情况
z-index

结论:
定位元素的层叠级别由z-index的值决定,z-index为auto则其层叠级别为0(注意:只是层级为0,其z-index值仍为auto);
同一层叠上下文中,层叠级别大的元素位于层叠级别小的元素之上;
同一层叠上下文中,层叠级别相同的元素根据html顺序决定元素的层叠关系,遵循“后来居上”原则。

3. 层叠顺序(stack order)

结合上面的例子进行总结,可得每一个层叠上下文内的层叠顺序:

Within each stacking context, the following layers are painted in back-to-front order:

  1. the background and borders of the element forming the stacking context.
  2. the child stacking contexts with negative stack levels (most negative first).
  3. the in-flow, non-inline-level, non-positioned descendants.
  4. the non-positioned floats.
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  7. the child stacking contexts with positive stack levels (least positive first).
    W3C

规范中的描述比较乏味,可结合w3help中的一张图片进行理解:

z-index

4. 层叠上下文(stacking context)

看完上述的例子,对于层叠顺序应该大致了解了。但突然冒出一个“层叠上下文”,这又是个什么鬼?

4.1 层叠上下文的特点

  1. stacking context可以嵌套
  2. 每个stacking context相对于兄弟元素是完全独立的,其内部规则不会影响到外部
  3. 每个stacking context元素都会被父stacking context当做一个元素施加stacking规则
    ——Abruzzi

4.2 层叠上下文的创建

4.2.1 css2

The root element forms the root stacking context. Other stacking contexts are generated by any positioned element (including relatively positioned elements) having a computed value of ‘z-index’ other than ‘auto’. Stacking contexts are not necessarily related to containing blocks.
——W3C

CSS2中规定创建层叠上下文的两种情况:
根元素(html);
定位元素(absolute/relative)且z-index的值不为auto。

注:在同一层叠上下文中,父元素、子元素与自身都被当作是并级关系进行层叠级别的比较。他们之间可能互相层叠。


eg.4-1:z-index为auto的定位元素没有创建层叠上下文

1
2
3
4
<div class="div1" style="position:relative">
<div class="div1_1" style="position:relative;z-index:9"></div>
</div>
<div class="div2" style="position:relative"></div>

1
2
3
4
div{border: 1px #000 dashed;height:200px;width:400px;text-align: center;}
.div1{background-color: #FF4363;}
.div1_1{background-color: #FC9D99;margin:100px 0 0 50px;width: 300px;}
.div2{background-color: #F9CCAD;line-height: 300px;}

z-index
分析上述例子:

  • 前提:div1、div1_1、div2都是定位元素;div1、div2都没有设置z-index值。
  • 假设:div1与div2创建了层叠上下文。不同层叠上下文之间是相对独立的,div1_1属于div1所创建的层叠上下文,与div2所创建的层叠上下文无关;根据“后来居上”原则,则div2应该在div1及其子元素div1_1之上。
  • 由上述例子可得:div1的子元素div1_1在div2之上。
  • 因此:div1、div2没有创建层叠上下文。

结论: z-index为auto的定位元素不会创建新的层叠上下文。


IE中的BUG:
在IE6-7浏览器中测试eg.4-1:
zindex

结论: ie6-7中,z-index为auto的定位元素也会创建新的层叠上下文。

4.2.2 css3

A stacking context is formed, anywhere in the document, by any element which is either

  • the root element (HTML),
  • positioned (absolutely or relatively) with a z-index value other than “auto”,
  • a flex item with a z-index value other than “auto”,that is the parent element display: flex|inline-flex,
  • elements with an opacity value less than 1. (See the specification for opacity),
  • elements with a transform value other than “none”,
  • elements with a mix-blend-mode value other than “normal”,
  • elements with a filter value other than “none”,
  • elements with isolation set to “isolate”,
  • position: fixed
  • specifying any attribute above in will-change even if you don’t specify values for these attributes directly (See this post)
  • elements with -webkit-overflow-scrolling set to “touch”
    ——MDN

CSS3中规定创建层叠上下文的十种情况:

  • 根元素 (HTML)
  • 绝对(absolute)定位或相对(relative)定位且 z-index 值不为”auto”
  • 一个 flex 项目(flex item),且 z-index 值不为 “auto”,也就是父元素 display: flex|inline-flex
  • 元素的 opacity 属性值小于 1(参考 the specification for opacity)
  • 元素的 transform 属性值不为 “none”
  • 元素的 mix-blend-mode 属性值不为 “normal”
  • 元素的 isolation 属性被设置为 “isolate”
  • 在 mobile WebKit 和 Chrome 22+ 内核的浏览器中,position: fixed 总是创建一个新的层叠上下文, 即使 z-index 的值是 “auto”
  • 在 will-change 中指定了任意 CSS 属性,即便你没有定义该元素的这些属性
  • 元素的 -webkit-overflow-scrolling 属性被设置 “touch”

eg.4-2:opacity创建新的层叠上下文
z-index
分析:

If an element with opacity less than 1 is not positioned, implementations must paint the layer it creates, within its parent stacking context, at the same stacking order that would be used if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’.
If an element with opacity less than 1 is positioned, the ‘z-index’ property applies as described in [CSS21], except that ‘auto’ is treated as ‘0’ since a new stacking context is always created.
——W3C

结论:
如果元素设置opacity小于1但未定位,视为css2中元素定位/z-index:0且opacity:1的情况;
如果元素设置opacity小于1且定位,z-index为auto,视为css2中元素定位且z-index:0的情况;
如果元素设置opacity小于1且定位,z-index不为auto,则根据css2的描述层叠。

(其他CSS3创建层叠上下文的元素情况与opacity相同,故不赘述。)


至此,应该能够明白:

  • 根元素(html)会创建一个新的层叠上下文;
  • 上述CSS2/CSS3中的元素会创建新的层叠上下文;
  • 在每一个层叠上下文中都遵循上文中提到的层叠顺序;
  • 每一个层叠上下文(除根元素)在它的父层叠上下文中都以一个普通元素来对待,其内部的层叠规则不影响其本身在父层叠上下文(即其所属层叠上下文)中的层叠顺序。

4.3 层叠上下文的层级

  • z-index值为auto的层叠上下文(z-index未设置的定位元素)层级为0;
  • z-index值为0(根元素/z-index为0的定位元素/css3中提到的除根元素与定位元素之外的其他会创建层叠上下文的未定位或已定位无设置z-index的元素)的层叠上下文层级为0;
  • z-index值已设置且不为0的层叠上下文层级(z-index已设置且不为0的定位元素)与z-index的值相等;
  • 相同层级的层叠上下文遵循“后来居上”的规则层叠。

5. 小结

  • 属于同一层叠上下文的元素(无论它们在结构上是兄弟或父子,甚至是祖孙),都遵循上文所述的层叠顺序;
  • 不同的层叠上下文之间,层级较大的层叠上下文元素及其内部元素位于层级小的层叠上下文元素及其内部元素之上;
  • 不同的层叠上下文之间互相独立;
  • 层叠上下文可嵌套,内部规则不影响外部。
感谢您的阅读,本文由 凹凸实验室 版权所有。如若转载,请注明出处:凹凸实验室(https://aotu.io/notes/2015/11/08/css-stack-order/