实现 ElTable 超长显示 … 并且鼠标移入有tooltip

10 篇文章 0 订阅
1 篇文章 0 订阅

实现 ElTable 超长显示 … 并且鼠标移入有tooltip

最近做了个需求要求在table中不允许折行,超长通过“...”显示,并且鼠标移入的时候有 tooltip 显示完整信息。

1.背景

  • 由于项目开始比较早,使用的 elementPlus 版本为 2.2.15,此时的elementplus 还不支持,控制单个 tableColums 显示tooltip(目前最新的elementPlus实现了这个功能)。

2.实现

做了大量调研,发现最好的办法就是通过一个新的节点去和tablecolums做比较判断,是否显示tooltip

方法一

  • 在渲染table的时候对tableColums做封装,使用elTooltip包一层,新建一个节点,和tableColums做判断。刚开始这个方法已经实现,用了一段时间,随着数据量的变大,发现这种方法导致table渲染很慢,接口数据回来两秒之后才能出了table(前提是tableTree或者一页200条数据)。所以萌生出了做优化的想法,也就出了方法二。

方法二

  • 现在我们定的方案是在table渲染完再做tooltip的判断

  • 使用指令的方法 在 mouseover 的时候判断是否渲染。下面先看代码。

  • 引入第三方组件库,我用的是这个版本 “@popperjs/core”: “^2.11.8”

      pnpm i @popperjs/core
    
  • 先新建指令 v-overflowTooltip

      /**
       * @description: 溢出文本提示
       */
      import type { App, Directive, DirectiveBinding } from 'vue'
      import { createPopper } from '@popperjs/core'
      import { useZIndex } from 'element-plus/es/hooks/use-z-index/index'
    
      // tooltip 下方小三角
      function renderArrow() {
        const arrow = document.createElement('div')
        arrow.className = 'el-popper__arrow'
        return arrow
      }
    
      // tooltip 正文部分
      function renderContent(value) {
        const { nextZIndex } = useZIndex()
        const content = document.createElement('div')
        content.className = 'el-popper is-light table-tooltip_text table-tooltip'
        content.innerHTML = value
        content.style.zIndex = String(nextZIndex())
        document.body.appendChild(content)
        return content
      }
    
      // 生命周期 先通过css 设置超长的...显示
      const created = (el) => {
        el.style.overflow = 'hidden'
        el.style.textOverflow = 'ellipsis'
        el.style.whiteSpace = 'nowrap'
      }
    
      // 获取 tableColums 的上下左右 padding
      const getPadding = (el: HTMLElement) => {
        const style = window.getComputedStyle(el, null)
        const paddingLeft = Number.parseInt(style.paddingLeft, 10) || 0
        const paddingRight = Number.parseInt(style.paddingRight, 10) || 0
        const paddingTop = Number.parseInt(style.paddingTop, 10) || 0
        const paddingBottom = Number.parseInt(style.paddingBottom, 10) || 0
        return {
          left: paddingLeft,
          right: paddingRight,
          top: paddingTop,
          bottom: paddingBottom
        }
      }
    
      const mounted = (el, binding: DirectiveBinding<any>) => {
        let removePopperTime: any = null
        const createTooltip = () => {
    
          // 创建新的节点用作参照物,判断是否渲染 tooltip
          const range = document.createRange()
          range.setStart(el, 0)
          range.setEnd(el, el.childNodes.length)
    
          const rangeWidth = range.getBoundingClientRect().width
          const rangeHeight = range.getBoundingClientRect().height
    
          const { top, left, right, bottom } = getPadding(el)
          const horizontalPadding = left + right
          const verticalPadding = top + bottom
    
          if (
            !(
              rangeWidth + horizontalPadding > el.offsetWidth ||
              rangeHeight + verticalPadding > el.offsetHeight ||
              el.scrollWidth > el.offsetWidth
            )
          ) {
            return
          }
    
          // 判断当前元素有没有未删除的Popper,有则阻止删除并且return
          if (removePopperTime) {
            clearTimeout(removePopperTime)
            removePopperTime = null
            return
          }
          // tooltip 显示的内容
          const value = binding.value || el.textContent
          if (!value) {
            return
          }
    
          // 创建Popper元素
          const content = renderContent(value)
          // 创建Popper小三角
          const arrow = renderArrow()
          content.appendChild(arrow)
          // 调用插件,渲染Popper
          // 还需要配置什么自己去查吧
          createPopper(el, content, {
            strategy: 'absolute',
            placement: 'top-start',
            modifiers: [
              {
                name: 'offset',
                options: {
                  offset: [0, -1]
                }
              },
              {
                name: 'arrow',
                options: {
                  element: arrow,
                  padding: 10
                }
              },
              {
                name: 'showAfter',
                enabled: true
              }
            ]
          })
          // 删除
          const removePopper = () => {
            removePopperTime = setTimeout(() => {
              try {
                content && document.body.removeChild(content)
                el.removeEventListener('mouseout', removePopper)
              } catch {}
                clearTimeout(removePopperTime)
                removePopperTime = null
            }, 100)
          }
    
          // 移入弹框后取消删除
          content.addEventListener('mouseover', () => {
            clearTimeout(removePopperTime)
            removePopperTime = null
          })
          // 元素移出
          el.addEventListener('mouseout', removePopper)
          // 弹框移出
          content.addEventListener('mouseout', removePopper)
        }
    
        el.addEventListener('mouseover', createTooltip)
      }
    
      const handlerOverflowTooltip: Directive = {
        created,
        mounted
      }
    
      export const setupOverflowTooltipDirective = (app: App<Element>) => {
        app.directive('overflowTooltip', handlerOverflowTooltip)
      }
    
    
  • 这样 指令就可以使用啦

  • 现在看table中的逻辑吧,,因为我们是对table的二次封装用的tsx,这块主要的问题是,如何绑上指令

      // 在tableColums 内容外层 包一个 div 做指令绑定
      // resolveDirective  withDirectives 是从 vue 导出的
      const overflowTooltip = resolveDirective('overflowTooltip')
    
      return withDirectives(h('div', { class: 'aaa' }, value), [[overflowTooltip]])
    
  • 现在就可以愉快享用了,祝君好运

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值