前言
在做项目布局回顾时,思考到了一个当初比较犹豫的问题「flex 布局能否产生层叠/堆叠上下文」。在网上也参考了国内外很多有价值的文章,此处小结一下,算是对层叠上下文这一部分做一次较为全面的总结。
核心概念
层叠上下文是什么
这里先引用 MDN 上的一段对层叠上下文的介绍:
层叠上下文是 HTML 元素的三维概念,这些 HTML 元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的 z 轴上延伸,HTML 元素依据其自身属性按照优先级顺序占用层叠上下文的空间。
简单来说,电脑屏幕给我们展现的是一个平面,此对应直角坐标系的 X、Y 轴,而垂直与电脑屏幕的这个不可见的轴(相当于直角坐标系的 Z 轴),其上面遍布着不同的堆叠层,而此堆叠层的层叠顺序就是我们所谓的层叠上下文了。
如果有设计经验的,那么这个堆叠层可以理解为 PS 中的画布,默认只有最初的一层(页面中的 html 元素)
层叠上下文的堆叠顺序
这边借用一张图来描述一下堆叠顺序:
此处对图做简单说明:
- 层叠上下文部分:相当于该层叠区域的装饰,优先级最低,处于最底层
- block 块状水平盒子:层叠区最为基础的布局方式,主要目的是为了控制呈现的样式
- float 浮动盒子:float 默认的
display
属性就是 block ,只是脱离了文档流,其也是一种布局方式 - inline/inline-block 水平盒子:层叠区默认的内容呈现部分,承载页面大多数内容部分(涉及到页面内容的标签基本上是 inline ,或者是隐式行内盒)
- z-index + position(和部分 CSS3 属性):控制元素在其所在的层叠区 Z 轴上的顺序(默认 auto,相当于 0),有正负值
其中比较有趣的一点是,inline/inline-block
的层级顺序要高于 block
元素,具体的体现就在“文字环绕”式布局了。
其实这么排序也有其理由,我们知道,页面其主要目的是为了呈现内容的,而块级元素偏向于布局(设置宽高、内外边距、边框、行内元素的大多数配置等等),行内元素偏向于内容(与其它内容的间隔、字体大小等等),因此不难理解行内元素应该高于块级元素的层叠级别了。
此外,对于默认情况(什么都没有设置),后面的元素默认堆叠顺序会高于前面的元素,具体的体现为两个 button 按钮,如果将后者的 margin
值设为负值,其会覆盖掉先前的按钮。
注:在层叠上下文中,其子元素同样也按照上面解释的规则进行层叠。 特别值得一提的是,其子元素的 z-index 值只在父级层叠上下文中有意义。子级层叠上下文被自动视为父级层叠上下文的一个独立单元。
如何产生层叠上下文
此处还是引用 MDN 上的介绍,文档中的层叠上下文由满足以下任意一个条件的元素形成(未做说明的会影响子元素的 fix 布局):
- 根元素 (HTML)
z-index
值不为 “auto” 的 绝对/相对定位(不影响子元素的 fix 布局)z-index
值不为 “auto” 的 flex 项目 (flex item),即:父元素display: flex|inline-flex
opacity
属性值小于 1 的元素(参考 the specification for opacity)(不影响子元素的 fix 布局)transform
属性值不为 “none” 的元素mix-blend-mode
属性值不为 “normal” 的元素(不影响子元素的 fix 布局)filter
值不为 “none” 的元素perspective
值不为 “none” 的元素isolation
属性被设置为 “isolate” 的元素(不影响子元素的 fix 布局)position: fixed
(不影响子元素的 fix 布局)will-change
中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考这篇文章-webkit-overflow-scrolling
属性被设置 “touch” 的元素(不影响子元素的 fix 布局)- (补充)
transform-style
为 preserve-3d 的元素
当然,这里仅仅只是列举了能够形成层叠上下文的一些情况,而其中也有部分翻译的的不是很准确(参考张鑫旭的文章,后面做了一些要点描述)
层叠上下文的层级是 HTML 元素层级的一个层级,因为只有某些元素才会创建层叠上下文。可以这样说,没有创建自己的层叠上下文的元素 将被父层叠上下文包含。 —— MDN
详解层叠上下文的形成
z-index
不为 auto 的 flex 项目
此指的是父元素的 display
属性为 flex/inline-flex ,其子元素的 z-index 值不为 auto,此时该子元素会形成堆叠区。
transform
属性值不为 “none” 的元素
在做 position: fixed
定位时,其默认是基于页面窗口创建堆叠区(根元素有个最基础的堆叠区),然而父级元素的 transform
属性值非 none 时,其会生成一个堆叠层,使得内部的 fixed 布局不是以窗口为基本的堆叠层,而是此父级本身,从而产生很“酸爽”的 bug 体验。
不会影响 fix 布局的会生成自身层叠的例子
此部分对于 -webkit-overflow-scrolling
属性未能尝试成功,可能只能手机端显现(未测试)
会影响 fix 布局的层叠的例子
有人做了比较全的 demo,我 fork 了一份,删除了错误的 flex 布局例子,去掉不受影响的部分。
源码地址:传送门
总结
对于层叠上下文,当发生层叠的时候,其覆盖关系遵循两条黄金准则:
- 谁大谁上:当具有明显的层叠水平标示的时候,如具体的 z-index 值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。
- 后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在 DOM 流中处于后面的元素会覆盖前面的元素。