Example autocomplete search giống như Sublime Text sử dụng javascript

Nội dung bài viết

Autocomplete search là một thuật toán rất phức tạp, và đỏi hòi nhiều thuật toán được áp dụng. Nếu bạn đã từng sử dụng Sublime Text để code thì ở đó bạn để ý rằng, đó là một hệ thống tìm kiếm thông minh, khi chúng ta tìm kiếm một vài ký tự thì hệ thống đã gợi ý cho bạn những từ khoá tìm kiếm cụ thể hơn.


Autocomplete search


Để hình dung hơn thì chúng ta xem một số hình ảnh sau: 

Hình 2: Tìm kiếm cfg


Qua hai hình ảnh chúng ta để ý thì khi tìm kiếm cfg thì nó sẽ gợi ý những từ liên quan như config, fold tag, package file. Hoặc chúng ta tìm kiếm java, thì hệ thống đã gợi ý rằng java, javascript, save as, java server page... Vậy làm cách nào để thực hiện chức năng này trên giao diện người dùng? Đây là một ví dụ có thể giúp bạn thực hiện một cách nhanh chóng và chuyên nghiệp hơn.


Example Autocomplete search javascript


Trong code này chủ yếu chúng ta sử dụng "RegExp" javascript. Nếu bạn nào chưa hiểu thì có thể tham khảo thêm về cách học "RegExp javascript". 


Trước tiên xem code thì có thể xem DEMO HERE để biết cách hoạt động như thế nào File javascript:


let Searcher = (() => {
      let escapeRegExp = /[\-#$\^*()+\[\]{}|\\,.?\s]/g;
      let escapeReg = reg => reg.replace(escapeRegExp, '\\$&');
      let groupLeft = '',
 groupRight = '';
      let groupReg = new RegExp(escapeReg(groupRight + groupLeft), 'g');
      let groupExtractReg = new RegExp('(' + escapeReg(groupLeft) + '[\\s\\S]+?' + escapeReg(groupRight) + ')', 'g');
      let findMax = (str, keyword) => {
        let max = 0;
        keyword = groupLeft + keyword + groupRight;
        str.replace(groupExtractReg, m => {
          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.push(e.replace(kr.regexp, kr.replacement)
                .replace(groupReg, ''));
            }
          }
          result = result.sort((a, b) => findMax(b, keyword) - findMax(a, keyword));
          console.log(`result::::`, result)
          //create div 
          result = result.map(el => {
            return `${el}`
          })
          return result;
        }
      };
    })();
    // console.log(document.querySelectorAll('.search-prev'));
    //List demo
    let list = [ 'javascript',
'es6',
'tipjs ',
'web_development ',
'nodejs ',
'object ',
'webdev ',
'lab-javascript ',
'async-await ',
'array ',
'tips and tricks javascript ',
'mongodb ',
'firebase ',
'promise ',
'auth02 ',
'authenticate token ',
'expressjs',
'token',
'jwt',
'cookie',
'series callback javascript',
'async',
'nginx',
'pattern',
'feature_javascript',
'reduce_javascript',
'build-project',
'exprejs',
'design_pattern',
'session',
'login',
'method',
'css',
'performance',
'jwt and restful api',
'es11',
'mongoose',
'newbie',
'array_javascript',
'data structures and algorithms',
'javascript',
'api',
'secure',
'module',
'rss_code',
'javascript_basic',
'object_assgin',
'spread_operator',
'javascript newbie',
'fireabse_notification',
'interview',
'query_javascript',
'callbacks',
'await',
'destructuring',
'redis',
'scope',
'jsonp',
'moment',
'transaction',
'cors',
'event_loop',
'npm',
'metadata',
'system',
'ebook',
'cluster',
'pm2',
'dom',
'hight-order-function',
'html',
'mysql',
'ffmpeg',
'verify-sms-firebase',
'mysql-nodejs',
'about performance javascript',
'reactjs',
'callback',
'basic',
'ajax',
'reduce',
'middleware',
'iterator',
'series es6 vs react',
'object_javascript',
'es5',
'refreshtoken',
'deep_copy_javascript',
'big-data',
'rss_new',
'http-status-code',
'medium',
'string',
'fetch',
'es12',
'tips_and_tricks',
'console',
'es10',
'cache_js',
'git',
'question',
'puppeteer']
    const divCon = document.getElementById('search-items')

    const input = document.querySelector('input');
    input.addEventListener('keypress', (e) => {
      divCon.innerHTML = '';
      const rs = Searcher.search(list, e.target.value);
      rs.forEach(el => {
        var div = document.createElement("div");
        div.className = 'result';
        div.innerHTML = el;
        divCon.appendChild(div)
      })
    });

File html:

<div class="search-box">
      
      <form no-validade>
        <input type="text" text="" tabindex="1" class="enter-key" onpropertychange="TextChange(this)"
          placeholder="Search..." />
      </form>
      <div class="search-prev" id="search-items">
      </div>
    </div>

Có thể xem qua một số hình ảnh sau khi code xong. 

XEM DEMO HERE

Có thể bạn đã bị missing