Vue 实现智能检测文字是否溢出,溢出显示省略号,鼠标悬浮显示全部【附封装组件完整代码+详细注释+粘贴即食】

一、场景需求

在项目中,经常会遇到文本内容超出容器的情况。为了提高用户体验,我希望在文字溢出时显示悬浮提示,未溢出时则不显示。

二、效果演示

在这里插入图片描述

三、实现原理

DOM宽度对比法:通过比较元素的scrollWidth(实际内容宽度)和clientWidth(可视区域宽度)判断是否溢出
动态绑定Tooltip:利用el-tooltip的disabled属性按需激活提示
响应式监听:结合Vue的$nextTick和watch实现动态数据更新后的自动检测

四、完整代码

<template>
  <div class="box">
    <div v-for="(item,i) in list" :key="i" class="items">
      <!-- 添加 el-tooltip 并绑定判断逻辑 -->
      <el-tooltip 
        :disabled="!shouldShowTooltip[i]" 
        :content="`${item.name}:${item.score}`" 
        placement="top">
        <span 
          :ref="el => { nameElements[i] = el }"
          class="name"
          @mouseenter="checkOverflow(i)">
          {{ item.name }}{{ item.score }}
        </span>
      </el-tooltip>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        {
          name: '我是测试名称1',
          score: '90'
        },
        {
          name: '我是测试我是测试我是测试我是测试',
          score: '92'
        },
        {
          name: '雪芽',
          score: '99'
        },
        {
          name: '果粒橙果粒橙果粒橙果粒橙果粒橙',
          score: '100'
        }
      ],
      shouldShowTooltip: [], // 存储是否需要显示tooltip
      nameElements: []       // 存储DOM引用
    }
  },
  watch: {
    list() {
      this.$nextTick(() => {
        this.list.forEach((_, i) => this.checkOverflow(i))
      })
    }
  },
  mounted() {
    // 初始化时检查一次
    this.$nextTick(() => {
      this.list.forEach((_, i) => this.checkOverflow(i))
    })
  },
  methods: {
    // 通过对比实际宽度和可视宽度判断是否溢出
    checkOverflow(index) {
      const el = this.nameElements[index]
      if (el) {
        this.$set(this.shouldShowTooltip, index, el.scrollWidth > el.clientWidth)
      }
    }
  }
}
</script>

<style lang="scss" scoped>
  .box {
    width: 300px;
    font-size: 30px;
    border: 1px solid red;
    display: flex;
    gap: 24px;
    flex-direction: column;
    .items {
      flex: 1;
      display: flex;
      .name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }
  }
</style>

五、封装组件

如果项目此需求需求量大,可以将此逻辑封装成组件,便于不同页面使用~

1、组件:
<template>
  <el-tooltip
    :disabled="!showTooltip || disabled"
    :content="content"
    :placement="placement"
    :popper-class="popperClass">
    <div
      ref="contentBox"
      class="auto-tooltip-wrapper"
      @mouseenter="handleCheckOverflow">
      {{ content }}
    </div>
  </el-tooltip>
</template>

<script>
export default {
  name: 'AutoTooltip',
  props: {
    disabled: Boolean,  // 是否完全禁用功能(优先级最高)
    content: String, // 显示内容(同时用于提示和内容区域)
    // 提示框位置,参考ElementUI的placement配置
    placement: {
      type: String,
      default: 'top'
    },
    popperClass: String,  // 自定义Tooltip的类名
    // 溢出容差(解决1像素级误差问题)
    tolerance: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      showTooltip: false,  // 控制提示显示状态
      observer: null // ResizeObserver实例
    }
  },
  mounted() {
    this.initObserver()
    this.checkOverflow()
  },
  beforeDestroy() {
    if (this.observer) {
      // 组件销毁时断开观察器
      this.observer.disconnect()
    }
  },
  methods: {
    /**
     * 初始化ResizeObserver
     * 用于监听元素尺寸变化自动检测溢出状态
     */
    initObserver() {
      if (typeof ResizeObserver === 'undefined') return

      try {
        this.observer = new ResizeObserver(() => {
          this.checkOverflow()
        })
        this.observer.observe(this.$refs.contentBox)
      } catch (e) {
        console.warn('ResizeObserver not supported')
      }
    },

    /**
     * 执行溢出检测的主方法
     * 1. 获取DOM引用
     * 2. 调用计算方法
     * 3. 更新显示状态
     */
    checkOverflow() {
      const el = this.$refs.contentBox
      if (!el) return

      this.showTooltip = this.calculateOverflow(el)
    },

    /**
     * 计算元素是否溢出
     * @param {HTMLElement} el - 要检测的元素
     * @returns {boolean} 是否发生溢出
     */
    calculateOverflow(el) {
      return el.scrollWidth > el.clientWidth + this.tolerance
    },

    /**
     * 鼠标进入的回调检测(兼容模式)
     * 当ResizeObserver不可用时,手动触发检测
     */
    handleCheckOverflow() {
      if (!this.observer) {
        this.checkOverflow()
      }
    }
  }
}
</script>

<style scoped>
  .auto-tooltip-wrapper {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
    box-sizing: border-box;
    max-width: 100%;
  }
</style>

2、页面内使用:
<template>
  <div class="box">
    <div v-for="(item,i) in list" :key="i" class="items">
      <AutoTooltip :content="`${item.name}:${item.score}`"></AutoTooltip>
    </div>
  </div>
</template>

<script>
import AutoTooltip from './AutoTooltip.vue'
export default {
  components: { AutoTooltip },
  data() {
    return {
      list: [
        {
          name: '我是测试名称1',
          score: '90'
        },
        {
          name: '我是测试我是测试我是测试我是测试',
          score: '92'
        },
        {
          name: '雪芽',
          score: '99'
        },
        {
          name: '果粒橙果粒橙果粒橙果粒橙果粒橙',
          score: '100'
        }
      ],
      shouldShowTooltip: [], // 存储是否需要显示tooltip
      nameElements: []       // 存储DOM引用
    }
  },
  watch: {
    list() {
      this.$nextTick(() => {
        this.list.forEach((_, i) => this.checkOverflow(i))
      })
    }
  },
  mounted() {
    // 初始化时检查一次
    this.$nextTick(() => {
      this.list.forEach((_, i) => this.checkOverflow(i))
    })
  },
  methods: {
    // 通过对比实际宽度和可视宽度判断是否溢出
    checkOverflow(index) {
      const el = this.nameElements[index]
      if (el) {
        this.$set(this.shouldShowTooltip, index, el.scrollWidth > el.clientWidth)
      }
    }
  }
}
</script>

<style lang="scss" scoped>
  .box {
    width: 300px;
    font-size: 30px;
    border: 1px solid red;
    display: flex;
    gap: 24px;
    flex-direction: column;
    .items {
      flex: 1;
      display: flex;
      .name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }
  }
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值