封装一个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);

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

相关推荐
hackeroink19 分钟前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者2 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240255 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人6 小时前
前端知识补充—CSS
前端·css