1. 问题现象
最近在做一个网站的首页,首页的右上角有一个绝对定位的大动画,同时首页还有三个小动画,小动画部分的效果是:鼠标滑入特定区域(如下图),展示动画。
由于 UI 提供的动画画布很大,所以遮盖住了下图所示特定区域的 div 元素;最外层 div 元素,包括三个部分:头部标题、设置了透明度的文本以及一段文本。
在 div 元素上设置 onmouse 事件后,诡异的事情发生了:只有鼠标滑入设置透明度的文本时,小动画才出现,滑入其他部分,小动画不出现。
这是为什么?
探究原因,是因为大动画部分设置了绝对定位以及 z-index,形成了层叠上下文,层叠水平高于普通元素,遮盖了 div 中的部分元素。
要解决上述问题,咱们需要先翻开书本,从层叠上下文和层叠顺序说起。
2. 层叠相关概念和原理
2.1 层叠上下文
它是一个三维的概念,在 CSS2.1 规范中,每个盒模型都可看做一个三维空间,分别为平铺在画布上的 X 轴、Y 轴以及表示层叠的 Z 轴。一般情况下,元素在页面上沿 X 轴 Y 轴平铺,用户察觉不到它们在 Z 轴上的层叠关系。而一旦元素发生堆叠,这时就能发现某个元素可能覆盖了另一个元素或者被另一个元素覆盖。
如果一个元素含有层叠上下文,那么这个元素则为层叠上下文元素。我们可以理解为这个元素比普通元素在 Z 轴上距离用户更近一些。
层叠上下文规则:
层叠上下文的层叠水平比普通元素要高;
层叠上下文可以嵌套,内部层叠上下文及其子元素的层叠水平受制于外部的层叠上下文;
层叠上下文和其兄弟元素相互独立,即在不同的层叠上下文中,元素的层叠顺序没有可比性;
不同的层叠上下文元素发生层叠的时候,元素的层叠水平由父级层叠上下文的层叠顺序决定;
2.2 层叠等级
在同一个层叠上下文中,它描述定义的是该层叠上下文中的层叠上下文元素在 Z 轴上的上下顺序。
当然,在普通元素中,也可以有层叠顺序,这时它描述定义的是这些普通元素在 Z 轴上的上下顺序。z-index 属性可以影响层叠水平,注意是“影响”,不是“决定”。
2.3 层叠顺序
以上的层叠上下文和层叠等级是概念,层叠顺序是指元素遵循的规则。在同一个层叠上下文中,其中的元素遵循下图的规则:
其中,border/background 为装饰属性,float 浮动盒子和 block 块状元素一般用作布局,inline/inline-block 内联元素包含的是网页内容。在网页中内容最重要,所以 inline/inline-block 的层叠等级顺序更高。
通过实例验证上述规则,结论如下:
1)负 index 的层叠等级大于层叠上下文、小于 block 块级水平盒子
可参考下文中"一些新的 css 属性会形成层叠上下文"中的第一个例子;
2)float 浮动盒子的层叠等级大于 block 水平盒子
3)inline/inline-block 水平盒子的层叠等级大于 float 浮动盒子
4)不依赖于 z-index 的层叠上下文的层叠等级大于行内元素的层叠等级(注:此处由于 opacity 属性,形成层叠上下文)
5)正 index 的层叠等级大于不依赖于 z-index 的层叠上下文的层叠等级
2.4 层叠准则
在同一个层叠上下文中,如果元素具有明显的层叠水平标识时(如 z-index),层叠水平高的元素在上面,距离用户“最近”;
当两个元素的层叠水平一致、层叠顺序相同时,DOM 流后面的元素会覆盖前面的元素
2.5 层叠上下文的形成
1)页面根元素,本身就具有层叠上下文,称为根层叠上下文
2)定位元素的 z-index 为数值时会形成层叠上下文
z-index:auto 时:
z-index:auto 时未形成层叠上下文,绿色长方形的 z-index 较大,所以绿色长方形遮盖了红色长方形;
z-index 的值为数值时:
z-index 的值为数值,形成了层叠上下文,绿色和红色长方形的父元素的层叠等级相比,红色长方形父元素的层叠等级更高,所以红色长方形遮盖了绿色长方形。
以上印证了:不同的层叠上下文元素发生层叠时,元素的层叠水平由父级层叠上下文的层叠顺序决定,同时也印证了 z-index 属性可以影响层叠水平,只是影响,不能决定。
3)一些新的 css 属性会形成层叠上下文
元素为 flex 项,z-index 为数值:
z-index 为数值的 flex 项,要满足两个条件,父级元素为 flex 或 inline-flex 布局,子元素的 z-index 为数组,方可形成层叠上下文。
情况 1:当父级元素不为 flex 或 inline-flex 布局时
在同一个层叠上下文中,负 index 的层叠顺序小于 block 块级元素,所以,红色长方形覆盖在绿色长方形的上面;
情况 2:当父级元素为 flex 或 inline-flex 布局时
当最外层父级 div 元素 display:flex 时,第二层 div 元素 z-index:1(数值),则第二层 div 元素为层叠上下文,由下图的层叠顺序规则可知,负 index 的层叠顺序大于层叠上下文,所以绿色长方形覆盖在红色长方形的上面。
opacity:
当 opacity 值不为 1 时,可形成层叠上下文
情况 1:当外层 div 元素未加透明度 opacity 时
在同一个层叠上下文中,负 index 的层叠顺序小于 block 块级元素,所以,红色长方形覆盖在绿色长方形的上面;
情况 2:当外层 div 元素加了透明度 opacity 时
当最外层父级 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)。
原文链接:
评论