目前常用的UI组件库都有 tooltip
,然而都没有文本溢出检测的功能,下面展示2种情况,以供参考。
Vue2 + elementUI
利用 elementUI
中 el-tooltip
组件的属性 disabled
,实现弹窗控制。
组件封装示例
<template>
<el-tooltip
ref="tooltip"
:disabled="tooltipDisabled"
:content="content"
>
<slot />
</el-tooltip>
</template>
<script>
export default {
name: 'TooltipCheckOverflow',
props: {
content: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
tooltipDisabled: true // 默认禁用 tooltip
}
},
mounted() {
this.$nextTick(() => {
this.onTooltipEvent() // DOM 加载完成后再执行
})
},
updated() {
this.$nextTick(() => {
this.onTooltipEvent()
})
},
methods: {
onTooltipEvent() {
const target = this.$refs.tooltip.$el // 获取目标
if (target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight) { // 未溢出
// target.style.pointerEvents = 'none' // 釜底抽薪,慎用,页面不存在DOM级刷新可用
return
}
this.tooltipDisabled = false // 仅当文本溢出时释放 tooltip
}
}
}
</script>
以上示例实现了最小化封装,其他属性功能可自行添加。
封装调用示例
<template>
<tooltip-check-overflow content="内容已溢出,...">
<span class="inner-txt ellipsis">测试内容是否溢出测试内容是否溢出测试内容是否溢出</span>
</tooltip-check-overflow>
</template>
<script>
import TooltipCheckOverflow from '@/components/TooltipCheckOverflow' //组件引入
...
components: {
TooltipCheckOverflow // 组件注册
},
...
</script>
<style scoped>
.inner-txt {
width: 20px;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
需要注意:
- 调用时,包含的内容
.inner-txt
要设置文本溢出相关样式(例如.ellipsis
)。 - 一定要确保
.inner-txt
的宽度是可控的,最好就如例子里所示宽度是写好的,如果发现封装组件失效,怎么都无法触发弹窗,大概率就是.inner-txt
的宽度出了问题。 - 封装组件时,判断溢出语句为
target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight
,这里不仅检测了宽度,还检测了高度,可以适配多行文本溢出的情况。 - 语句
target.style.pointerEvents = 'none'
会禁止当前 DOM 的所有鼠标事件,要慎用!如果当前页面不存在 DOM 级别的更新,也就是说在页面渲染完成后就确定是否需要 tooltip,以后也不改了,这种情况就可以使用该语句,一劳永逸。
Vue3 + arco-design
由于 arco-design
的 a-tooltip / a-popover
都没有 disabled
属性,所以只好通过控制 content-style
和 arrow-style
的方式来禁用/释放弹窗。
这里推荐用 a-popover
,感觉 a-tooltip
不好用。
组件封装示例
<template>
<a-popover
ref="tooltip"
:content="String(content)"
:arrow-style="{ ...arrowStyle, display: 'none' }"
:content-style="{ ...contentStyle, display: 'none' }"
@mouseenter="onTooltipEvent"
>
<slot />
</a-popover>
</template>
<script setup>
import { ref } from 'vue'
defineProps({
content: {
default: undefined
},
contentStyle: {
type: Object,
default: () => {}
},
arrowStyle: {
type: String,
default: () => {}
}
})
const tooltip = ref(null)
// 判断是否溢出,确定是否要显示 hover 弹窗
function onTooltipEvent() {
const el = tooltip.value, target = el.$el.nextElementSibling // 获取目标 DOM
if (target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight) { // 未溢出
// target.style.pointerEvents = 'none'
return
}
el.contentStyle.display = 'block'
el.arrowStyle.display = 'block'
}
</script>
同样是最小化封装,其他功能自行添加。调用方式和前者完全一样,不多赘述。
需要注意:
- 在
a-popover
里获取 DOM 和el-tooltip
不同,需要再取nextElementSibling
属性。 - 通过预制
display: none;
将弹窗隐藏,仅当文本溢出时才display: block;
,实现弹窗禁用/释放功能,这里的禁用只是看不见了,并非真正意义的消失,如果介意,且页面不会有 DOM 级别的更新,可以使用target.style.pointerEvents = 'none'
语句。
总结
elementUI
的el-tooltip
通过disabled
属性,在 DOM 渲染完成后立刻判断是否需要禁用/释放的方式进行组件封装;arco-design
的a-popover
通过content-style、arrow-style
的display
,在鼠标滑入(mouseenter
)事件中立刻判断是否应该显示的方式进行组件封装;- 两者调用方式相同,使用时一定要注意内部标签
.inner-txt
的宽度控制,否则组件可能失控。
以上展示了 Vue2 + elementUI
、Vue3 + arco-design
的封装示例,可以根据自己的项目情况灵活选用。
笔记主要为自用,欢迎友好交流!