Open xinglie opened 7 years ago
我们先看一下效果图:
这是搜索关键字cfg时,会自动匹配到config方法
cfg
config
同样,我们再看另一个例子
通过关键字bi会匹配到好几个结果
bi
这个和一些编辑器的搜索功能很像,比如sublime text,不需要知道关键字的完整拼写,只需要知道其中的几个字母即可。
那么这个功能在前端我们如何去实现呢?
不考虑性能的话,我们可以用正则简单实现如下:
把关键字拆分,加入(.?),如cfg最终为 (.?)(c)(.?)(f)(.?)(g)(.*?),然后拿这个正则去测试要搜索的列表,把符合要求的选项给拿出来即可
考虑到要高亮结果,我们还要生成对应的替换表达式,最后的函数如下
var escapeRegExp = /[\-#$\^*()+\[\]{}|\\,.?\s]/g; var KeyReg = (key) => { var src = ['(.*?)(']; var ks = key.split(''); if (ks.length) { while (ks.length) { src.push(ks.shift().replace(escapeRegExp, '\\$&'), ')(.*?)('); } src.pop(); } src.push(')(.*?)'); src = src.join(''); var reg = new RegExp(src, 'i'); var replacer = []; var start = key.length; var begin = 1; while (start > 0) { start--; replacer.push('$', begin, '($', begin + 1, ')'); begin += 2; } replacer.push('$', begin); info = { regexp: reg, replacement: replacer.join('') }; return info; };
调用KeyReg把关键字传入,拿返回值的regexp去检测搜索的列表,把符合的保存下来即可。
KeyReg
regexp
到目前为止我们只实现了搜索功能,按更优的体验来讲,在搜索结果中,要优先把相连匹配的放在首位,如bi关键字,要把bind结果放到beginUpdate前面。第二个截图是有优化的地方的。
bind
beginUpdate
要完成这个功能,我们使用KeyReg返回值中的replacement,用它进行检测,把结果中长度最长的放前面即可,这块代码以后有时间再补充
replacement
2018.5.31 今天重构了下,增加了结果排序,完整的代码及使用示例如下
let Searcher = (() => { let escapeRegExp = /[\-#$\^*()+\[\]{}|\\,.?\s]/g; let escapeReg = reg => reg.replace(escapeRegExp, '\\$&'); //groupLeft 与 groupRight是对结果进一步处理所使用的分割符,可以修改 let groupLeft = '(', groupRight = ')'; let groupReg = new RegExp(escapeReg(groupRight + groupLeft), 'g'); let groupExtractReg = new RegExp('(' + escapeReg(groupLeft) + '[\\s\\S]+?' + escapeReg(groupRight) + ')', 'g'); //从str中找到最大的匹配长度 let findMax = (str, keyword) => { let max = 0; keyword = groupLeft + keyword + groupRight; str.replace(groupExtractReg, m => { //keyword完整的出现在str中,则优秀级最高,排前面 if (keyword == m) { max = Number.MAX_SAFE_INTEGER; } else if (m.length > max) {//找最大长度 max = m.length; } }); return max; }; let keyReg = key => { let src = ['(.*?)(']; let ks = key.split(''); if (ks.length) { while (ks.length) { src.push(escapeReg(ks.shift()), ')(.*?)('); } src.pop(); } src.push(')(.*?)'); src = src.join(''); let reg = new RegExp(src, 'i'); let replacer = []; let start = key.length; let begin = 1; while (start > 0) { start--; replacer.push('$', begin, groupLeft + '$', begin + 1, groupRight); begin += 2; } replacer.push('$', begin); info = { regexp: reg, replacement: replacer.join('') }; return info; }; return { search(list, keyword) { //生成搜索正则 let kr = keyReg(userInput); let result = []; for (let e of list) { //如果匹配 if (kr.regexp.test(e)) { //把结果放入result数组中 result.push(e.replace(kr.regexp, kr.replacement) .replace(groupReg, '')); } } //对搜索结果进行排序 //1. 匹配关键字大小写一致的优先级最高,比如搜索up, 结果中的[user-page,beginUpdate,update,endUpdate],update要排在最前面,因为大小写匹配 //2. 匹配关键字长的排在前面 result = result.sort((a, b) => findMax(b, keyword) - findMax(a, keyword)); return result; } }; })(); //假设list是待搜索的列表 let list = ['config', 'user-page', 'bind', 'render', 'beginUpdate', 'update', 'endUpdate']; //假设userInput是用户输入的关键字 let userInput = 'up'; //获取搜索的结果 console.log(Searcher.search(list, userInput)); // ["(up)date", "begin(Up)date", "end(Up)date", "(u)ser-(p)age"]
对搜索结果中的内容做进一步处理渲染出来即可,比如把 ( 替换成 <span style="color:red"> 把 ) 替换成</span>显示到页面上就完成了高亮显示
(
<span style="color:red">
)
</span>
我们先看一下效果图:
这是搜索关键字
cfg
时,会自动匹配到config
方法同样,我们再看另一个例子
通过关键字
bi
会匹配到好几个结果这个和一些编辑器的搜索功能很像,比如sublime text,不需要知道关键字的完整拼写,只需要知道其中的几个字母即可。
那么这个功能在前端我们如何去实现呢?
不考虑性能的话,我们可以用正则简单实现如下:
考虑到要高亮结果,我们还要生成对应的替换表达式,最后的函数如下
调用
KeyReg
把关键字传入,拿返回值的regexp
去检测搜索的列表,把符合的保存下来即可。到目前为止我们只实现了搜索功能,按更优的体验来讲,在搜索结果中,要优先把相连匹配的放在首位,如
bi
关键字,要把bind
结果放到beginUpdate
前面。第二个截图是有优化的地方的。要完成这个功能,我们使用
KeyReg
返回值中的replacement
,用它进行检测,把结果中长度最长的放前面即可,这块代码以后有时间再补充2018.5.31 今天重构了下,增加了结果排序,完整的代码及使用示例如下
对搜索结果中的内容做进一步处理渲染出来即可,比如把
(
替换成<span style="color:red">
把)
替换成</span>
显示到页面上就完成了高亮显示