跳到主要内容

层叠上下文

概念

层叠上下文(stacking context) 是一个抽象概念,和 BFC 类似。可以理解为是一个体系块,这个块是独立的,在这个块内,定义了一系列的规则(层级等级 stacking level)来解释在发生堆叠时,图层的显示顺序。

层叠等级

W3官方定义一共 7 层,以图层按从后到前的顺序绘制:

  1. 形成层叠上下文的元素的背景和边框
  • the background and borders of the element forming the stacking context.
  1. 具有负的层叠等级(z-index)的子层叠上下文元素(负的越大越堆叠层级越低)
  • the child stacking contexts with negative stack levels (most negative first).
  1. 流式布局,而且是 非行内元素、非定位元素的子元素
  • the in-flow, non-inline-level, non-positioned descendants.
  1. 非定位元素的浮动元素
  • the non-positioned floats.
  1. 流式布局,而且是 行内元素(包括 display:table 和 display:inline)、非定位元素的子元素。
  • the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  1. 层叠等级为 0 的子层叠上下文或者子定位元素
  • the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  • 注:从个人理解,z-indexauto 也是这一层
  1. 正的层叠等级的子层叠上下文(正的越小越堆叠层级越低)
  • the child stacking contexts with positive stack levels (least positive first).

用 css 样式来表示各个等级

/* 形成层叠上下文的元素的背景和边框 */
.level-1 {
position: relative;
width: 1000px;
height: 100px;
z-index: 1;
background-color: salmon;
}

/* 具有负的层叠等级(`z-index`)的子层叠上下文元素(负的越大越堆叠层级越低) */
.level-2 {
position: relative;
z-index: -1;
width: 900px;
height: 200px;
background-color: seagreen;
}

/* 流式布局,而且是 非行内元素、非定位元素的子元素 */
.level-3 {
width: 800px;
height: 300px;
background-color: slateblue;
}

/* 非定位元素的浮动元素 */
.level-4 {
float: left;
width: 700px;
height: 400px;
background-color: slategray;
}

/* 流式布局,而且是 行内元素(包括 display:table 和 display:inline)、非定位元素的子元素。 */
.level-5 {
display: inline-block;
width: 600px;
height: 700px;
background-color: springgreen;
}

/* 层叠等级为 0 的子层叠上下文或者子定位元素 */
.level-6 {
position: relative;
z-index: 0;
width: 500px;
height: 800px;
background-color: steelblue;
}

/* 正的层叠等级的子层叠上下文(正的越小越堆叠层级越低) */
.level-7 {
position: relative;
z-index: 1;
width: 400px;
height: 700px;
background-color: tan;
}

创建层叠上下文

MDN中说明了,文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 文档根元素(<html>);
  • position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);
  • flex 容器的子元素,且 z-index 值不为 auto
  • grid 容器的子元素,且 z-index 值不为 auto
  • opacity 属性值小于 1 的元素;
  • mix-blend-mode 属性值不为 normal 的元素;
  • 以下任意属性值不为 none 的元素:
    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
  • isolation 属性值为 isolate 的元素;
  • -webkit-overflow-scrolling 属性值为 touch 的元素;
  • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;
  • contain 属性值为 layoutpaint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

在线演示

直接子元素

在一个层叠上下文中,直接子元素,按层叠等级显示

实时编辑器
function InStackingContext() {
  return (
    // 这个容器无意义,只是为了给这个示例一个高度
    <div
      style={{
        width: 90,
        height: 90,
      }}
    >
      {/* stacking context */}
      <div
        style={{
          position: 'relative',
          zIndex: 1,
          width: 90,
          height: 90,
          background: 'lightblue',
        }}
      >
        {/* level-6 */}
        <div
          style={{
            position: 'relative',
            width: 30,
            height: 90,
            backgroundColor: 'blue',
          }}
        />

        {/* level-3 */}
        <div
          style={{
            width: 60,
            height: 30,
            backgroundColor: 'blueviolet',
            marginTop: -90,
          }}
        />

        {/* level-2 */}
        <div
          style={{
            position: 'relative',
            zIndex: -1,
            width: 60,
            height: 60,
            backgroundColor: 'gray',
            marginTop: -30,
          }}
        />
      </div>
    </div>
  )
}
结果
Loading...

嵌套普通元素

在上面的层叠等级中可以看到,有些等级(如: level-3, level-4, level-5)它是没有创建层叠上下文的,它在这里只是看起来好像在绘制的时候,创建了一个层叠上下文。

主要区别表现在:这些元素(指 level-3, level-4, level-5)的后代是定位元素或者其他新的层叠上下文时

直接子元素没有创建层叠上下文,深度嵌套的 定位元素 或者 新的层叠上下文 的元素,参与最近的堆叠上下文进行绘制

实时编辑器
function InStackingContext() {
  return (
    // 这个容器无意义,只是为了给这个示例一个高度
    <div
      style={{
        width: 90,
        height: 90,
      }}
    >
      {/* stacking context */}
      <div
        style={{
          position: 'relative',
          zIndex: 1,
          width: 60,
          height: 60,
          background: 'gray',
        }}
      />

      {/* 普通元素 */}
      <div
        style={{
          width: 90,
          height: 30,
          background: 'lightblue',
          marginTop: -60,
        }}
      >
        {/* stacking context */}
        <div
          style={{
            position: 'relative',
            zIndex: 2,
            width: 30,
            height: 90,
            backgroundColor: 'blue',
          }}
        />
      </div>
    </div>
  )
}
结果
Loading...

嵌套新的层叠上下文

直接子元素创建层叠上下文时,该元素及其后代元素,将作为一个整体,参与最近的堆叠上下文进行绘制

实时编辑器
function InStackingContext() {
  return (
    // 这个容器无意义,只是为了给这个示例一个高度
    <div
      style={{
        width: 90,
        height: 90,
      }}
    >
      {/* stacking context */}
      <div
        style={{
          position: 'relative',
          zIndex: 1,
          width: 60,
          height: 60,
          background: 'gray',
        }}
      />

      {/* stacking context */}
      <div
        style={{
          position: 'relative',
          zIndex: 0,
          width: 90,
          height: 30,
          background: 'lightblue',
          marginTop: -60,
        }}
      >
        {/* stacking context */}
        <div
          style={{
            position: 'relative',
            zIndex: 2,
            width: 30,
            height: 90,
            backgroundColor: 'blue',
          }}
        />
      </div>
    </div>
  )
}
结果
Loading...

总结

  • 每个层叠上下文都完全独立于它的兄弟元素:当处理层叠时只考虑子元素。
  • 每个层叠上下文都是自包含的:当一个元素的内容发生层叠后,该元素将被作为整体在父级层叠上下文中按顺序进行层叠。
  • 未创建层叠上下文的元素,其定位后代元素或者新的层叠上下文,会参与父级层叠上下文绘制

另外需要注意的是

positionabsolute 或者 relative,此时 z-index 默认为 auto,它是不会创建层叠上下文的。 只有 z-index 为数字时才会创建层叠上下文。 虽然在一个层叠上下文中,两者的绘制顺序看起来是一致的,但一个创建层叠上下文,一个不会创建,还是有本质区别的。