cursor-关于自定义指令的问题处理

问题描述:

        今天碰见一个关于自定义指令的问题,在项目中关于el-tooltip组件封装了一个自定义组件,功能就是常规的悬浮窗显示鼠标选中的文本,做一个具体的全部展示。问题是这个数据是会更新的,但是悬浮窗中的文本数据仍然是前一次的数据,所以鼠标悬浮之后显示为上一次的数据,造成了这个问题,当然只要是定位到了问题,并且可以复现,那么还是老规矩,先交给cursor去试试。

修改前:

import type { DirectiveBinding } from 'vue'
import { ElTooltip } from 'element-plus'
import { h, render } from 'vue'

function setupTruncateTooltip(el: HTMLElement, binding: DirectiveBinding) {
  const updateTooltip = () => {
    const text = el.innerText
    const classes = el.className
    const inputWidth = el.offsetWidth
    const scrollWidth = el.scrollWidth

    // 内容长度大于元素长度时,显示tooltip
    if (scrollWidth > inputWidth) {
      if (!el.parentElement?.classList.contains('el-tooltip__trigger')) {
        // 创建tooltip vnode
        const tooltipVNode = h(ElTooltip, {
          content: text,
          placement: 'top-end',
          effect: 'dark',
        }, () => [h('span', { class: classes }, { default: () => el.cloneNode(true) })])

        // 渲染tooltip
        const container = document.createElement('div')
        render(tooltipVNode, container)

        // 找到tooltip元素
        const tooltipElement = container.querySelector('.el-tooltip__trigger') || container.firstElementChild

        if (tooltipElement) {
          el.parentNode?.insertBefore(tooltipElement, el)
          // el 增加一个style 样式为宽度100%
          el.style.width = '100%'
          tooltipElement.appendChild(el)
        }
      }
    } else {
      const tooltipTrigger = el.closest('.el-tooltip__trigger')
      // 清除style为100%
      el.style.cssText = ''
      if (tooltipTrigger && tooltipTrigger !== el) {
        tooltipTrigger.parentNode?.insertBefore(el, tooltipTrigger)
        tooltipTrigger.parentNode?.removeChild(tooltipTrigger)
      }
    }
  }

  // 初始设置
  updateTooltip()

  // 监听元素内容变化
  const observer = new MutationObserver(updateTooltip)
  observer.observe(el, {
    childList: true,
    characterData: true,
    subtree: true,
  })

  // 监听窗口大小变化
  window.addEventListener('resize', updateTooltip)

  // 清理函数
  el._cleanup = () => {
    observer.disconnect()
    window.removeEventListener('resize', updateTooltip)
  }
}

export default {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    setupTruncateTooltip(el, binding)
  },
  updated(el: HTMLElement, binding: DirectiveBinding) {
    setupTruncateTooltip(el, binding)
  },
}

修改后:

import type { DirectiveBinding } from 'vue'
import { ElTooltip } from 'element-plus'
import { h, render, nextTick } from 'vue'

// 扩展 HTMLElement 类型,添加自定义属性
declare global {
  interface HTMLElement {
    _tooltipInstance?: any
    _cleanup?: () => void
    _tooltipContainer?: HTMLElement
  }
}

function setupTruncateTooltip(el: HTMLElement, binding: DirectiveBinding) {
  const updateTooltip = async () => {
    // 等待 DOM 更新完成
    await nextTick()

    const text = el.innerText
    const classes = el.className
    const inputWidth = el.offsetWidth
    const scrollWidth = el.scrollWidth

    // 内容长度大于元素长度时,显示tooltip
    if (scrollWidth > inputWidth) {
      if (!el.parentElement?.classList.contains('el-tooltip__trigger')) {
        // 创建tooltip vnode
        const tooltipVNode = h(ElTooltip, {
          content: text,
          placement: 'top-end',
          effect: 'dark',
        }, () => [h('span', { class: classes }, { default: () => el.cloneNode(true) })])

        // 渲染tooltip
        const container = document.createElement('div')
        render(tooltipVNode, container)

        // 找到tooltip元素
        const tooltipElement = container.querySelector('.el-tooltip__trigger') || container.firstElementChild

        if (tooltipElement) {
          el.parentNode?.insertBefore(tooltipElement, el)
          // el 增加一个style 样式为宽度100%
          el.style.width = '100%'
          tooltipElement.appendChild(el)

          // 保存 tooltip 实例和容器引用,用于后续更新
          el._tooltipInstance = tooltipVNode
          el._tooltipContainer = container
        }
      } else {
        // 如果已经存在 tooltip,更新其内容
        updateExistingTooltip(el, text)
      }
    } else {
      const tooltipTrigger = el.closest('.el-tooltip__trigger')
      // 清除style为100%
      el.style.cssText = ''
      if (tooltipTrigger && tooltipTrigger !== el) {
        tooltipTrigger.parentNode?.insertBefore(el, tooltipTrigger)
        tooltipTrigger.parentNode?.removeChild(tooltipTrigger)

        // 清理引用
        el._tooltipInstance = undefined
        el._tooltipContainer = undefined
      }
    }
  }

  // 更新已存在的 tooltip 内容
  const updateExistingTooltip = (el: HTMLElement, newText: string) => {
    if (el._tooltipInstance && el._tooltipContainer) {
      // 重新创建 tooltip vnode 并更新内容
      const newTooltipVNode = h(ElTooltip, {
        content: newText,
        placement: 'top-end',
        effect: 'dark',
      }, () => [h('span', { class: el.className }, { default: () => el.cloneNode(true) })])

      // 重新渲染
      render(newTooltipVNode, el._tooltipContainer)

      // 更新引用
      el._tooltipInstance = newTooltipVNode
    }
  }

  // 初始设置
  updateTooltip()

  // 监听元素内容变化
  const observer = new MutationObserver(() => {
    // 使用防抖,避免频繁更新
    clearTimeout(el._updateTimer)
    el._updateTimer = setTimeout(updateTooltip, 50)
  })

  observer.observe(el, {
    childList: true,
    characterData: true,
    subtree: true,
  })

  // 监听窗口大小变化
  const handleResize = () => {
    clearTimeout(el._resizeTimer)
    el._resizeTimer = setTimeout(updateTooltip, 100)
  }
  window.addEventListener('resize', handleResize)

  // 清理函数
  el._cleanup = () => {
    observer.disconnect()
    window.removeEventListener('resize', handleResize)
    clearTimeout(el._updateTimer)
    clearTimeout(el._resizeTimer)

    // 清理 tooltip 相关引用
    if (el._tooltipContainer) {
      el._tooltipContainer.remove()
    }
    el._tooltipInstance = undefined
    el._tooltipContainer = undefined
  }
}

export default {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    setupTruncateTooltip(el, binding)
  },
  updated(el: HTMLElement, binding: DirectiveBinding) {
    // 在 updated 钩子中重新设置,确保数据更新后 tooltip 内容也更新
    setupTruncateTooltip(el, binding)
  },
  unmounted(el: HTMLElement) {
    // 组件卸载时清理
    if (el._cleanup) {
      el._cleanup()
    }
  },
}

结果:

        cursor不负众望,完成了我想要的结果。还是感叹AI的强大辅助能力,在这里记录一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小白-RMS

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值