vue3中教你如何使用指令解决文本的溢出提示

在我们项目开发中,经常会有超长文本溢出提示,未溢出则不提示的场景。
在项目开发中遇到了比较复杂的场景,在一个组织树中,我们使用了el-tree来显示组织树,文字长度不一,太长的显示不全,刚开始我们使用滚动条,结果不好看
后来我们就直接再el-tree中添加el-tooltip,发现没啥问题鼠标移入正常提示,但是再往下滚动的时候发现,页面会卡顿,因为每个el-tree都会产生一个el-tooltip,
只不过是通过v-if进行显示和隐藏的,频繁显示性能开销很大,性能极差,页面中还会出现一堆的 注释
想着开发一个指令,与el-tooltip类似,超出显示…移入显示内容

如下图所示,
请添加图片描述

<script lang="ts" setup>import {ref} from "vue";const treeData = ref([{"id": "65118224801137459281926519380023","title": "测试","level": 1,"parentId": "464064658652315648","type": "PROJECT","enable": 1,"list": [{"id": "65141629137928601691056522250367","title": "未超出长度","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []},{"id": "65141629137928601691056522250368","title": "超出隐藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []},{"id": "65141629137928601691056522250369","title": "超出隐藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []}]},
])const defaultProps = {children: 'list',label: 'title',
}const tooltipTitle = ref("")
const showTitle = ref(true)const onShowNameTipsMouseenter = (e) => {let target = e.target;let textLength = target.clientWidth;let containerLength = target.scrollWidth;if (textLength < containerLength) {tooltipTitle.value = e.target.innerText;showTitle.value = false;} else {showTitle.value = true;}
}</script><template><el-tree:data="treeData":props="defaultProps"style="width: 150px"><template #default="{ node }"><el-tooltip:content="tooltipTitle":disabled="showTitle"effect="dark"placement="top"><spanclass="span-ellipsis"@mouseover="onShowNameTipsMouseenter">{{ node.label }}</span></el-tooltip></template></el-tree>
</template><style lang="scss" scoped>
.span-ellipsis {width: 100%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;margin-right: 20px;
}
</style>

这样的代码比较复杂,而且复用性极低。如果在其他页面也有类似的场景,我们就不得不cv了

指令开发

如何判断是否溢出

这也算是一个知识点,首先我们需要判断文本是否溢出了节点。如何解决文本是否溢出,在element-ui中el-table有一个文本溢出可以隐藏,所以我再其源码中找到了这个element-ui/packages/table/src/table-body.js,判断溢出的方法,代码如下

const range = document.createRange();
range.setStart(cellChild, 0);
range.setEnd(cellChild, cellChild.childNodes.length);
const rangeWidth = range.getBoundingClientRect().width;
const padding = (parseInt(getStyle(cellChild, 'paddingLeft'), 10) || 0) +(parseInt(getStyle(cellChild, 'paddingRight'), 10) || 0);
if ((rangeWidth + padding > cellChild.offsetWidth || cellChild.scrollWidth > cellChild.offsetWidth) && this.$refs.tooltip) {// ...
}

我们稍微改造一下,拿来用

实现溢出指令

function getElStyleAttr(element, attr) {const styles = window.getComputedStyle(element)return styles[attr]
}const isOverflow = (target) => {const scrollWidth = target.scrollWidthconst offsetWidth = target.offsetWidthconst range = document.createRange()range.setStart(target, 0)range.setEnd(target, target.childNodes.length)const rangeWidth = range.getBoundingClientRect().widthconst padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
}
export const ellipsisTooltip = {mounted(el, binding) {// 避免用户遗漏样式,我们必须强制加上超出...样式el.style.overflow = 'hidden'el.style.textOverflow = 'ellipsis'el.style.whiteSpace = 'nowrap'const onMouseEnter = (e) => {if (isOverflow(el)) {console.log('溢出了')} else {console.log('未溢出')}}el.addEventListener('mouseenter', onMouseEnter)}
}

看下效果吧
请添加图片描述

如何把溢出的节点挂载到el-tooltip上

我们不直接像el-tooltip挂载到每个文本上面,这样就和el-tooltip没啥区别
我们直接把它添加在全局body下,而且每次直会显示一个,移出就隐藏删除添加的tooltip

首先我们需要准备好组件

MyTooltip.vue

<template><!-- 指示 --><transition name="el-fade-in-linear"><div v-show="tooltipShow" :style="tooltipStyle" class="wq-tooltip"><span class="wq-tooltip-text" v-text="text"></span><div :class="[{'left':placements === 'left'},{'bottom':placements==='bottom'},{'right':placements==='right'},{'top':placements==='top'}]" class="wq-tooltip-arrow"></div></div></transition>
</template><script>
import {ref, computed, onMounted} from 'vue'export default {setup() {// 显示弹框const tooltipShow = ref(false);// 提示内容const text = ref()// 方向const placements = ref('left')// 显示function showTip() {tooltipShow.value = true}//设置提示内容function setContent(content) {text.value = content}//隐藏function hiddenTip() {tooltipShow.value = false}// 位置const tooltipPosition = ref({x: 0,y: 0})const tooltipStyle = computed(() => {return {transform: `translate3d(${tooltipPosition.value.x}px,${tooltipPosition.value.y}px,0)`}})return {tooltipShow,showTip,hiddenTip,setContent,tooltipPosition,tooltipStyle,text,placements,}}
}
</script><style lang="scss" scoped>
// tooltip
.wq-tooltip {padding: 10px;font-size: 12px;line-height: 1.2;min-width: 10px;word-wrap: break-word;position: fixed;left: 0;top: 0;background: #303133;color: #fff;z-index: 1000;display: block;border-radius: 8px;font-weight: 500;pointer-events: none;
}// 小箭头
.wq-tooltip-arrow {position: absolute;width: 0;height: 0;border-width: 8px;border-style: solid;
}// 如果在左侧
.wq-tooltip-arrow.left {border-color: transparent transparent transparent #303133;right: -15px;top: 50%;transform: translate3d(0, -50%, 0);
}// 如果在下侧
.wq-tooltip-arrow.bottom {top: -15px;border-color: transparent transparent #303133 transparent;left: 50%;transform: translate3d(-50%, 0, 0);
}// 如果在右侧
.wq-tooltip-arrow.right {left: -15px;top: 50%;transform: translate3d(0, -50%, 0);border-color: transparent #303133 transparent transparent;
}// 如果在上侧
.wq-tooltip-arrow.top {bottom: -15px;border-color: #303133 transparent transparent transparent;left: 50%;transform: translate3d(-50%, 0, 0);
}/* 动画 */
.tooltip-enter-from,
.tooltip-leave-to {opacity: 0;transition: opacity .3s ease;
}.tooltip-leave-from,
.tooltip-enter-to {transition: opacity .1s ease;
}
</style>

在指令js中鼠标移入时挂载该组件,移出时销毁组件

directive.js

// 引入组件
import {createApp, nextTick} from "vue";
import MyToolTip from './MyToolTip.vue'// 位置定位
function calculationLocation(el, target, placements) {if (!el || !target) return;el.tooltipPosition.y = 0;el.tooltipPosition.x = 0;let el_dom = el.$el.nextElementSibling.getBoundingClientRect()let target_dom = target.getBoundingClientRect()if (placements === "left") {el.tooltipPosition.x = target_dom.x - el_dom.width - 10el.tooltipPosition.y = target_dom.y - el_dom.height / 2 + target_dom.height / 2} else if (placements === "bottom") {el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - el_dom.width / 2el.tooltipPosition.y = target_dom.y + el_dom.height + 10} else if (placements === "right") {el.tooltipPosition.x = target_dom.x + target_dom.width + 10el.tooltipPosition.y = target_dom.y - el_dom.height / 2 + target_dom.height / 2} else if (placements === "top") {el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - el_dom.width / 2el.tooltipPosition.y = target_dom.y - el_dom.height - 10}
}// 方向
const allPlacements = ['left', 'bottom', 'right', 'top']function getElStyleAttr(element, attr) {const styles = window.getComputedStyle(element)return styles[attr]
}const isOverflow = (target) => {const scrollWidth = target.scrollWidthconst offsetWidth = target.offsetWidthconst range = document.createRange()range.setStart(target, 0)range.setEnd(target, target.childNodes.length)const rangeWidth = range.getBoundingClientRect().widthconst padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
}export const ellipsisTooltip = {mounted(el, binding) {//获取指令的参数const {value: {placement, content, destroyOnLeave} = {}} = binding;// 加上超出...样式el.style.overflow = "hidden";el.style.textOverflow = "ellipsis";el.style.whiteSpace = "nowrap";//鼠标移开时 清除元素const onMouseLeave = () => {if (el.w_tipInstance) {el.w_tipInstance.hiddenTip()el.w_tooltip.remove()el.w_tipInstance = nullel.w_tooltip = null}};const onMouseEnter = () => {// 判断内容长度 需要展示if (isOverflow(el)) {const directiveList = allPlacements.filter(placement => binding.modifiers[placement])const placements = directiveList.length ? directiveList : allPlacementsif (!el.w_tooltip) {// 创建tooltip实例const vm = createApp(MyToolTip)// 创建根元素el.w_tooltip = document.createElement('div')// 挂载到页面document.body.appendChild(el.w_tooltip)el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}`el.w_tipInstance = vm.mount(el.w_tooltip)}// 设置 tooltip 显示方向el.w_tipInstance.placements = placement || placements[0] || 'top'// 设置显示内容el.w_tipInstance.setContent(content || el.innerText)// 使 tooltip 显示el.w_tipInstance.showTip()nextTick(() => {// 计算 tooltip 在页面中的位置calculationLocation(el.w_tipInstance, el, placements[0])})el._scrollHandler = () => {// 重新定位位置if (el.w_tipInstance && el.w_tipInstance.tooltipShow) calculationLocation(el.w_tipInstance, el, placements[0])}window.addEventListener('scroll', el._scrollHandler)const _destroyOnLeave = destroyOnLeave || trueif (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave);}};el.addEventListener("mouseenter", onMouseEnter);},unmounted(el) {if (el.w_tooltip) {document.body.removeChild(el.w_tooltip)}window.removeEventListener('scroll', el._scrollHandler)}
}

指令和组件都好了,我们就在全局配置指令即可

import App from './App.vue'
import {ellipsisTooltip} from './directive.js'const app = createApp(App);
app.directive('ellipse-tooltip', ellipsisTooltip);
app.mount('#app');

配置好全局指令后,页面中就可以直接使用了

<script lang="ts" setup>import {ref} from "vue";const treeData = ref([{"id": "65118224801137459281926519380023","title": "测试","level": 1,"parentId": "464064658652315648","type": "PROJECT","enable": 1,"list": [{"id": "65141629137928601691056522250367","title": "未超出长度","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []},{"id": "65141629137928601691056522250368","title": "超出隐藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []},{"id": "65141629137928601691056522250369","title": "超出隐藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏藏","level": 2,"parentId": "65118224801137459281926519380023","type": "PROJECT","enable": 1,"list": []}]},
])const defaultProps = {children: 'list',label: 'title',
}const tooltipTitle = ref("")
const showTitle = ref(true)const onShowNameTipsMouseenter = (e) => {let target = e.target;let textLength = target.clientWidth;let containerLength = target.scrollWidth;if (textLength < containerLength) {tooltipTitle.value = e.target.innerText;showTitle.value = false;} else {showTitle.value = true;}
}</script><template><el-tree:data="treeData":props="defaultProps"style="width: 150px"><template #default="{ node }"><!--      <el-tooltip--><!--          :content="tooltipTitle"--><!--          :disabled="showTitle"--><!--          effect="dark"--><!--          placement="top"--><!--      >--><span v-ellipse-tooltip.topclass="span-ellipsis"@mouseover="onShowNameTipsMouseenter">{{ node.label }}</span><!--      </el-tooltip>--></template></el-tree>
</template><style lang="scss" scoped>
.span-ellipsis {width: 100%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;margin-right: 20px;
}
</style>

看看效果
请添加图片描述

到这里我们的ellipse-tooltip指令就完成了,还有很大改进的地方,需要大佬们自行去完善了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/12169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS copy的正确姿势

参考文章 知识准备&#xff08;理解堆栈&#xff09; 堆区&#xff1a; 程序员管理 若程序员不释放&#xff0c;由os释放不同于数据结构中的堆&#xff0c;堆区的结构类似于数据结构中的链表栈区&#xff1a; 由编译器来管理 存放函数参数值&#xff0c;局部变量的值等结构类似…

Facebook消息群发脚本的制作思路!

在数字化社交日益盛行的今天&#xff0c;Facebook作为全球最大的社交平台之一&#xff0c;为企业和个人提供了广阔的交流与合作空间。 然而&#xff0c;手动向大量用户发送消息既耗时又低效&#xff0c;因此&#xff0c;开发一款能够自动群发消息的脚本成为了许多人的需求&…

【正点原子Linux连载】 第四十二章 多点电容触摸屏实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DLRK3568开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第四十…

汇昌联信:拼多多网店该如何开店?

拼多多网店的开设流程并不复杂&#xff0c;但需要细心和耐心去完成每一步。下面将详细阐述如何开设一家拼多多网店。 一、选择商品与定位 开设拼多多网店的第一步是确定你要销售的商品类型&#xff0c;这决定了你的目标客户群体和市场定位。你需要了解这些商品的市场需求、竞争…

CAST: Cross-Attention in Space and Time for Video Action Recognition

标题&#xff1a;CAST: 时空交叉注意力网络用于视频动作识别 原文链接&#xff1a;2311.18825v1 (arxiv.org)https://arxiv.org/pdf/2311.18825v1 源码链接&#xff1a;GitHub - KHU-VLL/CASThttps://github.com/KHU-VLL/CAST 发表&#xff1a;NeurIPS-2023&#xff08;CCF A…

【打字】打字训练之针对性键盘区域练习

本文章的核心点是&#xff1a;使用代码生成自己想要训练的键位的词汇&#xff0c;然后导入到打字软件针对性练习 一个程序员突然想纠正打字习惯源于腱鞘炎&#xff0c;虽然使用双拼打字已经不慢了&#xff0c;但是姿势不是很正确&#xff0c;导致了腱鞘炎。 所以想着好好纠正指…

消息队列选型

一、要解决的问题 1.1 异步 分析&#xff1a; 需要根据场景来判断。若整体链路的逻辑中&#xff0c;某些逻辑是不需要强实时的&#xff0c;滞后一段时间是允许的&#xff0c;同时又不会对用户带来不好的体验&#xff0c;那么可以使用MQ完成异步操作。 例如&#xff1a;秒杀场…

解锁客户需求密码:银行业数据分析在业务决策中的关键作用

一、引言 在数字化和大数据时代的浪潮下&#xff0c;银行业正经历着前所未有的变革。作为数据分析领域的资深专家&#xff0c;我深知数据分析在银行业务发展中的重要性和价值。本文将从银行业数据分析的角度出发&#xff0c;深入探讨相关业务场景下的数据分析应用&#xff0c;…

UVa11419 SAM I AM

UVa11419 SAM I AM 题目链接题意分析AC 代码 题目链接 UVA - 11419 SAM I AM 题意 给出一个 RC 大小的网格&#xff0c;网格上面放了一些目标。可以在网格外发射子弹&#xff0c;子弹会沿着垂直或者水平方向飞行&#xff0c;并且打掉飞行路径上的所有目标&#xff0c;如下图所…

Java 环境变量未生效

在配置 Java 环境变量的时候&#xff0c;有可能会出现修改了JDK的路径&#xff0c;但是Java的环境变量没有相应切换的情况。比如&#xff1a; 但此时在控制台使用java -version命令输出的JDK版本不是新配置的版本&#xff0c;依然是之前的&#xff0c;甚至提示找不到java命令&a…

汇昌联信科技:拼多多电商的运营流程有哪些?

在当今互联网高速发展的时代&#xff0c;电商平台层出不穷&#xff0c;其中拼多多以其独特的团购模式和低价策略迅速崛起&#xff0c;成为众多消费者和商家的新宠。那么&#xff0c;拼多多电商的运营流程究竟包含哪些环节呢?接下来&#xff0c;我们将从商品上架、营销推广、订…

B/S版+java开发的医院绩效考核系统maven+Visual Studio Code 医院绩效考核管理系统 提升医疗服务质量的关键

B/S版java开发的医院绩效考核系统mavenVisual Studio Code 医院绩效考核管理系统 提升医疗服务质量的关键 医院绩效评价系统的建设&#xff0c;优化医院绩效管理体系&#xff0c;规范化工作目标的设计、沟通、评价与反馈&#xff0c;改进和提供医院管理人员的管理能力和成效&am…

【Web】HNCTF 2024 题解(部分)

目录 Please_RCE_Me ezFlask GoJava ez_tp GPTS Please_RCE_Me <?php if($_GET[moran] flag){highlight_file(__FILE__);if(isset($_POST[task])&&isset($_POST[flag])){$str1 $_POST[task];$str2 $_POST[flag];if(preg_match(/system|eval|assert|call|…

Linux进程间几种通信机制

一. 简介 经过前一篇文章的学习&#xff0c; 我们知道Linux下有两种标准&#xff1a;system V标准和 Posix标准。 System V 和 POSIX 标准是操作系统提供的接口标准&#xff0c;它们规定了操作系统应该如何实现一些基本功能&#xff0c;比如线程、进程间通信、文件处理等。 …

数组的学习

一.数组定义方法&#xff1a; 方法一&#xff1a; 数组名(value0 value1 value2 ...) 方法二&#xff1a; 数组名([0]value [1]value [2]value ...) 方法三&#xff1a; 列表名"value0 value1 value2 ..." 数组名($列表名) 方法四&#xff1a; 数组名[0]"va…

暴雨分布式存储集群助重庆高校打造智慧校园

教育是国家发展的基石&#xff0c;教育兴则国家兴&#xff0c;教育强则国家强。党的二十大报告指出&#xff0c;“加快建设教育强国”&#xff0c;并提出到2035年“建成教育强国”的总体目标。随着数字时代的到来&#xff0c;以物联网、大数据、云计算和人工智能为代表的数字技…

redis原生命令及项目使用

主动更新策略 缓存问题及解决 布隆过滤出现哈希冲突解决方案: 选择合适的哈希函数:布隆过滤器的性能和哈希函数的选择密切相关。选择高效、低碰撞率的哈希函数可以降低误判率。通常使用的哈希函数有 MurmurHash、FNV 等。 合理设置过滤器大小:过滤器的大小(位数组的大小)…

学习笔记——字符串(单模+多模+练习题)

单模匹配 Brute Force算法&#xff08;暴力&#xff09; 算法思想 母串和模式串字符依次配对&#xff0c;如果配对成功则继续比较后面位置是否相同&#xff0c;如果出现匹配不成功的位置&#xff0c;则j&#xff08;模式串当前的位置&#xff09;从头开始&#xff0c;i&…

Docker和Kubernetes之间的关系

Docker和Kubernetes在容器化生态系统中各自扮演着不同的角色 它们之间是互补的&#xff0c;而不是替代关系。 Docker是一个开源的容器化平台&#xff0c;它允许开发人员将应用程序及其依赖项打包到一个可移植的容器中&#xff0c;并确保这些容器可以在任何Docker环境中一致地…

曲线救国:window 安装 docker

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…