elementUI表格中插入气泡,会出现bug"来回切换tab页气泡会乱串",导致这个问题的原因是,elementUI底层是通过js绘制的气泡,挂在到body中,通过display:none来控制显隐;当我们来回切换tab页的时候,时而出现display:block;的情况,这时候上一个界面的气泡就再新界面出现,导致气泡乱串了。
为了解决这个问题,尝试过好几种方式,目的就是让气泡隐藏成功或者销毁成功,但是发现这种思路只会让气泡乱串出现的频率降低,并不能从根本上解决问题,而且还会带来新的界面bug。下面先介绍一下错误的思路,带来什么样错误的bug.
第一种方式: 移出气泡dom,销毁气泡元素
// 移出气泡dom
$route: {
handler: function (val, oldVal) {
if (val !== oldVal) {
let dom = document.getElementsByClassName('ai-tooltip__popper')
for(let item of dom){
console.log(item)
document.body.removeChild(item)
this.$destroy(true);
}
}
},
// 深度观察监听
deep: true
}
在watch监听中,监听路由变化时,就销毁气泡。这时候给我们的界面带来一个bug,每当点击弹窗,关闭弹窗,然后打开有内容非常多的气泡界面,我们的elementUI表格就不能自适应,就会有一片空白,这个问题的出现是min-width失效了,this.$destroy(true);就是这行代码引发的。
第二种方式: 隐藏气泡,移出气泡
// 移出气泡dom
$route: {
handler: function (val, oldVal) {
if (val !== oldVal) {
let dom = document.getElementsByClassName('ai-tooltip__popper')
for(let item of dom){
document.body.removeChild(item)
// console.log(item.style,'style')
item.style.display = 'none'
}
}
},
// 深度观察监听
deep: true
}
这种方式,你会发现强制更改后,控制台里面当你来回切换tab页,是成功的,但是标签上偶然会失效;可以减少出现的频率;但是导致一个新的bug,那就是用了表格中气泡出现的属性,这种情形下的气泡不出现。
如果你遇到的需求和我的需求一致,那么你可以采用下面这种方式。如果你遇到的需求不一致,也有同样的bug,你可以看看下面这种方式,说不定可以找到灵感。
先说一下我们的需求(在前面有的篇幅中提到过这个需求):
1、表格高度固定4行,超过显示气泡,未超过不显示气泡;
2、表格内、气泡内需要显示原始格式(换行、空格等要与表单中保存是保持一致)
3、气泡内容太多,或导致界面闪屏,用以前提供的方法,给出最大高度,最大宽度,超过出现滚动条。
(也有固定8行的,下面例子中只介绍4行这种demo. 8行的,和这个是一样的。)
这里还要说明一下,这里的样式与纯css绘制气泡,支持适应最大宽度,最大高度这篇文中的有一些不同,虽然采用的是方式二: 将hover的目标与隐藏对象当作相邻的兄弟元素。
不同的地方在与定义方式不同,如果采用决定定位,气泡是不能超过表格中body的线的,及时给了z-index:9999,也是没用的,就像这个图一样。所以这里采用了固定定位

另外,这里隐藏的方式也不同,这里最开始采用的是display:none;后来发现,在计算气泡高度的时候,会有时候拿不到高度,这就与隐藏后是否占位置有关了。所以这里用了visibility: hidden;visibility: visible;来控制显示隐藏。
如果您需要了解表格根据高度显示气泡问题,可以先查看这篇文章:
elementUI表格内容根据高度来判断是否显示气泡,表格单元格显示原始格式(换行、空格
表格气泡内容显示不全,闪烁问题,可以先查看这篇文章:
elementUI中,表格内容太多,导致气泡会出现闪烁,或者显示不全问题解决
当前demo中也是用气泡滚动条来解决的,本例中未使用elementUI中的el-tooltip组件。
下面正式介绍实现方式:
界面效果展示:

样式方面:
// 方式二: 将hover的目标与隐藏对象当作相邻的兄弟元素
// 气泡盒子
.hover-box-nex{
/*display: none;*/
height: auto;
width: auto;
visibility: hidden;
position: fixed;
padding: 10px;
width: max-content;
max-width: 600px;
z-index:1111;
height: auto;
background: #f0f6fb ;
color: #0a92d9 ;
font-size: 16px ;
border: 1px solid #0a92d9 ;
line-height: 1.2;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
.tip-box-div{
max-height: 245px;
overflow-y: auto;
@media screen and (min-width: 1801px) {
max-height: 410px;
}
}
&:after{
content: '\00a0';
width: 0;
height: 0;
display: block;
border-color: transparent;
border-style: solid;
border-width: 6px;
position: fixed;
z-index:-1;
}
// 靠上
&.tip-top{
&:after{
border-bottom-color: #0a92d9 !important;
}
}
// 靠下
&.tip-bottom{
&:after{
border-top-color: #0a92d9 !important;
}
}
}
//hover的目标
.hover-box{
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
top:6px;
}
// //hover的目标 // 气泡盒子
.hover-box:hover + .show-hover-box-nex{
/*display: list-item;*/
/*list-style-type: none;*/
visibility: visible;
}
// 气泡盒子
.hover-box-nex:hover{
/*display: list-item;*/
/*list-style-type: none;*/
/*visibility: visible;*/
}
// 气泡盒子 显示
.show-hover-box-nex{
visibility: hidden;
&:hover{
visibility: visible;
}
}
// 气泡盒子 隐藏
.hide-hover-box-nex{
visibility: hidden;
&:hover{
visibility: hidden;
}
}
表格中的代码部分:
<el-table-column
label="商品名称"
align="left"
prop="name">
<template slot-scope="scope">
<div style="height: 100%; width: 100%;" @mouseover="mouseover">
<div class="hover-box" style="" v-html="scope.row.name"></div>
<div class="hover-box-nex"
:class="[
scope.column.position == 'top' ?'tip-top': 'tip-bottom',
scope.column.show==true && scope.column.rowId == scope.row.id ? 'show-hover-box-nex': 'hide-hover-box-nex'
]"
:id="scope.column.id+'-'+scope.row.id"
:style="scope.column.position == 'top'?scope.column.styleTop: scope.column.styleBottom">
<div class="tip-box-div" v-html="scope.row.name"></div>
</div>
</div>
</template>
</el-table-column>

可以看到这里用了很多的三目运算,这里需要给气泡绑定动态类,动态id,动态style。
scope.column.position == 'top' ?'tip-top': 'tip-bottom' 是为了确定气泡向上还是向下,从而给气泡中三角形设置样式

scope.column.show==true && scope.column.rowId == scope.row.id ? 'show-hover-box-nex': 'hide-hover-box-nex' 是为了判断当前这个单元格是否需要显示气泡,这里column中字段值改变,就代表一整列都一样,需要再就加一个row.id来区分。 如果没有 scope.column.rowId == scope.row.id 这个判断,就会出现一个问题,如果商品这一列下第一个单元格高度超出了,第二个单元格高度没超出,来回切换这2格单元格,未超出高度的单元格,鼠标移入也会出现气泡,这就与需求不符合了。
:id="scope.column.id+'-'+scope.row.id" 为了准确获取当前这个气泡的伪类位置
:style="scope.column.position == 'top'?scope.column.styleTop: scope.column.styleBottom" 为了这是气泡的位置
下面就是js部分了。

js代码:
/** 固定4行高度超出显示气泡begin */
handleCellMouseEnter(row, column, cell, event) {
const cellChild = event.target.querySelector('.cell'), // cell单元格
hoverChild = cell.querySelector(`.hover-box`), // 鼠标移入对象盒子
range = document.createRange(), // 创建一个范围(以鼠标移入对象为基准)
tipChild = cell.querySelector(`#${column.id}-${row.id}`) // 获取当前单元格下气泡盒子
range.setStart(hoverChild, 0); // 设置范围的起始点
range.setEnd(hoverChild,hoverChild.childNodes.length); // 设置范围的结束点
const rangeHeight = range.getBoundingClientRect().height; // 获取当前范围的高度
let cellChildClientObj = cellChild.getBoundingClientRect()
if(rangeHeight>cellChild.offsetHeight){
this.$set(column,'show',true)
this.$set(column,'rowId',row.id)
}else{
this.$set(column,'show',false)
this.$set(column,'rowId','')
}
const positionHeight = tipChild? (tipChild.offsetHeight+(cellChild.offsetHeight/3)*2) : 400, positionWidth = tipChild? (tipChild.offsetWidth+20): 650; // 获取气泡盒子高度、宽度
if(window.innerHeight - cellChildClientObj.top > positionHeight){ //窗口高度- 当前cell的top值 > 气泡盒子盒子高度
this.$set(column,'position','top')
// 强制更新伪类样式
document.styleSheets[0].addRule(`#${column.id}-${row.id}::after`,`top: ${cellChildClientObj.top+(cellChild.offsetHeight/3)*2-20}px; left:${cellChildClientObj.left+cellChild.offsetWidth/3-30}px;`);
window.innerWidth - cellChildClientObj.left > positionWidth ? this.$set(column,'styleTop',{top: (cellChildClientObj.top+(cellChild.offsetHeight/3)*2-8)+'px', left:(cellChildClientObj.left-cellChild.offsetWidth/3) + 'px'}) : this.$set(column,'styleTop',{top: (cellChildClientObj.top+(cellChild.offsetHeight/3)*2-8)+'px', left:(window.innerWidth - positionWidth )+'px'})
}else{ // 窗口高度- 当前cell的top值 <= 气泡盒子盒子高度
this.$set(column,'position','bottom')
// 强制更新伪类样式
document.styleSheets[0].addRule(`#${column.id}-${row.id}::after`,`top: ${cellChildClientObj.top+cellChild.offsetHeight/2}px; left:${cellChildClientObj.left+cellChild.offsetWidth/3-30}px;`);
window.innerWidth - cellChildClientObj.left > positionWidth ? this.$set(column,'styleBottom',{bottom: (window.innerHeight - cellChildClientObj.top-cellChild.offsetHeight/2)+'px', left:(cellChildClientObj.left-cellChild.offsetWidth/3) + 'px'}) : this.$set(column,'styleBottom',{bottom: (window.innerHeight - cellChildClientObj.top-cellChild.offsetHeight/2)+'px', left:(window.innerWidth - positionWidth )+'px'})
}
},
/** 固定高度超出显示气泡end */
补充说明:
本例中用到的公共样式:表格固定高度4行:
/deep/ .el-table.testHeightTip td {
.cell {
height: 92px;
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 4; /*可以显示的行数,超出部分用...表示 */
-webkit-box-orient: vertical;
white-space: pre-wrap;
text-align: left;
.ai-button {
padding: 7px 5px !important;
}
}
}

解决ElementUI表格气泡在切换tab时乱串、闪烁和位置偏移的问题,通过自制气泡并计算定位,确保箭头始终指向正确单元格。错误尝试包括销毁和隐藏气泡的DOM,但导致新的界面问题。最终解决方案是使用固定定位和动态样式调整,同时确保气泡不超过表格边界,并在内容过多时显示滚动条。
3389

被折叠的 条评论
为什么被折叠?



