Categories
程式開發

CSS层叠上下文和层叠顺序原理分析及实战


1. 问题现象

最近在做一个网站的首页,首页的右上角有一个绝对定位的大动画,同时首页还有三个小动画,小动画部分的效果是:鼠标滑入特定区域(如下图),展示动画。

由于UI提供的动画画布很大,所以遮盖住了下图所示特定区域的div元素;最外层div元素,包括三个部分:头部标题、设置了透明度的文本以及一段文本。

在div元素上设置onmouse事件后,诡异的事情发生了:只有鼠标滑入设置透明度的文本时,小动画才出现,滑入其他部分,小动画不出现。

这是为什么?

CSS层叠上下文和层叠顺序原理分析及实战 1

探究原因,是因为大动画部分设置了绝对定位以及z-index,形成了层叠上下文,层叠水平高于普通元素,遮盖了div中的部分元素。

要解决上述问题,咱们需要先翻开书本,从层叠上下文和层叠顺序说起。

2. 层叠相关概念和原理

2.1 层叠上下文

它是一个三维的概念,在CSS2.1规范中,每个盒模型都可看做一个三维空间,分别为平铺在画布上的X轴、Y轴以及表示层叠的Z轴。一般情况下,元素在页面上沿X轴Y轴平铺,用户察觉不到它们在Z轴上的层叠关系。而一旦元素发生堆叠,这时就能发现某个元素可能覆盖了另一个元素或者被另一个元素覆盖。

如果一个元素含有层叠上下文,那么这个元素则为层叠上下文元素。我们可以理解为这个元素比普通元素在Z轴上距离用户更近一些。

层叠上下文规则:

  • 层叠上下文的层叠水平比普通元素要高;
  • 层叠上下文可以嵌套,内部层叠上下文及其子元素的层叠水平受制于外部的层叠上下文;
  • 层叠上下文和其兄弟元素相互独立,即在不同的层叠上下文中,元素的层叠顺序没有可比性;
  • 不同的层叠上下文元素发生层叠的时候,元素的层叠水平由父级层叠上下文的层叠顺序决定;

2.2 层叠等级

在同一个层叠上下文中,它描述定义的是该层叠上下文中的层叠上下文元素在Z轴上的上下顺序。

当然,在普通元素中,也可以有层叠顺序,这时它描述定义的是这些普通元素在Z轴上的上下顺序。z-index属性可以影响层叠水平,注意是“影响”,不是“决定”。

2.3 层叠顺序

以上的层叠上下文和层叠等级是概念,层叠顺序是指元素遵循的规则。在同一个层叠上下文中,其中的元素遵循下图的规则:

CSS层叠上下文和层叠顺序原理分析及实战 2

其中,border/background为装饰属性,float浮动盒子和block块状元素一般用作布局,inline/inline-block内联元素包含的是网页内容。在网页中内容最重要,所以inline/inline-block的层叠等级顺序更高。

通过实例验证上述规则,结论如下:

1)负index的层叠等级大于层叠上下文、小于block块级水平盒子

可参考下文中”一些新的css属性会形成层叠上下文”中的第一个例子;

2)float浮动盒子的层叠等级大于block水平盒子

CSS层叠上下文和层叠顺序原理分析及实战 3

111111111111

3)inline/inline-block水平盒子的层叠等级大于float浮动盒子

CSS层叠上下文和层叠顺序原理分析及实战 4

111111111111

4)不依赖于z-index的层叠上下文的层叠等级大于行内元素的层叠等级(注:此处由于opacity属性,形成层叠上下文)

CSS层叠上下文和层叠顺序原理分析及实战 5

 
111111111111 

5)正index的层叠等级大于不依赖于z-index的层叠上下文的层叠等级

CSS层叠上下文和层叠顺序原理分析及实战 6

 
111111111111

2.4 层叠准则

  • 在同一个层叠上下文中,如果元素具有明显的层叠水平标识时(如z-index),层叠水平高的元素在上面,距离用户“最近”;
  • 当两个元素的层叠水平一致、层叠顺序相同时,DOM流后面的元素会覆盖前面的元素

2.5 层叠上下文的形成

1)页面根元素,本身就具有层叠上下文,称为根层叠上下文

2)定位元素的z-index为数值时会形成层叠上下文

  • z-index:auto时

CSS层叠上下文和层叠顺序原理分析及实战 7

 
111111111111

z-index:auto时未形成层叠上下文,绿色长方形的z-index较大,所以绿色长方形遮盖了红色长方形;

  • z-index的值为数值时

CSS层叠上下文和层叠顺序原理分析及实战 8

z-index的值为数值,形成了层叠上下文,绿色和红色长方形的父元素的层叠等级相比,红色长方形父元素的层叠等级更高,所以红色长方形遮盖了绿色长方形。

以上印证了:不同的层叠上下文元素发生层叠时,元素的层叠水平由父级层叠上下文的层叠顺序决定,同时也印证了z-index属性可以影响层叠水平,只是影响,不能决定。

3)一些新的css属性会形成层叠上下文

  • 元素为flex项,z-index为数值

z-index为数值的flex项,要满足两个条件,父级元素为flex或inline-flex布局,子元素的z-index为数组,方可形成层叠上下文。

情况1:当父级元素不为flex或inline-flex布局时

CSS层叠上下文和层叠顺序原理分析及实战 9

在同一个层叠上下文中,负index的层叠顺序小于block块级元素,所以,红色长方形覆盖在绿色长方形的上面;

情况2:当父级元素为flex或inline-flex布局时

CSS层叠上下文和层叠顺序原理分析及实战 10

当最外层父级div元素display:flex时,第二层div元素z-index:1(数值),则第二层div元素为层叠上下文,由下图的层叠顺序规则可知,负index的层叠顺序大于层叠上下文,所以绿色长方形覆盖在红色长方形的上面。

CSS层叠上下文和层叠顺序原理分析及实战 11

  • opacity

当opacity值不为1时,可形成层叠上下文

情况1:当外层div元素未加透明度opacity时

CSS层叠上下文和层叠顺序原理分析及实战 12

在同一个层叠上下文中,负index的层叠顺序小于block块级元素,所以,红色长方形覆盖在绿色长方形的上面;

情况2:当外层div元素加了透明度opacity时

CSS层叠上下文和层叠顺序原理分析及实战 13

当最外层父级div元素opacity:0.5时,子元素的z-index:-1,由层叠顺序规则可知,负index的层叠顺序大于层叠上下文,所以绿色长方形覆盖在红色长方形的上面;

  • 其他属性

transform不是none、mix-blend-mode不是normal、filter不是none、isolation:isolate和-webkit-overflow-scrolling:touch,都可形成层叠上下文,此时与opacity值不为1的情况相同。

3. 解决办法

针对文章开篇提到的问题,有三种解决方案:

方案一:给外层div元素设置position:relative,使div元素和绝对定位大动画的层叠水平相等

元素一旦成为定位元素,其z-index就会自动生效,且默认值为auto,也就是0级别。由于绝对定位的大动画的z-index为0,也就意味着层叠上下文的大动画元素和div定位元素是一个层叠顺序的。

于是当他们发生层叠时,遵循层叠准则第二条,div元素可正常响应鼠标划入事件。

方案二:通过设置外层div元素position:relative,z-index为非auto,形成层叠上下文

当z-index的值大于等于绝对定位的大动画的z-index值时,当他们发生层叠时,遵循层叠准则第一条,div元素可正常响应鼠标划入事件。

方案三:通过设置父元素display: flex|inline-flex,子元素作为flex项,z-index值是不为auto的数值时,子元素可形成层叠上下文

可给外层div元素设置diaplay:flex属性,内层元素设置z-index不为auto,则内层元素为层叠上下文元素,遵循层叠准则第二条,div元素可正常响应鼠标划入事件。

以上方案是基于绝对定位大动画的z-index为0时。若绝对定位大动画的z-index值大于0时,方案一就不生效了,方案二和方案三的z-index需大于等于大动画的z-index值。

4. 总结

一旦普通元素具有了层叠上下文,其层叠顺序就会变高。它的层叠顺序依赖以下两个原则:

  • 如果层叠上下文元素不依赖z-index数值,则其层叠顺序z-index:auto可看成z-index:0级别;
  • 如果层叠上下文元素依赖z-index数值,则其层叠顺序由z-index值决定

当遇到页面中有元素发生重叠,或者元素不能正常响应鼠标等事件时,可检查下该元素以及其父元素和兄弟元素所在的层叠上下文。遵循文中所说的规则,设置对应的css属性,改变元素层叠顺序,即可解决相应的问题。

本文转载自公众号贝壳产品技术(ID:beikeTC)。

原文链接

https://mp.weixin.qq.com/s?__biz=MzIyMTg0OTExOQ==&mid=2247485712&idx=2&sn=d788cf74f5d806636d54b6fd30b6025f&chksm=e8373a60df40b3763973faddf3d4db114591a07b3c4158ee1f939aefcab057e5afb939ada720&scene=27#wechat_redirect