你真的懂CSS 层叠上下文吗

1,395 阅读4分钟

在掘金社区发布文章,并在正文的第一句加入“我正在参加「掘金·启航计划」”

CSS 层叠上下文

在使用 bootstrap 的模态框时,出现了半透明遮罩始终盖住模态框的情况,
只有把模态框的 HTML 写到 body 之下才恢复正常,
预览:code.juejin.cn/pen/7146818…

后来有幸看到了张鑫旭在慕课网 CSS深入理解之 relative 的视频,
才算真正知晓了层叠上下文在 CSS 中的规则和运用技巧。

什么是层叠上下文和层叠水平

所有的上下文都代表着存储着某些关联信息,比如执行上下文、块级格式化上下文等等。
当进行定位布局的渲染时,如何标记这堆元素的覆盖关系呢,在指定点击事件时是怎么知道会先碰到最上层的元素的呢?
层叠上下文其实把它理解为 z 轴也没有问题,本文重点研究层叠上下文的表现与应用,暂不探究其原理。
developer.mozilla.org/zh-CN/docs/…

层叠水平,则是在创建了层叠上下文的情况下,z 轴层级上的表现。

形成层叠上下文

通常情况下,兄弟节点总是后者覆盖前者的。
注意:仅讨论 display: bock 的情况,其他 display 会有另一套规则

body
  - red【普通元素】
  - green【普通元素】

code.juejin.cn/pen/7146815…

但当某元素被创建层叠上下文后,它在 z 轴上会比普通元素高上一层而能覆盖普通元素。
注:z-index 属性有些特殊,比如:浏览器不区分 z-index: auto 或 z-index 负值只有定位时有效,等

添加以下属性即可创建层叠上下文 (随着 CSS3 属性还在增加,本表不全)

  • z-index 不为 auto 的 relative(此条有争议,比如 chrome79 没管 z-index)
  • absolute, fixed
  • z-index 不为 auto 的 flex 子项
  • opacity 不为 1
  • transform 不为 none
  • perspective 不为 none
  • filter 不为 none
  • mix-blend-mode 不为 normal
  • 带有 isolation: isolate
  • will-change 不为 none
  • 带有 -webkit-overflow-scrolling: touch
body
  - red【含层叠上下文】
  - green【普通元素】

code.juejin.cn/pen/7146815…

同级层叠水平后者居上

当存在处于同个 z-index 的层叠上下文时,即同级层叠水平,
那么会表现为 后者居上(注意:父级祖父级不能含有层叠上下文)

body
  - gray【普通元素】
    - red【含层叠上下文,z-index: auto】
  - pink【普通元素】
    - green【含层叠上下文,z-index: auto】

code.juejin.cn/pen/7146815…

不同级层叠水平大者居上

注:z-index 好像不同浏览器版本有差异,如需 z-index 生效最好加上定位,如 relative。

body
  - gray【普通元素】
    - red【含层叠上下文,z-index: 1】
  - pink【普通元素】
    - green【含层叠上下文,z-index: auto】

code.juejin.cn/pen/7146816…

层叠水平的嵌套

当父级或祖父级元素含有层叠上下文时,内部的层叠上下文类似于水平的水平这样的嵌套关系。
这意味着,当父级层叠水平不够高时,子级的层叠水平再高也没用。

body
  - gray【含层叠上下文,z-index: auto】
    - red【含层叠上下文,z-index: 999】
  - pink【含层叠上下文,z-index: auto】
    - green【含层叠上下文,z-index: 1】

code.juejin.cn/pen/7146817…

层叠水平内部覆盖规则改变

起初我并不能理解下图是何含义,但当我发现 z-index: -1 在父级是否含有层叠上下文时的不同表现时才意识到,
层叠水平内部元素的覆盖关系,可能与普通元素的覆盖关系有所不同。

如果父级不含层叠上下文,

body
  - gray【普通元素】
    - red【普通元素】
    - green【含层叠上下文,z-index: -1】

code.juejin.cn/pen/7146817…

body
  - gray【含层叠上下文,z-index: auto】
    - red【普通元素】
    - green【含层叠上下文,z-index: -1】

code.juejin.cn/pen/7146817…

总结

以上便是创建层叠上下文后与众不同的视觉表现的规律。

可以在遇到奇怪的覆盖样式问题时有一定理论思路:codepen.io/foreverZ133…

也可利用水平嵌套时 z-index: -1 的表现制作更多优秀效果:codepen.io/foreverZ133…

还可以利用层叠上下文高于普通元素的表现区别于普通元素的覆盖问题:codepen.io/foreverZ133…

更多案例:
codepen.io/foreverZ133…
codepen.io/hexagoncirc…

思考题:层叠上下文的底层实现

假如你正在开发一款基于 canvas 的布局工具,元素的数据结构如下:
明显不太方便处理元素覆盖的表现,而需要另一个数据来专门存储覆盖关系,那么它的数据结构是怎样的?
当产生点击事件时,向内寻找向内冒泡时,又是如何知道最终点中的是上层覆盖的元素的?

[
  selector: '',
  rect: { x: 0, y: 0, width: 100, height: 100, zIndex: 1 },
  children: [{
    selector: '',
    rect: { x: 0, y: 0, width: 100, height: 100 },
  }],
]

思考题:非层叠上下文时的覆盖关系

在研究过程中,我发现了一些奇特的规律,
比如 inline-block 始终高于其他 display 的元素,
或者,文字与背景中间还能再覆盖其他元素等。

可点开右侧链接来体验:codepen.io/foreverZ133…