场景
有这么一个场景,页面中有个表格,表格内容是根据后端的数据渲染出来的,表格头部有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对象去操作。