vue 自定义指令实现v-overflow-tooltip

利用element ui el-tooltip 组件实现

一、注册创建局部指令

<template>
    <!-- eslint-disable -->
    <div>
            <div class="t-ellipsis" v-overflow-tooltip style="width: 70%;">
                    文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位
            </div>
            <div class="t-ellipsis" v-overflow-tooltip style="width: 30%;">
                    文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位
            </div>
    
             <!-- 添加本组件全局公用el-tooltip -->
            <el-tooltip  placement="top" ref="tooltip" :content="tooltipContent"></el-tooltip>
    </div>
</template>

<script>
/* eslint-disable */
    // 获取style
function getStyle(obj, attr) {
    if (obj.currentStyle) {
            return obj.currentStyle[attr];
    } else {
            return getComputedStyle(obj)[attr];
    }
}
export default {
    data() {
        return {
            // 为el-tooltip设置content值
            tooltipContent: ''
        }
    },
    directives: {
        overflowTooltip: {
            inserted(el, binding, vnode, oldVnode) {
                const tooltip = vnode.context.$refs.tooltip
                el.__vueOverflowTooltipMouseenter__ = function() {
                    const childNodesLength = el.childNodes.length
                    if (childNodesLength) {
                        const range = document.createRange();
                        range.setStart(el, 0);
                        range.setEnd(el, childNodesLength);
                        const rangeWidth = range.getBoundingClientRect().width;
                        const boxSizing = getStyle(el, 'boxSizing');
                        const padding = boxSizing === 'border-box' ? 0 : (parseInt(getStyle(el, 'paddingLeft'), 10) + parseInt(getStyle(el, 'paddingRight'), 10))

                        if (rangeWidth + padding > el.offsetWidth || el.scrollWidth > el.offsetWidth) {
                            vnode.context.$data.tooltipContent = el.innerText || el.textContent;
                            tooltip.referenceElm = el;
                            tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none');
                            tooltip.doDestroy();
                            tooltip.setExpectedState(true);
                            tooltip.handleShowPopper()
                        }
                    }
                }

                el.__vueOverflowTooltipMouseleave__ = function() {
                    tooltip.setExpectedState(false);
                    tooltip.handleClosePopper();
                }

                // 绑定事件
                el.addEventListener('mouseenter', el.__vueOverflowTooltipMouseenter__);
                el.addEventListener('mouseleave', el.__vueOverflowTooltipMouseleave__);
            },
            unbind(el) {
                el.removeEventListener('mouseenter', el.__vueOverflowTooltipMouseenter__);
                el.removeEventListener('mouseleave', el.__vueOverflowTooltipMouseleave__);
                delete el.__vueOverflowTooltipMouseenter__;
                delete el.__vueOverflowTooltipMouseleave__; 
            }
        }
    }
}
</script>
    
<style>
.t-ellipsis {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
</style>

 

在线示例:


二、注册创建全局指令

1. 创建overflow-tooltip指令

import Vue from 'vue'
/* eslint-disable */

function getStyle(obj, attr) {
  if (obj.currentStyle) {
      return obj.currentStyle[attr];
  } else {
      return getComputedStyle(obj)[attr];
  }
}

Vue.directive('overflow-tooltip', {
  inserted(el, binding, vnode, oldVnode) {
    // 获取app.vue根节点下的 ref为tooltip的el-tooltip组件
    const tooltip = vnode.context.$root._vnode.child.$refs.tooltip
    // 给添加指令的el对象添加鼠标移入事件
    el.__vueOverflowTooltipMouseenter__ = function() {
      // 获取el.childNodes长度判断是否触发Range对象计算宽度
      const childNodesLength = el.childNodes.length
      if (childNodesLength) {
        // 使用 range 代替 scrollWidth 来判断文本是否溢出,这样做是为了解决潜在的 bug。
        const range = document.createRange();
        // 设置 range 的起点
        range.setStart(el, 0);
        // 设置 range 的终点,因为起终点都在同一个节点上,所以设置终点偏移量以选中节点的内容
        range.setEnd(el, childNodesLength);
        // 获取节点的内容的宽度
        const rangeWidth = range.getBoundingClientRect().width;
        // 获取el盒模型
        const boxSizing = getStyle(el, 'boxSizing');
        // 获取el左右pandding
        const padding = boxSizing === 'border-box' ? 0 : (parseInt(getStyle(el, 'paddingLeft'), 10) + parseInt(getStyle(el, 'paddingRight'), 10))

        if (rangeWidth + padding > el.offsetWidth || el.scrollWidth > el.offsetWidth) {
          // 改变app.vue根节点的data里tooltipContent 值(给el-tooltip组件的content设置值)
          tooltip.$parent.$data.tooltipContent = el.innerText || el.textContent;
          // 关联el
          tooltip.referenceElm = el;
          // 隐藏之前打开的popper
          tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none');
          tooltip.doDestroy();
          tooltip.setExpectedState(true);
          tooltip.handleShowPopper()
        }
      }
    }
    // 给添加指令的dom对象添加鼠标移出事件
    el.__vueOverflowTooltipMouseleave__ = function() {
      tooltip.setExpectedState(false);
      tooltip.handleClosePopper();
    }
    
    // 绑定事件
    el.addEventListener('mouseenter', el.__vueOverflowTooltipMouseenter__);
    el.addEventListener('mouseleave', el.__vueOverflowTooltipMouseleave__);
  },
  update: function () {},
  componentUpdated: function () {},
  unbind: function (el) {
    el.removeEventListener('mouseenter', el.__vueOverflowTooltipMouseenter__);
    el.removeEventListener('mouseleave', el.__vueOverflowTooltipMouseleave__);
    delete el.__vueOverflowTooltipMouseenter__;
    delete el.__vueOverflowTooltipMouseleave__; 
  }
})

2.app.vue 添加全局el-tooltip组件

<template>
  <div id="app">
    ...
    <router-view/>
    <!-- 添加全局公用el-tooltip -->
    <el-tooltip  placement="top" ref="tooltip" :content="tooltipContent"></el-tooltip>
  </div>
</template>
<script>
export default {
  data() {
    return {
     // 为el-tooltip设置content值
      tooltipContent: ''
    }
  },
  components: {
  },
  mounted() {
  },
  created() {
  },
  methods: {
  }
}
</script>

3.main.js 引入overflow-tooltip指令

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'element-ui/lib/theme-chalk/index.css'
import ElementUI from 'element-ui'
// 引入overflow-tooltip指令
import '@/directive/overflow-tooltip'


Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.use(window.ELEMENT, {
  size: 'small'
})
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

 4.应用v-overflow-tooltip 指令

<template>
  <div>
        <div class="t-ellipsis" v-overflow-tooltip style="width: 70%;">
            文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位
        </div>
        <div class="t-ellipsis" v-overflow-tooltip style="width: 30%;">
            文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位文字占位
        </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
    }
  },
  mounted() {
  },
  methods: {
  }
}
</script>
<style>
.t-ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>
 
posted @ 2023-07-07 10:18  一丝心情  阅读(752)  评论(1编辑  收藏  举报