用正则实现一个搜索高亮的功能

1,937 阅读2分钟

场景

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