随笔 - 11  文章 - 2  评论 - 1

深入理解CSS中的层叠上下文和层叠顺序

 

本文地址:http://www.zhangxinxu.com/wordpress/?p=5115

 

 

CSS3与新时代的层叠上下文
CSS3的出现除了带来了新属性,同时还对过去的很多规则发出了挑战。例如,CSS3 transform对overflow隐藏对position:fixed定位的影响等。而这里,层叠上下文这一块的影响要更加广泛与显著。

如下:

    1. z-index值不为autoflex项(父元素display:flex|inline-flex).
    2. 元素的opacity值不是1.
    3. 元素的transform值不是none.
    4. 元素mix-blend-mode值不是normal.
    5. 元素的filter值不是none.
    6. 元素的isolation值是isolate.
    7. will-change指定的属性值为上面任意一个。
    8. 元素的-webkit-overflow-scrolling设为touch.

      基本上每一项都有很多槽点。

      1. display:flex|inline-flex与层叠上下文
      注意,这里的规则有些负责复杂。要满足两个条件才能形成层叠上下文:条件1是父级需要是display:flex或者display:inline-flex水平,条件2是子元素的z-index不是auto,必须是数值。此时,这个子元素为层叠上下文元素,没错,注意了,是子元素,不是flex父级元素。

      眼见为实,给大家上例子吧。

      如下HTML和CSS代码:

      <div class="box">
          <div>
          	<img src="mm1.jpg">
          </div>
      </div>
      .box {  }
      .box > div { background-color: blue; z-index: 1; }    /* 此时该div是普通元素,z-index无效 */
      .box > div > img { 
        position: relative; z-index: -1; right: -150px;     /* 注意这里是负值z-index */
      }

      结果如下:

      会发现,妹子跑到蓝色背景的下面了。为什么呢?层叠顺序图可以找到答案,如下:
      负值z-index的层叠顺序

      从上图可以看出负值z-index的层叠顺序在block水平元素的下面,而蓝色背景div元素是个普通元素,因此,妹子直接穿越过去,在蓝色背景后面的显示了。

      现在,我们CSS微调下,增加display:flex, 如下:

      .box { display: flex; }
      .box > div { background-color: blue; z-index: 1; }    /* 此时该div是层叠上下文元素,同时z-index生效 */
      .box > div > img { 
        position: relative; z-index: -1; right: -150px;     /* 注意这里是负值z-index */
      }

      结果:

      会发现,妹子在蓝色背景上面显示了,为什么呢?层叠顺序图可以找到答案,如下:

      从上图可以看出负值z-index的层叠顺序在当前第一个父层叠上下文元素的上面,而此时,那个z-index值为1的蓝色背景<div>的父元素的display值是flex,一下子升官发财变成层叠上下文元素了,于是,图片在蓝色背景上面显示了。这个现象也证实了层叠上下文元素是flex子元素,而不是flex容器元素。

      另外,另外,这个例子也颠覆了我们传统的对z-index的理解。在CSS2.1时代,z-index属性必须和定位元素一起使用才有作用,但是,在CSS3的世界里,非定位元素也能和z-index愉快地搞基。

      2. opacity与层叠上下文
      我们直接看代码,原理和上面例子一样,就不解释了。

      如下HTML和CSS代码:

      <div class="box">
          <img src="mm1.jpg">
      </div>
      .box { background-color: blue;  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      然后价格透明度,例如50%透明:

      .box { background-color: blue; opacity: 0.5;  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      原因就是半透明元素具有层叠上下文,妹子图片的z-index:-1无法穿透,于是,在蓝色背景上面乖乖显示了。

      3. transform与层叠上下文
      应用了transform变换的元素同样具有菜单上下文。

      我们直接看应用后的结果,如下CSS代码:

      .box { background-color: blue; transform: rotate(15deg);  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      妹子同样在蓝色背景之上。

      4. mix-blend-mode与层叠上下文
      mix-blend-mode类似于PS中的混合模式,之前专门有文章介绍-“CSS3混合模式mix-blend-mode简介”。

      元素和白色背景混合。无论哪种模式,要么全白,要么没有任何变化。为了让大家有直观感受,因此,下面例子我特意加了个原创平铺背景:

      .box { background-color: blue; mix-blend-mode: darken;  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      需要注意的是,目前,IE浏览器(包括IE14)还不支持mix-blend-mode,因此,要想看到妹子在背景色之上,请使用Chrome或FireFox。

      同样的,因为蓝色背景元素升级成了层叠上下文,因此,z-index:-1无法穿透,在蓝色背景上显示了。

      5. filter与层叠上下文
      此处说的filter是CSS3中规范的滤镜,不是旧IE时代私有的那些,虽然目的类似。同样的,我之前有提过,例如图片的灰度或者图片的毛玻璃效果等。

      我们使用常见的模糊效果示意下:

      .box { background-color: blue; filter: blur(5px);  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      好吧,果然被你猜对了,妹子蓝色床上躺着,只是你眼镜摘了,看得有些不够真切罢了。

      6. isolation:isolate与层叠上下文
      isolation:isolate这个声明是mix-blend-mode应运而生的。默认情况下,mix-blend-mode会混合z轴所有层叠在下面的元素,要是我们不希望某个层叠的元素参与混合怎么办呢?就是使用isolation:isolate。由于一言难尽,我特意为此写了篇文章:“理解CSS3 isolation: isolate的表现和作用”,解释了其阻隔混合模式的原理,建议大家看下。

      要演示这个效果,我需要重新设计下,如下HTML结构:

      <img src="img/mm2.jpg" class="mode">
      <div class="box">
          <img src="mm1.jpg">
      </div>

      CSS主要代码如下:

      .mode {
        /* 竖妹子绝对定位,同时混合模式 */
        position: absolute; mix-blend-mode: darken;
      }    
      .box {
        background: blue;         
      }
      .box > img { 
        position: relative; z-index: -1;
      }

      结构如下:

      会发现,横妹子被混合模式了。此时,我们给妹子所在容器增加isolation:isolate,如下CSS所示:

      .mode {
        /* 竖妹子绝对定位,同时混合模式 */
        position: absolute; mix-blend-mode: darken;
      }    
      .box {
        background: blue; isolation:isolate;         
      }
      .box > img { 
        position: relative; z-index: -1;
      }

      结果为:

      会发现横着的妹子跑到蓝色背景上面了。这表明确实创建了层叠上下文。

      7. will-change与层叠上下文
      关于will-change,如果有同学还不了解,可以参见我之前写的文章:“使用CSS3 will-change提高页面滚动、动画等渲染性能”。

      都是类似的演示代码:

      .box { background-color: blue; will-change: transform;  }
      .box > img { 
        position: relative; z-index: -1; right: -150px;
      }

      结果如下:

      果然不出所料,妹子上了蓝色的背景。

      七、层叠上下文与层叠顺序

      本文多次提到,一旦普通元素具有了层叠上下文,其层叠顺序就会变高。那它的层叠顺序究竟在哪个位置呢?

      这里需要分两种情况讨论:

      1. 如果层叠上下文元素不依赖z-index数值,则其层叠顺序是z-index:auto可看成z:index:0级别;
      2. 如果层叠上下文元素依赖z-index数值,则其层叠顺序由z-index值决定。

      于是乎,我们上面提供的层叠顺序表,实际上还是缺少其他重要信息。我又花功夫重新绘制了一个更完整的7阶层叠顺序图(同样的版权所有,商业请购买,可得无水印大图):

      更完整的7阶层叠顺序图

      大家知道为什么定位元素会层叠在普通元素的上面吗?

      其根本原因就在于,元素一旦成为定位元素,其z-index就会自动生效,此时其z-index就是默认的auto,也就是0级别,根据上面的层叠顺序表,就会覆盖inlineblockfloat元素。

      而不支持z-index的层叠上下文元素天然z-index:auto级别,也就意味着,层叠上下文元素和定位元素是一个层叠顺序的,于是当他们发生层叠的时候,遵循的是“后来居上”准则。

      我们可以速度测试下:

      <img src="mm1" style="position:relative">
      <img src="mm2" style="transform:scale(1);">
      <img src="mm2" style="transform:scale(1);">
      <img src="mm1" style="position:relative">


      会发现,两者样式一模一样,仅仅是在DOM流中的位置不一样,导致他们的层叠表现不一样,后面的妹子趴在了前面妹子的身上。这也说明了,层叠上下文元素的层叠顺序就是z-index:auto级别。

      z-index值与层叠顺序
      如果元素支持z-index值,则层叠顺序就要好理解些了,比较数值大小嘛,小盆友都会,本质上是应用的“谁大谁上”的准则。在以前,我们只需要关心定位元素的z-index就好,但是,在CSS3时代,flex子项也支持z-index,使得我们面对的情况比以前要负复杂。然而,好的是,规则都是一样的,对于z-index的使用和表现也是如此,套用上面的7阶层叠顺序表就可以了。

      同样,举个简单例子,看下z-index:-1z-index:1变化对层叠表现的影响,如下两段HTML:

      <div style="display:flex; background:blue;">
         <img src="mm1.jpg" style="z-index:-1;">
      </div>
      <div style="display:flex; background:blue;">
         <img src="mm1.jpg" style="z-index:1;">
      </div>

      最后,会发现,z-index:-1跑到了背景色小面,而z-index:1高高在上。

      一个与层叠上下文相关的有趣的显示现象
      在实际项目中,我们可能会渐进使用CSS3的fadeIn淡入animation效果增强体验,于是,我们可能就会遇到类似下面的现象:

      您可以狠狠地点击这里:CSS3 fadeIn淡入animation动画有趣现象

      有一个绝对定位的黑色半透明层覆盖在图片上,默认显示是这样的:
      文字在妹子上

      但是,一旦图片开始走fadeIn淡出的CSS3动画,文字跑到图片后面去了
      文字跑到图片后面

      为什么会这样?

      实际上,学了本文的内容,就很简单了!fadeIn动画本质是opacity透明度的变化:

      @keyframes fadeIn {
        0% { 
          opacity: 0;
        }
        100% {
          opacity: 1;
        }
      }

      要知道,opacity的值不是1的时候,是具有层叠上下文的,层叠顺序是z-index:auto级别,跟没有z-index值的absolute绝对定位元素是平起平坐的。而本demo中的文字元素在图片元素的前面,于是,当CSS3动画只要不是最终一瞬间的opacity: 1,位于DOM流后面的图片就会遵循“后来居上”准则,覆盖文字。

      这就是原因,于是,我们想要解决这个问题就很简单。

      1. 调整DOM流的先后顺序;
      2. 提高文字的层叠顺序,例如,设置z-index:1;

 

posted @ 2016-01-26 12:08  Double405  阅读(945)  评论(0编辑  收藏  举报