z-index失效原因分析——由一个bug引发的对层叠上下文和z-index属性的深度思考
新年刚开工就被一个bug虐得整个人都不好了,特地记录下。
(一)bug描述
在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段提示的特效,所以做了一个提示层,但是这个提示层被固定的表格列遮住了,并且无论设置该层的z-index为多大都不能让其在固定列之上,效果如下:
(二)原因分析
通过对页面的html元素层级进行分析,把有可能影响层级的部分抽出来:
主要有这四部分会影响到元素的层级(关于设置了哪些属性会影响层级请看后面的附),下面逐一分析:
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;
另外,A、B最近的会创建层叠上下文的父元素F有这些样式: z-index: 1;
transform: translate3d(0px, 0px, 0px);
position: absolute;
(其实不会,因为子元素创建的所有层叠上下文只在父元素的层叠上下文中有效)
A元素的直接子元素E的样式: position: absolute;
width: 140px;
z-index: 2;
transform: translate3d(0px, 0px, 0px);
A和B是处于同一个层叠上下文(由其E创建的)中的,这样的话,应该是后面的元素(B)会覆盖前面的元素(A),但现在并不是这样。
原因:A最近的子元素F创建的层叠上下文(z-index:2)比B最近的子元素C(z-index:0)创建的层叠上下文高。
结论:这就导致了B的所有子元素(当然也包括我们的提示层C)都会比A的层级低,所以D的z-index设置为多大都没用。
(三)问题抽象
本来是一个应用场景中的问题,我们可以抽象为以下问题:
两个兄弟元素A和B,A的直接子元素E,其层级是2(position: absolute;z-index:2;transform: translate3d(0px, 0px, 0px);),
B的直接子元素D,其层级是0(position: absolute;z-index:0;transform: translate3d(0px, 0px, 0px);),
D下面还有一个子元素C,层级很大(z-index:9999),并且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,想直接看结论的可直接看最后一个Demo):
(五)解决方案
单纯从抽象出来的问题来看,解决的方案是D不要设置任何会创建层叠上下文的属性,并且让C的层级比E的高,这样的话,E自然会遮住B和D,而C又会遮住E,这就是我们要的效果。
不过在实际的项目环境中,D的transform属性一定会有(且其值会随表格的水平滚动条的拖动而改变),没法改变,所以在我看来这个问题似乎是无解的。
最后感谢我们前端组的同事water大神提供的三个解决方案:
- 让提示层往下移一点
- 把提示层改成浏览器默认的title
- 将提示层C放在D的外层,并控制其所在的位置
我采用了其中的第一种,因为这种方案体验还不错,并且实现起来比较简单。第二种方案体验不太好,title属性的提示效果有点延迟,第三种方案实现成本太大了。
最终的效果是这样的:
(六)经验总结
这个问题本身是很简单的,之所以花了折腾了这么久,最主要的原因是之前没有深入去思考过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群等)提供解决的思路(如何提问?点这里)。
附(会改变层叠上下文的情况):
翻译过来就是:
- 根元素<html>
- 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的元素
z-index失效原因分析——由一个bug引发的对层叠上下文和z-index属性的深度思考的更多相关文章
- kubectl get 后按2次tab键命令补全的失效原因分析
kubectl get 后按2次tab键命令补全的失效原因分析 2019/10/28 Chenxin a.bash客户端工具 在centos用户下, cd ~;echo "source &l ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- 关闭浏览器后Session失效原因分析
参考文章:http://www.tuicool.com/articles/VNbYjqm 首先需要理解一下几点: 1.Http是无状态的,即对于每一次请求都是一个全新的请求,服务器不保存上一次请求的信 ...
- Hexo next博客的pjax一个Bug引发的关于pjax用法的小技巧-----pjax后图片点击放大的js失效
文章目录 广告: 背景 发现 解决 get技能 广告: 本人博客地址:https://mmmmmm.me 源码:https://github.com/dataiyangu/dataiyangu.git ...
- Dorado7检验器失效原因分析
应用场景: AutoForm1使用包含A.B两个字段的DataType1. A字段不允许为空,并且B字段取值true时A字段需要重新输入,B取其它值时A值不需重新录入. 实现方法:A字段添加了非空检验 ...
- Jquery("#form_content").validationEngine()失效原因分析
使用validationEngine()函数对表单进行各种校验,由于多个页面都引用了相关js文件,后面子页面的validationEngine()始终不生效:....: 测试后发现重复引用了JQuer ...
- MyBatis 学习记录7 一个Bug引发的思考
主题 这次学习MyBatis的主题我想记录一个使用起来可能会遇到,但是没有经验的话很不好解决的BUG,在特定情况下很容易发生. 异常 java.lang.IllegalArgumentExceptio ...
- .net remoting和wcf自托管——一个bug引发的警示
一.解决问题,需要深入,并从细节入手,多从代码找原因,不能认为代码是死的,不会出错: 之前代码都运行良好,突然某一天,在我电脑上出问题了.出了问题,那就应该找出原因.其实这个问题,本身并不难,好歹给你 ...
- MySQL 5.6的一个bug引发的故障
突然收到告警,提示mysql宕机了,该服务器是从库.于是尝试登录服务器看看能否登录,发现可以登录,查看mysql进程也存在,尝试登录提示 ERROR (HY000): Too many connect ...
随机推荐
- BIM+GIS它们各有什么优缺点
BIM+GIS它们各有什么优缺点?应用有哪些优势?BIM模型精细程度高,语义信息丰富,侧重整合和管理建筑物自身所有阶段信息,包括建筑物所有微观图形化和非图形化信息,三维GIS侧重宏观.大范围地理环境与 ...
- DevExpress XtraReport报表预览时可编辑的功能
设置控件的EditOptions.Enabled=true即可 注册实现PrintingSystem. EditingFieldChanged事件,可获得当前发生更改的控件的值. 另:XtraRepo ...
- 线程队列分享ppt
- UNP——第六章,多路转接IO——select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ...
- java多线程---张孝祥
1.java web 中,一次http请求是一个任务,因为服务器里面有线程池的,存在一个线程处理多个请求任务. 2.在java中,vector,hashtable,concurrentHashMap是 ...
- rbd的增量备份和恢复
前言 快照的功能一般是基于时间点做一个标记,然后在某些需要的时候,将状态恢复到标记的那个点,这个有一个前提是底层的东西没用破坏,举个简单的例子,Vmware 里面对虚拟机做了一个快照,然后做了一些系统 ...
- 使用iptables做端口转发
通过iptables可以做转发 #!/bin/sh IPT="/sbin/iptables" /bin/echo "1" > /proc/sys/net/ ...
- API简介(二)
API简介(二) API简介(一)一文中,介绍了使用API的目的.设计.发行政策以及公共API的含义,本篇主要介绍API的用法,从库和框架.操作系统.远程API.Web API四个方面展开. 库和框架 ...
- 流量控制--5.Classless Queuing Disciplines (qdiscs)
Classless Queuing Disciplines (qdiscs) 本文涉及的队列规则(Qdisc)都可以作为接口上的主qdisc,或作为一个classful qdiscs的叶子类.这些是L ...
- 基于gin的golang web开发:集成swagger
在前后端分离的项目维护一份完整且及时更新的api文档会极大的提高我们的工作效率,传统项目中接口文档都是由后端开发手写的,这种文档很难保证及时性,久而久之便失去了参考意义.swagger给我们提供了一种 ...