likes
comments
collection
share

常用组件🔥文本省略(支持单行/多行,提示自适应)

作者站长头像
站长
· 阅读数 18

✨背景

哈喽,我是JLong😄,日常开发中,我们常常会遇到文本省略场景,业界常见UI库如Elment-ui中el-table提供show-overflow-tooltip属性支持内容过长时候省略显示tooltip,但经常有一些场景用户希望可以在tooltip上面选中复制内容,当前没看到比较适用的工具支持单行或者多行文本省略的同时,提示框自适应(自行感知文本是否省略决定显隐)

这篇文章总结梳理一下开发该组件遇到的知识点,如如何文本省略实现、如何判断文本是否省略、resize-observer-polyfill补丁监听resize等,完整代码下文附上。

✨文本省略

简单普及一下各种文本省略方法

✔单行省略

<div class="text" :title="content">
    {{ content }}
</div>
.text {
    width: 100px;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
    white-space: nowrap;
}

注意:需要宽度,没宽度也没办法省略,会出现无效情况

✔多行省略

<div class="text" :title="content">
    {{ content }}
</div>
.text {
    width: 100px;
    word-break: break-all;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

浏览器对-webkit-line-clamp的支持情况与浏览器的版本和厂商有关。以下是浏览器对-webkit-line-clamp的支持情况:

  • Chrome:从 Chrome 56 开始支持 -webkit-line-clamp 属性,但需要启用 CSS.WEBKIT.LINE_CLAMP 标志。在 Chrome 61 及更高版本中,该属性已默认启用。
  • Safari:从 Safari 9.1 开始支持 -webkit-line-clamp 属性。在 Safari 10 及更高版本中,该属性已默认启用。
  • Opera:从 Opera 33 开始支持 -webkit-line-clamp 属性。
  • Edge:从 Edge 15 开始支持 -webkit-line-clamp 属性。

需要注意的是,虽然 -webkit-line-clamp 属性在某些浏览器中受到支持,但它在其他浏览器中可能不受支持。因此,在使用 -webkit-line-clamp 属性时,最好使用 CSS 前缀或 JavaScript 方法来确保在所有浏览器中都能正确地显示文本。

其他就不汇总了,这里用到了这两种,其他还有限制宽高的,截取判断的,js判断的,伪元素等,有兴趣可以自行了解下

✨ 判断文本是否溢出

参考Determine if an HTML element's content overflows最高赞回答,主要有两种思路:

  1. 根据元素属性做判断:

单行:根据clientWidth和scrollWidth做判断,后者大说明溢出

多行:根据clientHeight和scrollHeight做判断,后者大说明溢出

el.clientWidth 表示元素的内部宽度,包括内边距(padding),但不包括边框(border)和外边距(margin)。

el.scrollWidth 表示元素的可滚动宽度,即元素的内容宽度加上溢出部分的宽度。

如果一个元素的 el.clientWidth 小于 el.scrollWidth,则说明该元素的内容溢出了其内部宽度,出现了文本溢出的情况,多行同理。

  1. 将div克隆一份但不显示(visibility:hidden),比较两者的宽度,如果副本的宽度大于元素本身的宽度,则表示溢出,否则未溢出(这种方式注意销毁克隆元素)

✨ 防抖+监听

防抖常见直接用lodash库debounce 即可 监听直接用window.addElementListener是不合适的,因为只需要监听元素宽高变化即可,这里使用ResizeObserver

ResizeObserver是一个用于监听元素大小变化事件的API,它可以在元素大小改变时触发回调函数。这个API主要应用于响应式设计,当页面布局需要动态调整时非常有用。然而,使用ResizeObserver API时需要注意,频繁调用getBoundingClientRect​、getComputedStyle等API会导致浏览器重排(reflow),从而影响页面性能。

ps:存在兼容性问题,不过可以用resize-observer-polyfill插件解决兼容性问题

✨代码(可直接复制)

<template>
    <el-popover
        :placement="placement"
        :disabled="!isShowToolTip"
        :trigger="trigger">
        <p
            ref="text"
            slot="reference"
            :class="[row === 1 ? 'single' : 'multiline']"
            :style="`-webkit-line-clamp: ${row === 1 ? 'none' : row}`"
            v-html="text">
        </p>
        <p v-html="text" :style="`text-align: ${align}; max-width: ${maxW}px`;"></p>
    </el-popover>
</template>
<script>
import ResizeObserver from 'resize-observer-polyfill'
import _ from 'lodash'
export default {
    props: {
        text: { type: String, default: '' },
        placement: { type: String, default: 'top' },
        trigger: { type: String, default: 'hover' },
        align: { type: String, default: 'left' },
        row: { type: [Number, String], default: 1 }, // 行数
        maxW: { type: [Number, String], default: 400 },
        resize: { type: Boolean, default: true } // 是否开启resize监听
    },
    data () {
        return { isShowToolTip: false, ro: null }
    },
    methods: {
        // 省略判断
        determine () {
            const node = this.$refs.text || null
            if (!node) return
            this.isShowToopTip = this.row === 1
                ? (node.clientWidth < node.scrollWidth) // 单行
                : (node.clientHeight < node.scrollHeight) // 多行
        },
        // 开启resize监听
        monitor () {
            if (this.resize) {
                this.ro = new ResizeObserver(_.debounce(this.determine, 500))
                this.ro.observe(this.$refs.text)
            } else {
                this.determine()
            }
        }
    },
    mounted () {
        this.isShowToolTip = false
        this.ro = null
        this.monitor()
    },
    beforeDestroy () {
        if (!this.resize || !this.ro || !this.$refs.text) return
        this.ro.unobserve(this.$refs.text)
    }
}
</script>
<style lang="scss" scoped>
.single {
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}
.multiline {
    word-break: break-all;
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
}
</style>