基于ElementUI实现v-tooltip指令的示例代码
作者:carl_chen
文本溢出隐藏并使用tooltip 提示的需求,相信在平时的开发中会经常遇到。
常规做法一般是使用 elementui 的 el-tooltip 组件,在每次组件更新的时候去获取元素的宽度/高度判断是否被隐藏。
受益于 element-plus的虚拟触发 tooltip 的实现,决定探究在 vue2 上也以一种简单的方式实现 tooltip 提示。
探究 tooltip 源码
源码地址:https://github.com/ElemeFE/element/blob/dev/packages/tooltip/src/main.js
在 render 阶段,可以看出 tooltip 组件会提取插槽中的第一个子元素进行渲染
render(h) {
// ....
const firstElement = this.getFirstElement();
if (!firstElement) return null;
const data = firstElement.data = firstElement.data || {};
data.staticClass = this.addTooltipClass(data.staticClass);
return firstElement;
},
所以在 mounted 阶段, $el 会获取到的其实就是传入插槽的第一个元素。
并且在这个阶段,会给元素添加上mouseenter、mouseleave的事件监听,来控制 hover 状态下是否显示 tooltip
mounted() {
this.referenceElm = this.$el;
if (this.$el.nodeType === 1) {
this.$el.setAttribute('aria-describedby', this.tooltipId);
this.$el.setAttribute('tabindex', this.tabindex);
on(this.referenceElm, 'mouseenter', this.show);
on(this.referenceElm, 'mouseleave', this.hide);
// ...
}
函数式调用 tooltip
在了解了 el-tooltip 的运行原理之后,我们能够封装一个模板,并且支持函数式调用。
通过 getEl 获取一个 DOM 元素,以便在唤起 tooltip 时元素的所处位置。
<template>
<el-tooltip ref="triggerRef" :manual="true">
<template #content>
{{ internalContent }}
</template>
</el-tooltip>
</template>
<script>
// useOverflowHidden 的逻辑自行定义
import { useOverflowHidden } from './use-overflow-hidden'
export default {
name: 'TooltipFunction',
props: {
getEl: {
type: Function,
default: () => null
},
getContent: {
type: Function,
default: () => ''
}
},
data() {
return {
internalContent: '',
isHover: false,
}
},
mounted() {
const el = this.getEl()
if (!el) return
this.$refs.triggerRef.referenceElm = el;
el.addEventListener('mousemove', this.onMouseEnter, false)
el.addEventListener('mouseleave', this.onMouseLeave, false)
},
methods: {
onMouseEnter() {
if (!this.isHover && useOverflowHidden(this.getEl())) {
this.internalContent = this.getContent()
this.isHover = true
this.$refs.triggerRef.showPopper = true
}
},
onMouseLeave() {
if (this.isHover) {
this.isHover = false;
this.$refs.triggerRef.showPopper = false
}
},
onDestroy() {
const el = this.getEl()
if (!el) return
el.removeEventListener('mousemove', this.onMouseEnter)
el.removeEventListener('mouseleave', this.onMouseLeave)
}
},
}
</script>
模版完成过后,我们还需要再写一个函数用来调用 TooltipFunction。
import Vue from 'vue'
import TooltipFunction from './tooltipFunction.vue'
function useTooltip(el) {
const Ctor = Vue.extend(TooltipWrapper)
const instance = new Ctor({
propsData: {
getContent,
getEl: () => el,
},
})
instance.$mount()
return instance
}
在代码中,我们只需在 mounted 中对需要 tooltip 的元素进行一次注册即可。
<template>
<div ref="dataRef">foo</div>
</template>
<script>
import useTooltip from './use-tooltip.js'
export default {
mounted() {
const el = this.$refs.dataRef
if (el) {
// 获取 content 的函数逻辑自行定义
this.tooltipIns = useTooltip(el, () => 'foo')
}
},
beforeDestory() {
this.tooltipIns?.onDestroy()
this.tooltipIns?.$destroy()
}
}
</script>
自定义指令 v-tooltip
上述虽然实现了函数式调用 tooltip 的方式,但是还需要对每个元素进行 ref 声明获取 DOM,以及组件销毁时 tooltip 的生命周期管理。vue 的自定义指令恰好能轻松解决这两件事情。
function setupTooltipDirection() {
const tooltipSymbol = Symbol('tooltip')
Vue.directive('tooltip', {
bind(el: HTMLElement) {
// 这里我们使用 DOM 元素上的 tooltip-content 作为 通信
const instance = createTooltipFactory(el, () => el.getAttribute('tooltip-content') || '')
Reflect.set(el, tooltipSymbol, instance)
},
unbind(el) {
const ins = Reflect.get(el, tooltipSymbol)
if (!ins) return
ins.onDestroy()
ins.$destroy()
Reflect.set(el, tooltipSymbol, null)
},
})
}
在业务中,即可通过简单的指令实现 tooltip 的提示需求:
<template>
<div v-tooltip tooltip-content="hello">hello</div>
</template>
到此这篇关于基于ElementUI实现v-tooltip指令的示例代码的文章就介绍到这了,更多相关ElementUI实现v-tooltip内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
