为什么浏览器会在带有负数 z-index 的元素后面渲染 body?

Why does the browser render the body behind elements with a negative z-index?

我发现了一个奇怪的问题,当我渲染一个带有负数z-index(z-index: -1)的元素的页面并调用document.elementsFromPoint时,结果是视觉上元素负数 z-index 出现在 body 上方,但 elementsFromPoint 将元素置于 <html><body> 元素之间。

<html>
  <head></head>
  <body>
    <main>
      <h1>A title</h1>
    </main>
    <img src="foo.bar" alt="foo bar">
  </body>
</html>

如果我应用 CSS 将图像移动到标题顶部,如下所示:

body {
  background-color: #FFF;
}

img {
  position: absolute;
}

然后我将从 elementsFromPoint 得到的 <h1> 下的堆栈将是:

<img>
<h1>
<main>
<body>
<html>

现在,如果我修改 CSS 使图像位于标题下:

body {
  background-color: #FFF;
}

img {
  position: absolute;
  z-index: -1;
}

然后在视觉上,图像出现在标题下方,但仍出现在 <body> 上方。但是调用 elementsFromPoint 我现在得到:

<h1>
<main>
<body>
<img>
<html>

请注意,<img> 元素位于 <body><html> 元素之间。这是有道理的,因为 <body> 将具有 0 的默认 z-index,因此 <img> 元素应呈现在其下方。

但如果是这样,为什么浏览器会在 <body> 上方呈现图像?我已经在 Chrome 和 Firefox 中检查过了,结果是一样的。

Here is a working demo


编辑: 下面是这种奇怪效果的演示。面包片都是<body>标签的内容,另外两张图片夹在中间。

body {
  background-image: url('https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/microsoft/209/bread_1f35e.png');
  background-repeat: no-repeat;
  font-size: 110px;
  padding: 0.25em;
}

img {
  position: absolute;
  left: 10px;
  top: 10px;
  z-index: -1;
}

img + img {
  top: 25px;
  left: 25px;
}

<img src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/microsoft/209/herb_1f33f.png" alt="greens in the sandwich">
<img src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/microsoft/209/bacon_1f953.png" alt="meat between the bread">

首先,默认 z-indexauto 而不是 0。这是有区别的,因为最后一个会创建一个堆栈上下文,使它里面的元素(即使有负 z-index)总是在上面渲染。此处有更多详细信息:Why can't an element with a z-index value cover its child?

那么你面对的是background propagation,这是将背景颜色设置为body元素时的一种特殊行为,这会让你认为元素不在后面。

然后你的图片设置为 position:absolute 使其脱离流因此它不会影响 body 的高度所以即使它在后面我们仍然可以看到它。

这里有一些例子来说明不同的情况:

body {
  position: relative;
  background-color: #000;
}

img {
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  width: 100%;
}

h1 {
  margin: 0;
  background-color: rgba(255, 255, 255);
  padding: 0.5em 1em;
  text-align: center;
  font-size: 2.5rem;
}
<main>
  <h1>A polar bear</h1>
  <pre>
  </pre>
</main>
<img src="https://upload.wikimedia.org/wikipedia/commons/3/3d/Polar_Bear_AdF.jpg" alt="A Polar Bear">

在上面的背景中,背景从 body 传播到 html,图像在 body 后面渲染并且 body 高度等于 h1.

让我们通过将背景设置为 html

来禁用传播

body {
  position: relative;
  background-color: #000;
}

html {
 background:red;
}

img {
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  width: 100%;
}

h1 {
  margin: 0;
  background-color: rgba(255, 255, 255);
  padding: 0.5em 1em;
  text-align: center;
  font-size: 2.5rem;
}
<main>
  <h1>A polar bear</h1>
  <pre>
  </pre>
</main>
<img src="https://upload.wikimedia.org/wikipedia/commons/3/3d/Polar_Bear_AdF.jpg" alt="A Polar Bear">

现在更清楚了,我们有 html(红色),然后是图像,然后是 body(黑色)及其内容。您还可以看到 body 的高度受限于其 in-flow 内容。

现在让我们将 z-index:0 添加到 body:

body {
  position: relative;
  background-color: #000;
  z-index:0;
}

html {
 background:red;
}

img {
  position: absolute;
  z-index: -1;
  top: 0;
  left: 0;
  width: 100%;
}

h1 {
  margin: 0;
  background-color: rgba(255, 255, 255);
  padding: 0.5em 1em;
  text-align: center;
  font-size: 2.5rem;
}
<main>
  <h1>A polar bear</h1>
  <pre>
  </pre>
</main>
<img src="https://upload.wikimedia.org/wikipedia/commons/3/3d/Polar_Bear_AdF.jpg" alt="A Polar Bear">

body 现在正在创建一个堆栈上下文,强制在内部绘制图像,现在的顺序是 html 然后 body 然后是它的内容(图像然后是标题)