当 z-index 不符合直觉时

369 阅读3分钟

你是否碰到过这种情况:两个 div 都是 fixed 定位,但 z-index 值小的反而可以盖在 z-index 值大的上面。

position:fixed 和 z-index

首先明确以下两个属性:
1、position:fixed; 根据 MDN 文档上的说法

fixed元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform, perspectivefilter 属性非 none 时,容器由视口改为该祖先。

2、z-index 只在定位元素上有效,即 position 的值不为 static 时,而且 z-index 的值越大,元素的位置越靠前。

参考链接:developer.mozilla.org/zh-CN/docs/…

所以当我们在一个 div 上使用 position:fixed 时,只要把 z-index 的值设置得越大,显示出来时就越靠前(不被遮挡)。

以上就是我在此之前的理解。

不一样的情况出现了

今天碰到了这种情况,两个 div 都是 fixed 定位,但是一个 z-index:20; 的元素,把一个 z-index:1001; 的元素给挡住了。

有如下代码:


    <div class="a">
      <div class="b">
        b ---- z-index: 20;
        <div class="c">c ---- z-index: 1001;</div>
      </div>
    </div>

    <div class="d">d ---- z-index: 21;</div>
    
    <style>
      .a {
        width: 100vw;
        height: 100vh;
      }

      .b {
        position: relative;
        z-index: 20;
        width: 100%;
        height: 70%;
        background-color: antiquewhite;
      }

      .c {
        position: fixed;
        z-index: 1001;
        left: 30%;
        top: 30%;
        width: 30%;
        height: 30%;
        background-color: aquamarine;
      }

      .d {
        position: fixed;
        z-index: 21;
        width: 30%;
        height: 30%;
        left: 40%;
        top: 40%;
        background-color: red;
      }
    </style>

效果:
a 包含 b,b 包含 c,而 d 与 a 同级,c 和 d 都设置为 position: fixed; 其中 c 的 z-index 为1001,而 d 的 z-index 为21,原本期望的结果是 c 在 d 的上面,如下图所示:

image.png

实际运行出来的结果是 d 在 c 的上面。

image.png

分析原因

经过分析,问题出在 b 上面:
在 b 上面设置了属性 position: relative;z-index: 20; ,当设置了这两个属性之后,在 b 上面就会创建一个层叠上下文,而 c 又是 b 的子元素,所以即使在 c 上面设置 z-index:1001;,它也只是在 b 以内有效,超出 b 元素范围后,c 的 z-index 值是没有意义的;
b 元素的 z-index 值为20,它的参考对象是根元素 <html>,d 的 z-index 值为21,参考对象也是根元素 <html>,所以 d 会覆盖在 b 上面,而 c 又是 b 的子元素,无法突破 b,所以 c 也只能被 d 覆盖。

层叠上下文

产生上面这个问题的根本原因就是层叠上下文,所以弄清楚层叠上下文才是关键,请先仔细阅读 MDN 上的文档:层叠上下文

在我们使用 z-index 属性时,一定要考虑层叠上下文,要清楚当前元素设置 z-index 之后,究竟是在哪个层叠上下文中起作用的,换句话说就是以哪个元素作参考的。

产生层叠上下文的条件有十几种,文档中的元素只要满足其中一个,就会在当前元素(起名叫 father)上形成层叠上下文,而 father 元素的所有子元素,如果设置了 z-index ,它们在渲染时,参考的就是 father 元素,谁的 z-index 值大,谁就在前面。
但即使给这些子元素设置的 z-index 值再高,当超出 father 元素之后,他们的 z-index 就没有用了,因为这些子元素和其它元素已经不在一个层级上了。至于能不能显示在前面,就要看 father 元素的 z-index 了。

搞清楚层叠上下文,在安排元素显示层级时特别有用,还能避免一起意料之外的情况发生,你就能明白,为什么 Element-UI 中有些组件,比如 Dialog、Drawer 这类弹框组件,会有一个 append-to-body 属性来解决层级问题。