层叠上下文
层叠上下文(Stacking context)是 HTML 元素的三维概念,这些元素沿着一条假想的相对于用户的 z 轴排列,用户被假定面对着视口或网页。层叠上下文决定了元素如何沿 z 轴(可以将其视为屏幕上的“深度”维度)相互堆叠。层叠上下文决定了重叠内容的视觉呈现顺序。
层叠上下文中的元素与该上下文之外的元素是独立堆叠的,这确保了一个层叠上下文中的元素不会干扰另一个层叠上下文中的元素的堆叠顺序。每个层叠上下文都与其兄弟元素完全独立:只有后代元素在处理堆叠时才会被考虑。
每个层叠上下文都是自包含的。在元素的内部内容完成堆叠后,整个元素在其父层叠上下文的堆叠顺序中被视为一个单一的单元。
在层叠上下文中,子元素根据所有兄弟元素的 z-index 值进行堆叠。这些嵌套元素的层叠上下文仅在其父上下文中才有意义。在父层叠上下文中,层叠上下文被原子化地视为一个单一单元。层叠上下文可以包含在其他层叠上下文中,并共同创建一个层叠上下文的层级结构。
层叠上下文的层级结构是 HTML 元素层级结构的一个子集,因为只有某些元素会创建层叠上下文。不创建自身层叠上下文的元素会被父层叠上下文同化。
创建层叠上下文的特性
在文档的任何位置,只要元素满足以下任一情况,就会形成一个层叠上下文
-
文档的根元素(
<html>)。 -
position值为fixed或sticky的元素。 -
container-type值为size或inline-size的元素(参见容器查询)。 -
作为弹性(flex)元素,且
z-index值不为auto的元素。 -
作为网格(grid)元素,且
z-index值不为auto的元素。 -
opacity值小于1的元素。 -
mix-blend-mode值不为normal的元素。 -
以下任一属性的值不为
none的元素 -
isolation值为isolate的元素。 -
will-change值指定了任何在非初始值时会创建层叠上下文的属性的元素。 -
contain值为layout或paint,或包含这两个值之一的复合值(即contain: strict、contain: content)的元素。 -
被放入顶层(top layer)的元素及其对应的
::backdrop。例如全屏和popover 元素。 -
使用
@keyframes对创建层叠上下文的属性(如opacity)进行动画处理,且animation-fill-mode设置为forwards的元素。
嵌套的层叠上下文
层叠上下文可以包含在其他层叠上下文中,它们可以共同创建一个层叠上下文的层级结构。
文档的根元素是一个层叠上下文,在大多数情况下,它包含嵌套的层叠上下文,其中许多又会包含更多的层叠上下文。在每个层叠上下文中,子元素都根据使用 z-index 中解释的相同规则进行堆叠。重要的是,其子层叠上下文的 z-index 值仅在其父层叠上下文中才有意义。在父层叠上下文中,层叠上下文被原子化地视为一个单一单元。
要弄清楚堆叠元素沿 z 轴的渲染顺序,可以将每个索引值想象成一种“版本号”,其中子元素代表其父元素主版本号下的次版本号。
为了演示每个元素的堆叠顺序如何参与其祖先层叠上下文的堆叠顺序,我们来看一个包含六个容器元素的示例页面。其中有三个兄弟 <article> 元素。最后一个 <article> 包含三个兄弟 <section> 元素,而第三个 article 的 <h1> 和 <code> 出现在第一个和第二个兄弟 <section> 元素之间。
<article id="container1">
<h1>Article element #1</h1>
<code>
position: relative;<br />
z-index: 5;
</code>
</article>
<article id="container2">
<h1>Article Element #2</h1>
<code>
position: relative;<br />
z-index: 2;
</code>
</article>
<article id="container3">
<section id="container4">
<h1>Section Element #4</h1>
<code>
position: relative;<br />
z-index: 6;
</code>
</section>
<h1>Article Element #3</h1>
<code>
position: absolute;<br />
z-index: 4;
</code>
<section id="container5">
<h1>Section Element #5</h1>
<code>
position: relative;<br />
z-index: 1;
</code>
</section>
<section id="container6">
<h1>Section Element #6</h1>
<code>
position: absolute;<br />
z-index: 3;
</code>
</section>
</article>
每个容器元素的 opacity 都小于 1,并且 position 设置为 relative 或 absolute。当元素的 z-index 值不为 auto 时,这些属性-值对会创建一个层叠上下文。
section,
article {
opacity: 0.85;
position: relative;
}
#container1 {
z-index: 5;
}
#container2 {
z-index: 2;
}
#container3 {
z-index: 4;
position: absolute;
top: 40px;
left: 180px;
}
#container4 {
z-index: 6;
}
#container5 {
z-index: 1;
}
#container6 {
z-index: 3;
position: absolute;
top: 20px;
left: 180px;
}
为了简洁起见,颜色、字体、对齐和盒模型的 CSS 属性已被隐藏。
上述示例中的层叠上下文层级结构如下
Root │ ├── ARTICLE #1 ├── ARTICLE #2 └── ARTICLE #3 │ ├── SECTION #4 ├──── ARTICLE #3 content ├── SECTION #5 └── SECTION #6
这三个 <section> 元素是 ARTICLE #3 的子元素。因此,section 元素的堆叠完全在 ARTICLE #3 内部解析。一旦 ARTICLE #3 内部的堆叠和渲染完成,整个 ARTICLE #3 元素就会被传递到根元素中,与其兄弟 <article> 元素进行堆叠。
通过将 z-index 比作“版本号”,我们可以看到 z-index 为 1 的元素(SECTION #5)如何堆叠在 z-index 为 2 的元素(ARTICLE #2)之上,以及 z-index 为 6 的元素(SECTION #4)如何堆叠在 z-index 为 5 的元素(ARTICLE #1)之下。SECTION #4 渲染在 ARTICLE #1 之下,是因为 ARTICLE #1 的 z-index (5) 在根元素的层叠上下文中有效,而 SECTION #4 的 z-index (6) 在 ARTICLE #3 (z-index: 4) 的层叠上下文中有效。所以 SECTION #4 在 ARTICLE #1 之下,因为 SECTION #4 属于 ARTICLE #3,而 ARTICLE #3 的 z-index 值较低(4-6 小于 5-0)。
出于同样的原因,ARTICLE #2 (z-index: 2) 渲染在 SECTION #5 (z-index: 1) 之下,因为 SECTION #5 属于 ARTICLE #3 (z-index: 4),而 ARTICLE #3 的 z-index 值更高(2-0 小于 4-1)。
ARTICLE #3 的 z-index 是 4,但这个值与嵌套在其中的三个 section 的 z-index 无关,因为它们属于不同的层叠上下文。
在我们的示例中(按最终渲染顺序排序)
- 根
-
ARTICLE #2: (
z-index: 2),渲染顺序为2-0 -
ARTICLE #3: (
z-index: 4),渲染顺序为4-0- SECTION #5: (
z-index: 1),堆叠在 (z-index: 4) 的元素下,渲染顺序为4-1 - SECTION #6: (
z-index: 3),堆叠在 (z-index: 4) 的元素下,渲染顺序为4-3 - SECTION #4: (
z-index: 6),堆叠在 (z-index: 4) 的元素下,渲染顺序为4-6
- SECTION #5: (
-
ARTICLE #1: (
z-index: 5),渲染顺序为5-0
-
其他示例
其他示例包括最后一层设置 z-index 的 2 级层级结构、所有层级都设置 z-index 的 2 级 HTML 层级结构,以及第二层设置 z-index 的 3 级 HTML 层级结构。