当重叠时,为什么相邻元素的背景不包含内容?

Tem*_*fif 24 html css background overlap

情况如下:

body {
  margin: 0;
  background: pink;
  color: #fff;
}

.box {
  margin-top: 20px;
  background: red;
}

.bottom {
  text-align: right;
  background: green;
  animation: animate 2s infinite alternate linear;
}

@keyframes animate {
  from {
    margin-top: 10px;
  }
  to {
    margin-top: -40px;
  }
}
Run Code Online (Sandbox Code Playgroud)
<div class="box">
  some content
</div>
<div class="bottom">
  other content
</div>
Run Code Online (Sandbox Code Playgroud)

发生了什么?

正如您所看到的,我们有两个div没有任何复杂样式(简单的背景颜色).我div通过应用否定来使第二个与第一个重叠margin-top.我期待看到一个完全重叠另一个,但事实并非如此.第二个div是在第一个内容和背景之间滑动,对我来说这是一个奇怪的行为.

这里的动画无关,我只是用它来更好地展示行为.我们可以简单地添加负边距而不用动画,我们会有同样的事情:

body {
  margin: 0;
  background: pink;
  color: #fff;
}

.box {
  margin-top: 20px;
  background: red;
}

.bottom {
  margin-top:-10px;
  text-align: right;
  background: green;
}
Run Code Online (Sandbox Code Playgroud)
<div class="box">
  some content
</div>
<div class="bottom">
  other content
</div>
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:为什么这样的行为?


顺便说一句,我们都知道CSS有一些棘手的事情我们在第一次遇到它们时并不怀疑(比如边缘折叠,从身体到html的背景传播,白空间问题等等)但是他们在某处清楚地解释了,我希望找到一个官方资源,我可以清楚地理解这一点,不仅会得到类似"也许这种情况发生的原因......","我怀疑这与......有关","我认为它与......有关..."等等.


我对此的看法/解释

我认为像文本这样的内容比背景和其他视觉样式更重要,所以当我们有重叠时,我们将所有文本放在顶部,所有其他样式放在底部,我们决定每个组内的顺序然后我们打印结果.

这是一个更复杂的例子:

body {
  margin: 0;
  background: pink;
  color: #fff;
}

div {
  font-size: 39px;
  line-height: 28px;
  margin-bottom: -20px;
  font-weight: bold;
}

body :nth-child(1) {
  background: red;
  border:3px solid brown;
}

body :nth-child(2) {
  background: blue;
  border:3px solid yellow;
  color: #000;
}

body :nth-child(3) {
  background: green;
  border:3px solid orange;
}
Run Code Online (Sandbox Code Playgroud)
<div>
  some content
</div>
<div>
  other content
</div>
<div>
  more content
</div>
Run Code Online (Sandbox Code Playgroud)

我们可以清楚地看到可视堆栈如下(从下到上):

  • 第一个 div(背景+边框)的样式
  • 第二个 div(背景+边框)的样式
  • 第三个 div的样式(背景+边框)
  • 第一个 div的文本内容
  • 第二个 div的文本内容
  • 第三个 div的文本内容

重要提示:在回答之前,请注意我不是要找到解决方法或如何避免这种情况.通过简单地添加position:relative行为消失,我们可以玩z-index决定堆叠.我想了解为什么会发生这样的事情.

val*_*als 30

警告:阅读以下信息可能会影响您的心理健康.

生成堆叠上下文的元素的后代的绘制顺序(请参阅z-index属性)是:

  1. 如果元素是根元素:
    1. 整个画布上元素的背景颜色.
    2. 元素的背景图像,在整个画布上,锚定在为根元素绘制时将使用的原点.
  2. 如果元素是
    • 块,列表项或其他等效块:
      1. 元素的背景颜色,除非它是根元素.
      2. 元素的背景图像,除非它是根元素.
      3. 元素的列规则.
      4. 元素的边界.
    • 否则,如果元素是块级表:
      1. 表背景(颜色然后图像),除非它是根元素.
      2. 列组背景(颜色然后图像).
      3. 列背景(颜色然后图像).
      4. 行组背景(颜色然后图像).
      5. 行背景(颜色然后图像).
      6. 细胞背景(颜色然后图像).
      7. 多列的单元格列规则.
      8. 所有表格边框(按树状顺序分隔边框).
  3. 堆叠上下文由z-索引顺序中的负z-索引(不包括0)形成的后代(最多为负数)然后是树顺序.
  4. 对于树中顺序的所有流入,非定位,块级后代:
    • 如果元素是块,列表项或其他等效块:
      1. 元素的背景颜色.
      2. 元素的背景图像.
      3. 元素的列规则.
      4. 元素的边界.
    • 否则,元素是一个表:
      1. 表背景(颜色然后图像).
      2. 列组背景(颜色然后图像).
      3. 列背景(颜色然后图像).
      4. 行组背景(颜色然后图像).
      5. 行背景(颜色然后图像).
      6. 细胞背景(颜色然后图像).
      7. 单元格列规则(多列).
      8. 所有表格边框(按树状顺序分隔边框).
  5. 所有未定位的浮动后代,按树顺序排列.对于其中的每一个,将元素视为创建新的堆叠上下文,但实际创建新堆叠上下文的任何定位的后代和后代都被视为父堆叠上下文的一部分,而不是新的堆叠上下文.
  6. 如果元素是生成堆叠上下文的内联元素,则:
    1. 对于元素所在的每个行框:
      1. 跳转到7.2.1以获取该行框中元素的框(按树顺序).
  7. 否则:首先是元素,然后是树中顺序的所有流入,非定位,块级后代:

    1. 如果元素是块级替换元素,则:原子地替换内容.
    2. 否则,对于该元素的每个行框:

      1. 对于该元素的子元素的每个框,在该行框中,按树顺序:

        1. 元素的背景颜色.
        2. 元素的背景图像.
        3. 元素的列规则.
        4. 元素的边界.
        5. 对于内联元素:
          1. 对于此行框中的所有元素in-flow,non-located,inline-level子元素,以及此行框中元素内的所有文本行,按树顺序:
            1. 如果这是一段文字,那么:
              1. 任何影响元素文本的下划线,按照应用下划线的元素的树顺序(如果有的话,最深的元素的下划线,如果有的话,最上面绘制,而根元素的下划线,如果有的话,则绘制在最底层).
              2. 影响元素的文本上划线任何,在树的顺序应用上划线的元件的(使得最深元件的上划线,如果有的话,被涂成最上和最根元素的上划线,如果有的话,被吸入最下面).
              3. 文本
              4. 任何行通过影响该元素的文本中,在树的顺序应用的线通(该元件,使得最深元件的线通过,如果有的话,被涂成最上和最根元素的删除线,如果有的话,是最底层).
            2. 否则,跳转到该元素的7.2.1
        6. 对于内联块和内联表元素:
          1. 对于其中的每一个,将元素视为创建新的堆叠上下文,但实际创建新堆叠上下文的任何定位的后代和后代都被视为父堆叠上下文的一部分,而不是新的堆叠上下文.
        7. 对于内联级替换元素:
          1. 被替换的内容,原子地.
        8. 可选地,元素的轮廓(见下面的10).

        注意,某些框可能是通过行拆分或Unicode双向算法生成的.

    3. 可选地,如果元素是块级,则元素的轮廓(参见下面的10).

  8. 所有定位,不透明度或变换后代,按树顺序分为以下类别:

    1. 所有定位的后代都带有'z-index:auto'或'z-index:0',按树顺序排列.对于那些"的z-index:汽车",治疗的元素,就好像它创造了一个新的堆叠内容,但实际上创建一个新的堆叠内容的任何位置子孙和后代应被视为父堆叠内容,没有这个新的组成部分.对于那些'z-index:0'的人,请处理以原子方式生成的堆叠上下文.

    2. 具有不透明度小于1的所有不透明度后代(按树顺序)创建以原子方式生成的堆叠上下文.

    3. 所有变换后代都使用变换而不是以树的顺序,创建以原子方式生成的堆叠上下文.
  9. 堆叠由z-index顺序(最小的第一个)的z-indices大于或等于1的定位后代形成的上下文,然后是树顺序.

现在认真,请参阅w3c绘画订单文档

在4.1节中,绘制了儿童的背景

在第4.4点,绘制了儿童的边界.

第4点完成后,您的代码段的所有背景和边框都已绘制完毕

现在,在第7.2.1.5.1.1.3点中,绘制了儿童的文本.

这是您所看到的行为.

另请注意,这很容易改变这种行为.我们可以激活点8.2,(设置不透明度),它会像您预期的那样绘制:

body {
  margin: 0;
  background: pink;
  color: #fff;
}

.box {
  margin-top: 20px;
  background: red;
}

.bottom {
  text-align: right;
  background: green;
  animation: animate 2s infinite alternate linear;
  opacity: 0.9999;
}

@keyframes animate {
  from {
    margin-top: 10px;
  }
  to {
    margin-top: -40px;
  }
}
Run Code Online (Sandbox Code Playgroud)
<div class="box">
  some content
</div>
<div class="bottom">
  other content
</div>
Run Code Online (Sandbox Code Playgroud)

另一个片段,在文档中显示了几点:

请注意,步骤4中的所有边框和背景都在步骤3之后和setp 5之前呈现.但步骤4中的文本是步骤7,因此在步骤5中的文本之后呈现

div {
  width: 200px;
  height: 100px;
  border: solid 10px;
  font-size: 40px;
}

.step3 {
  border-color: red;
  background-color: lightpink;
  z-index: -1;
  position: relative;
  margin-left: 10px;
}

.step41 {
  border-color: brown;
  background-color: yellow;
  margin-left: 30px;
  margin-top: -40px;
}

.step42 {
  border-color: blue;
  background-color: lightblue;
  margin-left: 50px;
  margin-top: -40px;
  color: red;
}

.step43 {
  border-color: green;
  background-color: lightgreen;
  margin-left: 160px;
  margin-top: -150px;
  color: crimson;
}

.step5 {
  float: left;
  background-color: white;
  margin-top: -30px;
}

div:hover {
  position: relative;
}
Run Code Online (Sandbox Code Playgroud)
<div class="step3">Step 3 negative zindex</div>
<div class="step41">step4 In flow, number 1</div>
<div class="step42">In flow, number 2</div>
<div class="step43">In flow, number 3</div>
<div class="step5">step 5 float</div>
Run Code Online (Sandbox Code Playgroud)

我不知道这是否算作一个用例:这个元素定位相对设置的初始行为更自然

div {
  width: 100px;
  height: 1.3em;
  border: solid 12px tomato;
  font-size: 18px;
}

div:hover {
  position: relative;
}
Run Code Online (Sandbox Code Playgroud)
<div>a long stretch of text overflowing to the other div</div>
<div></div>
Run Code Online (Sandbox Code Playgroud)


Mun*_*nna 7

这个问题的答案远不是每个人都在挖掘的。因为我们都坚信这是一种可以利用用户体验的东西,某种直观的东西。有没有人认为这可以为任何 CPU 处理带来好处?

浏览器渲染引擎不会完全绘制一个元素,然后继续绘制下一个元素,因为我们有 GPU 技术。渲染引擎在不同的堆叠层中逐层绘制元素,然后将这些元素交给 GPU,然后 GPU 将所有层光栅化为显示在屏幕上的复合层。

浏览器渲染层

那么,图层是如何创建的呢?

  • 渲染引擎在基础层上绘制所有背景和边框。
  • 为指定了定位的元素创建一个新层。该层堆叠在基础层上,供 GPU 对其进行光栅化。当元素移动时,渲染引擎只告诉 GPU 移动图层,其余的由 GPU 完成。节省了 CPU 的绘制开销。
  • 为指定了不透明度的元素创建一个新层。当不透明度改变时,GPU 会执行光栅化工作。
  • 类似地,为输入、按钮、画布、视频创建一个新层,所有可能导致 CPU 重复绘制的东西都作为新层传输到 GPU。我们节省了很多 CPU 处理,对吧?:)
  • 拒绝为具有负边距的元素创建任何新图层 :( 。这很明显,浏览器无法为每个具有边距的元素发布新图层,它们很多。即使它具有负边距,它也可能会或可能不会与其他元素重叠。唯一可以确定的方法是查看渲染树。有什么意义?我们正在尝试节省处理,查看每个元素的渲染树会破坏它。
  • 为文本元素发布一个新层。GPU 可以更有效地将文本光栅化到背景上,那么为什么要在基础层上用 CPU 绘制它呢??这是最初的问题。我想现在每个人都可以回答了。

为什么将文本绘制在基础层之上的单独层上?

  • CPU 不需要在背景上绘制文本,CPU 很高兴。
  • 当文本颜色或阴影改变时,CPU 只绘制文本层,GPU 在背景上对其进行光栅化。
  • 当背景改变时,CPU 只绘制背景层,GPU 会在其上光栅化文本。
  • 当文本在固定背景上滚动时,不需要绘画,CPU 只告诉 GPU 移动文本层,GPU 完成其余的工作。
  • 还有很多……

现在让我们来看看我们因此而遇到的魔法。

绘画和光栅化

  • 情况 1:当 position relative 添加到第二个 div 时,它会得到一个新图层,这个新图层堆叠在基础图层上,甚至堆叠在第一个 div 的文本图层上。
  • 情况 2:应用不透明度时会发生同样的事情。
  • 情况 3:任何变换样式都会创建一个新图层。
  • 案例4:这个很有趣。margin 不会为第二个 div 创建任何图层,它是在绘制第一个 div 后绘制在基础 div 上,因此它是在同一图层上的第一个 div 上绘制的。第一个 div 上的文本位于它自己的层上,该层堆叠在基础层上,因此 GPU 将文本栅格化到第二个 div 的背景上。

[来源:developers.google.com ]

  • 很明显,你做了一件了不起的工作。但是,很抱歉这样说,我认为您的结论并不准确。解释为什么我不同意这个会太长,而且主要是基于意见,所以我认为这样做不值得......无论如何,我会为你的回答所做的工作量投票显示。 (2认同)

Mat*_*ski 5

这是因为等级制度......我会试着解释一下......

body {
  margin: 0;
  background: pink;
  color: #fff;
}

.box {
  margin-top: 20px;
  background: red;
}

.bottom {
  margin-top:-10px;
  text-align: right;
  background: green;
}
Run Code Online (Sandbox Code Playgroud)
<div class="box">
  some content
</div>
<div class="bottom">
  other content
</div>
Run Code Online (Sandbox Code Playgroud)

就像在您的示例中,我们的层次结构如下所示:

.box
    .box content
.bottom
    .bottom content
Run Code Online (Sandbox Code Playgroud)

所以,现在如果你不通过position: relative,那么它将使用正常的 HTML 层次结构而不检查 div...

您在.boxand实现了背景.bottom,因此在这种情况下,当您添加margin-top.bottom时:

  • .bottom并且.box具有相同的水平层次位置,但.bottom具有更大的垂直,因此会重叠.box背景
  • .bottom content并且.box content具有比它们更大的位置.bottom.box因此将重叠它们中的每一个
  • .bottom content.box content由于更大的垂直层次结构会重叠