📋大纲
🚀简介
1.问题解决
1.1.代码部分
1.2.核心部分
1.3.逻辑部分
2.核心思路
2.1.微/宏任务
3.个人理解
简介
自己遇到了这种问题也是找了许多文章,都进行了尝试,但是基本没什么作用;最后找到了这个一篇文章,看了一下,茅塞顿开;也顺利的解决了这个问题;后来那篇文章找不到了;自己写一篇吧,算是做个记录吧;
如果这篇文章解决了您的问题,帮忙点个赞哦👍;
✍创作不易;
🤖解决问题先~
👀代码部分
vue
<div class="rBtm" ref="tooltipBox">
<div class="rBtmL">
<div class="personBox" v-for="(value, key, index) in dataLeft" :key="key">
<div class="personIcon">
<img :src="dataLeft[key].icon" alt="">
</div>
<div class="contentPerson" ref="contentPerson">
<el-tooltip :disabled="!isShowTooltip[index]" ref="myTooltip" :append-to-body="false" class="item" effect="dark"
:content="computedContent(formData[value.value])" placement="top">
<span ref="content" @mouseenter="toggleTooltip(index)" v-if="formData[value.value]" class="ellipsis">{{
formData[value.value] }}</span>
<span v-else style="font-size: 14px;color: #ccc;">暂无数据</span>
</el-tooltip>
</div>
<div class="personKey">
{{ key }}
</div>
</div>
</div>
<div class="rBtmR">
<div class="rBtmR-item" v-for="(value, key) in dataRight" :key="key">
<span class="itemKey">{{ key }}</span>
<span class="itemValue" v-if="formData[value]">{{ formData[value] }}</span>
<span v-else style="font-size: 14px;color: #ccc;">暂无数据</span>
</div>
</div>
</div>
🎯核心部分
ref
1.tooltipBox
定位 父级盒子
2.myTooltip
定位 组件盒子
3.content
定位 自身
4.toggleTooltip
鼠标事件
5.第 10 行代码 鼠标悬停事件 引用@mouseenter="toggleTooltip(index)"
v-for
注意以上代码用的是 v-for
🎶逻辑部分
javaScript
// path:src/utils/common.js
// 检查是否全屏
export function isFull () {
// 判断浏览器是否处于全屏状态 (需要考虑兼容问题)
// 火狐浏览器
let screenFull = document.mozFullScreen ||
document.fullScreen ||
// 谷歌浏览器及Webkit内核浏览器
document.webkitIsFullScreen ||
document.webkitRequestFullScreen ||
document.mozRequestFullScreen ||
document.msFullscreenEnabled
if (screenFull === undefined) {
screenFull = false
}
return screenFull
}
...
javaScript
import { isFull } from '@/utils/common.js';
export const bimGeoMixin = {
data() {
return {
isShowTooltip: [], // 控制每个 el-tooltip 的显示状态,使用数组来保存多个 tooltip 的状态
};
},
methods: {
toggleTooltip(index) {
// 如果全屏状态下,需要特殊处理
if (!isFull()) return;
setTimeout(() => {
if (this.$refs.myTooltip) {
this.$refs.myTooltip.forEach((item) => {
// 可以打印 item 看看呢;console.log(item,'item',index)
this.$refs.tooltipBox.appendChild(item.popperVM.$el);
});
}
}, 100);
}
};
🤖核心思路
逻辑
🌼 首先要给鼠标悬停的文字部分加上一个
鼠标悬停
事件;1.在此事件内判断当前是否为全屏状态
isFull
;2.判断浏览器是否处于全屏状态 (需要考虑兼容问题)
3.如果不是全屏状态则不需要特殊处理,直接
return
;4.因为某个盒子进入全屏之后,会发生
重排重绘
,虽然 Vue 的 DOM 更新可能已经完成,但全屏状态触发的布局变化可能还在进行中,因此this.$nextTick(微任务)
的时机不够晚,无法捕捉到这些变化。所以这里我是用了setTimeout(宏任务)
延迟执行了 100 毫秒,给浏览器足够的时间完成全屏引发的布局计算和渲染操作。全屏模式下的重绘和重排有时比 Vue 的 DOM 更新要慢一些。
setTimeout
会等到这些操作结束后再执行代码,从而确保el-tooltip
的 DOM 正确渲染并可以被附加到新的位置。5.因为数据是多个,所以代码中用到了 v-for ,所以这里的
el-tooltip
是多个的,所以需要 变量isShowTooltip
去保存每一个的状态,当然 在toggleTooltip
事件中需要传递index
,以便分辨出应该触发哪一个el-tooltip
组件的提示框;
🤖个人理解
全屏状态下,el-tooltip无法展示提示框的原因可能是因为以下几点:
1.el-tooltip 这些元素都是插入到body里面的,而当某一div全屏后由于层级原因无法显示这些组件;
2.全屏模式下,浏览器会对 DOM 进行重新渲染和布局。在这种情况下,tooltip 可能无法正确计算其位置,导致其未能显示或显示在错误的位置。
上述代码主要是获取 el-toolitp 元素,在元素全屏时,找到el-tooltip的祖父元素然后重新插入到 祖父父级盒子里;
⏰微任务、宏任务
概念:
宏任务(Macrotask) 和 微任务(Microtask) 是指在 JavaScript 异步任务处理中的两种不同类型的任务。这两种任务的执行顺序和优先级不同,理解它们有助于掌握 JavaScript 的事件循环机制。
宏任务:
宏任务 是指由宿主环境(如浏览器或 Node.js)调度的任务,每个宏任务会在事件循环的每次循环中执行一个。 常见的宏任务
-
setTimeout
-
setInterval
-
setImmediate
(Node.js 环境) -
I/O
事件回调(如文件读取、网络请求等) -
UI 渲染
(浏览器的重排、重绘) -
用户输入事件(如
click
、mousedown
、keyup
等)
微任务:
微任务 的执行时机比宏任务更早,它们会在当前宏任务执行完毕后、下一个宏任务开始之前执行。微任务通常用于处理异步操作的后续逻辑,比如 Promise
的回调。
常见的微任务:
Promise.then
、Promise.catch
、Promise.finally
MutationObserver
process.nextTick
(Node.js 环境)nextTick(()=>{})
(vue)
宏任务与微任务的区别:
执行顺序:
- 在每轮事件循环中,宏任务会首先执行。
- 在宏任务执行完后,所有微任务队列中的任务会依次执行,直到微任务队列清空后才开始执行下一轮宏任务。
优先级:
- 微任务的优先级比宏任务高,因此微任务会在当前的宏任务完成后立即执行,而无需等待下一个事件循环。
任务调度:
- 宏任务是由事件循环调度的。
- 微任务是在每个宏任务执行完毕后立即执行的,不会等到下一轮事件循环。
事件循环中的宏任务和微任务流程:
- 先执行同步代码,所有同步代码都属于当前宏任务。
- 当前宏任务执行完后,执行所有的微任务(如
Promise
回调)。 - 如果微任务队列清空,检查是否有新的宏任务(如
setTimeout
回调),如果有,执行新的宏任务。 - 重复上述过程,不断循环。
结束~ 拜拜;