今天有一个需求,点击选中某个英文单词,然后对这个单词做一些处理,例如高亮背景、查看一些详细信息等等,今天简单实现了一下,效果如下:(支持移动端,chrome和sarafi浏览器均能正常使用。语言vue3 + typescript)

选词

由于要动态添加给某些单词动态添加一些标签,我们这里可以考虑使用 v-html

首先我们先编写一下简单的结构

<script setup lang="ts">
</script>

<template>
  <div class="container" v-html="shortArticle"></div>
</template>

<style>
.container {
  font-size: 18px;
}
</style>

然后,我们将需要处理的短文变换为 span 标签包裹,这里的思路是按照空格划分,然后添加span结构,最后拼接到一起返回。这里有一些边缘条件要考虑,比如can't(whichyes!等等,按照空格划分出来的数据有一点问题。

如果不做处理的话,一些标点符号也会高亮出来,就不太正确了。下面是处理逻辑,整体比较简单,就不解释了。

function addElementSpan(str: string): string {
  return str
    .split(' ')
    .map((item) => {
      const { start, word, end } = getWord(item)
      return `${start}<span>${word}</span>${end} `
    })
    .join('')
}

function getWord(str: string) {
  let word = ''
  let start = ''
  let end = ''
  let j = str.length - 1
  let i = 0

  while (i < str.length) {
    if (/^[a-zA-Z]$/.test(str[i])) {
      break
    }
    start = start + str[i]
    i += 1
  }

  while (j >= 0) {
    if (/^[a-zA-Z]$/.test(str[j])) {
      break
    }
    end = str[j] + end
    j -= 1
  }

  word = str.slice(i, j + 1)

  // 处理数字
  if (!word && start === end) {
    start = ''
  }

  return {
    start,
    word,
    end
  }
}

现在我们来实现效果

<script setup lang="ts">
import { computed } from 'vue'
import { addElementSpan } from './utils'

const str = `It works fine if you move the navbar outside the header. See below. For the reason, according to MDN: The element is positioned according to the normal flow of the document, and then offset
    relative to its flow root and containing block based on the values of top, right, bottom, and
    left. For the containing block: The containing block is the ancestor to which the element is
    relatively positioned So, when I do not misunderstand, the navbar is positioned at offset 0
    within the header as soon as it is scrolled outside the viewport (which, clearly, means, you
    can't see it anymore).`

const shortArticle = computed(() => {
  return addElementSpan(str)
})

function setColor(event: any) {
  // console.log(event.target.innerText) 获取选中的文本
  event.target?.classList.add('word_highlight')
}
</script>

<template>
  <div class="container" @click="setColor($event)" v-html="shortArticle"></div>
</template>

<style>
.word_highlight {
  background-color: red;
}
</style>

在父亲元素上添加点击事件,触发事件点击之后,调用 setColor 函数,高亮背景(添加class)

不过有一点小小的问题,点击 div 的空白区域或者非英文单词区域会直接整个背景变成红色,控制台打印event.target.innerText 可以发现它的值为整个文本,所以我们可以根据判断打印的文本长度和需要设置的文本长度是否一致来解决这个问题。(ps:⬆️面的示例代码 str 字符串使用了反引号模板字符串,直接使用下面会影响结果)

function setColor(event: any) {
  // console.log(event.target.innerText)
  if(str !== event.target.innerText){
    event.target?.classList.add('word_highlight')
  }
}

对于 event.target 不太了解的伙伴可以看这篇文章 ➡️ Event.target - Web API 接口参考 | MDN (mozilla.org)

(和 event.target 类似的还有一个属性 event.currentTarget,不太了解的伙伴可以看下这篇文章 ➡️ Event.currentTarget - Web API 接口参考 | MDN (mozilla.org),它俩的区别是 event.target 指的是事件触发的元素,而event.currentTarget 指的是事件绑定的元素)

原文链接:https://juejin.cn/post/7223733256688025661