咱们先看一下效果图:css

这是搜索关键字 cfg
时,会自动匹配到 config
方法前端
一样,咱们再看另外一个例子swift

经过关键字 bi
会匹配到好几个结果segmentfault
这个和一些编辑器的搜索功能很像,好比 sublime text,不须要知道关键字的完整拼写,只须要知道其中的几个字母便可。数组
那么这个功能在前端咱们如何去实现呢?ruby
不考虑性能的话,咱们能够用正则简单实现以下:bash
把关键字拆分,加入 (.?),如 cfg 最终为 (.?)(c)(.?)(f)(.?)(g)(.*?), 而后拿这个正则去测试要搜索的列表,把符合要求的选项给拿出来便可编辑器
考虑到要高亮结果,咱们还要生成对应的替换表达式,最后的函数以下svg
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
去检测搜索的列表,把符合的保存下来便可。
到目前为止咱们只实现了搜索功能,按更优的体验来说,在搜索结果中,要优先把相连匹配的放在首位,如 bi
关键字,要把 bind
结果放到 beginUpdate
前面。第二个截图是有优化的地方的。
要完成这个功能,咱们使用 KeyReg
返回值中的 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"]
对搜索结果中的内容作进一步处理渲染出来便可,好比把 (
替换成 <spanstyle="color:red">
把 )
替换成 </span>
显示到页面上就完成了高亮显示
做者:行列
https://segmentfault.com/a/1190000015486180
本文同步分享在 博客“grain先森”(JianShu)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。