Encapsulate a vue3 multi-line text beyond omitting expansion/collapse and tooltip display components

Encapsulate a vue3 multi-line text beyond omitting expansion/collapse and tooltip display components

Foreword

I believe everyone can encounter such a problem. The text exceeds the omission, so how can we prevent the page from being filled with dense text and make all the content clear and convenient for users to see. When using the UI framework, we often use the tooltip component to wrap text content that is too long. However, there is a problem during my use. If the wrapped content is too short, the toltip is also displayed, which is obviously not the best solution. And using tooltips in H5 is not the best choice. Expanding and collapsing are usually used to hide documents that are too long. I combined the two, manually building the wheel and manually encapsulating a common component. git address: https://gitee.com/fcli/vue-text-overflow.git

First picture:

Detailed implementation

Expand and collapse

1. First, you need to implement the expand and collapse functions, use css pseudo-classes to add expand and collapse text, float the text to surround the operation button, and dynamically change -webkit-line-clamp and The value of --bottom is used to adjust the paragraph style. The specific code is implemented as follows:

<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 & amp; & amp; showExpBtn"></label>
    <slot></slot>
</div>

css style, dynamically change the max-height of the text container when expanding and collapsing to display and hide content.

.text {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    transition: .3s max-height;
}


.exp:checked + .text {
    max-height: 2000px;
    /* Just exceed the maximum row height */
}

.exp:checked + .text {
    -webkit-line-clamp: 999 !important;
    /*Just set a large enough number of rows*/
    max-height: none;
}

.exp:checked + .text .btn::after {
    content: 'Collapse'
}

.exp:checked + .text .btn::before {
    visibility: hidden;
    /*Hide ellipses in expanded state*/
}

.btn::after {
    content: 'expand'
}


.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. Adapt to the responsiveness in the slot and dynamically calculate the content height. Use MutationObserver to monitor the content changes of the node. When the content changes, determine whether scrollHeight is greater than clientHeight to display the expansion operation.

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);
})
//Process and calculate whether to display the expand and collapse buttons
const handleShowExp = () => {
    if (textContent.value.scrollHeight > textContent.value.clientHeight) {
        showExpBtn.value = true;
        getBtnHeight();
    }
}

const getBtnHeight = () => {
    //Calculate the height of the expand/collapse button and change it dynamically
    const offsetHeight = optBtn.value.offsetHeight - 2
    bottom.value = `-${offsetHeight}px`
    handelTooltip();
}
tooltip

After checking that elementui’s popover and tooltip are secondary encapsulation based on popperjs, so I directly use popperjs to implement the tooltip function.

1. First install popperjs through npm.

npm i @popperjs/core

2. Reference it in the page and bind it to the corresponding DOM. For specific usage methods, please refer to the official website: https://popper.js.org/

import { createPopper } from '@popperjs/core';

3. Add template custom tooltip content

<div ref="tooltipRef" id="tooltip" v-if="showOverflowTooltip" class="tooltip" v-show="tooltipShow">
    <slot></slot>
    <div id="arrow" data-popper-arrow></div>
</div>

4. Create a popover through createPopper and bind the trigger event. When the mouse enters, the tooltip is displayed, and when the mouseleave, the tooltip is displayed.

//Handle tooltip event
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. Customize tooltip style

#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;
}

Use

After the above series of moving bricks, let’s finally try the effect. Use the component just now in app.vue:

<template>
  <div class="content">
    <vue-text-overflow :showOverflowTooltip="false" :showExport="true" :lineNum="3">
      This is a piece of text <span style="color:red">with html tag</span>. The text exceeds the omission overflow test. The text exceeds the omission overflow test. The text exceeds the omission overflow test. The text exceeds the omission overflow test. The text exceeds the omission overflow test. test, text exceeds the ellipsis overflow test, text exceeds the ellipsis overflow test, text exceeds the ellipsis overflow test, text exceeds the ellipsis overflow test, text exceeds the ellipsis overflow test, text exceeds the ellipse overflow test, text exceeds the ellipse overflow test
    </vue-text-overflow>
    </div>
</template>

<script setup lang="ts">
import VueTextOverflow from './plugin/index.vue';
components:{
  VueTextOverflow
}
</script>

The component options are as follows:

Attribute Attribute name Type Optional value
showOverflowTooltip Whether the display tooltip is exceeded Boolean false
showExport Whether to display the expand/collapse operation button Boolean true
lineNum How many lines are omitted Number 3

Finally

This article only shows part of the main source code. If you need all the source code, you can visit the git address: https://gitee.com/fcli/vue-text-overflow.git
If you need to apply it in the project, you can also install it directly through npm:

npm install @fcli/vue-text-overflow --save-dev to install

Use in projects
import vueTextOverflow from '@fcli/vue-text-overflow';
const app=createApp(App)
app.use(vueTextOverflow);

You think it’s good, please like and collect it