Implement a display ellipsis that exceeds the content, and the mouse floats into the display tooltip. If it does not exceed, the tooltip component is not displayed

Implement a display ellipsis that exceeds the content, and the mouse floats into the display tooltip. If it does not exceed, the tooltip component is not displayed

ps: this component is based on element plus and implemented using vue3 the latest setup syntax. If you don't know, you can use other technology stacks according to my idea.

background

In many parts of the project, there is a need to display ellipsis and then float the mouse to display tooltip. Before that, I found that some projects display tooltip when the mouse floats in, regardless of whether it is exceeded; Some even display ellipsis without tooltip, that is, in this case, users don't even know the complete information. I don't think this should be the desired effect of the product. It may be that there are too many demands before, or there are too many people handling this project, so they don't pay attention to this subtle function. Then I took advantage of the idle period of this version iteration to sort out and find the products one by one to unify and improve this function in the project. The main technology stack of the project is vue2, which encapsulates a component in the project. Here, I want to use vue3 + element plus to organize your ideas and consolidate and learn the new setup syntax of vue3.

Function point

  • Display ellipsis exceeded
  • When the ellipsis is displayed, move the mouse in to display all
  • Considering that it is not plain text, you can customize the content area
  • Consider that the content displayed by tooltip can be customized

realization

  • Display ellipsis exceeded
    In fact, needless to say, we just need to implement it directly with css, that is, the common three, plus the limitation of width.

    <template>
      <div class="content" :style="{width: props.width}">
        {{props.content}}
      </div>
    </template>
    <script setup lang="ts">
      // Defines the type of props
      interface props {
        content: string,
        width: string
      }
      // Use withDefaults to assign default values to props
      const props = withDefaults(defineProps<props>(), {
        content: '',
        width: ''
      })
    </script>
    <style>
      .content {
        overflow: hidden; 
        white-space: nowrap;
        text-overflow: ellipsis
      }
    </style>
    

    This now enables the display of ellipsis beyond. Let's call to see the effect:

    Component call code

    <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: 'Hello, Hello, hello'
      })
    </script>
    
    <template>
      <ShowTooltip :content="content.data" width="200px"/>
    </template>
    

Episode: don't use setup syntax. I don't know. I feel really fragrant after using it. Components are registered automatically. Just import them. There is no need to write setup return separately. The template can be used directly. props also has a separately defined interface, and the previous context also separates attrs and emit... You can still try if you are interested. OK, let's get down to business and continue our component writing.

  • When the ellipsis is displayed, move the mouse in to display all

    • First, move the mouse to display the tooltip. Here, we use the El tooltip component in element plus.

      <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">
        // Defines the type of props
        interface props {
          content: string,
          width: string
        }
        // Use withDefaults to assign default values to props
        const props = withDefaults(defineProps<props>(), {
          content: '',
          width: ''
        })
      </script>
      <style>
        .content {
          overflow: hidden; 
          white-space: nowrap;
          text-overflow: ellipsis
        }
      </style>
      

      Now it only realizes the mouse moving in to display the tooltip, and does not distinguish whether it is exceeded or not.

    • Display tooltip only when the implementation exceeds

      Let's first think about how to judge whether it is exceeded.
      In fact, it is not difficult to think that we can use the width of the content to compare with the width of the outer box. When the width of the content is greater than or equal to the width of the box, the tooltip is displayed. We can use the span tag not affected by the css style, and the width is automatically expanded by the content. In this way, we can wrap the content with the span tag, and then calculate the width. Without saying much, we can go directly to the code.

        <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'
          // Defines the type of props
          interface props {
            content: string,
            width: string
          }
          // Use withDefaults to assign default values to props
          const props = withDefaults(defineProps<props>(), {
            content: '',
            width: ''
          })
          // Use isShow to control whether the tooltip is displayed
          let isShow = ref<boolean>(true)
          // Define a ref on the span tag
          const contentRef  = ref()
          const isShowTooltip = function (): void {
            // Calculate the offset width of the span tag and the offset width of the box element, and assign a value to 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>
      

      At this point, we have been able to meet the display of text content, and we have completed half of the work.

  • Considering that it is not plain text, you can customize the content area
    In fact, this step is relatively simple, that is, writing slots.

      <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>
    

    call

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

    The main reason for considering content as a slot is that it exists in the project: the values are listed in the form of tag, which exceeds the display of tooltip. I'll probably restore it here.

      <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: 'Hello, Hello, hello'
        })
        const tags = reactive([
          'Apple',
          'Pear',
          'Banana',
          'Mango',
          'pitaya',
          'kiwifruit'
        ])
      </script>
    
      <template>
        <!-- Simple text -->
        <ShowTooltip :content="content.data" width="200px"/>
      <br/>
        <!-- Customize content -->
        <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>
    
    

  • Consider that the content displayed by tooltip can be customized
    In the same way as above, the user-defined area slot is compatible with two toolipcontent and content. Direct code

      <template>
        <el-tooltip
          effect="dark"
          :content="props.tooltipContent ? props.tooltipContent : props.content"
          placement="top"
          :disabled="isShow"
        >
          <template #content>
            <!-- See the default values here first tooltipContent Yes or no, give default content -->
            <slot name="tooltipContent">{{props.tooltipContent ? props.tooltipContent : props.content}}</slot>
          </template>
          <div class="content" :style="{width: props.width}" @mouseover="isShowTooltip">
            <span ref="contentRef">
              <!-- Give a default value without a write slot, which is compatible with plain text -->
              <slot name="content">{{props.content}}</slot>
            </span>
          </div>
        </el-tooltip>
      </template>
      <script setup lang="ts">
        import { ref } from 'vue'
        // Defines the type of props
        interface props {
          content: string,
          width: string,
          tooltipContent?: string
        }
        // Use withDefaults to assign default values to props
        const props = withDefaults(defineProps<props>(), {
          content: '',
          width: '',
          tooltipContent: ''
        })
        // Use isShow to control whether the tooltip is displayed
        let isShow = ref<boolean>(true)
        // Define a ref on the span tag
        const contentRef  = ref()
        const isShowTooltip = function (): void {
          // Calculate the offset width of the span tag and the offset width of the box element, and assign a value to 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>
    

    call

      <ShowTooltip width="200px">
        <template v-slot:tooltipContent>
          <span>1223214234</span>
        </template>
        <template v-slot:content>
          <el-tag v-for="item in tags" :key="item" class="tag-item"> {{item}}</el-tag>
        </template>
      </ShowTooltip>
    

Summary

For this component, we mainly rely on several properties passed in and the slot thrown out for flexible use.

attribute
  • Width: the width of the box. This property is required if you want to use beyond display
  • Content: text content
  • Tooltipcontent: the text content displayed by tooltip. When the attribute is not transmitted, the content will be displayed
slot
  • Content: content slot, custom content area
  • Tooltipcontent: tooltipcontent custom area
Component complete code
<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">
        <!-- Give a default value without a write slot, which is compatible with plain text -->
        <slot name="content">{{props.content}}</slot>
      </span>
    </div>
  </el-tooltip>
</template>
<script setup lang="ts">
  import { ref, useSlots } from 'vue'
  // Defines the type of props
  interface props {
    content?: string,
    width: string,
    tooltipContent?: string
  }
  // Use withDefaults to assign default values to props
  const props = withDefaults(defineProps<props>(), {
    content: '',
    width: '',
    tooltipContent: ''
  })
  // Use isShow to control whether the tooltip is displayed
  let isShow = ref<boolean>(true)
  // Define a ref on the span tag
  const contentRef  = ref()
  const isShowTooltip = function (): void {
    // Calculate the offset width of the span tag and the offset width of the box element, and assign a value to 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>

Aside: you can implement more functions based on this as needed, such as click trigger or mouse move in trigger tooltip

Tags: Javascript Vue

Posted by Ron Woolley on Wed, 22 Sep 2021 12:00:41 +0530