为啥具有 z-index 值的元素不能覆盖其子元素?

Posted

技术标签:

【中文标题】为啥具有 z-index 值的元素不能覆盖其子元素?【英文标题】:Why can't an element with a z-index value cover its child?为什么具有 z-index 值的元素不能覆盖其子元素? 【发布时间】:2019-07-20 17:29:39 【问题描述】:

今天经过几个小时的调试,我很难学会这条规则:

如果父元素的 z-index 为任何值,则无论您如何更改子元素的 CSS,父元素都永远无法覆盖(堆叠在其上)其子元素

我如何从逻辑上理解这种行为?规格上有吗?

.container 
  width: 600px;
  height: 600px;
  background-color: salmon;
  position: relative;
  z-index: 99;
  padding-top: 10px;


h1 
  background-color: pink;
  position: relative;
  z-index: -1;
  font-family: monospace;
<div class="container">
  <h1>1. I can never be covered by parent if my z-index is positive.</h1>
  <h1>2. Even when my z-index is nagative, I still can never be covered if my parent has any z-index at all.</h1>
</div>

【问题讨论】:

检查 this 出来,读起来会很有趣... developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/… philipwalton.com/articles/what-no-one-told-you-about-z-index @HarisGhauri 接受的答案有你需要的解释;) @LoiNguyenHuynh 是的,没错。主要技巧是避免子元素被困 在其父元素创建的堆叠上下文中。两者(父子元素)将属于同一个堆叠上下文(上层 dom 元素),它们的行为类似于兄弟元素(两者之间可以有任何绘制顺序) 【参考方案1】:

The Mozilla documentation 确实说

z-index CSS 属性设置定位元素及其后代或弹性项目的 z 顺序。

这是来自another *** article 的一些额外逻辑,与子代与后代相关。

【讨论】:

这是一本好书。以下是相关部分:...are children of DIV #3, so stacking of those elements is completely resolved within DIV#3. 感谢您的回答,但它没有回答问题的标题,抱歉我稍微编辑了问题标题,但没有编辑内容【参考方案2】:

考虑这一点的一个好方法是每个父级都包含自己的堆栈上下文。兄弟元素共享父元素的堆叠顺序,因此可能会相互重叠。

子元素总是根据其父元素获取堆叠上下文。因此,需要一个负 z-index 值来将子级推到其父级 (0) 堆叠上下文“后面”。

从其父上下文中删除元素的唯一方法是使用position: fixed,因为这实际上强制它使用窗口作为上下文。

【讨论】:

如果父母有自己的 z-index,比如 99,我如何理解父母永远无法覆盖其孩子的事实?我将问题标题编辑得更明确,但没有编辑问题内容。【参考方案3】:

我如何从逻辑上理解这种行为?

对我来说,很难用逻辑来理解你的问题。父级包含其子级。一个碗可以被另一个碗盖住。但是你不能用碗盖住汤,除非你把汤从碗里拿出来。

z-Index 设置重叠元素的顺序。父级不能重叠其子级。

恕我直言,这完全合乎逻辑。

【讨论】:

【参考方案4】:

您需要了解两件重要的事情:绘制顺序和堆叠上下文。如果您参考the specification,您可以找到绘制元素的方式和时间。

    堆叠上下文具有负 z 索引(不包括 0)按 z 索引顺序(最负值在前)然后是树顺序的定位后代形成。

    所有定位的、不透明的或变换的后代,按树形顺序分为以下几类:
      所有具有“z-index:auto”或“z-index:0”的定位后代,按树顺序排列。

    堆叠上下文z-indices 大于或等于 1 按 z-index 顺序(从小到大)然后是树顺序的定位后代形成。

从这里可以清楚地看出,我们首先在步骤 (3) 绘制带有负值 z-index 的元素,然后在步骤 (8) 绘制 z-index 等于 0 的元素,最后在步骤 (8) 绘制带有正值 z-index 的元素(9),这是合乎逻辑的。我们也可以在the specification的另一部分阅读:

每个盒子都属于一个堆叠上下文。给定堆叠上下文中的每个盒子都有一个整数堆叠级别,这是它在 z 轴上相对于同一堆叠上下文中的其他盒子的位置。具有较高堆栈级别的框总是在具有较低堆栈级别的框之前格式化。盒子可能有负的堆栈级别。在堆叠上下文中具有相同堆叠级别的框根据文档树顺序从下到上堆叠。

还有

建立本地堆叠上下文的元素会生成一个具有两个堆叠级别的框:一个用于它创建的堆叠上下文(始终为 0),另一个用于它所属的堆叠上下文(由 z-index 属性给出) .


要了解每个元素何时被绘制,您需要了解其堆叠上下文以及此堆叠上下文(由z-index 定义)内的其堆叠级别。您还需要知道该元素是否建立了堆叠上下文。这是棘手的部分,因为设置 z-index 会这样做:

对于定位框,z-index 属性指定:

    框在当前堆叠上下文中的堆叠级别。 框是否建立堆叠上下文

值的含义如下:

&lt;integer&gt;

这个整数是当前堆栈上下文中生成的盒子的堆栈级别。该框还建立了一个新的堆叠上下文

auto

生成的盒子在当前堆叠上下文中的堆叠层级为0。盒子不会建立新的堆叠上下文,除非它是根元素。


现在我们掌握了所有信息,可以更好地了解每个案例。如果父元素的z-index 值不是auto,那么它将创建一个堆叠上下文,因此子元素将被绘制在其z-index 是(负或正)的内部。子元素的z-index 将简单地告诉我们父元素内的绘画顺序(这涵盖了您的第二点)

现在,如果只有子元素有一个正的z-index 并且我们在父元素上没有设置任何内容,那么考虑到绘制顺序,子元素将在后面绘制(在步骤(9)中),父元素在步骤( 8)。上面绘制父元素的唯一合乎逻辑的方法是增加z-index,但是这样做会使我们陷入之前的情况,即父元素将建立一个堆叠上下文,子元素将属于它。

在为子元素设置 z-index 时,无法让父元素高于子元素。此外,如果我们将z-index 设置为不同于auto(正或负)的父元素,则无法将父元素置于子元素之上。1

我们可以在其父元素下方有一个子元素的唯一情况是在子元素上设置一个负数z-index并将父元素保持在z-index: auto,因此这不会创建堆叠上下文并遵循绘画顺序孩子会先被画出来。


除了z-index,还有other properties that create a stacking context。如果您遇到预期的堆叠顺序,您还需要考虑这些属性,以查看是否创建了堆叠上下文。


我们可以从上面得出的一些重要事实:

    堆叠上下文可以包含在其他堆叠上下文中,并共同创建堆叠上下文的层次结构。 每个堆叠上下文完全独立于其兄弟元素:处理堆叠时仅考虑后代元素。 每个堆叠上下文都是自包含的:在元素的内容被堆叠之后,整个元素被认为是父堆叠上下文的堆叠顺序。 ref

1:如果我们考虑使用 3D 变换,会有一些 hacky 方法。

一个元素位于其父元素之下的示例,即使该元素指定了z-index

.box 
  position:relative;
  z-index:0;
  height:80px;
  background:blue;
  transform-style: preserve-3d; /* This is important */

.box > div 
  margin:0 50px;
  height:100px;
  background:red; 
  z-index:-1; /* this will do nothing */
  transform:translateZ(-1px); /* this will do the magic */
<div class="box">
  <div></div>
</div>

另一个例子,我们可以在另一个堆叠上下文中的两个元素之间放置一个元素:

.box 
  position: relative;
  z-index: 0;
  height: 80px;
  background: blue;


.box>div 
  margin: 0 50px;
  height: 100px;
  background: red;
  z-index: 5;
  transform: translateZ(2px);


.outside 
  height: 50px;
  background: green;
  margin: -10px 40px;
  transform: translateZ(1px);


body 
  transform-style: preserve-3d;
<div class="box">
  <div></div>
</div>

<div class="outside"></div>

我们也可以有一些疯狂的堆叠顺序,如下所示:

.box 
  width: 100px;
  height: 100px;
  position: absolute;


body 
  transform-style: preserve-3d;
<div class="box" style="top:100px;left:50px;background:red;"></div>
<div class="box" style="top: 50px;left: 115px;background:blue;"></div>
<div class="box" style="top: 101px;left: 170px;background:green;"></div>
<div class="box" style="top: 175px;left: 115px;background:purple;transform: rotateY(-1deg);"></div>

我们应该注意,使用这种 hack 可能会产生一些副作用,因为transform-styleperspectivetransform 会影响position:absolute/fixed 元素。相关:CSS-Filter on parent breaks child positioning

【讨论】:

太好了,这个答案涵盖了文章中所有晦涩的边缘,以及到目前为止我读过的关于这个主题的其他答案。我无法区分术语 stack contextpositioned elementopacity 形成一个堆栈上下文...(然后它会变成一个定位元素还是什么?),当 z-index 实际影响时,bla bla,vvv现在我知道有stack level绘画顺序(这比我想象的绘画顺序要复杂得多)。阅读此答案后,我感觉我现在正在掌握堆叠上下文/绘画顺序/z-index 的东西。 请您解释一下“in tree order”吗? @aderchox 了解 html 代码的编写顺序。如果一个 div 写在另一个 div 之后,那么它在树形顺序之后。如果 div 是另一个元素的子元素,则父元素是树顺序中的第一个,依此类推 @TemaniAfif 但是这样做会让我们陷入之前的情况,即父元素将建立一个堆叠上下文,而子元素将属于它这对我提出了一个问题.那么属于创建了堆叠上下文的元素的元素是否会被绘制在它上面?这些行是否与您昨晚评论的我的问题完全相关(我的时区:))?你给 body 一个负 z-index 并给子元素一个负 z-index 然后它在父元素后面,同时保持可点击。是因为 body 变成了父堆叠上下文吗? @aderchox 您不应该同时考虑所有元素阅读绘画顺序规则。如果您检查链接,您将阅读以下内容:生成堆叠上下文的元素的后代的绘制顺序(参见 z-index 属性)是 .. 所以所有规则都适用于一个堆叠语境。如果没有将 z-index 应用于 body,这个将不会创建堆叠上下文,并且将在您的其他元素的同一个元素上,并且在逻辑上是那个元素(之前将绘制负 z-index)。

以上是关于为啥具有 z-index 值的元素不能覆盖其子元素?的主要内容,如果未能解决你的问题,请参考以下文章

为啥叠放次序不显示

DIV父容器使用了relative后,其子元素flash就被挡住了。怎么办

CSS样式中如何将下面的元素覆盖上面的元素?

更高z-index覆盖的元素上的悬停事件?

z-index详解

z-index详解