如何实现文本溢出的时候才悬浮展示完整内容?我们知道,element-ui的tooltip组件不管文本有没有溢出,鼠标悬浮
我们知道,element-ui
的tooltip
组件不管文本有没有溢出,鼠标悬浮上去都会展示内容。那么要实现只有文本溢出的时候鼠标悬浮上去才展示内容完整该怎么实现呢?
思路分析
文本溢出的判断条件是el.scrollWidth > el.offsetWidth
, 只要满足这个条件就监听鼠标的mouseenter
和mouseleave
事件,并悬浮展示完整内容不就好了吗?
问题来了,在什么情况下判断这个文本溢出条件呢?
判断文本溢出条件是否满足得操作具体的dom
元素,而自定义指令恰好能满足需求。
第一个问题解决了,那鼠标悬浮上去那个展示效果怎么做呢?
这个效果其实tooltip
组件已经做好了不是吗?我们直接获取组件实例并调用相关方法不就行了吗?
话不多说,开整!
先做一些准备工作
import Vue from 'vue';
import { Tooltip } from 'element-ui';
Vue.use(Tooltip);
let tooltipContent
let props
const ctx = '@@store'
// 创建一个Vue实例并渲染为真实DOM,内有一个空的el-tooltip组件
const vm = new Vue({
render (h) {
return (<Tooltip ref="customToolTipRef" placement="top" content={ tooltipContent } { ...{ props } }></Tooltip>)
}
}).$mount()
const tooltipVM = vm.$refs.customToolTipRef
上述代码定义了一个空的el-tooltip
组件,tooltipContent
为组件所要渲染的内容,props
为传给组件的属性, ctx
是命名空间,为了方便维护el
上的对象,tooltipVM
为组件实例。
接下来就开始定义指令
import debounce from 'throttle-debounce/debounce';
const activateTooltip = debounce(50, tooltipVM => tooltipVM.handleShowPopper());
Vue.directive('overflowTooltip', {
// 只调用一次,指令第一次绑定到元素时调用。在这里进行一次初始化设置,初始化鼠标事件,控制el-tooltip的展开与收起
bind: function (el, binding, vnode) {
el[ctx] = {
tooltipContent: '',
props: {},
handleMouseEnter: () => {
// 展开el-tooltip方法,将el-tooltip的引用元素指向当前绑定节点,然后执行展开逻辑
tooltipContent = el[ctx].tooltipContent
props = el[ctx].props
vm.$forceUpdate()
tooltipVM.referenceElm = el
tooltipVM.$refs.popper && (tooltipVM.$refs.popper.style.display = 'none')
tooltipVM.doDestroy()
tooltipVM.setExpectedState(true)
activateTooltip(tooltipVM)
},
handleMouseLeave: () => {
// 关闭el-tooltip方法,销毁内部popperJS的实例后走关闭逻辑
tooltipVM.doDestroy()
tooltipVM.setExpectedState(false)
tooltipVM.handleClosePopper()
}
}
},
})
当指令第一次绑定到元素时,我们进行初始化设置。
handleMouseEnter
方法中把el-tooltip组件的引用元素绑定节点,并把该组件渲染出来的元素设置隐藏(刚绑定的时候鼠标悬浮上去不应该展示提示框),把悬浮的提示框置为初始状态(防止保留上一次的状态),然后再展示提示框。这里用了防抖来避免频繁触发导致的性能问题。
handleMouseLeave
方法把提示框实例销毁,然后关闭当前提示框。
然后根据文本是否溢出来进行相关操作
const overflowHandler = (el, binding, vnode) => {
// 获取元素文本内容,作为el-tooltip的默认content进行展示
el[ctx].tooltipContent = el.innerText || el.textContent
// 获取通过指令接收的绑定值
el[ctx].props = { ...binding.value }
const computedStyle = getComputedStyle(el)
// 使用range对象判断文本是否有溢出,优先考虑使用range对象, 因为 scrollWidth 属性在火狐浏览器 v32 版本中有 bug。当元素的 CSS 属性中使用了 text-overflow: ellipsis 和 box-sizing: border-box 时获取到的 scrollWidth 的值会比真实值偏小
const range = document.createRange()
range.setStart(el, 0)
range.setEnd(el, el.childNodes.length)
const rangeDOM = range.getBoundingClientRect()
const padding = parseInt(computedStyle.paddingLeft.replace('px', '')) + parseInt(computedStyle.paddingRight.replace('px', ''))
const rangeWidth = Math.round(rangeDOM.width)
if (rangeWidth + padding > el.offsetWidth || el.scrollWidth > el.offsetWidth) {
// 文本溢出了,绑定鼠标事件
el.addEventListener('mouseenter', el[ctx].handleMouseEnter)
el.addEventListener('mouseleave', el[ctx].handleMouseLeave)
} else {
// 文本未溢出,移除鼠标事件
el.removeEventListener('mouseenter', el[ctx].handleMouseEnter)
el.removeEventListener('mouseleave', el[ctx].handleMouseLeave)
}
}
Vue.directive('overflowTooltip', {
...,
inserted: overflowHandler,
componentUpdated: overflowHandler,
unbind (el) {
delete el[ctx]
}
})
在Dom
元素插入到父节点的时候和组件更新的时候处理悬浮展示。overflowHandler
函数中首先获取了元素文本内容和指令所绑定的值,并判断文件是否溢出,溢出的话就绑定鼠标事件,未溢出就解绑鼠标事件。这里值得注意的是,scrollWidth并不是所有的浏览器都兼容,我们就使用document.createRange()
来创建文本对象,并获取文本宽度和左右padding
,以此来判断文本是否溢出。
最后指令与元素解绑的时候删除el
上的全部属性。
/**
* 指令功能:元素内容溢出隐藏时悬浮tooltip展示详细内容,元素内容未溢出时不展示
* 基于element-table的show-overflow-tooltip原理与el-tooltip的功能实现
* 使用方式:
* <div v-overflow-tooltip>这是一段会溢出的文本内容</div>
*
* 指令也可传递参数,参数内容为el-tooltip的参数配置:
* <div v-overflow-tooltip="{content:'啦啦啦啦~~~'}">这是一段会溢出的文本内容</div>
* */
完结~撒花~~~
转载自:https://juejin.cn/post/7411160922645446690