封装一个vue3 多行文本超出省略展开/收起和tooltip展示组件

封装一个vue3 多行文本超出省略展开/收起和tooltip展示组件

前言

相信大家都能遇到这样一个问题,文本超出省略了,那么怎么既能让页面不被密密麻麻的文字占满,又能让所有的内容让用户能够清晰便捷地看到。在使用UI框架时我们常用tooltip组件去包裹过长的文本内容,但是,在我使用过程中存在这样一个问题,包裹的内容如果过短也展示了toltip,这样明显并不是最好地解决方案。以及在H5中使用tooltip也不是最好地选择,通常使用展开和收起来隐藏过长的文档。我将这两者结合起来,手动造轮子,手动封装一个通用组件。git地址:gitee.com/fcli/vue-te...

先上图:

具体实现

展开和收起

1、首先需要实现展开和收起功能,使用css伪类增加展开和收起文字,通过浮动使文字能够环绕操作按钮,动态改变-webkit-line-clamp--bottom的值来调整段落样式,具体代码实现如下:

typescript 复制代码
<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来展示和隐藏内容。

css 复制代码
.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来展示展开操作。

ini 复制代码
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。

css 复制代码
npm i @popperjs/core

2、在页面中引用,并绑定对应dom,具体使用方法可参考官网:popper.js.org/

javascript 复制代码
import { createPopper } from '@popperjs/core';

3、添加模板自定义tooltip内容

xml 复制代码
<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。

ini 复制代码
//处理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样式

css 复制代码
#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中使用刚刚的组件:

xml 复制代码
<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地址:gitee.com/fcli/vue-te... 如果需要在项目中应用也可以通过npm直接安装:

bash 复制代码
npm install @fcli/vue-text-overflow --save-dev 来安装

在项目中使用
import vueTextOverflow from '@fcli/vue-text-overflow';
const app=createApp(App)
app.use(vueTextOverflow);

觉得不错,欢迎点赞收藏🙏

相关推荐
逐·風4 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫4 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦5 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子5 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山6 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享6 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
清灵xmf8 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨8 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL8 小时前
npm入门教程1:npm简介
前端·npm·node.js
小白白一枚1119 小时前
css实现div被图片撑开
前端·css