咱們先看一下效果圖:前端
這是搜索關鍵字cfg
時,會自動匹配到config
方法數組
一樣,咱們再看另外一個例子編輯器
經過關鍵字bi
會匹配到好幾個結果函數
這個和一些編輯器的搜索功能很像,好比sublime text,不須要知道關鍵字的完整拼寫,只須要知道其中的幾個字母便可。性能
那麼這個功能在前端咱們如何去實現呢?測試
不考慮性能的話,咱們能夠用正則簡單實現以下:優化
把關鍵字拆分,加入(. ?),如cfg最終爲 (.?)(c)(. ?)(f)(.?)(g)(.*?),而後拿這個正則去測試要搜索的列表,把符合要求的選項給拿出來便可
考慮到要高亮結果,咱們還要生成對應的替換表達式,最後的函數以下spa
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
去檢測搜索的列表,把符合的保存下來便可。3d
到目前爲止咱們只實現了搜索功能,按更優的體驗來說,在搜索結果中,要優先把相連匹配的放在首位,如bi
關鍵字,要把bind
結果放到beginUpdate
前面。第二個截圖是有優化的地方的。code
要完成這個功能,咱們使用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(keyword); 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>
顯示到頁面上就完成了高亮顯示