toolTip文字溢出如何解决?给你几个万能移植方案!

在实际项目工程中,大家经常会遇到类似需求------文字溢出,鼠标悬停显示title,反之,则取消悬停title效果

如果用了组件库,那么在类似table等组件,确实内嵌类似功能,可以直接用。

反之,如果你自己写的组件,想要有上述效果,那么该怎么办?

给大家几个"万能方案",随取随用,并且我不会把代码写的过于冗余+过长,以大家看的舒适+便于移植为主。

如果你觉得适用,不妨关注西红柿,一起交流学习!那么下面直接开始正题吧!

一.对于市面tooltip的解决方案,目前有以下几种

toolTip的核心原理,就是找到"文字溢出的临界值",比如宽度,高度等等,你是要一行溢出效果,还是多行溢出,都有相应的解决办法。

1.第一种方法是"根据offsetWidth+scrollWidth 解决",这种方案属于传统方案,而且网上例子很多,而且部分博主封装成了自定义指令。缺点在于"代码量"太大,移植到你自己的项目中,需要花一点时间,优点在于"bug少+稳定"。

2.第二种方法算是下下策,根据字数进行限制。这里重点要区分文字和数字、字符的数量,因为十个汉字的位置,可以容纳更多数字,因此按照length来界定范围,还是会出bug,后面还得改。

3.第三种办法玩法很多,其实还是对比宽度、高度,拿到文字溢出的临界值,最后再去判断,当然我要讲的也是这一种。

二.toolTip demo移植方案

这一块我不考虑你目前用的什么"组件库",而是从demo层面,来实现原生title悬停显示效果。

如果你想更改"title"显示框的效果,那就把你所在组件库的toolTip拿出来,在外面包一层,再用我下面的"文字溢出判断方式",来判断即可。(如果看到这有点懵,问题不大,接着看,你会豁然开朗!)

再说一点,上述图片中的title显示效果,属于浏览器内置的css样式,无法改,并且不建议"硬改",否则会出现浏览器不兼容等疑难bug,想换颜色,换位置等等,你去组件库把toolTip拎出来包一层即可。

方法一:

先把我的代码放上

treeData是我的数据,因为里面有很多小模块,所以进行了循环。

第二层div加了鼠标事件(移入、移出),主要用来判断"文字溢出的临界点,到底是true还是false)

div里面包裹了两个span,{{item.classifyName}}是我要显示的文字内容。

v-if后面接的是一个布尔类型的开关 ,用以判断文字是否溢出,所以在template这里,已经很明显了,你可以按照你自己的项目,进行对照。

结构不是死的,根据实际情况去改。

上述图片代码奉上:

ini 复制代码
<div
        class="leftChild"
        v-for="(item, index) in treeData"
        :key="index"
        @click="handleChild(item)"
        style="font-weight: 600; font-size: 12px; padding: 0 20px"
      >
        <div
          @mouseenter="e => isShowToltip(e, index)"
          @mouseleave="hideTip(index)"
        >
          <span ref="treeDom" v-if="item.isShow" :title="item.classifyName">
            {{ item.classifyName }}</span
          >
          <span ref="treeDom" v-else> {{ item.classifyName }}</span>
        </div>
      </div>

接下来要做的就是"判断文字溢出的临界点 ",重点是isShowToltip函数+hideTip函数,写完功能也就实现了。

先上图一睹为快!

解释一下isShowToltip函数:

第一步设置了一个开关bool,布尔类型,用来判断文字是否溢出。

第二步获取父元素的宽度,是在上图@mouseenter取得回调函数,e.target这个就不解释了,很简单。

重点说一下getBoundingClientRect

getBoundingClientRect()获取元素位置,这个方法没有参数

getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。

getBoundingClientRect()是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。
该函数会返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height

child.getBoundingClientRect().width就是获取当前元素的宽度。

咱们拿到外层div宽度后,再和里层span的宽度进行对照,结果就是"文字是否溢出"的开关(核心原理,是不是很简洁!)

回到正题,lastSpan指的是内层span的宽度,这里初始化为0,下一步会赋值。

Array.from(e.target.children).forEach((child, i) => { lastSpan = child.getBoundingClientRect().width console.log(e.target 的第 ${i} 个子元素宽度, lastSpan) })

这一步有点说法,因为使用getBoundingClientRect的前提是获取一个dom,只有在dom后面,才能用。

而e.target.children是一个类数组对象,明显不符合,所以做了数组化(Array.from)

后面对其进行了循环赋值。循环的内容,其实就是里面的span,一般情况下,都是一个div包一个span,所以说到底其实就是在循环"这个title",你可以直接用。

到了下一步,其实父元素+子元素的实际宽度都拿到了,那么比较,再给开关赋值即可!

this.treeData[index].isShow = bool这一步,其实我把开关放到了"我个人的数据中",如果你和我的情况类似,可直接用(把treeData变量换成你的名字即可)。

反之,那么可以把开关的布尔值return返回,同样可以在模板中进行判断。(举一反三即可)

至于hideTip函数,这个就不用解释了,那就是鼠标移出,关闭开关(这一步可有可无,因为每次鼠标移入,都会重置开关)

那么第一种方法就完结撒花了,是不是很简单!!!

再说第二种方法,原理类似,先放template模板结构:

代码如下:

ini 复制代码
<div
        class="leftChild"
        v-for="(item, index) in treeData"
        :key="index"
        @click="handleChild(item)"
        style="font-weight: 600; font-size: 12px; padding: 0 20px"
      >
        <span
          ref="treeDom"
          v-if="isOverflowing(item.classifyName)"
          :title="item.classifyName"
        >
          {{ item.classifyName }}</span
        >
        <span ref="treeDom" v-else> {{ item.classifyName }}</span>
      </div>

这里我想重点说一下isOverflowing,也是最关键的实现点。

我先把实现图奉上:

代码示意:

ini 复制代码
 isOverflowing (text) {
      const node = document.createElement('div')
      node.style.position = 'absolute'
      node.style.opacity = '0'
      node.style.whiteSpace = 'nowrap'
      node.innerText = text

      document.body.appendChild(node)
      const isOverflow = node.offsetWidth > 256
      document.body.removeChild(node)
      return isOverflow
    },

isOverflowing方法的实现原理,同样是判断"文字溢出的临界点"

这里面我新建一个dom------div,然后对其进行绝对定位+css样式设置

接着把你的title赋值给它,也就是node.innerText = text这一步。

直白一点,现在我就是模拟一个dom,1比1精仿你的title及外层盒子。

把它加在html上,也就是document.body.appendChild(node)这一步。

然后拿到其offsetWidth数值,和我最外层盒子进行判断。

这里有人会有疑问,为什么要用offsetWidth?为什么不用scrollWidth?

首先我1:1精仿的盒子,并没有限制宽度,而是它的内容,会完全展示在div里,我拿offsetWidth,就是其title的总长度。

那么接下来const isOverflow = node.offsetWidth > 256这一步就很好理解了,256你自己换成自己外层盒子的宽度即可。

最后比对完,把精仿的div盒子removeChild移除掉即可。

函数最终返回值是布尔类型,用以判断title内容是否溢出,在template模板上v-if判断不同情况即可。

另外再讲一下对于title样式的设置。

可能有的人想换一种效果,比如:

其实同样很简单,拿你项目的配套toolTip组件,在最外面包一层即可,顺便把:title属性去掉,即可完成!

另外对于超出显示省略号的问题,在你的盒子上加上这三个属性就可以:

css 复制代码
1.  white-space: nowrap !important;
1.  overflow: hidden !important;
1.  text-overflow: ellipsis !important;

两种方法任选其一,直接移植在项目中,就很简单了!

最终效果就是这样:

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax