前言:css
前不久,同事S遇到了一个关于position和z-index的问题。 他折腾了一天没搞定,群发了邮件寻求帮助, 我一开始觉得很简单,就主动说帮忙,简单尝试以后,才发现貌似没那么简单。 问题主要围绕position和z-index。react
问题:wordpress
咱们项目用的是react,同事S有一个table,table里有row,这个table用了一个第三方component,具体什么component无所谓了,总之这些row都有一个css属性:spa
positon: absolute;
而后,在每一个row中有个自定义的下拉框组件,下拉框点击后,会弹出一个选项的容器,他指望选项(options)的容器div(暂称div-options)在页面的顶层,也就是不会被其余东西遮盖。可是结果就是被当前row以后的row给遮盖了。div-options有以下css属性:code
position: fixed;
问题的初始状态相似以下例子:component
https://codepen.io/bee0060/pen/GGvMxb对象
在个人例子中,能够简单的对div-options添加大于0的z-index属性解决。 只是在我同事的本地,貌似仅仅这样没法解决,具体缘由当时没有意识到因此也就没有细查。 不过我如今的猜想是,每一个row上面都有z-index属性,相似以下例子:(没错,该例子中div-options会被后面的row遮盖)blog
https://codepen.io/bee0060/pen/PaVwvjget
在发现只给div-options增长z-index没法解决问题后,我也尝试建议我同事给每一个row增长一个z-index,并且值小于div-options的z-index, 可是div-options依然被后面的row遮盖。博客
当时我以为真是难以想象,整我的都懵了。
后来试了几种办法没成功就下班回家了。 在下班路上我好想意识到了什么,就回家试了下,发现了如下两种解决方案:
1. 在全部row有z-ind"Wx的状况下, 给div-options所在的row一个较大的z-index值,这样div-options无需z-index也不会被遮盖 , https://codepen.io/bee0060/pen/eKepZG
2. 将div-options提取到table以外,让他们没有父子层级关系,再给一个较大的z-idnex也能够解决,https://codepen.io/bee0060/pen/yEozro
在当前项目环境下,考虑到咱们用的是react,我建议我 同事采用第二个解决方案。由于在显示div-options时去修改所在row的z-index,这个操做要影响的对象太多了,并且会对上级(父级)组件形成影响,感受不太好,要知道,影响的组件越多,就会触发越多的diff和re-render,因此我仍是但愿这个操做只须要通知尽量少的组件就能完成。
问题是解决了,可是到底这问题是怎么引发的呢?
我查阅了一些资料, 下面的是个人一些分析和猜测,主要面向但愿快速解决问题并得到一个大概思路的朋友。
若是须要更详细准确的解释,我须要查阅更多资料后另开一篇博客,或者大家能够看下其余更详细的博客,正好这就有一篇我感受不错的。
好,那么下面我要开始表演了。
缘由分析:
首先,有些废话仍是要说。
我列一下关键的分析依据:
1. 只有已经定位的元素(即position属性值是非static的元素)的z-index属性会生效
2. 在同一堆叠中,z-index值大的遮盖 小的对象
3. 在同一堆叠中,若z-index值相同,后面的对象遮盖前面的对象。
4. z-index为数字(包括0)的元素会在当前堆叠上下文(暂称为堆叠A)中建立堆叠层级(暂称堆叠B),堆叠B可视为堆叠A的子级堆叠。
5. 一个对象只与处于相同堆叠上下文的对象进行z-index的比较并决定哪一个遮盖哪一个。
6. 若A 和B对象处于同一 堆叠上下文,且根据z-index规则已肯定B显示在A上面,那么堆叠B及其全部子级堆叠都将显示在堆叠A以及堆叠A的子级堆叠之上。(由于子级堆叠的显示顺序没法超出父级堆叠的顺序,不管z-index的数值多大)
上面屡次出现”堆叠“这个名词,堆叠能够理解为css空间中,延Z轴方向互相层叠的多个小空间。 不少博客在图例中把堆叠画得像一张纸同样,其实并不许确,由于堆叠中能够有子级堆叠,里面能够有无限个层级,因此我以为理解为空间更合适。 能够理解成较扁平的空间。 而css空间(或你叫DOM或HTML空间也能够)中默认会建立一个堆叠。
看完上面几条,估计你们也有头绪了。 在css空间里,若是对象没有被定位(position不为static)或已定为但z-index不为数字, 那么该对象会被至于父级元素所在的堆叠以内,不然会在父级对象所在的堆叠中建立新的堆叠。 通常状况下,若是页面中全都是不会建立堆叠的对象(未定位或z-index不为数字),那么所有对象都将被置于默认堆叠内。
而每一个堆叠内的子堆叠都没法超出其父堆叠的顺序进行显示,若是把堆叠理解成空间,这个应该比较好理解了。用修仙小说的话说就是:张三是某个世界里的人,他再牛也不能超出你所在的世界(固然通常修真小说写的都是能超出,你能理解个人话就好)。
那么结合以前遇到的问题就好理解了。
- 默认状况下,row只有position:absolute, 而都没有z-index时,他们都处于默认堆叠中,且按row的先后顺序进行遮盖, 而div-options属于某一个 row,按顺序的话,他顶多和他所在的row使用一个显示顺序,因此天然会被后面的row遮盖。
- 单独给div-options添加大于0的z-index后, div-options会在默认堆叠(即全部row所在的堆叠)上建立堆叠层级,且因为z-index大于0,在与处于同一父级堆叠中的全部row对象比较后,因为row都没有z-index,显示顺序和z-index为0时相同,因此div-options会显示全部在row之上,也就不会被遮盖了。
- 在给row都增长一个数字的z-index(即便是0)以后,后续的rows又会遮盖div-options。由于指定了数字z-index的row都会在父级堆叠中建立本身的堆叠,而div-options会在其父级row的堆叠内再建立堆叠。 而div-options的父级row(暂称rowP)只会与处于同一个父级堆叠的其余row的堆叠进行显示顺序比较, 因此后续row会显示在rowP之上。而div-options是rowP的堆叠的子堆叠,因此不管div-options的z-index多大,都没法超出rowP所在的堆叠。因此div-options会被后续rows遮盖。
因此个人两个解决办法就是从两方面 着手:
1. 给rowP更大的z-index,让其显示在全部兄弟rows之上,那么div-options不管有无z-index都不会被后续row遮盖。
2. 将div-options抽出来,再也不做为rowP的子对象,也就不会再守rowP的堆叠顺序影响,进而能够实现让div-options不被后续row遮盖,由于div-options与全部rows能够处于同一个父级堆叠中,谁先谁后就只由他们各自的z-index值决定。
好了,分析思考和猜想就到这了,原本想尽可能简短,但仍是写了那么多。
最后说句最重要的: 若有谬误,欢迎指正!
后话:
在解决过程当中,没在网上找到该问题的相关资料’ 我(虽而后来找到了),并且本身以前研究也不深,本着DRY原则,避免下次遇到又要花时间折腾,故写下此文。
参考资料:
2. 《CSS权威指南》