文字超长显示省略号和toolTip

Daotin 于 2022-07-22 发布 编辑

问题描述

项目中经常会遇到文字超长,超出之后需要显示省略号和el-tooltip的情况。

简单粗暴的方法就是统一显示tooltip,不管是否有显示省略号,但是这样显然不太友好。

最好的办法就是在显示省略号的时候,才显示tooltip。

那么,如何知道是否显示了省略号呢?

问题分析

方案一

最先想到的方法是,根据宽度来计算最多容纳文字的个数,如果实际文字的个数有超过这个最大个数,则显示tooltip。

但是这个问题也很严重,首先英文和中文的长度是不一样的,而且还有可能有特殊符号,而且如果屏幕有了缩放,那就更不准确了,所以这种方案很快就pass了。

方案二

也就是现在使用的方案,通过判断当前文本的offsetWidthscrollWidth,来确定是否显示tooltip,这种准确性就很可靠了,scrollWidth是文本实际的宽度不会因为显示了省略号而变化。

方案三

与方案二类似,不过比较的是文本的scrollWidth和其父元素的clientWidth,如果文本的scrollWidth大于其父元素的clientWidth,则显示tooltip。

具体代码实现

代码中除了单行文字超出显示省略号好tooltip之外,还加入了多行显示省略号好tooltip功能,主要是根据css属性-webkit-line-clamp来操作的。

多行显示tooltip的依据是dom.offsetHeight < dom.scrollHeight,也就是说,多行显示省略号后,其scrollHeight是不变的,类似scrollWidth,所以可以作为判断依据。

为了方便使用,本来想封装成自定义指令的形式,但是奈何el-tooltip不支持js调用的形式,所以只能封装成一个组件代码BaseElliTip.vue,代码如下:


<script lang="ts" setup>
/**
 * desc: 文本超出显示省略号,并且显示tooltip
 * feat:支持自定义多行显示省略
 * usage:
 *  1、<BaseElliTip>显示文本</BaseElliTip>
 *  2、<BaseElliTip text="显示文本"></BaseElliTip>
 *  3、<BaseElliTip content="显示文本">显示文本</BaseElliTip>
 *  4、<BaseElliTip :lineNumber="3">显示文本</BaseElliTip>
 */
import {computed, ref} from "vue";

interface IProps {
  // 显示内容
  text?: string,
  // 显示位置
  placement?: string,
  // tip内容
  content?: string,
  // 要显示的行数
  lineNumber: number
}

const prop = withDefaults(defineProps<IProps>(), {
  placement: 'top',
  lineNumber: 1
})

let showTooltip = ref(true);

function onMouseEnter(e) {
  const dom = e.target;
  console.log(dom.getBoundingClientRect().width, dom.clientWidth, dom.offsetWidth, dom.scrollWidth, dom.parentElement.offsetWidth)
  console.log(dom.offsetHeight, dom.scrollHeight)
  // 单行会比较 dom.offsetWidth < dom.scrollWidth
  // 多行会比较 dom.offsetHeight < dom.scrollHeight
  showTooltip.value = !(dom.offsetWidth < dom.scrollWidth || dom.offsetHeight < dom.scrollHeight);
}

</script>

<template>
  <el-tooltip class="elli-tip-box" v-bind="$attrs" :disabled="showTooltip" :placement="placement">
    <template #content>
      <span v-if="content || text">{ { content || text } }</span>
      <span v-else><slot></slot></span>
    </template>

    <div :class="lineNumber>1 ? 'text-ellipsis-multiple' : 'truncate'" @mouseenter.stop="onMouseEnter">
      <slot>{ { text } }</slot>
    </div>
  </el-tooltip>
</template>

<style lang="less" scoped>
//多行文本文字超过行数限制后显示省略号
// caniuse兼容性不支持IE):https://caniuse.com/?search=-webkit-line-clamp
.text-ellipsis-multiple {
  word-break: break-all;
  text-overflow: ellipsis;
  overflow: hidden;
  display: -moz-box;
  display: -webkit-box;
  display: -ms-flexbox;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-line-clamp: v-bind('prop.lineNumber');
}
</style>

-webkit-line-clamp属性的兼容性还是不错的,除了IE之外,主流浏览器基本都支持。

参考链接