6

z-index与堆叠上下文

动机

最近项目中同时使用z-index和opacity遇到一些问题,z-index值设了很大,但是不起作用。找了一些资料,重新梳理了z-index的重叠规则。

z-index

MDN中z-index的描述为:用于指定已经定位的元素(即position:relative/absolute/fixed)在文中的堆叠顺序。按照数值放置,高的在上面。
当z-index不为auto时,将产生堆叠上下文(stacking context)。下面结合几个例子说说堆叠上下文是什么。

常规堆叠顺序

没有使用z-index时,默认z-index:auto,此时>不会产生堆叠上下文(stacking context),所有元素都处在同一层,同父元素的层级

The box does not establish a new local stacking context. The stack level of the generated box in the current stacking context is the same as its parent's box.

此时堆叠顺序如下(从下到上):

  • 根元素(即HTML元素)的background和borders

  • 正常流中非定位后代元素(这些元素顺序按照HTML文档出现顺序,后面的会覆盖前面的)

  • 浮动元素

  • 正常流中已定位后代元素(这些元素顺序按照HTML文档出现顺序,后面的会覆盖前面的)

下面看看例子:


 #absdiv1 {
        position: absolute;
        width: 150px;
        height: 200px;
        top: 10px;
        right: 140px;
        border: 1px dashed #990000;
        background-color: #ffdddd;
    }

    #normdiv {
        height: 100px;
        border: 1px dashed #999966;
        background-color: #ffffcc;
        margin: 0px 10px 0px 10px;
        text-align: left;
    }

    #flodiv1 {
        margin: 0px 10px 0px 20px;
        float: left;
        width: 150px;
        height: 200px;
        border: 1px dashed #009900;
        background-color: #ccffcc;
    }

    #flodiv2 {
        margin: 0px 20px 0px 10px;
        float: right;
        width: 150px;
        height: 200px;
        border: 1px dashed #009900;
        background-color: #ccffcc;
    }

为了说明效果,div按照相反的堆叠顺序放置

<div id="absdiv1" class="opacity">
        <br /><span class="bold">DIV #1</span>
        <br />position: absolute;
    </div>

    <div id="flodiv1" class="opacity">
        <br /><span class="bold">DIV #2</span>
        <br />float: left;
    </div>

    <div id="flodiv2" class="opacity">
        <br /><span class="bold">DIV #3</span>
        <br />float: right;
    </div>

    <br />
    <div id="normdiv">
        <br /><span class="bold">DIV #4</span>
        <br />no positioning
    </div>

在没有z-index的情况下,html顺序为#1->#2->#3->#4->#5,渲染顺序为#4(正常流中非定位后代元素)->#2(浮动元素)->#3(浮动元素)->#1(正常流中已定位后代元素)->#5(正常流中已定位后代元素)。

clipboard.png

上述是没有z-index时的默认情况,渲染层就是layer 0。

z-index生成堆叠上下文改变堆叠顺序

z-index有几个关键点:

  • 只适用于已经定位的元素(即position:relative/absolute/fixed)</li>

  • 渲染顺序按照z-index大小,从低到高

  • z-index生效时将产生堆叠上下文
    可以看到,由于#1,#3,#5设置了position,z-index生效,而 #2和#4未生效,z-index:0。

clipboard.png

上面div都是同级的,下面看看在子元素中设置z-index的情况。在#3中加入子元素#6 #7,在#4中设置子元素#8。

clipboard.png

#3中的子元素#6 #7虽然设置了z-index:10,z-index:11,但仍处于#1的下面。这是因为#3的z-index 生效,生成了堆叠上下文(stacking context),而处在堆叠上下文的元素的z-index只在当前父元素下有效,子堆叠上下文被看做是父堆叠上下文中一个独立的模块,相邻的堆叠上下文完全没关系。

再来看看#4中的子元素#8,由于#4的没有设置position,因此z-index无效,还处在z-index:0中,且没有生成堆叠上下文。而子元素#8 z-index生效,将和#5,#3,#1处在同级,在#3之后,#4之前。

总结

  • 设置position且z-index不为auto,将产生堆叠上下文,堆叠规则异于常规堆叠。

  • 处在堆叠上下文的元素的z-index只在当前父元素下有效,子堆叠上下文被看做是父堆叠上下文中一个独立的模块,相邻的堆叠上下文完全没关系。意味着如果一个元素位于一个最低位置的层,那你z-index设置得再大,它也不会出现在其它层元素的上面。我们可以将产生上下文堆叠的层级按照如下规则理解:

    • #2,#4z-index未生效(未产生堆叠上下文),同父元素(html)的设置,值为默认auto,即0。

    • #1,#3,#5 z-index生效(产生堆叠上下文),分别是4,3,1。

    • #6,#7的父元素#3为3,故#6为3.10(z-index生效,产生堆叠上下文),#7为3.0(z-index未生效,未产生堆叠上下文)

    • #8父元素#2 为0,#8 z-index生效(产生堆叠上下文),为3.

产生堆叠上下文的几种情况

除了z-index+position,[MDN-stacking_context]中列举了产生堆叠上下文的几种情况。

  • HTML根文档

  • 当一个元素position为(absolute或relative),且拥有一个z-index值(不为auto)

  • 当一个元素position为fixed或sticky

  • 当一个元素是flexbox的子元素,且拥有一个z-index值(不为auto)

  • 当一个元素被设置了opacity(小于1),transforms, filters, perspective,clip-path,css-regions, paged media,mask / mask-image / mask-border,mix-blend-mode(不为normal), isolation (值为isolate),-webkit-overflow-scrolling (值为touch),will-change 等属性。

以opacity为例,我们在#4上加入opacity:0.5。此时#4生成堆叠上下文,其子元素#8最终为0.3。在#1,#3,#5之下。
clipboard.png

参考文献

https://developer.mozilla.org...
https://developer.mozilla.org...
http://www.myexception.cn/HTM...
http://blog.csdn.net/u0143463...
http://www.w3cplus.com/css/wh...
https://www.w3.org/TR/CSS2/zi...


chenluyan
389 声望22 粉丝