1、项目背景:bug出现
近期 《高质量代码的项目:SmartAdmin》 有人反馈 ant desgin vue
的popup
组件被 “ 标签页 ” 遮挡(如下图), 于是 作为“十年全栈且六边形”的开发人员直接将 z-index
改为了 9999
,发现竟然不生效,然后浪费了一天摸鱼时间才解决,记录下z-index
的一些好玩的问题。
2、问题复现: 好神奇
当遇到这种问题上来肯定F12
来看下z-index
层级;
第一步,查看下层:标签页 PageTagDiv
的z-index
的大小,发现为:3;
第二步,查看popup
的 z-index
的大小,发现为:1030;
神奇的结果:
z-index
为 1030
的popup
组件 竟然被 z-index
层级为 3
的 PageTagDiv
组件 遮挡了!
按照前端z-index原理,数值越大越在上层,不被遮挡,所以就排查吧
3、解决问题第一阶段:position问题
在我的前端技术知识中,有印象 position
属性影响 z-index
是否生效,于是百度查了下,很多文章有说position
属性影响了z-index
,摘抄如下:
z-index属性用于控制元素在层叠上下文中的显示顺序。当z-index不起作用时,可能是由于以下几个原因:
- 没有指定元素的定位属性:z-index属性只对定位元素(position属性值为relative、absolute或fixed)起作用。如果元素没有指定定位属性,z-index将不会生效。因此,需要确保元素的position属性已正确设置。
- 元素的定位属性值不正确:如果元素的定位属性值设置不正确,z-index也不会生效。例如,如果元素的position属性值为static(默认值),则z-index属性将不起作用。需要将元素的position属性值设置为relative、absolute或fixed。
- 元素的层叠上下文不正确:每个层叠上下文都有自己的层叠水平,z-index只在同一层叠上下文中才有意义。如果元素的z-index属性没有在正确的层叠上下文中设置,它将无法影响其他层叠上下文中的元素。可以通过设置父元素的position属性值为relative、absolute或fixed来创建新的层叠上下文。
- 元素的z-index值不正确:如果多个元素都具有定位属性且属于同一层叠上下文,那么z-index值较大的元素将覆盖z-index值较小的元素。因此,需要确保所需元素的z-index值较大。
总结起来,要使z-index生效,需要确保元素具有正确的定位属性(relative、absolute或fixed),在正确的层叠上下文中,并且具有较大的z-index值。如果仍然无法解决z-index不起作用的问题,可能需要检查其他CSS属性或JavaScript代码是否对元素的显示顺序产生了影响。
于是按照上文给的方案各种修改 position
属性,发现并未生效。耗时4小时!
3、解决问题第二阶段:层叠上下文(Stacking Context)
上面文字中有提到 层叠上下文(Stacking Context),我突然有灵感了,以为我有印象 popup
与PageTagDiv
不是一个层级内的,如下图:
红色圈为对应两个组件,他们并不在一个 html根元素中,所以感觉查了下 z-index 层叠上下文(Stacking Context)
3.1、z-index层叠上下文是什么?
MDN官方:假定用户正面向(浏览器)视窗或网页, HTML 元素沿着其相对于用户的一条虚构的 z 轴排开,层叠上下文就是对这些 HTML 元素的一个三维构想。
The stacking context is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user, who is assumed to be facing the viewport or the webpage. HTML elements occupy this space in priority order based on element attributes.
每个网页都有一个默认的层叠上下文,这个层叠上下文的根元素就是html元素。html标签中的一切都被置于这个默认的层叠上下文的一个层叠层上(body)。当一个元素创建一个层叠上下文时,它的所有子元素都会受到父元素的层叠顺序影响。这意味着如果一个层叠上下文位于一个最低位置的层,那么其子元素的z-index设置得再大,它都不会出现在其他层叠上下文元素的上面。
3.2、层叠水平与层叠顺序
“层叠水平”,英文称作“stacking level”,在同一层叠上下文中的不同元素重叠时,它们的显示顺序会遵循层叠水平的规则,而z-index能够影响元素的层叠水平。
重点:在讨论元素基于层叠水平进行排序时,是限制在单个层叠上下文内的。层叠水平不等于z-index属性,所有的元素都存在层叠水平,而z-index属性只能改变定位元素及flex盒子的孩子元素的层叠水平。
再来说说层叠顺序。“层叠顺序”,英文名为“stacking order”,表示元素发生层叠时候有着特定的垂直显示顺序,这里需要注意,上面的层叠上下文和层叠水平是概念,而这里讲到的层叠顺序则是规则。
在一个层叠上下文中按照层叠顺序把元素分为7种层叠水平,默认的层叠顺序如下图所示:
解释如下:
- (1)背景和边框--形成层叠上下文元素的背景和边框。位于层叠上下文中的最底层。
- (2)负z-index--层叠上下文内z-index值为负的定位元素。
- (3)块级盒子--层叠上下文中非行内非定位元素。
- (4)浮动盒子--非定位浮动元素。
- (5)行内/行内快盒子 -- 层叠上下文中,inline和inline-block非定位元素。
- (6)z-index:0 /auto -- 定位元素。单纯考虑层叠水平,两者表现一样,但实际上对层叠上下文影响不一样。
- (7)正z-index值 -- 定位元素。z-index值越大,越靠近用户。
在平时开发时,我们经常会使用(2)、(6)、(7),大部分元素的层叠水平都低于z-index为0的定位元素。
为什么inline/inline-block元素的层叠顺序比浮动元素和块元素都高呢? 因为:border/background一般为装饰属性,而浮动和块元素一般用作布局,内联元素都是内容。网页中展示最重要的是内容,因此内容的层叠顺序比较高,当发生层叠时,重要的文字和图片等内容优先暴露在屏幕上(如下图)。
4、解决问题第三阶段:最终解决
看到z-index上下文以后,终于明白了,因为我的问题中 popup
与PageTagDiv
不是一个层级内的,所以即使z-index
为 1030
的popup
组件大于z-index
层级为 3
的 PageTagDiv
组件,依然会遮挡。
于是目标定位改层级就可以了,查看了下 Ant desgin vue 的 popup
官方文档,有个 getPopupContainer
属性,设置弹出层的容器,这里我设置为返回 body即可,代码如下
function getPopupContainer() {
return document.body;
}
这样就在最外层的<body>
层级内了,然后问题解决,层级图如下:
解决问题5小时!
5、z-index总结
z-index
一定要关注层叠上下文(Stacking Context),即:层叠水平(Stacking Level)与层叠顺序(Stacking Order)都需要关注才能生效。
具体使用技巧总结如下:
- 第一步:首先先看要比较的两个元素是否处于同一个层叠上下文(Stacking Context)中
- 1)如果是,谁的层叠等级大,谁在上面
- 2)如果两个元素不在同一SC中,先比较他们的父SC
- 当两个元素层叠水平相同、层叠顺序相同时,在 DOM 结构中后面的元素层叠等级在前面元素之上
其他注意事项:
- CSS3时,无position属性,z-index值也可能生效,因为css3很多默认
display
为flex
- 浏览器兼容性差异
- 避免在transform元素下做fixed定位
6、感想
一个十年全栈就这样被z-index
浪费了摸鱼时间的8小时,也应了那句话“活到老,写到老”。
感谢文章引用:
1、🔥SmartAdmin以「高质量代码」为核心,「简洁、高效、安全」的快速开发平台;基于SpringBoot2/3+Sa-Token+Mybatis-Plus和Vue3 +Ant Design Vue+UniApp (提供JavaScript和TypeScript双版本、Java8和java17双版本);满足三级等保、网络安全、数据安全等功能要求。并重磅开源千余家企业在使用的《高质量代码规范》等
2、详解z-index