情景:
最近在完成公司需求的过程中,遇到一个需求是当用户输入关键字搜索资讯时,搜索出的资讯标题中包含搜索关键字的内容需要高亮显示。
思路分析
- 首先想到的是使用标签来包裹内容中匹配的搜索关键字,并在标签中添加一个类名配合v-html指令来实现文字高亮的效果。
- 正则匹配搜索关键字
那么如何根据用户的输入生成对应的正则呢? 第一个想到的是用户输入输入什么就使用什么来生成正则,比如用户输入的是“股东”,生成的正则会是
/股东/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>