Vue弹框组件如何挂载到合适的页面节点

948 阅读3分钟

需求场景

日常开发中,我们可能会需要实现类似这样的组件,组件带有弹框

在这里插入图片描述

一般实现

一般都是父盒子【position: relative;】弹框盒子相对父盒子【position: absolute;】 这种做法平时不会有什么问题,但是当有父盒子设置【overflow: hidden;】时,就会出现这种现象

在这里插入图片描述

是的,超出的部分已被隐藏了

学会观察

如果认真观察其他ui库的类似组件的话,你会发现它们都被挂载到body下面去了,所以该组件的父盒子影响不到弹窗的盒子 在这里插入图片描述

尝试实现

我们给弹框盒子加上一个ref属性 在这里插入图片描述

在点击事件里,我们使用this.$refs.downSelect获取该节点,并将其追加到body下 在这里插入图片描述

这里使用this.$nextTick(),是因为我控制弹框的显示是使用v-if,需要下次更新才能获取的到,好啦,我们看下效果

在这里插入图片描述

可以看到,已经挂载到body下了,不过你会发现,定位的位置好像不对呀!别急,下一步就是要计算弹框在body定位的位置了

思路讲解

我们原先相对于父盒子定位是没有问题的,现在相对于body,那么就需要计算出父盒子与body的距离 先给父盒子添加ref 在这里插入图片描述

点击事件获取pageChange,并且打印offsetTop属性 在这里插入图片描述

结果是

在这里插入图片描述

讲一下这个offsetTop: 通俗一点讲就是,获取距离最近设置position属性的父节点的顶部距离

来看下例子:

<div class="a">
    <div class="b" style="position: relative;">
    	<div class="c"></div>
    	<div class="c2"></div>
    </div>
    <div class="b2"></div>
</div>

c2.offsetTop获取的就是c2顶部距离b顶部的距离

<div class="a" style="position: relative;">
    <div class="b">
    	<div class="c"></div>
    	<div class="c2"></div>
    </div>
    <div class="b2"></div>
</div>

c2.offsetTop获取的就是c2顶部距离a顶部的距离

注:如果都没有设置position,那c2.offsetTop获取的就是距离body顶部的距离 同样的offsetLeft属性,对应的就是左边的距离

好啦,思路就是这样,我们来试下

继续尝试

我这个demo都没有设置position,我们直接把offsetTop和offsetLeft赋值给弹框的top、left 在这里插入图片描述

看下效果

在这里插入图片描述

嗯,效果还不错,不过还差一点,我们设置下transform: translate(); 偏移量为pageChange的高度 在这里插入图片描述

最终效果如下 截图.png 成功追加到body下了,并且位置也没问题

小小总结下

这篇文章只是讲解了如何将弹框添加到body下,并且获取定位的合适距离 这种情况只适合页面的滚动发生在body上 如果你有设置另外的滚动页面滚动盒子 例如:弹窗里面嵌套选择下拉框 这时就需要将【弹框】挂载到【弹窗】的滚动盒子下面 同样的定位也要相对弹窗的滚动盒子 如果你不确定挂载的父盒子的话,可能需要js查找符合条件的节点啦

我用的是向上递归查找,代码也放一下吧

function dfsOutScrollNode(node) {
	const styles = getComputedStyle(node, null)
	const overflow = styles['overflow']
	const overflow_y = styles['overflow-y']
	const overflow_x = styles['overflow-x']
	let isScroll = ['auto', 'scroll'].indexOf(overflow)
	isScroll += ['auto', 'scroll'].indexOf(overflow_y)
	isScroll += ['auto', 'scroll'].indexOf(overflow_x)
	if (isScroll !== -3 || node === document.body) {
	  return node
	}
	return this.dfsOutScrollNode(node.parentNode)
}

欢迎评论~