【Vue笔记】带文本溢出检测的 tooltip

613 阅读3分钟

目前常用的UI组件库都有 tooltip,然而都没有文本溢出检测的功能,下面展示2种情况,以供参考。

Vue2 + elementUI

利用 elementUIel-tooltip 组件的属性 disabled ,实现弹窗控制。

组件封装示例

<template>
  <el-tooltip
    ref="tooltip"
    :disabled="tooltipDisabled"
    :content="content"
  >
    <slot />
  </el-tooltip>
</template>

<script>
export default {
  name: 'TooltipCheckOverflow',
  props: {
    content: {
      type: String,
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      tooltipDisabled: true // 默认禁用 tooltip
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.onTooltipEvent() // DOM 加载完成后再执行
    })
  },
  updated() {
    this.$nextTick(() => {
      this.onTooltipEvent()
    })
  },
  methods: {
    onTooltipEvent() {
      const target = this.$refs.tooltip.$el // 获取目标
      if (target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight) { // 未溢出
        // target.style.pointerEvents = 'none' // 釜底抽薪,慎用,页面不存在DOM级刷新可用
        return
      }
      this.tooltipDisabled = false // 仅当文本溢出时释放 tooltip
    }
  }
}
</script>

以上示例实现了最小化封装,其他属性功能可自行添加。

封装调用示例

<template>
  <tooltip-check-overflow content="内容已溢出,...">
    <span class="inner-txt ellipsis">测试内容是否溢出测试内容是否溢出测试内容是否溢出</span>
  </tooltip-check-overflow>
</template>

<script>
import TooltipCheckOverflow from '@/components/TooltipCheckOverflow' //组件引入

...
    components: {
      TooltipCheckOverflow // 组件注册
    },
...
</script>

<style scoped>
.inner-txt {
  width: 20px;  
}

.ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>

需要注意

  1. 调用时,包含的内容 .inner-txt 要设置文本溢出相关样式(例如 .ellipsis)。
  2. 一定要确保 .inner-txt 的宽度是可控的,最好就如例子里所示宽度是写好的,如果发现封装组件失效,怎么都无法触发弹窗,大概率就是 .inner-txt 的宽度出了问题。
  3. 封装组件时,判断溢出语句为 target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight,这里不仅检测了宽度,还检测了高度,可以适配多行文本溢出的情况。
  4. 语句 target.style.pointerEvents = 'none' 会禁止当前 DOM 的所有鼠标事件,要慎用!如果当前页面不存在 DOM 级别的更新,也就是说在页面渲染完成后就确定是否需要 tooltip,以后也不改了,这种情况就可以使用该语句,一劳永逸。

Vue3 + arco-design

由于 arco-designa-tooltip / a-popover 都没有 disabled 属性,所以只好通过控制 content-stylearrow-style 的方式来禁用/释放弹窗。 这里推荐用 a-popover,感觉 a-tooltip 不好用。

组件封装示例

<template>
  <a-popover
    ref="tooltip"
    :content="String(content)"
    :arrow-style="{ ...arrowStyle, display: 'none' }"
    :content-style="{ ...contentStyle, display: 'none' }"
    @mouseenter="onTooltipEvent"
  >
    <slot />
  </a-popover>
</template>

<script setup>
import { ref } from 'vue'

defineProps({
  content: {
    default: undefined
  },
  contentStyle: {
    type: Object,
    default: () => {}
  },
  arrowStyle: {
    type: String,
    default: () => {}
  }
})

const tooltip = ref(null)

// 判断是否溢出,确定是否要显示 hover 弹窗
function onTooltipEvent() {
  const el = tooltip.value, target = el.$el.nextElementSibling // 获取目标 DOM
  if (target.clientWidth === target.scrollWidth && target.clientHeight === target.scrollHeight) { // 未溢出
    // target.style.pointerEvents = 'none'
    return
  }
  el.contentStyle.display = 'block'
  el.arrowStyle.display = 'block'
}
</script>

同样是最小化封装,其他功能自行添加。调用方式和前者完全一样,不多赘述。

需要注意

  1. a-popover 里获取 DOM 和 el-tooltip 不同,需要再取 nextElementSibling 属性。
  2. 通过预制 display: none; 将弹窗隐藏,仅当文本溢出时才 display: block;,实现弹窗禁用/释放功能,这里的禁用只是看不见了,并非真正意义的消失,如果介意,且页面不会有 DOM 级别的更新,可以使用 target.style.pointerEvents = 'none' 语句。

总结

  1. elementUIel-tooltip 通过 disabled 属性,在 DOM 渲染完成后立刻判断是否需要禁用/释放的方式进行组件封装;
  2. arco-designa-popover 通过 content-style、arrow-styledisplay,在鼠标滑入(mouseenter)事件中立刻判断是否应该显示的方式进行组件封装;
  3. 两者调用方式相同,使用时一定要注意内部标签 .inner-txt 的宽度控制,否则组件可能失控。

以上展示了 Vue2 + elementUIVue3 + arco-design 的封装示例,可以根据自己的项目情况灵活选用。

笔记主要为自用,欢迎友好交流!