场景
有这么一个场景,页面中有个表格,表格内容是根据后端的数据渲染出来的,表格头部有input表单元素,通过触发input事件实时搜索列表内容,重新渲染列表。
一切都运行的如此和谐,可是突然有一天,产品过来说要实现搜索的高亮功能,就是根据搜索框的内容,将页面上搜索出来的东西进行高亮。
乍一听这需求很容易啊,直接用JavaScript的String.prototype.replace就可以实现了,比如这样:
function formatSearch(origin: string, search: string) {
return origin.replace(new RegExp(search, 'ig'), function($1) {
return `<span class="light">${$1}</span>`;
});
}
以上代码就可以把一个字符串加上加上HTML标签,再自定义一下.light的样式就行了。
可是如果需要处理的字符串是一个HTML的字符串怎么办?
方案
HTML字符串与普通字符串的区别无非就是里面多了一些特殊的字符,比如<、>、?、/这些,所以当我们做正则匹配的时候,就需要把这样的情况排除在外,如何排除呢?
这就让我们想到了两个正则表达式:(?<=pattern)与(?=pattern)
(?<=pattern)名为:Positive lookbehind,翻译为零宽负向先行断言。
具体功能用人类的语言描述出来难以理解,通过例子才比较容易看明白。比如我们要处理的字符串为hello word,我们用(?<=\bhe)\w+去匹配,匹配出的结果为llo。显而易见,它的功能是:一个字符串,只有以pattern开头才能被匹配到。
(?=pattern)名为:Positive lookahead,翻译为零宽正向先行断言。
还是以hello word为例,我们用\w+(?=llo)去匹配,匹配出的结果为he。显而易见,它的功能是:一个字符串,只有以pattern结尾才能才能匹配到。
有了这两个神器,我们就可以把今天的主角请出来了,主角登场,另起一行:
// pattern 为想要匹配的内容
/(?<=(<.*>))[^<>]*(pattern).*?(?=<\/)/gi
通过这段代码,就可以实现一个基本的高亮功能了。但是这段代码有个bug,就是当pattern为<时,会出现问题,html标签会被匹配到,所以这段代码也不是万能,需要我们去兼容一下只搜索<的情况。
备注
如果想做一个非常完善的HTML文本搜索高亮,正则表达式可能是无能为力的,需要我们先把HTML的文本转换为DOM,然后用DOM对象去操作。