css之搞清楚z-index

在 CSS 中,我们都明确的知道用 z-index 可以来控制 HTML 的层级顺序。元素有着越大的指数将会排在页面的最顶部:

<style>
  .box {
    position: relative;
    width: 50px;
    height: 50px;
    border: 3px solid;
    background: silver;
  }
  .first.box {
    z-index: 2;
  }
  .second.box {
    z-index: 1;
    margin-top: -20px;
    margin-left: 20px;
  }
</style>

<div class="first box"></div>
<div class="second box"></div>

在这里插入图片描述
因为 .first.box 比 .second.box 有着更大的 z-index 的数值,所有它展示在前面。假如我们把 z-index 的声明从代码中移除的话,那么 .first.box 将会被 .second.box 遮挡了。

在这里插入图片描述
但事情往往不是这么简单。有时候大的 z-index 不是战无不胜的。让我们来看看小明同学写的这个代码到底发生了什么!

<style>
  header {
    position: relative;
    z-index: 2;
  }
  .tooltip {
    position: absolute;
    z-index: 999999;
  }
  main {
    position: relative;
    z-index: 1;
  }
</style>

<header>
  My Cool Site
</header>
<main>
  <div class="tooltip">
    A tooltip
  </div>
  <p>Some main content</p>
</main>

在这里插入图片描述
小明明明声明了.tooltip 的 z-index:999999, 是大于 header 的 z-index:2 的。 可是为什么 header 还是展示在最上层呢?
在揭秘这个神秘面纱之前,我们需要去学习一个叫做 stacking contexts 的知识点。这个只是点虽然让人费解,但却是CSS最为基础的机制。在剩下的这片文章中,我们将会了解到他们是什么?他们是怎样运作的,以及我们怎么能在代码中用他们取得便利。

本篇文章面向的读者:所有被 z-index 曾经支配过恐惧的前端开发。

层(layers)和组(groups)

如果你曾经用过类似 PS 或是 Figma 这种图文编辑软件,那么你应该熟悉层级的概念:

在这里插入图片描述
下面这张图像千层饼一样有3层不同的画布。最低层的是一张小喵咪的照片。在照片之上的是2层傻傻的细节,最后合成后就是一张长胡子的天使小猫咪了!

在PS里,我们可以组合这些层级:

在这里插入图片描述
就像文件夹一样,一个组可以让我们组合一系列的层级。组合之间的层级不能被混合在一起。所有狗狗的层级都会盖在猫咪的层级之上。

当我们导出最终组合的时候,我们一点也看不到猫咪,因为全部的层级都已经被猫咪覆盖了:

在这里插入图片描述
同样的,CSS层级的运作方式其实和 PS 差不多: 元素们都被组合成为 stacking contexts。当我们给元素一个z-index,那么这个值只会和在相同 context 下的其他元素竞争。z-index 不是全局的。
在默认的情况下, 一个简单的 HTML 文本只有一个上下文,它包括了所有的节点。 但是,我们可以添加更多的上下文!
创建上下文的方法有很多种, 但是最普遍的方法是通过结合两个声明,

position 和 z-index :
.some-element {
  position: relative;
  z-index: 1;
}

当两个声明在一起的时候,一个秘密的开关被打开了: 我们就这样创建了一个新的上下文,元素 .some-element 和它的子元素将会被划分成一个组。

让我们来重新回顾一下之前的那个例子:

<style>
  header {
    position: relative;
    z-index: 2;
  }
  .tooltip {
    position: absolute;
    z-index: 999999;
  }
  main {
    position: relative;
    z-index: 1;
  }
</style>
<header>
  My Cool Site
</header>
<main>
  <div class="tooltip">
    A tooltip
  </div>
  <p>Some main content</p>
</main>

我们可以先画出这段代码的上下文结构:
在这里插入图片描述
虽然 .tooltip 元素的 z-index 是 999999,但是那个值只在 main 标签里才能生效。这个 z-index 只能控制 .tooltip 是展示在 p 标签的上方还是下方而已。
纵观根上下文,我们可以比较 header 标签和 main 标签所在的位置。 因为 main 标签的 z-index 比 header 小。所以 main 和它的子元素都展示在 header 的下方。

修改上面的例子

所以我们该如何修改小明同学的代码来让 tooltip 展示在 header 的上方呢?其实非常的简单,我们完全不需要给 main 标签添加 z-index:

在这里插入图片描述
没了 z-index 的 main 标签不会去创建上下文。 现在我们再去看代码的上下文结构将会是这样的:

在这里插入图片描述
现在,因为 header 和 tooltip 元素在相同的上下文中,他们的 z-index 变开始较量,最后决出胜利者!

注意: 这里我们不是在讨论那些元素的父子关系。不管 tooltip 是多么复杂地被其他元素所嵌套,浏览器只关心层叠上下文。

打破规则
在上面更改代码的例子中,我们只是把 z-index 从 main 标签中移除了,因为刚刚 z-index 完全没有起什么作用。 但是如果 main 标签真的需要 z-index 来创建一个上下文呢?怎么样在不移除 z-index 的情况下来实现呢?
根据CSS的规则,很遗憾我们没有办法通过其他CSS方法达成我们要的效果:一个上下文里的元素永远都不能拿来和其他上下文比较。
幸运的是,我们还是可以通过其他方法来达成那样的效果,我们需要动一下小脑筋。
我们可以把 tooltip 移出 main 标签,挂在 body 标签下面,然后通过 CSS 定位来让 tooltip 看起来是 header 的子元素。

作者:智云健康大前端团队链接:https://juejin.cn/post/6951640002526707743来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

更多创建层叠上下文的方法

我们已经见到了如何通过结合 relative absolute 和 z-index 的方法来创建上下文,但是那不是唯一的方法!我们还可以通过这些法子:

  • 把透明度 opacity 设置成比 1 小的值
  • 把 position 设置为 fixed 或者 sticky (这种情况无需提供 z-index)
  • 把 mix-blend-mode 设置为 multiply、hard-light、difference(normal不行🙅)
  • 把 z-index 添加到一个带有 display:flex 或者 display: grid 的容器里
  • 使用 transform, filter, clip-path,或者 perpective
  • 把 will-change 设置为 opacity 或者 transform
  • 通过 isolation: isolation 直接创建上下文(很快就会讲到这个的使用!)

其他详见 MDN 实现清单

下面这个例子中我们使用了 will-change 给 main 标签创建了一个上下文,所以我们依旧得到了之前的结果:

在这里插入图片描述

z-index 的常见误知

我们已经知道了为了让 z-index 生效,我们需要去给 position 设置成 relative 和 absolute 对吧?

其实不完全是这样的,再来看看下面这段代码:

在这里插入图片描述
第二个盒子被赋值了 z-index 被展示在其他盒子的上方。但是我们看不到哪里声明了 position 对吧!
大部分情况下,z-index 只能在拥有 position 的元素生效(relative/absolute)。但是在 flexbox 布局时,flex 子元素的 z-index 即使 position 为 static 也可以生效哦~

再让我们捋一捋。。

有件奇怪的事我们可能需要再仔细琢磨一下。
在之前的PS比喻中,我们能很好的区分组的概念和层级的概念:所有可视的元素都是层级,组只是一个来帮助组合层级的容器而已。
在浏览器里,这个概念就有一些些模糊。所有使用了 z-index 的元素创建了上下文。
一般来说当我们决定使用 z-index 的时候,我们的目的仅仅是希望改变那个元素在当前父上下文的位置。我们并不希望在那个元素上创建一个上下文!我们需要在这个上面再好好考虑一下。
当一个上下文被创建了,它会把所有子元素给扁平化。所有子元素给安排的明明白白,我们本质上把他们都锁在了内部。
我们不应该把 z-index 只想象成改变元素顺序的工具,我们也应该把它当成包裹(组合)它子元素的方法。如果组没有生成的话 z-index 是不会生效的。

我们已经见到了之前的例子,上下文有时候会造成细微的,难debug的情况。假如 z-index 能在全局进行比较会不会好一些呢?
我不觉得,这是一些我的理由:

  1. 假如我们有一个很复杂的结构,我们需要给很多很多元素赋予 z-index,z-index 通货膨胀了解一下!
  2. 虽然我不是个浏览器工程师,我能想象现在的设计有助于浏览器的执行效率。没有现在的设计,浏览器需要和许多有 z-index
    的元素进行比较,感觉上会有很多额外的工作要做!
  3. 当我们理解了上下文,我们可以借此去封印一些元素。在组件驱动的框架里这是个非常强大的模式,比如 React。

原文:
https://www.joshwcomeau.com/css/stacking-contexts/
原译文:
https://juejin.cn/post/6951640002526707743


补充:

在一个层叠上下文中,一共可能出现七个层叠等级,从最低到最高排列,依次是:

  1. 背景和边框 :形成层叠上下文的元素的背景和边框,它是整个上下文中层叠等级最低的。
  2. Z-Index 为负数 :设置了 z-index 为负数的子元素以及由它所产生的层叠上下文
  3. 块级盒模型:位于正常文档流中的、块级的、非定位的子元素
  4. 浮动盒模型 :浮动的、非定位的子元素
  5. 内联盒模型 :位于正常文档流中的、内联的、非定位的子元素
  6. Z-index 为 0:设置了 z-index 为 0 的、定位的子元素以及由它所产生的层叠上下文
  7. Z-Index 为正数 :设置了 z-index 为正数的、定位的子元素以及由它所产生的层叠上下文,它是整个上下文中层叠等级最高的

在这里插入图片描述
这七个层叠等级就构成了层叠顺序的规则。符合层叠等级七的元素,会比层叠等级在一到六的元素更“贴近我们”,符合层叠等级五的元素,会比层叠等级二的元素更“贴近我们”,以此类推。

第一次学习这些层叠规则的时候,我感觉收获了很多新的东西。如果只着眼于层叠等级二、六和七(也就是涉及到 z-index 的等级),那么大部分时候,我们对于 z-index 的理解是正确的。正的 z-index 的层级比 0 要高,而 0 又比负的要高,一切都符合直觉,可能大多数人到这里就不继续往后探究了。

我之前就是这样,在看到这些规则之前,以为除了正的和负的 z-index ,其它情况都可以看作是 z-index 为0 —— 不过现在我们很清楚了,这种想法是错误的。事实是,大部分元素的层级都要低于 z-index:0。

还有一个有趣的细节是,非定位的元素实际位于四种不同的层叠等级中。乍一想觉得很奇怪,不过其实这是很合理的。假设所有的非定位元素都位于同一个层叠等级,那么我们就没办法在 div (块级盒)上看到文本(内联盒)了。

参考文章
https://blog.csdn.net/zz_jesse/article/details/113904054?utm_medium=distribute.pc_relevant.none-task-blog-2defaultOPENSEARCHdefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2defaultOPENSEARCHdefault-1.control

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值