美文网首页
CSS 中的层叠上下文

CSS 中的层叠上下文

作者: 永恒君_9b95 | 来源:发表于2017-12-07 13:21 被阅读0次

    在使用 bootstrap 的模态框时,出现了半透明遮罩始终盖住模态框的情况,
    只有把模态框的 HTML 写到 body 之下才恢复正常,
    当时郁闷得对着屏幕举了半分钟的中指。(程序员最好看看佛经,学会清心寡欲)

    后来有幸看到了张鑫旭在慕课网 CSS深入理解之 relative 的视频,
    才算真正知晓了层叠上下文在 CSS 中的规则和运用技巧。

    还有些名称比较类似的学术语言和概念,格式化上下文、执行上下文、音频上下文、2D上下文等,感兴趣地可以去搜一下玩玩。

    废话有些多,下面就正式开始吧!

    什么是层叠上下文和层叠水平

    层叠上下文其实把它理解为 z 轴也没有问题,屏幕上面叠着一层层图层(不是所有的元素都是)。

    层叠水平则表示同一层层叠上下文对应的图层。

    创建层叠上下文

    那么怎样才能产生层叠上下文,让它的 z 轴不一样呢?
    以下情况会创建层叠上下文(随着 CSS3 属性还在增加,本表不全):

    • z-index 不为 auto 的 position 定位元素
    • z-index 不为 auto 的 flex
    • opacity 不为 1
    • transform 不为 none
    • perspective 不为 none
    • filter 不为 none
    • mix-blend-mode 不为 normal
    • 带有 isolation: isolate
    • will-change 不为 none
    • 带有 -webkit-overflow-scrolling: touch
    <style>
    .box {
      width: 100px;
      height: 100px;
      margin: 0 auto -50px;
    }
    .box1 {background: pink}
    .box2 {background: grey}
    .box1 {left: 10px}
    
    .box1 { transform: translate(0,0) }
    </style>
    
    <div class="box box1"></div> <!-- 粉色 -->
    <div class="box box2"></div> <!-- 灰色 -->
    
    20171122221904

    此例中 .box1 创建了层叠上下文,z 轴上就比普通元素层级更高了,因此覆盖了 .box2。

    同级层叠水平下的层叠顺序

    而当有两个层叠上下文时,它们就有了兄弟和父子关系(假设现在都处于同级层叠水平)。

    当为兄弟关系时(层叠上下文的兄弟关系,非 DOM 的兄弟),它们总是 后者居上 的。

    <style>
    .box {
      width: 100px;
      height: 100px;
      margin: 0 auto -50px;
    }
    .item {
      /* 两个 dom 中子级创建层叠上下文,构成兄弟关系的层叠水平 */
      transform: translate(0,0);
      width: 90px;
      height: 90px;
      top: -10px;
    }
    .box1 {background: pink}
    .box2 {background: grey}
    .item1 {background: red}
    .item2 {background: green}
    .item1 {left: -10px;}
    .item2 {left: -20px;}
    </style>
    
    <div class="box box1"> <!-- 粉色 -->
      <div class="item item1">item1</div> <!-- 红色 -->
    </div>
    <div class="box box2"> <!-- 灰色 -->
      <div class="item item2">item2</div> <!-- 绿色 -->
    </div>
    
    <!-- .item 变为层叠上下文,所以比 .box 这种普通元素层级更高. -->
    <!-- 且 .item 遵循后者居上,所以 .item2 覆盖了 .item1 -->
    
    20171122162647

    当为父子关系时,子级就存在父级的“作用域”内了,父级高我才高,不然我再高也没用。

    <style>
    .box { position: relative; z-index: 0 }
    .item1 { z-index: 999; }
    /* 紧跟上例,本来 .item2 由谁后谁上的原则位于上面。 */
    /* 当现在其父级 .box 也创建的层叠上下文,且遵循谁后谁上,因此 .box2 要高于 .box1。 */
    /* 此处 .item1 的父级已经低于 .box2 了,本身层级再高也还是在 .box1 的范围内而已。 */
    </style>
    
    20171122164912

    注:此处 position: relative; 但不写 z-index: 0(即 z-index: auto),它会覆盖普通元素,但并不会创建层叠上下文。 下面这个也是类似的例子,

    .box { position: relative; margin: 0 0 10px }
    .item {
      position: relative;
      z-index: -1;
    }
    .box2 { z-index: 0; }
    / 只有 z-index 不为 auto 才创建层叠上下文 /
    
    20171123134429

    不同级层叠水平下的层叠顺序

    既 z-index 不为 auto 的情况,这时它们总是 谁大谁上 的。

    <style>
    .box1 { z-index: 2; }
    /* 这个就很好理解了,.box 现在是兄弟关系,.box1 的 z-index 更大,那么它就更高 */
    </style>
    
    20171122173308

    关于最初 BUG 的解读

    回到最初那个 bootstrap 内容框在黑底下的问题,就很方便理解了。

    <div class="content">
      <div class="modal"></div> <!-- modal 内容框 -->
    </div>
    <div class="modal-backdrop"></div> <!-- 黑色半透明底 -->
    

    .modal 和 .modal-backdrop 都定位了,成为了层叠上下文,而如果 .content 由于需要或误操作也创建了层叠上下文,那么 .content 和 .modal 就有了兄弟关系,谁后谁上+谁大谁上,最终造成黑底的层级高于了 .content 的层级,而 .modal 作为 .content 范围内子级也就因此被覆盖住了。

    最终,要么我们把 .modal 从 .content 拿出放置于与 .modal-backdrop 同级,要么把 .content 的层级提高得比 .modal-backdrop 更大(如果 .content 还有背景色那就是另一回事了)。

    所以个人认为 bootstrap 这样写是不好的,要遇上一个不懂层叠上下文的,想破头也不知道为什么。
    我推荐下面的这种写法:(自创的,后来看 layui 等框架也是这样写的,看来我没做错)

    <style>
    .modal {
      position: absolute;
      top: 0; bottom: 0;
      left: 0; right: 0;
      opacity: 0;
      z-index: -1;
      overflow: hidden;
      transition: opacity .3s, z-index 0s .3s;
    }
    .modal-bg {
      position: absolute;
      top: 0; bottom: 0;
      left: 0; right: 0;
      opacity: 0;
      transition: opacity .15s;
      background: rgba(0,0,0,.8);
    }
    .modal.in {
      opacity: 1;
      z-index: 1;
      transition: opacity .3s, z-index 0s;
    }
    .modal.in .modal-bg {
      opacity: 1;
    }
    .modal-box {
      transform: translate3d(0,0,0);
    }
    </style>
    
    <div class="modal">
      <div class="modal-bg bg"></div>  <!-- 黑底 -->
      <div class="modal-box">内容</div>
    </div>
    

    就像张鑫旭大神所说,如果你理解了层叠上下文,那么就根本不需要把 z-index 设得很大了,1-2 足矣。

    非层叠上下文的覆盖关系

    在张大神的 此文 中存在着这样一张图,一直让我产生着奇怪的误解。

    zxx

    后来才想通,这是非层叠上下文时的覆盖关系,但很容易让我们产生混淆。

    比如 float / inline 等,和 relative 下 z-index 非 auto 一样,都是会产生覆盖,但不会创建层叠上下文。

    里面的 background 和 z-index 真的实在是蛊惑人心,个人认为大可忽略不论。

    相关文章

      网友评论

          本文标题:CSS 中的层叠上下文

          本文链接:https://www.haomeiwen.com/subject/gvctmxtx.html