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;

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

最终效果就是这样:

相关推荐
火³可²7 分钟前
【uniapp】轮播图
前端·vue.js·uni-app·php
Ian102518 分钟前
《Learn Three.js》学习(3)光源
前端·javascript·学习·webgl·图形学·三维·三维光源
帅比九日18 分钟前
基于@ohos/axios学习HarmonyOS Next的网络数据请求
前端·华为·harmonyos
NonDay35 分钟前
wxWidgets-ImageView
android·java·javascript
涔溪36 分钟前
css Grid网格布局
前端·css
秦老师Q1 小时前
HTML CSS JS基础考试题与答案
开发语言·前端·javascript·css·面试·html
AndyGoWei1 小时前
BFC (Block Formatting Context) 原理规则,看这篇就够了
前端·css·html
ss_Tina2 小时前
el-row el-col显示失效问题修复
javascript·vue.js·elementui
BillKu2 小时前
修改插槽样式,el-input 插槽 append 的样式
前端·css·vue.js
hongkid2 小时前
v-model在h函数和jsx下应该如何写
javascript·vue.js·elementui