十年全栈又花8小时,给出局部全屏的各种弹窗被遮挡的终极解决方案

196 阅读4分钟

1、项目背景:新bug又出现

即前两天出现 十年全栈,8个小时解决z-index不生效问题 后又出现了神奇问题,近期 《高质量代码的项目:SmartAdmin》 有人反馈 table 局部全屏下,第二个Modal弹窗,会被第一个Drawer抽题遮挡,如下图,经排查,也不是 z-index问题,最终祭出一个终极解决方案,能彻底解决各类局部全屏情况下的弹窗遮挡等问题。

image.png

2、需求与实现:很经典的一个需求场景

需求:

项目是是需要表格table有个全屏操作的按钮,点击会将表格进行全屏,如下图; image.png

代码实现:

代码使用的是浏览器自带的方法,将 tabledom元素根据浏览器类型,进行全屏操作,伪代码如下:

let contentElement = document.getElementById("smartAdminContent");
contentElement.requestFullscreen();
element.webkitRequestFullScreen();
....

3、Bug复现

非全屏模式下,第一步Drawer抽屉,然后第二步弹出Modal是没有问题的; 但是全屏模式下,会出现各种弹窗的问题,如:Modal不显示、下拉框a-select不显示等等各类问题;真可谓是神奇! 感兴趣的大佬可以写一个小demo试试。

4、尝试解决思路1:z-index问题

即前两天出现 十年全栈,8个小时解决z-index不生效问题 后彻底了解了z-index层级问题的知识后,反应应该是此问题,于是开启z-index排查,发现还真是 z-index,我一修改z-index数值大小就好了。直接修改代码,调整z-index如下:

image.png

一切顺利,但是 a-select 又不显示了,难不成都要改一遍z-index?疯了吗?

5、尝试解决思路2:上网查

网上总结下几个解决方案:

方案1:

修改getContainer,改为 body或者全屏元素,如下代码;结论:不可用,不可能每个组件都改一下,太麻烦了。

message.config({
  maxCount: 3,
  getContainer:() => document.getElementById('thisPage') || document.body //父组件元素ID
})

方案2:

改变挂载对象。挂到根节点上:document.documentElement,再使用定位fixed目标元素,改变z-index

  • 问题:position: fixed滚动条无法滚动;
  • 原因:position:fixed相对于视口viewport的定位,元素在屏幕滚动时不会发生改变;
  • 解决方案:当元素祖先的 transform, perspectivefilter 属性非 none 时,容器由视口改为该祖先。同时给祖先元素设置一个transform: scale(1)属性。

结论都好复杂,且不容易理解。

6、尝试解决思路3:终极解决方案

根据以上方案带来的思路,发现所有的矛盾点都指向了 局部元素 全屏,因为局部元素 全屏的优先级要比 body 要高,但是所有 ModalMessage等又都是挂载到到body上,那么冲突就来了,根本解决不了,于是换了一个思路豁然开朗了;

既然矛盾都出在了body上,那么我就直接将body全屏不就可以了,和之前非全屏一样,ModalMessage等挂载到到body上就没有问题了。

模型如下:非全屏的元素全屏的时候都隐藏,然后全屏元素铺满body,全屏body即可。

aaa.png

代码如下:

111.png

222.png

7、局部全屏总结

在使用 ant design vue或者element plus等类似UI框架都会出现这个局部全屏,弹出框或者抽屉等等被遮挡的情况,原理呢如上述第6点,放弃z-index解决思路,直接从body下手,从根本上解决问题:

全屏时:

  • 1)非全屏其他元素 全部隐藏
  • 2)修改定位,将全屏元素充满body
  • 3)将body全屏

退出全屏时:

  • 1)非全屏其他元素 全部显示
  • 2)修改定位,将全屏元素恢复之前
  • 3)取消body全屏

其他注意事项:

  • 隐藏可以使用vuev-show
  • 跨组件的标识可以使用 pinia存储全屏状态

8、感想

一个十年全栈再次被局部全屏浪费了摸鱼时间的8小时,又应了那句话“活到老,写到老”。

感谢文章引用:

1、高质量代码SmartAdmin
2、全屏的坑