vue渲染数组各子项实现文本超出宽度显示tooltip

vue渲染数组各子项实现文本超出宽度显示tooltip

需求背景

vue项目中,通过v-for渲染数组,子项中均存在一行描述文字。当描述文字超出固定宽度时,显示省略号并且鼠标悬浮时显示tooltip;当描述文字不超出固定宽度时则正常显示,且鼠标悬浮不显示tooltip

否决方案

否决的两个方案均基于文本宽度与容器宽度的计算,具体实现将在后续实际方案中再进行详细阐述。否决方案仅做思路阐述,不做过于具体的赘述

1.存储各子项宽度计算结果

  • 具体实现:各子项描述文字容器绑定独有的ref,页面挂载完成后遍历各ref获取到对应元素并计算文本宽度与容器宽度关系,将计算结果存储在数组中。根据该结果通过v-if进行判断是否渲染tooltip
  • 否决原因:需要计算并存储所有子项的宽度对比,在数组量较大的情况下会有一定的性能问题。且在部分情境下,还需要考虑页面resize事件后是否要重新计算的问题

2.宽度计算结果实时应用渲染

  • 具体实现:各子项描述文字容器绑定独有的ref,各子项通过v-if绑定宽度计算函数,计算函数通过ref获取到对应元素并计算文本宽度与容器宽度关系,并返回计算结果。
  • 否决原因:v-if绑定的计算函数,内部需要在页面渲染完成后才能获取到对应的元素进行计算,但是无论是使用$nextTick还是结合Promise微任务都无法做到在渲染完成后计算结果并返回用于渲染更新

实现方案

宽度计算(核心)

// clientWidth为容器宽度,scrollWidth为文本完全展示时的实际宽度
isOverflowing(element) {
  return element ? element.scrollWidth > element.clientWidth : false
},

tooltip控制

<!-- 固定渲染tooltip,但手动控制显隐  -->
<a-tooltip
   :title="item.des"
   placement="top"
   :visible="visibleId == item.id">
</a-tooltip>

悬浮计算

<!-- 元素悬浮时才进行宽度计算 -->
<div
  :ref="'textRef_' + item.id"
  class="des"
  @mouseenter="handleMouseEnter(item.id)"
  @mouseleave="handleMouseLeave"
>
  {{ item.des }}
</div>

计算结果存储

// 处理鼠标进入事件
handleMouseEnter(id) {
  // 此处refs返回的是一个数组
  // 若并非绑定在v-for数组内部的子元素上时,refs返回的则是单个元素,需注意区分
  // 不一定需要refs才能获取到元素,通过事件本身传递的event对象也可获取到对应target
  const element = this.$refs['textRef_' + id][0]
  if (this.isOverflowing(element)) {
    this.visibleId = id
  }
},
// 处理鼠标离开事件
handleMouseLeave() {
  this.visibleId = ''
},

完整代码

<template>
  <div
    v-for="(item, index) in list"
    :key="index"
    class="module_item"
  >
    <div class="module_header">
      <div class="module_title">
        <div class="title">{{ item.title }}</div>
        <a-tooltip
          :title="item.des"
          placement="top"
          :visible="visibleId == item.id"
        >
          <div
            :ref="'textRef_' + item.id"
            class="des"
            @mouseenter="handleMouseEnter(item.id)"
            @mouseleave="handleMouseLeave"
          >
            {{ item.des }}
          </div>
        </a-tooltip>
      </div>
    </div>
    <div class="module_content">
      <!-- 内容 -->  
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      visibleId: '',
      list: []
    }
  },
  methods: {
    isOverflowing(element) {
      return element ? element.scrollWidth > element.clientWidth : false
    },
    // 处理鼠标进入事件
    handleMouseEnter(id) {
      const element = this.$refs['textRef_' + id][0]
      if (this.isOverflowing(element)) {
        this.visibleId = id
      }
    },
    // 处理鼠标离开事件
    handleMouseLeave() {
      this.visibleId = ''
    },
  }
}
</script>

<style lang="less" scoped>
.module_item {
  margin-top: 0.12rem;
  &:first-child {
    margin-top: 0;
  }
  .module_header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    .module_title {
      display: flex;
      align-items: center;
      gap: 0.08rem;
      .title {
        color: #000;
        font-size: 0.14rem;
      }
      .des {
        width: 1.83rem;
        color: #999;
        font-size: 0.1rem;
        text-align: left;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
  }
  .modules {
    margin-top: 0.08rem;
    display: flex;
    gap: 0.08rem;
  }
}
</style>

总结

  1. 悬浮时计算,不需要一次性计算所有元素的计算结果,性能良好
  2. 仅存储单个宽度计算结果控制显隐,无需冗余数据
  3. 无需v-if进行判断渲染
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值