基于Element封装一个tooltip指令

651 阅读1分钟

背景

项目中使用Element中的tooltip可以快速实现hover功能;而该组件默认触发机制是鼠标移动到对应的元素上时就显示tooltip;但是业务需要子级文本超出父级才显示,所以该行为往往不符合业务需求,这时候就涉及到元素的动态计算,而此时使用tooltip组件反而使模块中出现很多重复代码。

封装Tooltip指令

    import Vue from 'vue'

    import Popper from 'element-ui/src/utils/popper'
    import PopupManager from 'element-ui/src/utils/popup/popup-manager'
    
    class Tooltip {
        constructor() {
            this.options = {
                offset: 0,
                arrowOffset: 0,
                placement: 'top',
                boundariesPadding: 10,
                gpuAcceleration: false,
            }
            this.tooltip = new Vue({
                data() {
                    return {
                        content: '',
                        showPopper: false,
                        disabled: false,
                        effect: 'dark'
                    }
                },
                render() {
                    return <div v-show={ !this.disabled && this.showPopper } class={ ['el-tooltip__popper', "is-"+this.effect, this.popperClass] }>
                        { this.content }<div x-arrow class="popper__arrow"></div>
                    </div>
                }
            }).$mount()
            document.body.appendChild(this.tooltip.$el)
        }
        handleEnter(props, el) {

            let content = props.value.content

            if (typeof content === 'function' && !(content = content())) return

            el = el.target || el

            if (props.modifiers.overflow) {
                const style = getComputedStyle(el)
                // const range = document.createRange()
                // range.setStart(el, 0)
                // range.setEnd(el, el.childNodes.length)
                const rangeDom = el.getBoundingClientRect()
                let width = parseInt(style.paddingLeft) + parseInt(style.paddingRight) + Math.round(rangeDom.width)

                if (width <= el.parentNode.offsetWidth) return
            }

            this.tooltip.showPopper = true
            this.tooltip.content = content

            this.tooltip.$nextTick(() => {
                const popperJs = new Popper(el, this.tooltip.$el, Object.assign(this.options, props.value.options || {}))
                popperJs._popper.style.zIndex = PopupManager.nextZIndex()
            })
        }
        handleLeave() {
            this.tooltip.showPopper = false
        }
        bind(el, binding) {
            if (!binding.value.content) return
            el.addEventListener('mouseenter', binding.def.handleEnter.bind(binding.def, binding))
            el.addEventListener('mouseleave', binding.def.handleLeave.bind(binding.def))
        }
        unbind(el, binding) {
            if (!binding.value.content) return
            el.removeEventListener('mouseenter', binding.def.handleEnter)
            el.removeEventListener('mouseenter', binding.def.handleLeave)
        }
    }
    Vue.directive('popper', new Tooltip())

如何使用

注意:使用当前指令并且修饰符为overflow的元素,不能有同级元素

使用方法如下

<span v-popper.overflow="{ content: 'Top center', options: { placement: 'top-start' } }">Top center</span>

该指令有两个修饰符,分别为overflow、hover(默认值);另外有两个参数content、options; 当content内容是动态数据时,此时content是个函数。

最终效果

image.png