60

前端做模糊搜索

 5 years ago
source link: http://www.10tiao.com/html/471/201807/2651555684/5.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

微信关注「网页设计自学平台」

我们先看一下效果图:

这是搜索关键字 cfg时,会自动匹配到 config方法

同样,我们再看另一个例子

通过关键字 bi会匹配到好几个结果

这个和一些编辑器的搜索功能很像,比如 sublime text,不需要知道关键字的完整拼写,只需要知道其中的几个字母即可。

那么这个功能在前端我们如何去实现呢?

不考虑性能的话,我们可以用正则简单实现如下:

把关键字拆分,加入 (.?),如 cfg 最终为 (.?)(c)(.?)(f)(.?)(g)(.*?), 然后拿这个正则去测试要搜索的列表,把符合要求的选项给拿出来即可

考虑到要高亮结果,我们还要生成对应的替换表达式,最后的函数如下

  1. var escapeRegExp =/[-#$^*()+[]{}|\,.?s]/g;

  2. varKeyReg=(key)=>{

  3.    var src =['(.*?)('];

  4.    var ks = key.split('');

  5.    if(ks.length){

  6.        while(ks.length){

  7.            src.push(ks.shift().replace(escapeRegExp,'\$&'),')(.*?)(');

  8.        }

  9.        src.pop();

  10.    }

  11.    src.push(')(.*?)');

  12.    src = src.join('');

  13.    var reg =newRegExp(src,'i');

  14.    var replacer =[];

  15.    var start = key.length;

  16.    varbegin=1;

  17.    while(start >0){

  18.        start--;

  19.        replacer.push('$',begin,'($',begin+1,')');

  20.        begin+=2;

  21.    }

  22.    replacer.push('$',begin);

  23.    info ={

  24.        regexp: reg,

  25.        replacement: replacer.join('')

  26.    };

  27.    return info;

  28. };

调用 KeyReg把关键字传入,拿返回值的 regexp去检测搜索的列表,把符合的保存下来即可。

到目前为止我们只实现了搜索功能,按更优的体验来讲,在搜索结果中,要优先把相连匹配的放在首位,如 bi关键字,要把 bind结果放到 beginUpdate前面。第二个截图是有优化的地方的。

要完成这个功能,我们使用 KeyReg返回值中的 replacement,用它进行检测,把结果中长度最长的放前面即可,这块代码以后有时间再补充

2018.5.31 今天重构了下,增加了结果排序,完整的代码及使用示例如下

  1. letSearcher=(()=>{

  2.    let escapeRegExp =/[-#$^*()+[]{}|\,.?s]/g;

  3.    let escapeReg = reg => reg.replace(escapeRegExp,'\$&');

  4.    //groupLeft 与 groupRight是对结果进一步处理所使用的分割符,可以修改

  5.    let groupLeft ='(',

  6.        groupRight =')';

  7.    let groupReg =newRegExp(escapeReg(groupRight + groupLeft),'g');

  8.    let groupExtractReg =newRegExp('('+ escapeReg(groupLeft)+'[\s\S]+?'+ escapeReg(groupRight)+')','g');

  9.    //从str中找到最大的匹配长度

  10.    let findMax =(str, keyword)=>{

  11.        let max =0;

  12.        keyword = groupLeft + keyword + groupRight;

  13.        str.replace(groupExtractReg, m =>{

  14.            //keyword完整的出现在str中,则优秀级最高,排前面

  15.            if(keyword == m){

  16.                max =Number.MAX_SAFE_INTEGER;

  17.            }elseif(m.length > max){//找最大长度

  18.                max = m.length;

  19.            }

  20.        });

  21.        return max;

  22.    };

  23.    let keyReg = key =>{

  24.        let src =['(.*?)('];

  25.        let ks = key.split('');

  26.        if(ks.length){

  27.            while(ks.length){

  28.                src.push(escapeReg(ks.shift()),')(.*?)(');

  29.            }

  30.            src.pop();

  31.        }

  32.        src.push(')(.*?)');

  33.        src = src.join('');

  34.        let reg =newRegExp(src,'i');

  35.        let replacer =[];

  36.        let start = key.length;

  37.        letbegin=1;

  38.        while(start >0){

  39.            start--;

  40.            replacer.push('$',begin, groupLeft +'$',begin+1, groupRight);

  41.            begin+=2;

  42.        }

  43.        replacer.push('$',begin);

  44.        info ={

  45.            regexp: reg,

  46.            replacement: replacer.join('')

  47.        };

  48.        return info;

  49.    };

  50.    return{

  51.        search(list, keyword){

  52.            //生成搜索正则

  53.            let kr = keyReg(userInput);

  54.            let result =[];

  55.            for(let e of list){

  56.                //如果匹配

  57.                if(kr.regexp.test(e)){

  58.                    //把结果放入result数组中

  59.                    result.push(e.replace(kr.regexp, kr.replacement)

  60.                        .replace(groupReg,''));

  61.                }

  62.            }

  63.            //对搜索结果进行排序

  64.            //1. 匹配关键字大小写一致的优先级最高,比如搜索up, 结果中的[user-page,beginUpdate,update,endUpdate],update要排在最前面,因为大小写匹配

  65.            //2. 匹配关键字长的排在前面

  66.            result = result.sort((a, b)=> findMax(b, keyword)- findMax(a, keyword));

  67.            return result;

  68.        }

  69.    };

  70. })();

  71. //假设list是待搜索的列表

  72. let list =['config','user-page','bind','render','beginUpdate','update','endUpdate'];

  73. //假设userInput是用户输入的关键字

  74. let userInput ='up';

  75. //获取搜索的结果

  76. console.log(Searcher.search(list, userInput));

  77. // ["(up)date", "begin(Up)date", "end(Up)date", "(u)ser-(p)age"]

对搜索结果中的内容做进一步处理渲染出来即可,比如把 ( 替换成 <spanstyle="color:red"> 把 ) 替换成 </span>显示到页面上就完成了高亮显示


作者:行列

https://segmentfault.com/a/1190000015486180


H5网站模板大全 不用写代码自己就能动手嵌套一个网站 领取


戳“阅读原文”入群免费领取前端开发教程


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK