Vue与搜索关键字匹配的内容高亮显示

1,339 阅读1分钟

情景:

最近在完成公司需求的过程中,遇到一个需求是当用户输入关键字搜索资讯时,搜索出的资讯标题中包含搜索关键字的内容需要高亮显示。

思路分析

  1. 首先想到的是使用标签来包裹内容中匹配的搜索关键字,并在标签中添加一个类名配合v-html指令来实现文字高亮的效果。
  2. 正则匹配搜索关键字

    那么如何根据用户的输入生成对应的正则呢? 第一个想到的是用户输入输入什么就使用什么来生成正则,比如用户输入的是“股东”,生成的正则会是/股东/g 这个设计可以满足很多情况了,但如果用户输入了一些具有特殊含义的字符时会出现bug,比如用户输入了$^*(){}[]+?.\中的字符,生成的正则会是/./g... 忽略英文字母大小写

代码实现

// 1. 首先需要将用户输入的关键字进行转义,生成一个新的字符串使用这个字符串再来生成正则表达式
// 2. 资讯标题中将符合关键字的文字使用`span`标签来包裹起来并添加类名`highlight`

// 高亮显示
highLight(content = '') {
  // 将搜索关键字转义
  const key = this.keyword.replace(/([\$\^\*\(\)\+\?\{\}\[\]\.\\])/g, '\\$1')
  const reg = new RegExp(`(${key})`, 'igm')
  return content.replace(reg, '<span class="highlight">$1</span>')
}

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <style>
    #search {
      padding: 5px;
    }
    .highlight {
      color: #5dc4ff;
    }
    .news-item {
      border: 1px solid #aaa;
      padding: 10px 20px;
      margin-bottom: 10px;
    }
    .news-item:first-of-type {
      margin-top: 20px;
    }
    .title {
      font-weight: 400;
      color: #555555;
      line-height: 20px;
      margin-bottom: 5px;
    }
    .content {
      font-size: 15px;
      font-family: PingFangSC-Regular, PingFang SC;
      font-weight: 400;
      color: #222222;
      line-height: 21px;
    }
  </style>
</head>
<body>
  <div id="app">
    <label for="search">搜索关键字:</label>
    <input type="text" id="search" placeholder="请输入搜索关键字" v-model="keyword">

    <news-list v-for="(news, index) in newsList" :key="index" :news="news" :keyword="keyword" />
  </div>

  <script>
    Vue.component('news-list', {
      template: `
        <div class="news-item">
          <div class="title">{{news.title}}</div>
          <div class="content" v-html="highLight(news.content)"></div>
        </div>
      `,
      props: ['news', 'keyword'],
      methods: {
        highLight(content = '') {
          // 将搜索关键字转义
          const key = this.keyword.replace(/([\$\^\*\(\)\+\?\{\}\[\]\.\\])/g, '\\$1')
          const reg = new RegExp(`(${key})`, 'igm')
          return content.replace(reg, '<span class="highlight">$1</span>')
        }
      },
    })
    new Vue({
      el: '#app',
      data() {
        return {
          keyword: '',
          newsList: [
            { title: '标题一', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题二', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题三', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题四', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题五', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题六', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题七', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题八', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题九', content: 'hello, world+$^*()+{}[]?.\\' },
            { title: '标题十', content: 'hello, world+$^*()+{}[]?.\\' }
          ]
        }
      },
    })
  </script>
</body>
</html>