How to implement auto-complete?

I. Logic

  1. For each input, wait 500ms to make API call, to load 40 items
  2. scroll down close to bottom ( less than 20px left ), make API call to load another 40 items
  3. could add local cache, to speed up response

II. code

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    <!DOCTYPE html>
    <html>
    <head>
    <style>
    #wrapper {
    margin-top: 10px;
    overflow: auto;
    width: 250px;
    display: inline-block;
    border: 1px solid red;
    overflow: hidden;
    }

    .listWrapper{
    border: 1px solid blue;
    height: 200px;
    width: 140px;
    overflow: auto;
    scroll: auto;
    }

    </style>
    </head>
    <body>
    <div id="wrapper">
    <div>
    <input id="inputText" />
    <button>submit</button>
    </div>
    </div>

    <script>

    let body = document.getElementsByTagName("BODY")[0]
    let data = Array.from({length: 40}, () => "recommdation"+Math.floor(Math.random() * 40));

    const API = "https://www.demo.com/potentialList"
    let isFetching = false // used for scroll
    let isEnd = false // used for scroll


    // create new element below <input> inside div wrapper. For both 'input' & 'scroll'
    function appendList(elementCollection, wrapper, isScroll){
    if(isScroll){
    let listWrapper = document.getElementsByClassName("listWrapper")[0]
    data.forEach( ele=>{
    let itemNode = document.createElement("DIV")
    let textNode = document.createTextNode(ele)
    itemNode.appendChild(textNode)
    listWrapper.appendChild(itemNode)
    })
    return
    }

    let oldlistWrapper = document.getElementsByClassName("listWrapper")
    if(oldlistWrapper.length > 0){
    wrapper.removeChild(oldlistWrapper[0])
    }
    let listWrapper = document.createElement("DIV")
    listWrapper.setAttribute("class", "listWrapper")
    let list = Array.prototype.slice.call(elementCollection)
    data.forEach( ele=>{
    let itemNode = document.createElement("DIV")
    let textNode = document.createTextNode(ele)
    itemNode.appendChild(textNode)
    listWrapper.appendChild(itemNode)
    })
    listWrapper.addEventListener("click", putToInput)
    listWrapper.addEventListener("scroll", scrollHandler)
    wrapper.appendChild(listWrapper)

    }


    // onclick callback for items provided
    function putToInput(e){
    let input = document.getElementById("inputText")
    let wrapper = document.getElementById("wrapper")
    let listWrapper = document.getElementsByClassName("listWrapper")[0]
    input.value = e.target.innerHTML
    console.log(e.target)
    wrapper.removeChild(listWrapper)
    }


    // make API call
    function makeAPICall(API, val, isScroll){
    console.log("val: ", val)
    // 1. first check local cache

    // 2. hit fail, call API
    let ajax = new XMLHttpRequest()
    ajax.open("GET", "https://www.w3schools.com/js/ajax_info.txt", true)
    ajax.onreadystatechange = function(){
    if(this.readyState === 4 && this.status === 200 ){
    console.log(this.responseText)
    let parser = new DOMParser()
    let a = parser.parseFromString(this.responseText, "text/html")
    console.log(a)
    let body = a.getElementsByTagName("BODY")[0]
    let wrapper = document.getElementById("wrapper")
    if(val.localeCompare("scroll made request")===0){
    isFetching = false
    }
    appendList(data, wrapper, isScroll)
    }
    }
    ajax.send()
    }

    // scroll handler for listWrapper
    function scrollHandler(e){
    let listWrapper = e.target
    let boxHeight = listWrapper.clientHeight
    let scrollTop = listWrapper.scrollTop
    let scrollHeight = listWrapper.scrollHeight

    let remainder = scrollHeight - scrollTop
    let diff = remainder - boxHeight
    console.log("box height: ", boxHeight)
    console.log("scrollTop: ", scrollTop)
    console.log("scrollHeight: ", scrollHeight)
    console.log("diff: ", diff)

    if( diff < 20 && !isFetching && !isEnd ){
    isFetching = true
    makeAPICall(API, "scroll made request", true)
    }
    }

    // debounce 500ms to make API call
    let timeHandler = null
    function inputHandler(e){
    clearTimeout(timeHandler)
    let val = e.target.value
    timeHandler = setTimeout( function(){makeAPICall(API, "keypress made request", false)} ,500 )
    }

    let input = document.getElementById("inputText")
    addEventListener("input", inputHandler)
    </script>

    </body>
    </html>