<div id="app">
  <input id="searchbox" placeholder="Type something here..." />
  <div id="results" />
</div>
#app{
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

#searchbox {
  width: 30%;
  height: 40px;
  font-size: 24px;
  font-family: monospace;
  color: black;
  border: 1px solid green;
  outline: 0;
  padding: 0 1rem 0 1rem;
}

#searchbox:focus {
  border: 2px solid green;
  outline: 0;
}

#results {
  width: 30%;
  height: 150px;
  overflow: auto;
  margin-top: 0.5rem;
}

#results > p {
  text-align: center;
  font-size: 20px;
}
const getRandomString = () => {
  return Math.random()
    .toString(36)
    .substring(2, 15);
}
 
const getRandomNumberLessThan = max => Math.floor(Math.random() * max);
 
const makeServerCall = searchText => {
  if (searchText === '') {
    return Promise.resolve([]);
  }
   
  let results = [];
 
  for (let i = 0; i < 2 + getRandomNumberLessThan(5); i++) {
    results.push(`${searchText} - ${getRandomString()}`);
  }
 
  return new Promise((resolve, reject) => {
    window.setTimeout(
      () => {
        if (Math.random() < 0.9) {
          resolve(results);
        } else {
          reject('Internal server error');
        }
      },
      1000 * Math.random()
    );
  });
};

const debounce = (func, delay) => { 
  let timerId; 
  return function(...args) { 
    clearTimeout(timerId) 
    timerId = setTimeout(() => func.apply(this, args), delay)
  }; 
};

const updateSearchResults = ( searchResults ) => {
  const resultDiv = document.getElementById('results');
  resultDiv.innerHTML = "";
  searchResults.forEach((str) => {
    const p = document.createElement('p');
    p.innerHTML = str;
    resultDiv.append(p);
    resultDiv.append(document.createElement('hr'));
  });
}


class AutoCompleter {
   searchbox = null;
   constructor() {
     this.searchbox = document.getElementById('searchbox');
     this.debouncedServerCall = debounce(async (searchText) => {
       const result = await makeServerCall(searchText);
       updateSearchResults(result);
     }, 1000);
     this.handleTextChange = this.handleTextChange.bind(this);
     this.searchbox.addEventListener("input", this.handleTextChange);
   }
  
   handleTextChange(event) {
       const searchStr = event.target.value;
       this.searchbox.value = searchStr; 
       this.debouncedServerCall(searchStr);
    }
}

const ac = new AutoCompleter();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js