解决vue多组件页面中,组件内部遮罩层的层级问题

在vue项目中遇到了一个问题: 某个多组件页面中有多个组件,组件内部做了遮罩样式,出现了A组件中遮罩总是无法遮盖B组件的问题。对此问题进行研究

关于z-index需要注意的一些问题


<template>
	<div class="content">
		<div>
			我想象在黄昏和黑夜的边界,有一条极窄的缝隙,另一个世界的阴风从那里刮过来。坐了几个黄昏,我似乎有点明白了。有一种消沉的力量,一种广大的消沉,在黄昏时来。在那个时刻,事物的意义在飘散。在一点一点黑下来的天空中,什么都显得无关紧要。你先是有点慌,然后释然,然后你就不存在了。那种感受,没有亲身体验,实在难于形容。如果你在山野中,在暮色四合时凝望过一棵树,足够长久地凝望一棵树,直到你和它一并消融在黑暗中,成为夜的一部分——这种体验,经过多次,你就会无可挽回地成为一个古怪的人。对什么都心不在焉,游离于现实之外。本地有个说法,叫心野掉了。心野掉了就念不进书,就没心思干活,就只适合日复一日地坐在野地里发呆,在黄昏和夜晚的缝隙中一次又一次地消融。你就很难再回到真实的人世间,捡起上进心,努力去做一个世俗的成功者了。因为你已经知道了,在山野中,在天一点一点黑下来的时刻,一切都无关紧要。知道了就没法再不知道。
		</div>
		<div class="div1">
			<div class="div1-child1"></div>
			<div class="div1-child2"></div>
			<div class="mask"></div>
		</div>
		<div class="div2">
			<div class="div2-child1"></div>
		</div>
	</div>
</template>

<script>
</script>

<style lang="scss" scoped>
	.content {
		display: relative;
		color: #FFF;
		font-size: 20px;
	}

	.div1 {
		position: absolute;
		left: 0;
		top: 0;
		width: 500px;
		height: 500px;
		display: flex;
		align-items: center;
		justify-content: space-around;
		background-color: #FFF;
		z-index: 1;
		div {
			background-color: #0077AA;
			width: 100px;
			height: 100px;
		}

		.mask {
			position: fixed;
			top: 0;
			left: 0;
			bottom: 0;
			right: 0;
			width: 100%;
			height: 100%;
			background-color: rgba($color: blue, $alpha: 1);
			z-index: 1;
		}
	}

	.div2 {
		position: absolute;
		right: 0;
		top: 0;
		width: 500px;
		height: 500px;
		display: flex;
		align-items: center;
		justify-content: space-around;
		background-color: red;
		z-index: 2;
		// opacity: 0.9;
		div {
			background-color: yellow;
			width: 100px;
			height: 100px;
			z-index: 9;
		}

		.mask {
			position: fixed;
			top: 0;
			left: 0;
			bottom: 0;
			right: 0;
			width: 100%;
			height: 100%;
			background-color: rgba($color: blue, $alpha: 0.5);
			z-index: 1;
		}
	}
</style>
  1. 只有当元素的position为:relative,absolute,fixed等脱离了文档流的定位时,z-index才会生效。
  2. z-index的值并不是一定越大越靠前。在同一父元素内部,z-index值越大越靠前。在不同父元素中,元素优先比较同级父元素的z-index。同层级(在同一父元素下)的元素,当且仅当某一元素z-index大于其他兄弟元素,此元素的子元素才能遮盖此元素的兄弟元素。如代码所示
    • 在div1中,child1和child2之间层级数越大,越靠前
    • 在div1和div2中, div1的层级为1,其内部的遮罩mask层级永远无法遮盖div2,而div2中的mask可以遮盖div1
  3. 使用opcity<1属性会形成堆叠上下文。形成的层叠结构在z-index: auto 和 z-index: 1 之间。这也导致,如果同层级元素都设置opcity属性,元素的子元素层级永远无法遮盖其兄弟元素的子元素。

tips: 以下属性都是形成堆叠上下文

  • 根元素 (HTML),
  • z-index 值不为 "auto"的 绝对/相对定位,
  • 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex,
  • opacity 属性值小于 1 的元素(参考 the specification for opacity),
  • transform 属性值不为 "none"的元素,
  • mix-blend-mode 属性值不为 "normal"的元素,
  • filter值不为“none”的元素,
  • perspective值不为“none”的元素,
  • isolation 属性被设置为 "isolate"的元素,
  • position: fixed
  • 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考 这篇文章)
  • -webkit-overflow-scrolling 属性被设置 "touch"的元素

考虑到的解决方案有三种

  1. 用vue3新提供的 teleport 组件
<teleport to='#app'></teleport>

teleport组件可以将其内部包裹的元素添加到页面中的其他位置。to的属性值可以是body或者其他可使用的 元素查询字符串。比如 #app。在实际使用中发现,vue中,teleport 只能将元素添加到 #app 根元素下,或者#app外的其他元素(待确定,还请指正),且添加到其他元素后样式要部分重写。不符合项目需求,放弃

  1. 直接将需要遮罩的组件放在跟路径下。此操作需要改变项目结构,工作量比较大。适合拉新项目的时候使用。另外 由于组件嵌套比较多,建议使用eventBus传值

  2. 不改变项目结构,当需要弹窗是把弹窗所在父元素的z-index 设为最高。此方法修改最少,最终采用这种。

Logo

前往低代码交流专区

更多推荐