当前位置: 首页 > news >正文

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>

相关文章:

  • Vue前端开发-Coupon组件
  • javaweb自用笔记:请求参数、响应、分层解耦、
  • 一二三应用开发平台——能力扩展:多数据源支持
  • 模板方法模式的C++实现示例
  • React基础之项目实战
  • 贵工程寝室报修系统的设计与实现
  • JavaScript 变量与常量全面解析
  • 在线程间共享数据_第三章_《C++并发编程》笔记
  • 什么是Flask
  • spring6概述
  • HTML 基础
  • el-pagination的使用说明
  • STM32---FreeRTOS消息队列
  • HarmonyOS学习第18天:多媒体功能全解析
  • day19-前端Web——Vue3+TS+ElementPlus
  • vue3 二次封装uni-ui中的组件,并且组件中有 v-model 的解决方法
  • 三级缓存架构
  • 2025解决软件供应链安全,开源安全的版本答案:SCA+SBOM
  • 基于DeepSeek,构建个人本地RAG知识库
  • 机电公司管理信息系统小程序+论文源码调试讲解
  • 秦洪看盘|热门股或将退潮,短线波动难免
  • 购房成本再降低!今年首次降息落地,30年期百万房贷月供将减少54元
  • 去年上海60岁及以上户籍老年人口占总人口的37.6%
  • “打铁”热邂逅江南水乡,长三角首个国际级铁三赛事有何不同
  • 永久基本农田竟沦为垃圾场,湖南湘潭回应:全面启动专项整治
  • 前四月国家铁路发送货物12.99亿吨,同比增长3.6%