z-index为什么会不起作用

一、引子

见下图,我们有 onetwothree 三个 div

one 嵌套了一个 nesteddiv

nestedz-index 等于 100,twoz-index 只等于 1。

1-1 引子

但是 nested 却仍然被 two 覆盖。

Why?

别着急,我们慢慢剖析。

二、正常的元素顺序

我们新建三个div

<div>one</div>
<div>two</div>
<div>three</div>

页面效果展示如下(详细代码见附录 A):

2-1 正常的元素顺序

正常的文档流中,元素次序是跟元素在文档中出现次序一致。

three 出现最后,次序最高,覆盖掉 two

以此类推,two 出现比 one 稍晚,所以覆盖住 one

三、positioned元素脱颖而出

当我们为 two 加上 position:relative 后(代码见附录B):

3-1 positioned元素

two 脱颖而出,覆盖住了 onethree。

这是因为,浏览器先绘制 non-positioned 元素,再绘制 positioned 元素。

non-positioned元素position 属性值为默认值 static,那么不为 static 的就是 positioned元素

四、都是positioned元素呢?

three 也设置为 position: relative 的话(详细代码见附录 C):

4-1 都是positioned元素

变回老样子,positioned 元素次序和在文档出现次序一致。

想要让 two 再次脱颖而出,那考虑 z-index 吧!

五、z-index 的用处

当把 two 设置为 z-index: 1 后(详细代码见附录 D):

5-1 把two的z-index设置为1

通过 z-indextwo 排到了前面。

“Z”代表 X-Y-Z 笛卡尔坐标系中的深度。

z-index 数值高的元素会排在 z-index 数字较低的元素之前。


使用 z-index 要注意两个陷阱:

1. z-index 只应用于 positioned元素

2. 隐式地创建一个 层叠上下文(stack context)。

🚀 注意, 层叠上下文和 BFC(Block Formatting Context)有所区别:

层叠上下文解决元素次序问题(一个元素是否排在另一个元素之前),而 BFC 解决文档流和一个元素是否会被重叠。

六、 层叠上下文

当你在一个 positioned 元素上应用 z-index,会隐式创建一个全新的 层叠上下文,这个元素同时成为了该 层叠上下文的根元素。

上节的 two 就是一个层叠上下文的根元素。


z-index 只控制当前层叠上下文的元素次序。

我们把 onetwo 设为position: relative,并且 z-index 都为 1

并且在 one 中内嵌一个 nesteddivnested 设置(具体代码见附录E):

position: absolute;

z-index: 100;

结果展示的就是我们开头所提到的:

6-1 z-index失效

虽然 nested 的 z-index 为 100,可是还是被 two 所覆盖。

这是因为,nestedtwo 分别属于两个不同的层叠上下文。

z-index 只控制自己当前层叠上下文的元素次序。

七、 深入层叠上下文

除了 z-index 会创建层叠上下文,opacity 的值低于 1 也会创建,还有 transformfilter 也会,全部请详见附录F。


层叠上下文中元素的次序按照以下规则:

1. 层叠上下文的根元素

⬇️

2. z-index 为负数的 positioned 元素

⬇️

3. Non-positioned 元素

⬇️

4. z-indexauto的 positioned 元素

⬇️

5. z-index为正数的 positioned 元素


非必要不创建层叠上下文 ⚠️

在上一节的例子中,我们就感受到了,多个层叠上下文,一方面会造成混乱,另一方面会使我们受挫,达不到想要的效果。

八、 消除z-index魔术字的技巧

魔术字(magic number)指使用直接的数字字面量,这个值背后所代表的意思是隐藏着的,就像魔术一样。

在之前的例子中,z-index 为 1,或者为 100,都可以算魔术字。

消除魔术字,最好的方法是用变量规范起来。


比如,我们用变量去规范程序中各个 z-index 的值:

--z-loading-indicator: 100;
--z-nav-menu: 200;
--z-dropdown-menu: 300;
--z-modal-backdrop: 400;
--z-modal-body: 410;

附录A

HTML代码

<body>
    <div class="box one">one</div>
    <div class="box two">two</div>
    <div class="box three">three</div>
</body>

CSS代码

body {
    margin: 40px;
}

.box {
    display: inline-block;
    width: 200px;
    line-height: 200px;
    text-align: center;
    border: 2px solid black;
    background-color: #ea5;
    margin-left: -60px;
    vertical-align: top;
}

.one {
    margin-left: 0;
}

.two {
    margin-top: 30px;
}

.three {
    margin-top: 60px;
}

附录B

HTML代码

<body>
    <div class="box one">one</div>
    <!-- 增加一个class名 -->
    <div class="box two positioned">two</div>
    <div class="box three">three</div>
</body>

CSS代码

body {
    margin: 40px;
}

.box {
    display: inline-block;
    width: 200px;
    line-height: 200px;
    text-align: center;
    border: 2px solid black;
    background-color: #ea5;
    margin-left: -60px;
    vertical-align: top;
}

.one {
    margin-left: 0;
}

.two {
    margin-top: 30px;
}

.three {
    margin-top: 60px;
}

/* 增加设置position值 */
.positioned {
    position: relative;
    background-color: #5ae;
}

附录C

HTML代码

<body>
    <div class="box one">one</div>
    <div class="box two positioned">two</div>
    <!-- three也设置为positioned -->
    <div class="box three positioned">three</div>
</body>

CSS代码

body {
    margin: 40px;
}

.box {
    display: inline-block;
    width: 200px;
    line-height: 200px;
    text-align: center;
    border: 2px solid black;
    background-color: #ea5;
    margin-left: -60px;
    vertical-align: top;
}

.one {
    margin-left: 0;
}

.two {
    margin-top: 30px;
}

.three {
    margin-top: 60px;
}

/* CSS其实不变 */
.positioned {
    position: relative;
    background-color: #5ae;
}

附录D

HTML代码

<body>
    <div class="box one">one</div>
    <div class="box two positioned">two</div>
    <div class="box three positioned">three</div>
</body>

CSS代码

body {
    margin: 40px;
}

.box {
    display: inline-block;
    width: 200px;
    line-height: 200px;
    text-align: center;
    border: 2px solid black;
    background-color: #ea5;
    margin-left: -60px;
    vertical-align: top;
}

.one {
    margin-left: 0;
}

.two {
    margin-top: 30px;
}

.three {
    margin-top: 60px;
}

.positioned {
    position: relative;
    background-color: #5ae;
}

/* 设置z-index */
.two{
    z-index: 1;
}

附录E

HTML代码

<body>
    <div class="box one positioned">
        one
        <div class="absolute">nested</div>
    </div>
    <div class="box two positioned">two</div>
    <div class="box three">three</div>
</body>

CSS代码

body {
    margin: 40px;
}

.box {
    display: inline-block;
    width: 200px;
    line-height: 200px;
    text-align: center;
    border: 2px solid black;
    background-color: #ea5;
    margin-left: -60px;
    vertical-align: top;
}

.one {
    margin-left: 0;
}

.two {
    margin-top: 30px;
}

.three {
    margin-top: 60px;
}

.positioned {
    position: relative;
    background-color: #5ae;
    z-index: 1;
}

.absolute {
    position: absolute;
    top: 1em;
    right: 1em;
    height: 2em;
    background-color: #fff;
    border: 2px dashed #888;
    padding: 1em;
    line-height: initial;
    z-index: 100;
}

附录F

满足以下任意条件可创建层叠上下文[2]

  • 文档根元素(<html>);
  • position属性值为absoluterelative,并且z-index不为auto的元素;
  • position属性值为fixedsticky的元素
  • flex容器的子元素,且z-index值不为auto
  • grid容器的子元素,且z-index值不为auto
  • opacity属性值小于1的元素
  • mix-blend-mode属性值不为normal的元素;
  • 以下任意属性值不为none的元素
    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
  • isolation属性值为isolate的元素
  • -webkit-overflow-scrolling属性值为touch的元素
  • 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素(参考这篇文章[3]
  • contain属性值为layoutpaint或包含它们其中之一的合成值,比如(contain: strictcontain: content)的元素

Reference

[1] CSS In Depth: 7.4 Stacking contexts and z-index

[2] 层叠上下文: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

[3] Everything You Need to Know About the CSS will-change Property: https://dev.opera.com/articles/css-will-change-property/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值