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

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

相关推荐
程序员凡尘16 分钟前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步6 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者6 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋8 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120538 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢8 小时前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写9 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js