跳至主要內容

使用自定义指令实现高亮

chensino原创大约 2 分钟

需求

需求是: 在前端页面有很多文本,要求提取文本,根据规则判断文本是否和规则互斥,比如“女”和“前列腺”就是互斥的。当互斥时,需要高亮,同时当鼠标 指在文本上,弹出提示框,提示“女和前列腺规则互斥” 在页面中,通过自定义指令,实现对文本的局部高亮,其中高亮内容是通过接口动态获取。

实现方案

利用vue的自定义指令,实现局部高亮。组件调用自定义指令,同时传入高亮内容,指令内部根据传入的高亮内容,对文本进行局部高亮。

代码

export default {
  mounted(el, binding) {
    const applyHighlight = () => {
      const wordsToHighlight = binding.value.words
      const highlightColor = binding.value.color || '#e8b61d'

      function highlightText(node: any) {
        if (node.nodeType === 3) {
          // 文本节点
          let text = node.nodeValue
          wordsToHighlight.forEach((word: string) => {
            const regex = new RegExp(`(${word})`, 'gi')
            // 替换文本,在需要高亮的文本前添加背景色高亮
            text = text.replace(
              regex,
              `<span style="background-color: ${highlightColor};cursor: pointer;" class="hightLightText" >$1</span>`,
            )
          })
          const tempDiv = document.createElement('div')
          tempDiv.innerHTML = text
          while (tempDiv.firstChild) {
            node.parentNode.insertBefore(tempDiv.firstChild, node)
          }
          node.parentNode.removeChild(node)
        } else if (
          node.nodeType === 1 &&
          node.childNodes &&
          !/(script|style)/i.test(node.tagName)
        ) {
          for (let i = 0; i < node.childNodes.length; i++) {
            highlightText(node.childNodes[i])
          }
        }
      }
      highlightText(el)
    }

    // 初次高亮
    applyHighlight()

    // 在指令对象上添加 applyHighlight 方法
    el.__vueApplyHighlight = applyHighlight
  },
  updated(el) {
    if (el.__vueApplyHighlight) {
      el.__vueApplyHighlight()
    }
  },
  unmounted(el) {
    if (el.__vueApplyHighlight) {
      delete el.__vueApplyHighlight
    }
  },
}

在组件中,调用自定义指令,同时传入高亮内容,大概如下:

  <ElTabPane
          v-highlight-text="{
            words: highLightWords,
          }"
          @mouseover="handleHightLightText"
          label="文本报告"
          name="textReport"></ElTabPane>

扩展

以上只实现了高亮,还需要实现鼠标悬浮自动提示规则互斥的文本。

    <el-tooltip
      v-model:visible="wordsTooltip.visible"
      :content="wordsTooltip.content"
      placement="top"
      effect="dark"
      virtual-triggering
      :raw-content="true"
      :virtual-ref="wordsTooltipRef"></el-tooltip>

写一个el-tooltip组件,默认隐藏,当鼠标悬浮在文本上时,显示提示框,再提取高亮文本,当成key,根据key从一个map获取规则互斥内容。


const wordsTooltip = reactive({
  visible:false,
  content: '',
  position: {
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  }
})
const wordsTooltipRef = ref({
  getBoundingClientRect() {
    return wordsTooltip.position
  },
})

const handleHightLightText = (event: MouseEvent) => {
  wordsTooltip.visible = false
  const target = event.target as HTMLElement
  let className = target?.className
  let nodeName = target?.nodeName

  if (className == 'hightLightText' && nodeName == 'SPAN') {
    const text = target.innerText
    const filterWords = ruleWords.value.filter(v => {
      return v.rule.find(v=>v.includes(text))
    })

    if(filterWords.length){
      wordsTooltip.visible = true
      const rect = target.getBoundingClientRect()
      wordsTooltip.content = filterWords.map(v=>v.desc).join('<br />')
      wordsTooltip.position = DOMRect.fromRect({
        width: 0,
        height: 0,
        x: rect.left+(rect.width/2),
        y: rect.top,
      })
    }
  }else{
    wordsTooltip.visible = false
  }
}

参考elementui的tooltip组件,虚拟触发~