封装一个vue3 多行文本超出省略展开/收起和tooltip展示组件
前言
相信大家都能遇到这样一个问题,文本超出省略了,那么怎么既能让页面不被密密麻麻的文字占满,又能让所有的内容让用户能够清晰便捷地看到。在使用UI框架时我们常用tooltip组件去包裹过长的文本内容,但是,在我使用过程中存在这样一个问题,包裹的内容如果过短也展示了toltip,这样明显并不是最好地解决方案。以及在H5中使用tooltip也不是最好地选择,通常使用展开和收起来隐藏过长的文档。我将这两者结合起来,手动造轮子,手动封装一个通用组件。git地址:https://gitee.com/fcli/vue-text-overflow.git
先上图:
具体实现
展开和收起
1、首先需要实现展开和收起功能,使用css伪类增加展开和收起文字,通过浮动使文字能够环绕操作按钮,动态改变-webkit-line-clamp
和--bottom
的值来调整段落样式,具体代码实现如下:
<input type="checkbox" class="exp" id="exp" v-show="false">
<div class="text" ref="textContent" :style="{ '--bottom': bottom, '-webkit-line-clamp': lineNum }">
<label class="btn" ref="optBtn" for="exp" v-show="showExport && showExpBtn"></label>
<slot></slot>
</div>
css样式,在展开和收起时动态改变文字容器的max-height
来展示和隐藏内容。
.text {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
transition: .3s max-height;
}
.exp:checked+.text {
max-height: 2000px;
/*超出最大行高度就可以了*/
}
.exp:checked+.text {
-webkit-line-clamp: 999 !important;
/*设置一个足够大的行数就可以了*/
max-height: none;
}
.exp:checked+.text .btn::after {
content: '收起'
}
.exp:checked+.text .btn::before {
visibility: hidden;
/*在展开状态下隐藏省略号*/
}
.btn::after {
content: '展开'
}
.text::before {
content: '';
float: right;
width: 0;
height: 100%;
margin-bottom: var(--bottom);
}
.btn {
margin-right: 12px;
float: right;
clear: both;
cursor: pointer;
color: #377ef9;
}
2、适配slot中的响应式,动态计算内容高度。使用MutationObserver监听节点的内容变化,当内容变化时通过判断scrollHeight是否大于clientHeight来展示展开操作。
onMounted(() => {
getBtnHeight();
const mutation = new MutationObserver(handleShowExp);
const config = {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
}
mutation.observe(textContent.value, config);
})
//处理计算是否展示展开和收起按钮
const handleShowExp = () => {
if (textContent.value.scrollHeight > textContent.value.clientHeight) {
showExpBtn.value = true;
getBtnHeight();
}
}
const getBtnHeight = () => {
//计算展开/收起按钮的高度,动态改变
const offsetHeight = optBtn.value.offsetHeight - 2
bottom.value = `-${offsetHeight}px`
handelTooltip();
}
tooltip提示
经过查看elementui的popover和tooltip都是基于popperjs来二次封装的,因此我直接采用popperjs来实现tooltip功能。
1、首先通过npm安装popperjs。
npm i @popperjs/core
2、在页面中引用,并绑定对应dom,具体使用方法可参考官网:https://popper.js.org/
import { createPopper } from '@popperjs/core';
3、添加模板自定义tooltip内容
<div ref="tooltipRef" id="tooltip" v-if="showOverflowTooltip" class="tooltip" v-show="tooltipShow">
<slot></slot>
<div id="arrow" data-popper-arrow></div>
</div>
4、通过createPopper创建popover 并绑定触发事件,当鼠标mouseenter时展示tooltip,mouseleave时因此tooltip。
//处理tooltip事件
const handelTooltip = () => {
const button: any = textContent.value;
const tooltip: any = tooltipRef.value;
const popperInstance = createPopper(button, tooltip, {
placement: 'top',
modifiers: [
{
name: 'offset',
options: {
offset: [0, 8],
},
},
],
});
function show() {
// Make the tooltip visible
tooltipShow.value = true;
// Enable the event listeners
popperInstance.setOptions((options) => ({
...options,
modifiers: [
...options.modifiers,
{ name: 'eventListeners', enabled: true },
],
}));
// Update its position
popperInstance.update();
}
function hide() {
tooltipShow.value = false;
// Disable the event listeners
popperInstance.setOptions((options) => ({
...options,
modifiers: [
...options.modifiers,
{ name: 'eventListeners', enabled: false },
],
}));
}
const showEvents = ['mouseenter', 'focus'];
const hideEvents = ['mouseleave', 'blur'];
showEvents.forEach((event) => {
button.addEventListener(event, show);
});
hideEvents.forEach((event) => {
button.addEventListener(event, hide);
});
}
5、自定义tooltip样式
#arrow,
#arrow::before {
position: absolute;
width: 8px;
height: 8px;
background: inherit;
}
#arrow {
visibility: hidden;
}
#arrow::before {
visibility: visible;
content: '';
transform: rotate(45deg);
}
#tooltip[data-popper-placement^='top']>#arrow {
bottom: -4px;
}
#tooltip[data-popper-placement^='bottom']>#arrow {
top: -4px;
}
#tooltip[data-popper-placement^='left']>#arrow {
right: -4px;
}
#tooltip[data-popper-placement^='right']>#arrow {
left: -4px;
}
.tooltip {
background: #333;
color: #fff;
border-radius: 4px;
font-size: 14px;
padding: 4px 8px;
max-width: 300px;
}
使用
经过上面一系列搬砖,最终我们来试试效果吧。在app.vue中使用刚刚的组件:
<template>
<div class="content">
<vue-text-overflow :showOverflowTooltip="false" :showExport="true" :lineNum="3">
这是一段文字<span style="color:red">带html标签</span>,文本超出省略溢出测试文本超出省略溢出测试文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试,文本超出省略溢出测试
</vue-text-overflow>
</div>
</template>
<script setup lang="ts">
import VueTextOverflow from './plugin/index.vue';
components:{
VueTextOverflow
}
</script>
组件option如下所示:
属性 | 属性名称 | 类型 | 可选值 |
---|---|---|---|
showOverflowTooltip | 是否超出展示tooltip | Boolean | false |
showExport | 是否展示展开/收起 操作按钮 | Boolean | true |
lineNum | 超过多少行省略 | Number | 3 |
最后
本文只展示了部分主要源码,如果需要全部源码可访问git地址:https://gitee.com/fcli/vue-text-overflow.git
如果需要在项目中应用也可以通过npm直接安装:
npm install @fcli/vue-text-overflow --save-dev 来安装
在项目中使用
import vueTextOverflow from '@fcli/vue-text-overflow';
const app=createApp(App)
app.use(vueTextOverflow);
觉得不错,欢迎点赞收藏🙏