CSS——关于z-index及层叠上下文(stacking context)

CSS——关于z-index及层叠上下文(stacking context)

z-index

该部分内容根据CSS规范翻译。

z-index
'z-index'
Value: auto | <integer> | inherit
Initial: auto
Applies to: positioned elements
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified

注意:z-index属性只能应用于position属性不为static的元素,即定位元素。

对于定位元素来说,z-index属性指定:

  • 在当前层叠上下文中该定位元素盒子的堆叠层级(stack level);
  • 该定位元素盒子是否形成新的层叠上下文(stacking context)。

z-index属性可取值的含义:

  • 如果值为整数,那么这个整数指的是这个盒子(the generated box)在当前层叠上下文中的堆叠层级。同时,这个盒子对内也形成一个层叠上下文。

  • auto 如果值为auto,那么这个盒子(the generated box)在当前层叠上下文中的堆叠层级为0。同时,这个盒子对内不形成层叠上下文,除非他是根元素或者是fixed定位元素。

注意:z-index值为 0 与z-index值为auto的区别。区别是前者对内创建一个新的层叠上下文,后者不创建新的层叠上下文。除了这点区别,两者的层级是一样的。

在CSS 2.2中,每一个盒子处在三维空间中。除了水平轴和垂直轴外,还有一个z轴(z-axis),这三个轴共同决定了盒子在三维空间中的位置。
盒子在z轴上的位置与盒子之间的重叠关系尤其相关。

下面讨论盒子如何在z轴上定位。

渲染树在画布上的绘制顺序遵循层叠上下文。一个层叠上下文可以包含深一层次的层叠上下文。站在父层叠上下文的角度讲,层叠上下文是原子性的。处在一个层叠上下文中的盒子不会影响他自身所包含的盒子。

每一个盒子都处在层叠上下文中。如果在给定的层叠上下文中的给每一个定位的盒子(positioned box)指定一个整数的堆叠层级,那么这个堆叠层级就是在同一层叠上下文中的这些定位盒子在z轴上的相对位置。拥有较大堆叠层级的盒子处在拥有较小堆叠层级的盒子的前面,即距离用户更近。堆叠层级可以为负值。在同一层叠上下文中,拥有相同堆叠层级的盒子根据后来居上的规则堆叠。

产生层叠上下文的情况:

  • 根元素形成根层叠上下文(the root stacking context)。
  • 任何定位元素(position不为static的元素)且该元素的z-index属性不为auto。

随着CSS的不断发展,也会有新的CSS属性会产生层叠上下文,比如CSS 3中的opacity属性等。

层叠上下文与包含块不存在绝对的相关关系。

在每一个层叠上下文中,以下层次顺序按照后来居上的规则绘制(序号越大,堆放得越靠前,距离用户越近):

  1. 产生层叠上下文的元素的background和borders
  2. 拥有负堆叠层级(negative stack levels)的子层叠上下文(child stacking contexts)
  3. 在文档流中的(in-flow),非行内级的(non-inline-level),非定位(non-positioned)的后代元素
  4. 非定位的浮动元素
  5. 在文档流中的(in-flow),行内级的(inline-level),非定位(non-positioned)的后代元素,包括行内块级元素(inline blocks)和行内表格元素(inline tables)
  6. 堆叠层级为 0 的子堆叠上下文(child stacking contexts)和堆叠层级为 0 的定位的后代元素
  7. 堆叠层级为正的子堆叠上下文

在每一个层叠上下文中,在绘制堆叠层级为0的定位元素、非定位的浮动元素、行内块元素和行内表格元素时,除了那些参与到当前层叠上下文的定位后代元素和子层叠上下文,这些元素表现的好像对内部后代元素创建了一个新的层叠上下文一样。

上述关于层次的绘制规则递归地适用于任何层叠上下文。

这里详细描述了层叠上下文的绘制次序。

下面例子中元素盒子(使用id属性命名)的层级顺序为:"text2"=0, "image"=1, "text3"=2, and "text1"=3。
"text2"盒子的层级继承自根盒子。其他盒子的层级使用z-index属性指定。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
  <HEAD>
    <TITLE>Z-order positioning</TITLE>
    <STYLE type="text/css">
      .pile { 
        position: absolute; 
        left: 2in; 
        top: 2in; 
        width: 3in; 
        height: 3in; 
      }
    </STYLE>
  </HEAD>
  <BODY>
    <P>
      <IMG id="image" class="pile" 
           src="butterfly.png" alt="A butterfly image"
           style="z-index: 1">

    <DIV id="text1" class="pile" 
         style="z-index: 3">
      This text will overlay the butterfly image.
    </DIV>

    <DIV id="text2">
      This text will be beneath everything.
    </DIV>

    <DIV id="text3" class="pile" 
         style="z-index: 2">
      This text will underlay text1, but overlay the butterfly image
    </DIV>
  </BODY>
</HTML>

这个例子中演示了透明度(transparency)的概念。盒子的背景默认是透明的,为了使被盒子盖住的内容也能被看到。在这个例子中每一个盒子的背景都是透明的,可以通过background属性改变这种默认行为。(注意:官方给的示例代码中貌似多了一个p标签)

产生层叠上下文的条件

MDN中列举的创建新的层叠上下文的情形(满足以下任意一个条件可创建新的层叠上下文):

  • 文档根元素(<html>);
  • position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;(这一条与规范有出入)
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);
  • flex (flexbox (en-US)) 容器的子元素,且 z-index 值不为 auto;
  • grid (grid) 容器的子元素,且 z-index 值不为 auto;
  • opacity 属性值小于 1 的元素(参见 the specification for opacity);
  • mix-blend-mode 属性值不为 normal 的元素;
  • 以下任意属性值不为 none 的元素:
    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
  • isolation 属性值为 isolate 的元素;
  • -webkit-overflow-scrolling 属性值为 touch 的元素;
  • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素(参考这篇文章);
  • contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

层叠上下文的详细描述

该部分内容根据CSS规范翻译。

下面的内容描述了CSS2.2绘制顺序的更多细节。

定义

Tree Order 树顺序
处理完与盒子位置相关的属性之后,按照内容的逻辑顺序(非视觉顺序)对渲染树进行前序深度优先遍历。
Element 元素
在描述中,元素是指实际的元素、伪元素、和匿名盒子。在适当位置的伪元素和匿名盒子被当做后代元素处理。例如,在行盒的内容之前,处在:before盒子之前的外部列表标记(li标签下的:marker伪元素),等等。

绘制顺序

堆叠在底部的元素离用户最远,堆叠在顶部的元素离用户最近。

            |	   |	     |	  |
            |		|    |	  |	    ⇦ ☻
            |		|	  |	    user
z-index:  canvas  -1	0    1	  2

层叠上下文的背景和z-index为负数的子层叠上下文在底层,z-index为正数的子层叠上下文在顶层。

如果用户代理没有给画布(上面示意图中的canvas)指定颜色,那么这个画布就是透明的。这个画布包含根元素并且是无限延展的。
默认情况下,视口将画布的原点固定在画布的左上角。

对于产生层叠上下文的元素,绘制他的后代元素的逻辑顺序是:

1. 如果这个元素是根元素
    1. 元素的背景颜色覆盖整个画布
    2. 从画布的原点绘制元素的背景图片

2. 如果这个元素是块元素,列表项(list-item),或者其他与块元素等同的元素
    1. 元素的背景颜色
    2. 元素的背景图片
    3. 元素的边框
否则,如果这个元素是块级表格元素
    1. 表格的背景(先颜色后图片)
    2. 列组的背景(先颜色后图片)
    3. 列的背景(先颜色后图片)
    4. 行组的背景(先颜色后图片)
    5. 行的背景(先颜色后图片)
    6. 单元格的背景(先颜色后图片)
    7. 整个表格的边框(按照在树顺序中指定的边框)

3. z-index的值为负的定位后代元素按照z-index的值的大小顺序

4. 对于处在文档流中的, 非定位的, 块级的后代元素,按照树顺序:如果这个后代元素是块元素,列表项(list-item),或者其他与块元素等同的元素:
    1. 元素的背景颜色
    2. 元素的背景图片
    3. 元素的边框
否则,如果这个后代元素是表格元素
    1. 表格的背景(先颜色后图片)
    2. 列组的背景(先颜色后图片)
    3. 列的背景(先颜色后图片)
    4. 行组的背景(先颜色后图片)
    5. 行的背景(先颜色后图片)
    6. 单元格的背景(先颜色后图片)
    7. 整个表格的边框(按照在树顺序中指定的边框)

5. 按照树顺序,绘制所有非定位的浮动后代元素。对于每一个这样的后代元素把他们按照创建了新的层叠上下文处理(实际没有创建)。但是对于该后代元素自己的任何定位后代元素和确实创建了层叠上下文的后代元素,他们应该参与到当前层叠上下文中。

6. 如果这个元素是行内元素(这个行内元素创建了层叠上下文),那么:
    1. 对于这个行内元素所处的行盒:
        1. 跳转到下面的7.2.1
7. 否则,先绘制这个元素,然后按照树顺序绘制这个元素的所有文档流中的, 非定位的, 块级的后代元素:
    1. 如果这个元素是块级替换元素,那么绘制替换内容
    2. 否则,对于这个元素的每一个行盒
        1. 在行盒中的每一个盒子都是这个元素的子元素,按照树顺序:
            1. 盒子的背景颜色
            2. 盒子的背景图片
            3. 盒子的边框
            4. 对于行内元素
                1. 对于这个行内元素的所有的处在文档流中的、 非定位的、行内级的子元素以及这个行内元素所有的文本,按照树顺序:
                    1. 如果是文本:
                        1. 任何影响文本的下划线,按照树顺序添加下划线(如果最深的元素有下划线则绘制在最上层,如果根元素有下划线则绘制在最下层)
                        2. 任何影响文本的上划线,按照树顺序添加上划线(如果最深的元素有上划线则绘制在最上层,如果根元素有上划线则绘制在最下层)
                        3. 绘制文本
                        4. 任何影响文本的贯穿线,按照树顺序添加贯穿线(如果最深的元素有贯穿线则绘制在最上层,如果根元素有贯穿线则绘制在最下层)
                    2. 否则,跳回到7.2.1
                对于行内块和行内表格元素:
                    1. 对于每一个这样的元素,把他们当做创建了新的层叠上下文处理(实际没有创建)。但是对于该后代元素自己的任何定位后代元素和确实创建了层叠上下文的后代元素,他们应该参与到当前层叠上下文中。
                对于行内替换元素:
                    1. 绘制替换内容
            // 这些盒子有一些是换行或者Unicode双向算法产生的。
        2. 绘制元素的轮廓线(参考第10条)
    3. 如果这个元素是块级元素,绘制元素的轮廓线(参考第10条)

8. 所有z-index属性值是auto或者0的定位后代元素,按照树顺序绘制。对于z-index属性为auto的元素,把他们当做创建了新的层叠上下文一样处理(实际没有创建)。但是对于该后代元素自己的任何定位后代元素和确实创建了层叠上下文的后代元素,他们应该参与到当前层叠上下文中。对于z-index为0的元素,他们确实创建了一个新的层叠上下文。

9. 接着是z-index值大于等于1的定位后代元素(他们创建了新的层叠上下文,值越大越靠近用户)。

10. 最后,在上述步骤中没有绘制轮廓的实现(这里的实现指的是浏览器)必须在此阶段根据堆叠上下文绘制轮廓。(推荐在此阶段绘制轮廓线而不是在上面的步骤中绘制)  

说明

根元素的背景只绘制一次,覆盖整个画布。
当按照树顺序绘制双向行内元素的背景时,他们按照视觉顺序定位。
因为在CSS2.2中没有规定行内元素背景的定位,所以由浏览器自己规定最终的实现。CSS3中也许会规定更多的细节。

例子

下面看例子(没有考虑opacity),例子以根元素创建的层叠上下文为基础。

例子一

CSS代码:

    div {
        font: 12px Arial;
    }
    body{
        border:1px solid white;
    }
    span.bold { font-weight: bold; }
    .text-style{
        text-align: center;
        border:1px solid black;
    }
    #div1{
        float: left;
        width: 300px;
        height: 50px;
        background-color: pink;
        text-align: right;
    }
    #div2{
        position: absolute;
        width: 280px;
        height:120px;
        top: 50px;
        background-color: gray;
        text-align: right;
    }
    
    #div3{
        width: 450px;
        height: 300px;
        text-align: right;
        margin-top: 20px;
    }
    #div4{
        position: relative;
        width: 160px;
        height: 80px;
        z-index: 1;
        top:-300px;
        background-color: rgb(34,200,150);
    }

html代码:

<div id="div1" class="text-style">
    <p><span class="bold">DIV #1</span>
    <br />float left</p>
</div>

<div id="div2" class="text-style">
    <p><span class="bold">DIV #2</span>
    <br />position: absolute</p>
</div>

<div id="div3" class="text-style">
    <p><span class="bold">DIV #3</span>
    <br />no positioning, no float
    </p>
</div>


<div id="div4" class="text-style">
    <p><span class="bold">DIV #4</span>

    <br />position: relative
        <br /> z-index: 1
        </p>
</div>

效果:

图1

从图中可以看出,浮动元素,定位元素,常规元素,设置了z-index的元素,四种元素在同一个层叠上下文中的层叠次序一目了然。

例子二

CSS代码:

#div5{
    position: absolute;
    border: 1px solid black;
    width:350px;
    height: 300px;
    z-index: 0;
    background-color: #5555FF;
    text-align: right;
}
#div6{
    position: absolute;
    border: 1px solid black;
    width:250px;
    height: 200px;
    z-index: auto;
    background-color: #FFBB66;
    text-align: right;
}
#div7{
    position: absolute;
    border: 1px solid black;
    width:450px;
    height: 180px;
    z-index: -1;
    background-color: #66FF66;
    text-align: right;
}
#div8{
    position: relative;
    border: 1px solid black;
    width:150px;
    height: 250px;
    z-index: 1;
    background-color: #E38EFF;
}

html代码:

<div id="div5" class="text-style">
    <p><span class="bold">DIV #5</span>
    <br />position: absolute
    <br /> z-index: 0
    </p>
</div>

<div id="div6" class="text-style">
    <p><span class="bold">DIV #6</span>
    <br />position: absolute
    <br /> z-index: auto
    </p>
</div>

<div id="div7" class="text-style">
    <p><span class="bold">DIV #7</span>
    <br />position: absolute
    <br /> z-index: -1
    </p>
</div>

<div id="div8" class="text-style">
    <p><span class="bold">DIV #8</span>
    <br />position: relative
        <br /> z-index: 1
        </p>
</div>

效果:

图片2

参考资料:

  1. z-index
  2. Elaborate description of Stacking Contexts
  3. The stacking context
posted @ 2016-12-15 21:04  Fogwind  阅读(2146)  评论(0编辑  收藏  举报