[BUGCASE]层叠上下文和z-index属性使用不当引发的文本被遮挡的问题

一、问题描述

  • 在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段一个提示的特效,所以做了一个提示层
  • 这个提示层被固定(拖动表格的水平滚动条时固定)的表格列遮住
  • 无论设置该提示层的z-index为多大,都不能让其在固定表格列列之上

效果如下:

二、问题分析

通过对页面的DOM层级进行分析,把有可能影响层级的部分抽出来:

主要有以下几个部分会影响到元素的层级(关于设置了哪些属性会影响层级请看后面的附),下面逐一分析:

  • A元素和B元素都有一个样式是position:absolute;,因此有可能影响到层级(其实不会影响,因为没有设置z-index)
  • D元素有以下样式可能会影响到层级:position: absolute; z-index: 0; transform: translate3d(0px, 0px, 0px);
  • C元素有以下样式可能会影响到层级: opacity: 1; transform: translate(-50%, 0); z-index: 99999999; position: absolute;
  • E元素的样式: position: absolute; width: 140px; z-index: 2; transform: translate3d(0px, 0px, 0px);

A和B是处于同一个层叠上下文(由其父元素创建的)中的,这样的话,应该是后面的元素(B)会覆盖前面的元素(A),但现在并不是这样。

原因:A最近的子元素E创建的层叠上下文(z-index:2)比B最近的子元素C(z-index:0)创建的层叠上下文的层级高。

结论:这就导致了B的所有子元素(当然也包括我们的提示层C)都会比A的层级低,所以C的z-index设置为多大都没用。

三、问题抽象

本来是一个应用场景中的问题,我们可以抽象为以下问题:

已知:

(1) 两个兄弟元素A和B,A的直接子元素E,其层级是2

position: absolute;z-index:2;transform: translate3d(0px, 0px, 0px);

(2) B的直接子元素D,其层级是0

position: absolute;z-index:0;transform: translate3d(0px, 0px, 0px);

(3) D下面还有一个子元素C,层级很大

z-index:9999

(4) 并且transform属性不能改,也不能去掉

问:

怎么实现C在A的上面(层级上的上面),B在A的下面?

HTML结构大概这样:

<div class="div-a">A is here
    <div class="div-e">E is here</div>
</div>
<div class="div-b">B is here
    <div class="div-d">D is here
        <div class="div-c">C is here</div>
    </div>
</div>

四、缺陷归类

样式缺陷

五、建立演示

以下是问题定位过程中建立的几个Demo(其中最后一个是最合理的Demo,想直接看结论的可直接看最后一个Demo):

z-index失效原因探索Demo1

z-index失效原因探索Demo2

z-index失效原因探索Demo3

最合理的Demo

六、解决方案

单纯从抽象出来的问题来看,解决的方案是D不要设置任何会创建层叠上下文的属性,并且让C的层级比E的高,这样的话,E自然会遮住B和D,而C又会遮住E,这就是我们要的效果。点这里看效果

不过在实际的项目环境中,D的transform属性一定会有(且其值会随表格的水平滚动条的拖动而改变),没法改变,所以在我看来这个问题似乎是无解的,只能采取一些替代方案。

这里要感谢我们前端组的同事water大神提供的三个替代方案:

  • 让提示层往下移一点
  • 把提示层改成浏览器默认的title
  • 将提示层C放在D的外层,并控制其所在的位置

我采用了其中的第一种,因为这种方案体验还不错,并且实现起来比较简单。

第二种方案体验不太好,title属性的提示效果有点延迟,第三种方案实现成本太大了。

---------------------------------------- 2017-12-9 分割线 -----------------------------------------------

有很多开源组件使用了方案三,可以直接用,比如:Ant Design的Popover

---------------------------------------- 2017-12-9 分割线 -----------------------------------------------

最终的效果是这样的:

七、经验总结

这个问题本身是很简单的,之所以折腾了这么久,最主要的原因是之前没有深入去思考过z-index属性和层叠上下文(stacking context)的规则和原理,只是把问题解决了,而没有深入去思考:

  • 为什么这么做可以?
  • 背后的原理是什么?
  • 有没有别的或者更好的方法?

现在这个问题也是一样,如果只是把提示层移下来完事,就不管这个问题了,也就不会深入去思考层叠上下文和z-index的相关知识和原理,这样永远无法真正的进步,无法成为领域的专家,永远只是大厦的建造者,而不是大厦的设计者。

从这个bug的修复过程中,我学到(领悟到)了以下三点经验:

1.关于z-index和层叠上下文原理相关的专业知识

  • 层叠上下文(stacking context)并不只是z-index(必须配合position才能生效)才能创建,还有很多其他元素(如:opacity、transform等)也可以创建层叠上下文,不信点这里
  • 在存在层叠上下文的情况下,z-index的大小决定了层叠水平(stacking level),即谁在谁上面,这是“谁大谁上”原则,不信点这里
  • 层叠水平的比较只有在同一级别的DOM节点的层叠上下文中才有意义,就比如上面例子中的D和E比较是有意义的,但是C和E比较就没有意义了,因为如果D的层级比E小的话,C层级再大也没用,也不会在E之上,不信看这里
  • 在同一DOM节点,并且层级水平一样的情况下,在HTML文档中写在后面的元素会遮住前面的元素(后者会在前者上面),这是“后来居上”原则,不信点这里

2.深入探索的精神

遇到问题,多思考:

  • 问题是如何出现的?
  • 为什么会出现?
  • 涉及到哪一块的知识?
  • 背后的原理是什么?

然后才是着手去解决:

  • 先想方设法自己寻找解决方案
  • 解决了回顾下这个问题,对这个问题进行抽象,看下有没有更好的解决方案
  • 参考别人是如何解决这类问题的,别人的方法有什么优劣,并学习别人的闪光点,用微创新的方式,试着对现有的解决方案进行改良、优化、重构

3.解决问题的方法论

  • 遇到问题,先分析这是个什么问题(如何分析?需要扎实的基础和丰富的实践经验),并根据自己的猜测去实验试错
  • 自己解决不了,再自行Google/Baidu,并继续实验试错
  • 还是解决不了,问导师、同事、朋友、网友(论坛、QQ群等)提供解决的思路(如何提问?点这里)

八、参考资料

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

会改变层叠上下文的情况:

翻译过来就是:

  • 根元素
  • position(值为"absolute"或"relative") + z-index(不为"auto")
  • flex item(即父元素有"display:flex|inline-flex"属性的元素) + z-index(不为"auto")
  • opacity小于1的元素
  • transform不为"none"的元素
  • mix-blend-mode不为"normal"的元素
  • filter不为"none"的元素
  • perspective不为"none"的元素
  • isolation:isolate的元素
  • position:fixed的元素
  • 在will-change中指定了任意CSS属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling:touch的元素

原文地址:https://www.cnblogs.com/kagol/p/10283180.html

时间: 2024-10-09 19:22:46

[BUGCASE]层叠上下文和z-index属性使用不当引发的文本被遮挡的问题的相关文章

深入理解CSS中的层叠上下文和层叠顺序(转)

by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpress/?p=5115 零.世间的道理都是想通的 在这个世界上,凡事都有个先后顺序,凡物都有个论资排辈.比方说食堂排队打饭,对吧,讲求先到先得,总不可能一拥而上.再比如说话语权,老婆的话永远是对的,领导的话永远是对的. 在CSS届,也是如此.只是,一般情况下,大家歌舞升平,看不出什么差异,即所谓的众生平等.但是,当发生冲突发生纠葛的时

CSS中的层叠上下文和层叠顺序

copy @ from http://www.zhangxinxu.com 四.务必牢记的层叠准则 下面这两个是层叠领域的黄金准则.当元素发生层叠的时候,其覆盖关系遵循下面2个准则: 谁大谁上:当具有明显的层叠水平标示的时候,如识别的z-indx值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个.通俗讲就是官大的压死官小的. 后来居上:当元素的层叠水平一致.层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素. 在CSS和HTML领域,只要元素发生了重叠,都离不开上面这两个

[转]深入理解CSS中的层叠上下文和层叠顺序

http://www.zhangxinxu.com/wordpress/2016/01/understand-css-stacking-context-order-z-index/ 零.世间的道理都是想通的 在这个世界上,凡事都有个先后顺序,凡物都有个论资排辈.比方说食堂排队打饭,对吧,讲求先到先得,总不可能一拥而上.再比如说话语权,老婆的话永远是对的,领导的话永远是对的. 在CSS届,也是如此.只是,一般情况下,大家歌舞升平,看不出什么差异,即所谓的众生平等.但是,当发生冲突发生纠葛的时候,显

深入理解CSS中的层叠上下文和层叠顺序

零.世间的道理都是想通的 在这个世界上,凡事都有个先后顺序,凡物都有个论资排辈.比方说食堂排队打饭,对吧,讲求先到先得,总不可能一拥而上.再比如说话语权,老婆的话永远是对的,领导的话永远是对的. 在CSS届,也是如此.只是,一般情况下,大家歌舞升平,看不出什么差异,即所谓的众生平等.但是,当发生冲突发生纠葛的时候,显然,是不可能做到完全等同的,先后顺序,身份差异就显现出来了.例如,杰克和罗斯,只能一人浮在木板上,此时,出现了冲突,结果大家都知道的.那对于CSS世界中的元素而言,所谓的“冲突”指什

关于css的层叠上下文和层叠顺序问题

关于css的层叠上下文和层叠样式问题 最近在项目中遇到了一个让我欲仙欲死的问题,我给项目中的图片设置了一个淡入效果,几opacity变化,但当我在它的上面有一个定位元素时,动画结束后,定位元素居然被遮住了,百思不得解,谷歌了白天,才知道是层叠上下文.层叠顺序搞得鬼,所以这里把搜索的结果记下来,帮自己,也帮能够看到的盆友. 这里贴出一个解释很详细的地址,要是看了不太明白的,可以戳这里哦 首先层叠上下文是什么鬼呢? 层叠上下文,英文称作"stacking context". 是HTML中的

图解Elasticsearch中的_source、_all、store和index属性

Elasticsearch中有几个关键属性容易混淆,很多人搞不清楚_source字段里存储的是什么?store属性的true或false和_source字段有什么关系?store属性设置为true和_all有什么关系?index属性又起到什么作用?什么时候设置store属性为true?什么时候应该开启_all字段?本文通过图解的方式,深入理解Elasticsearch中的_source._all.store和index属性. 图1 Elasticsearch中的_source._all.stor

使用jstl的Foreach 和jquery的each()的index属性

最近项目中用到隔行换色问题,使用到了jstl的foreach和jquery的each进行遍历. 首先jstl技术.除了常用的items,var外,还有一个下标属性varStatus,从0开始,使用起来很是方便. <c:forEach items="${persons }" var="person" varStatus="i"> <c:if test="i%2==0"> do something <

(一)javascript中的数组index属性——获取数组的索引值

例如:要做到这样的效果 点击每个选项时,会显示不同的div. 我们的做法:在javascript中,先把所有的div的display设置为none,然后在根据当前的数组里的索引值进行一个显示div的过程. 下面的例子就是: 首先,把妙味课堂.妙味茶馆.苗味视频选项的div设置为display:none: 然后,早在之前就以前设置好了数组的索引值 btn[i].index=i; 于是当所有div都设置为display:none后,再把点击的那个div的display设置为block就可以了 con

jq第二天 (属性-CSS 类 ,html/文本/值 )

addClass() 方法向被选元素添加一个或多个类. <script> $(function(){ $('button').click(function(){ $('p').addClass('nss') }) })</script><style type="text/css"> .nss{background-color: #2e6da4}</style> <p>4565665</p><p>2131