该篇文章系转载:DiuDiu_yang,地址:https://blog.csdn.net/qq_41261490/article/details/120415205

实现一个内容超出显示省略号,并鼠标浮入显示tooltip,不超出的不显示tooltip组件

ps:该组件是基于element-plus,使用vue3最新的setup语法糖实现的。不清楚的大家可以根据我的思路用其他技术栈实现。

背景
       项目中有很多地方有超出显示省略号,然后鼠标浮入显示tooltip的需求。在这之前,我发现项目中有些是鼠标浮入都显示tooltip,无关乎是否超出;还有一些甚至超出显示省略号,而没有加tooltip,也就是这种情况用户连完整信息都不清楚。我感觉这应该不是产品想要的效果,可能是之前需求太多,或者这个项目经手的人太多,导致没有注意到这种细微的功能。然后我趁着这版本迭代的空闲期整理了一下,找产品挨个对了一下,想统一和完善项目中这种功能。项目主要的技术栈是vue2,在项目中封装了一个组件。在这里,我想着用vue3+element-plus实现,来给大家整理思路,以及巩固和学习一下vue3新setup语法糖。

功能点
    1.超出显示省略号
    2.显示省略号的情况下,鼠标移入显示全部
    3.考虑不是纯文本的情况,即可自定义内容区
    4.考虑tooltip显示的内容可自定义

实现

     1.超出显示省略号
       这个其实不用多说,我们直接用css来实现就好,就是常见老三样,加上宽度的限制。

  • <template>
      <div class="content" :style="{width: props.width}">
        {{props.content}}
      </div>
    </template>
    <script setup lang="ts">
      // 定义props的类型
      interface props {
        content: string,
        width: string
      }
      // 使用withDefaults来给props赋默认值
      const props = withDefaults(defineProps<props>(), {
        content: '',
        width: ''
      })
    </script>
    <style>
      .content {
        overflow: hidden; 
        white-space: nowrap;
        text-overflow: ellipsis
      }
    </style>
    

    现在这样就实现了超出显示省略号。我们来调用看看效果:
    在这里插入图片描述
    组件调用代码

<script setup lang="ts">
  // This starter template is using Vue 3 <script setup> SFCs
  // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
  // import HelloWorld from './components/HelloWorld.vue'
  import { reactive } from 'vue';
  import ShowTooltip from './showTooltip.vue';
  const content = reactive({
    data: '您好您好您好您好您好您好您好您好您好您好您好'
  })
</script>

<template>
  <ShowTooltip :content="content.data" width="200px"/>
</template>

小插曲:不用setup语法糖不知道,用了之后感觉真香。组件自动注册,只需引入就好。不用再单独写setup return,模版直接能用。props也是有单独定义的接口,之前的context也分开了attrs和emit…大家有兴趣的还是可以去尝试尝试。好言归正传,继续我们的组件编写。

      显示省略号的情况下,鼠标移入显示全部

     2.先实现鼠标移入显tooltip,我们这儿tooltip就使用element-plus里的el-tooltip组件。

  • <template>
      <el-tooltip
        effect="dark"
        :content="props.content"
        placement="top"
      >
        <div class="content" :style="{width: props.width}">
          {{props.content}}
        </div>
      </el-tooltip>
    </template>
    <script setup lang="ts">
      // 定义props的类型
      interface props {
        content: string,
        width: string
      }
      // 使用withDefaults来给props赋默认值
      const props = withDefaults(defineProps<props>(), {
        content: '',
        width: ''
      })
    </script>
    <style>
      .content {
        overflow: hidden; 
        white-space: nowrap;
        text-overflow: ellipsis
      }
    </style>
    

    现在只是实现了鼠标移入显示tooltip,并没有区分是否超出。

       3.实现超出才显示tooltip

我们先思考一下,如何判断是否超出。
其实不难想到,我们可以利用内容的width和外层盒子的宽度作对比。当内容的宽度大于等于盒子的宽度是,就显示tooltip。我们可以利用span标签不受css样式影响,宽度是内容自动撑开的,这样我们可以将内容使用span标签包裹起来,然后计算宽度,话不多说,直接上代码。

  <template>
    <el-tooltip
      effect="dark"
      :content="props.content"
      placement="top"
      :disabled="isShow"
    >
      <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
        <span ref="contentRef">{{props.content}}</span>
      </div>
    </el-tooltip>
  </template>
  <script setup lang="ts">
    import { ref } from 'vue'
    // 定义props的类型
    interface props {
      content: string,
      width: string
    }
    // 使用withDefaults来给props赋默认值
    const props = withDefaults(defineProps<props>(), {
      content: '',
      width: ''
    })
    // 使用isShow来控制tooltip是否显示
    let isShow = ref<boolean>(true)
    // 在span标签上定义一个ref
    const contentRef  = ref()
    const isShowTooltip = function (): void {
      // 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值
      if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) {
        isShow.value = true
      } else {
        isShow.value = false
      }
    }
  </script>
  <style>
    .content {
      overflow: hidden; 
      white-space: nowrap;
      text-overflow: ellipsis
    }
  </style>
    • 到这里,已经能满足文本内容的显示了,我们也完成一半的工作了。

  • 考虑不是纯文本的情况,即可自定义内容区
    其实到这一步就比较简单了,就是写插槽。

  •   <template>
        <el-tooltip
          effect="dark"
          :content="props.content"
          placement="top"
          :disabled="isShow"
        >
          <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
            <span ref="contentRef">
              <slot name="content">{{props.content}}</slot>
            </span>
          </div>
        </el-tooltip>
      </template>
    

    调用

  <ShowTooltip :content="content.data" width="200px">
    <template v-slot:content>1212324323</template>
  </ShowTooltip>

考虑将内容作为插槽主要是因为项目中存在:以tag的形式列举值,超出tooltip显示。这里我大概还原一下。

  <script setup lang="ts">
    // This starter template is using Vue 3 <script setup> SFCs
    // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
    // import HelloWorld from './components/HelloWorld.vue'
    import { reactive } from 'vue';
    import ShowTooltip from './showTooltip.vue';
    const content = reactive({
      data: '您好您好您好您好您好您好您好您好您好您好您好'
    })
    const tags = reactive([
      '苹果',
      '梨子',
      '香蕉',
      '芒果',
      '火龙果',
      '猕猴桃'
    ])
  </script>

  <template>
    <!-- 单纯的文本 -->
    <ShowTooltip :content="content.data" width="200px"/>
  <br/>
    <!-- 将内容自定义 -->
    <ShowTooltip :content="content.data" width="200px">
      <template v-slot:content>
        <el-tag v-for="item in tags" :key="item" class="tag-item"> {{item}}</el-tag>
      </template>
    </ShowTooltip>
  </template>

  <style>
    .tag-item {
      margin-left: 5px;
    }
  </style>

  • 在这里插入图片描述

  • 考虑tooltip显示的内容可自定义
    采取上面同样的办法,将自定义区域slot,并且兼容两个tooltipContent 与 content。直接上代码

  <template>
    <el-tooltip
      effect="dark"
      :content="props.tooltipContent ? props.tooltipContent : props.content"
      placement="top"
      :disabled="isShow"
    >
      <template #content>
        <!-- 此处的默认值先看tooltipContent有没有,没有就给默认content -->
        <slot name="tooltipContent">{{props.tooltipContent ? props.tooltipContent : props.content}}</slot>
      </template>
      <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
        <span ref="contentRef">
          <!-- 给一个没有写插槽的默认值,兼容纯文本的情况 -->
          <slot name="content">{{props.content}}</slot>
        </span>
      </div>
    </el-tooltip>
  </template>
  <script setup lang="ts">
    import { ref } from 'vue'
    // 定义props的类型
    interface props {
      content: string,
      width: string,
      tooltipContent?: string
    }
    // 使用withDefaults来给props赋默认值
    const props = withDefaults(defineProps<props>(), {
      content: '',
      width: '',
      tooltipContent: ''
    })
    // 使用isShow来控制tooltip是否显示
    let isShow = ref<boolean>(true)
    // 在span标签上定义一个ref
    const contentRef  = ref()
    const isShowTooltip = function (): void {
      // 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值
      if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) {
        isShow.value = true
      } else {
        isShow.value = false
      }
    }
  </script>
  <style>
    .content {
      overflow: hidden; 
      white-space: nowrap;
      text-overflow: ellipsis
    }
  </style>

调用


小结
这个组件我们主要依赖传入的几个属性,及抛出的slot灵活使用。

属性
width: 盒子的宽度,要使用超出显示…,该属性必填
content: 文本内容
tooltipContent: tooltip显示的文本内容,不传该属性时,显示content
slot
content: 内容插槽,自定义内容区域
tooltipContent: tooltip内容自定义区域
组件完整代码

<template>
  <el-tooltip
    effect="dark"
    :content="props.tooltipContent ? props.tooltipContent : props.content"
    placement="top"
    :disabled="isShow"
  >
    <template #content>
      <slot name="tooltipContent">{{props.tooltipContent ? props.tooltipContent : props.content}}</slot>
    </template>
    <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
      <span ref="contentRef">
        <!-- 给一个没有写插槽的默认值,兼容纯文本的情况 -->
        <slot name="content">{{props.content}}</slot>
      </span>
    </div>
  </el-tooltip>
</template>
<script setup lang="ts">
  import { ref, useSlots } from 'vue'
  // 定义props的类型
  interface props {
    content?: string,
    width: string,
    tooltipContent?: string
  }
  // 使用withDefaults来给props赋默认值
  const props = withDefaults(defineProps<props>(), {
    content: '',
    width: '',
    tooltipContent: ''
  })
  // 使用isShow来控制tooltip是否显示
  let isShow = ref<boolean>(true)
  // 在span标签上定义一个ref
  const contentRef  = ref()
  const isShowTooltip = function (): void {
    // 计算span标签的offsetWidth与盒子元素的offsetWidth,给isShow赋值
    if(contentRef.value.parentNode.offsetWidth > contentRef.value.offsetWidth) {
      isShow.value = true
    } else {
      isShow.value = false
    }
  }
</script>
<style>
  .content {
    overflow: hidden; 
    white-space: nowrap;
    text-overflow: ellipsis
  }
</style>

题外话:大家可以根据需要基于此实现更多功能,比如点击触发,还是鼠标移入触发tooltip等

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐