目录
应用场景
项目中很多地方需要显示一些字段信息,比如项目信息,项目说明的字段,总会有需求是显示当字段内容过多的时候,加省略号显示,然后显示tooltip,这个功能在el-table里好实现,只是需要show-overflow-tooltip就可以实现,因为很多地方需要用到,所以对这个功能进行了封装,有两种实现方式,适配elementUI和element-plus。
一、封装当文字过长时显示省略号和tooltip
1.父组件
既然是封装,先新建一个文件,比如:ToolTip.vue,作为封装的页面,再在父组件中引入这个组件,引入需要一些信息,既然是封装的tooltip,那么肯定需要tooltip的content,朝向,还有ref的name,方便区分。如下,父组件最好在外层有一个样式,方便固定宽度。
<div class="title">
<ToolTip :content="projectName" placement="bottom" ref-name="tooltipOver"></ToolTip>
</div>
2.子组件
el-tooltip有一个属性disabled,通过判断内容是否需要显示省略号来决定是否禁用这个tooltip。
全部代码如下:
<template>
<div class="text-tooltip">
<el-tooltip class="text-truncate" effect="dark" :disabled="isShowTooltip" :content="content" placement="top">
<p class="over-flow" :class="className" @mouseover="onMouseOver(refName)">
<span :ref="refName" class="ellipsis">{{ content || '' }}</span>
</p>
</el-tooltip>
</div>
</template>
<script>
export default {
name: 'textTooltip',
props: {
// 显示的文字内容
content: {
type: String,
default: () => {
return ''
}
},
// 外层框的样式,在传入的这个类名中设置文字显示的宽度
className: {
type: String,
default: () => {
return ''
}
},
// 为页面文字标识(如在同一页面中调用多次组件,此参数不可重复)
refName: {
type: String,
default: () => {
return ''
}
}
},
data() {
return {
isShowTooltip: true
}
},
mounted() {
//判断宽度是否需要出现toooltip
this.checkTooltipVisibility()
//优化resize
window.addEventListener('resize', this.checkTooltipVisibility)
},
beforeUnmount() {
window.removeEventListener('resize', this.checkTooltipVisibility)
},
methods: {
onMouseOver(refName) {
//这里的refName是子组件的refName不是从父组件传来的,所以需要在@mouseover里传递,使得页面能找到这个dom
let parentWidth = this.$refs[refName].parentNode.offsetWidth
let contentWidth = this.$refs[refName].offsetWidth
// 判断是否开启tooltip功能
if (contentWidth > parentWidth) {
this.isShowTooltip = false
} else {
this.isShowTooltip = true
}
},
checkTooltipVisibility() {
const spanEl = this.$refs[this.refName]
if (spanEl) {
const parentWidth = spanEl.parentNode.offsetWidth
const contentWidth = spanEl.offsetWidth
this.isShowTooltip = contentWidth > parentWidth
}
}
}
}
</script>
<style lang="scss" scoped>
.over-flow {
overflow: hidden;
white-space: nowrap;
width: 100%; /* 设置父元素的宽度 */
}
.ellipsis {
width: 100%; /* 设置子元素的宽度,确保父元素的宽度能容纳子元素*/
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 添加省略号 */
}
//这里还可以将外层的className对应的class写在这里
p {
margin: 0;
}
</style>
注意:当应用样式 .over-flow
的父元素的宽度不足以容纳子元素时,子元素的溢出部分将被隐藏,省略号也可能无法显示出来。这也是我之前一直修改但是样式依旧无法呈现的原因,后来将子元素和父元素的宽度设置了100%(具体值也可以)就成功了,实现效果如下:
element细节如下,如果不生效可以看看是否和我出现的问题一致。
这样就实现了当页面宽度变化的时候也能重新计算然后显示省略号或者不显示。
当然计算dom的宽度是一个方法,也可以通过计算content的内容长度,比如有多少个字符这种简答的方式来显示(之前一直用的这种方法,在某些地方还不得不继续用,那里没有固定的宽度)
前者这种方法显然更好一点,这种方法在element-ui里可以将span换成el-text,比较省事,不用考虑样式问题,如下:
<span :ref="refName" class="ellipsis">{{ content || '' }}</span>
//换成el-text
<el-text :ref="refName" truncated>{{ content || '' }}</el-text>
但是!element-plus依旧废弃了el-text,页面解析不出来这个组件,打包后页面也会无法显示省略号!
3.两种解决方法和待完善点
当天发现写完这个组件,并不能显示tooltip,原因很简单,最里层的span并不能溢出<p>的宽度,所以根据这种情况,我需要满足两个需求:
1是子元素需要能宽度溢出父元素,
2是溢出时显示tooltip(计算得出的溢出,那么需要子元素的宽度大于父元素)和省略号
先看2,要求显示省略号,那么就需要子元素的宽度比较在父元素的宽度内,代码里也提及这一点,但是我需要满足1需求,所以两个需求冲突。
解决方案1:更改样式,当组件初始化的时候,先将样式改为不能溢出但显示省略号的样式1(上文提及的样式),当鼠标移上去的时候改为宽度能溢出的样式2,鼠标移出时,再改为样式1
<template>
<div class="text-tooltip">
<el-tooltip class="item" effect="dark" :disabled="isShowTooltip" :content="content" placement="top">
<div class="over-flow" :class="className" @mouseover="onMouseOver(refName)" @mouseout="onMouseOut(refName)">
<span :ref="refName" class="ellipsis">{{ content || '' }}</span>
</div>
</el-tooltip>
</div>
</template>
methods: {
onMouseOver(refName) {
//更换样式为子元素可以溢出父元素的样式
this.$refs[refName].style.removeProperty('width')
this.$refs[refName].style.overflow = 'visible'
this.$refs[refName].style.whiteSpace = 'nowrap'
this.$refs[refName].style.textOverflow = 'ellipsis'
this.$refs[refName].parentNode.style.overflow = 'hidden'
this.$refs[refName].parentNode.style.width = '100%'
let parentWidth = this.$refs[refName].parentNode.offsetWidth
let contentWidth = this.$refs[refName].offsetWidth
// 判断是否开启tooltip功能
this.isShowTooltip = true
}
},
onMouseOut(refName) {
// 重置样式为省略文本
this.$refs[refName].style.overflow = 'hidden'
this.$refs[refName].style.width = '100%'
this.$refs[refName].style.whiteSpace = 'nowrap'
this.$refs[refName].style.textOverflow = 'ellipsis'
this.$refs[refName].parentNode.style.overflow = 'visible'
this.$refs[refName].parentNode.style.width = '100%'
},
checkTooltipVisibility() {
const spanEl = this.$refs[this.refName]
if (spanEl) {
//初始样式为省略文本
this.onMouseOut(this.refName)
const parentWidth = spanEl.parentNode.offsetWidth
const contentWidth = spanEl.offsetWidth
if (contentWidth > parentWidth) {
this.isShowTooltip = false
} else {
this.isShowTooltip = true
}
}
}
}
<style lang="scss" scoped>
//初始的省略显示
.over-flow {
overflow: visible; /* 溢出隐藏 */
white-space: nowrap;
width: 100%; /* 设置父元素的宽度 */
}
.ellipsis {
// width: 100%; /* 设置子元素的宽度,确保父元素的宽度能容纳子元素*/
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 可以溢出 */
text-overflow: ellipsis; /* 添加省略号 */
}
div {
margin: 0;
}
解决方案2:直接不计算,全部显示tooltip,这样省事,span显示样式1
<el-tooltip class="item" effect="dark" :disabled="!isShowTooltip" :content="content" placement="top">
<div class="over-flow" :class="className">
<span :ref="refName" class="ellipsis">{{ content || '' }}</span>
</div>
</el-tooltip>
<style lang="scss" scoped>
//初始的省略显示
.over-flow {
overflow: hidden; /* 溢出隐藏 */
white-space: nowrap;
width: 100%; /* 设置父元素的宽度 */
}
.ellipsis {
width: 100%; /* 设置子元素的宽度,确保父元素的宽度能容纳子元素*/
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 添加省略号 */
}
综上。实在不知道有没有其他好解决的方案,有知道的大佬评论区能指导一下吗?
分割线~~~~~~~~~~~~2023年11月7日
二、多行溢出时显示tooltip,没有考虑封装
最近发现一种新的显示tooltip的方式,虽然没有解决上述两种的弊端,但是也能提供一种解决思路。
直接上代码!
<el-tooltip
ref="tooltips"
:content="item.table_name"
effect="dark"
:disabled="!showToolTip[index]"
placement="bottom"
>
<span class="table-name" @mouseenter="visibilityChange($event, index)">
{{ item.table_name }}
</span>
</el-tooltip>
// 表单名称,超过两行时溢出则showToolTip
const tooltips = ref([])
const showToolTip = ref([])
const visibilityChange = (event, index) => {
const spanElement = event.target
if (spanElement.offsetHeight < spanElement.scrollHeight) {
showToolTip.value[index] = true
} else {
showToolTip.value[index] = false
}
}
.table-name {
flex: 1;
line-height: 18px;
padding-right: 3px;
// 内容换行最多两行,并超长时溢出
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
/*将对象作为弹性伸缩盒子模型显示*/
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/*控制显示换行2行*/
// max-height: 36px; /* 控制最大高度,即两行高度 */
}
这里是通过 mouseenter ,也就是鼠标移上去时,去计算 spanElement.offsetHeight < spanElement.scrollHeight,如果超过则代表溢出了,那么就需要显示内容提示。
这两个高度代表的意思:
offsetHeight
和scrollHeight
是用于获取元素的高度的两个不同属性,它们在Web开发中经常用于处理元素的滚动和布局。
offsetHeight
:
offsetHeight
是一个只读属性,用于获取元素的可见高度,包括元素的高度、内边距(padding)、边框(border)和垂直滚动条(如果有的话)。- 通常,
offsetHeight
用于获取元素在文档流中占据的高度,包括所有的可见内容。
scrollHeight
:
scrollHeight
也是一个只读属性,用于获取元素的滚动高度,即元素内容的总高度,包括不可见的部分。- 如果元素内容比其可见区域要高,
scrollHeight
将返回内容的总高度,包括不可见部分,而如果元素内容在可见区域内,则scrollHeight
等于offsetHeight
。通常情况下,
offsetHeight
用于获取元素的外部高度,而scrollHeight
用于获取元素的内部滚动高度。这些属性在处理滚动容器、动态加载内容以及计算元素高度时非常有用。
以上是GPT的解释。
实现效果如下:
如果没有超过,则不显示tooltip了。当然样式方面可以自定义超过2行或者多行,也可以设置新的样式控制。
这种方式会浪费一些性能,如果不是元素很多的页面,应该影响不大。这种解决方法应该也可以吧。