实现一个内容超出显示省略号,并鼠标浮入显示tooltip,不超出的不显示tooltip组件
ps:该组件是基于element-plus,使用vue3最新的setup语法糖实现的。不清楚的大家可以根据我的思路用其他技术栈实现。
背景
项目中有很多地方有超出显示省略号,然后鼠标浮入显示tooltip的需求。在这之前,我发现项目中有些是鼠标浮入都显示tooltip,无关乎是否超出;还有一些甚至超出显示省略号,而没有加tooltip,也就是这种情况用户连完整信息都不清楚。我感觉这应该不是产品想要的效果,可能是之前需求太多,或者这个项目经手的人太多,导致没有注意到这种细微的功能。然后我趁着这版本迭代的空闲期整理了一下,找产品挨个对了一下,想统一和完善项目中这种功能。项目主要的技术栈是vue2,在项目中封装了一个组件。在这里,我想着用vue3+element-plus实现,来给大家整理思路,以及巩固和学习一下vue3新setup语法糖。
功能点
- 超出显示省略号
- 显示省略号的情况下,鼠标移入显示全部
- 考虑不是纯文本的情况,即可自定义内容区
- 考虑tooltip显示的内容可自定义
实现
-
超出显示省略号 这个其实不用多说,我们直接用css来实现就好,就是常见老三样,加上宽度的限制。
<template> <div class="content" :style="{width: props.width}"> {{props.content}} </div> </template> <script setup lang="ts"> // 定义props的类型 interface props { content: string, width: string } // 使用withDefaults来给props赋默认值 const props = withDefaults(defineProps<props>(), { content: '', width: '' }) </script> <style> .content { overflow: hidden; white-space: nowrap; text-overflow: ellipsis } </style>
现在这样就实现了超出显示省略号。我们来调用看看效果: 组件调用代码
<script setup lang="ts"> // This starter template is using Vue 3 <script setup> SFCs // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup // import HelloWorld from './components/HelloWorld.vue' import { reactive } from 'vue'; import ShowTooltip from './showTooltip.vue'; const content = reactive({ data: '您好您好您好您好您好您好您好您好您好您好您好' }) </script> <template> <ShowTooltip :content="content.data" width="200px"/> </template>
小插曲:不用setup语法糖不知道,用了之后感觉真香。组件自动注册,只需引入就好。不用再单独写setup return,模版直接能用。props也是有单独定义的接口,之前的context也分开了attrs和emit...大家有兴趣的还是可以去尝试尝试。好言归正传,继续我们的组件编写。
-
显示省略号的情况下,鼠标移入显示全部
-
先实现鼠标移入显tooltip,我们这儿tooltip就使用element-plus里的el-tooltip组件。
<template> <el-tooltip effect="dark" :content="props.content" placement="top" > <div class="content" :style="{width: props.width}"> {{props.content}} </div> </el-tooltip> </template> <script setup lang="ts"> // 定义props的类型 interface props { content: string, width: string } // 使用withDefaults来给props赋默认值 const props = withDefaults(defineProps<props>(), { content: '', width: '' }) </script> <style> .content { overflow: hidden; white-space: nowrap; text-overflow: ellipsis } </style>
现在只是实现了鼠标移入显示tooltip,并没有区分是否超出。
-
实现超出才显示tooltip
我们先思考一下,如何判断是否超出。 其实不难想到,我们可以利用内容的width和外层盒子的宽度作对比。当内容的宽度大于等于盒子的宽度是,就显示tooltip。我们可以利用span标签不受css样式影响,宽度是内容自动撑开的,这样我们可以将内容使用span标签包裹起来,然后计算宽度,话不多说,直接上代码。
<template> <el-tooltip effect="dark" :content="props.content" placement="top" :disabled="isShow" > <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip"> <span ref="contentRef">{{props.content}}</span> </div> </el-tooltip> </template> <script setup lang="ts"> import { ref } from 'vue' // 定义props的类型 interface props { content: string, width: string } // 使用withDefaults来给props赋默认值 const props = withDefaults(defineProps<props>(), { content: '', width: '' }) // 使用isShow来控制tooltip是否显示 let isShow = ref<boolean>(true) // 在span标签上定义一个ref const contentRef = ref() const isShowTooltip = function (): void { // 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值 if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) { isShow.value = true } else { isShow.value = false } } </script> <style> .content { overflow: hidden; white-space: nowrap; text-overflow: ellipsis } </style>
到这里,已经能满足文本内容的显示了,我们也完成一半的工作了。
-
-
考虑不是纯文本的情况,即可自定义内容区 其实到这一步就比较简单了,就是写插槽。
<template> <el-tooltip effect="dark" :content="props.content" placement="top" :disabled="isShow" > <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip"> <span ref="contentRef"> <slot name="content">{{props.content}}</slot> </span> </div> </el-tooltip> </template>
调用
<ShowTooltip :content="content.data" width="200px"> <template v-slot:content>1212324323</template> </ShowTooltip>
考虑将内容作为插槽主要是因为项目中存在:以tag的形式列举值,超出tooltip显示。这里我大概还原一下。
<script setup lang="ts"> // This starter template is using Vue 3 <script setup> SFCs // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup // import HelloWorld from './components/HelloWorld.vue' import { reactive } from 'vue'; import ShowTooltip from './showTooltip.vue'; const content = reactive({ data: '您好您好您好您好您好您好您好您好您好您好您好' }) const tags = reactive([ '苹果', '梨子', '香蕉', '芒果', '火龙果', '猕猴桃' ]) </script> <template> <!-- 单纯的文本 --> <ShowTooltip :content="content.data" width="200px"/> <br/> <!-- 将内容自定义 --> <ShowTooltip :content="content.data" width="200px"> <template v-slot:content> <el-tag v-for="item in tags" :key="item" class="tag-item"> {{item}}</el-tag> </template> </ShowTooltip> </template> <style> .tag-item { margin-left: 5px; } </style>
-
考虑tooltip显示的内容可自定义 采取上面同样的办法,将自定义区域slot,并且兼容两个tooltipContent 与 content。直接上代码
<template> <el-tooltip effect="dark" :content="props.tooltipContent ? props.tooltipContent : props.content" placement="top" :disabled="isShow" > <template #content> <!-- 此处的默认值先看tooltipContent有没有,没有就给默认content --> <slot name="tooltipContent">{{props.tooltipContent ? props.tooltipContent : props.content}}</slot> </template> <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip"> <span ref="contentRef"> <!-- 给一个没有写插槽的默认值,兼容纯文本的情况 --> <slot name="content">{{props.content}}</slot> </span> </div> </el-tooltip> </template> <script setup lang="ts"> import { ref } from 'vue' // 定义props的类型 interface props { content: string, width: string, tooltipContent?: string } // 使用withDefaults来给props赋默认值 const props = withDefaults(defineProps<props>(), { content: '', width: '', tooltipContent: '' }) // 使用isShow来控制tooltip是否显示 let isShow = ref<boolean>(true) // 在span标签上定义一个ref const contentRef = ref() const isShowTooltip = function (): void { // 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值 if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) { isShow.value = true } else { isShow.value = false } } </script> <style> .content { overflow: hidden; white-space: nowrap; text-overflow: ellipsis } </style>
调用
<ShowTooltip width="200px"> <template v-slot:tooltipContent> <span>1223214234</span> </template> <template v-slot:content> <el-tag v-for="item in tags" :key="item" class="tag-item"> {{item}}</el-tag> </template> </ShowTooltip>
小结
这个组件我们主要依赖传入的几个属性,及抛出的slot灵活使用。
属性
- width: 盒子的宽度,要使用超出显示...,该属性必填
- content: 文本内容
- tooltipContent: tooltip显示的文本内容,不传该属性时,显示content
slot
- content: 内容插槽,自定义内容区域
- tooltipContent: tooltip内容自定义区域
组件完整代码
<template>
<el-tooltip
effect="dark"
:content="props.tooltipContent ? props.tooltipContent : props.content"
placement="top"
:disabled="isShow"
>
<template #content>
<slot name="tooltipContent">{{props.tooltipContent ? props.tooltipContent : props.content}}</slot>
</template>
<div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
<span ref="contentRef">
<!-- 给一个没有写插槽的默认值,兼容纯文本的情况 -->
<slot name="content">{{props.content}}</slot>
</span>
</div>
</el-tooltip>
</template>
<script setup lang="ts">
import { ref, useSlots } from 'vue'
// 定义props的类型
interface props {
content?: string,
width: string,
tooltipContent?: string
}
// 使用withDefaults来给props赋默认值
const props = withDefaults(defineProps<props>(), {
content: '',
width: '',
tooltipContent: ''
})
// 使用isShow来控制tooltip是否显示
let isShow = ref<boolean>(true)
// 在span标签上定义一个ref
const contentRef = ref()
const isShowTooltip = function (): void {
// 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值
if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) {
isShow.value = true
} else {
isShow.value = false
}
}
</script>
<style>
.content {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis
}
</style>
题外话:大家可以根据需要基于此实现更多功能,比如点击触发,还是鼠标移入触发tooltip等