-1

I have a search box and I want to display search results while typing instantly; but on fast typing I have problems.

JavaScript:

function CreateXmlHttp() {
    var xmlhttp;
    try {
        xmlhttp = new XMLHttpRequest();
    } catch (e) {
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        }catch (e) {
            try {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {
                alert("your browser doesn't support ajax");
                return false;
            }
        }
    }
    return xmlhttp;
}
function searchfunc(value) {
    if (value!='') {
        var xmlhttp = CreateXmlHttp();
        xmlhttp.open('GET','http://example.com/ajax/instant_search.php?q='+value,true);
        xmlhttp.send(null);
        xmlhttp.onreadystatechange=function() {
            if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                document.getElementById('search_result').innerHTML = xmlhttp.responseText+'<li><a href="http://example.com/search.php?q='+value+'">full search for <strong>'+value+'</strong></a></li>';
            }
        }
    } else document.getElementById('search_result').innerHTML = '';
}

HTML:

<input id="search_box" type="text" placeholder="type to search..." onkeyup="searchfunc(this.value)">
<ul id="search_result"></ul>

how can I abort previous XMLHttpRequest on new key presses?

Pourbahrami
  • 319
  • 1
  • 16

2 Answers2

0

If you're going to abort the request anyway, you might as well instead prevent the request from being sent in the first place if you detect that the user is still typing.

var timeout, timer = 150;
function searchfunc(value) {
    clearTimeout(timeout);
    setTimeout(function () {
        if (value!='') {
            var xmlhttp = CreateXmlHttp();
            xmlhttp.open('GET','http://example.com/ajax/instant_search.php?q='+value,true);
            xmlhttp.send(null);
            xmlhttp.onreadystatechange=function() {
                if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                    document.getElementById('search_result').innerHTML = xmlhttp.responseText+'<li><a href="http://example.com/search.php?q='+value+'">full search for <strong>'+value+'</strong></a></li>';
                }
            }
        } else document.getElementById('search_result').innerHTML = '';
    }, timer);
}

What this will do is when the user presses a key, a setTimeout is started that waits 150ms. If the function is triggered again within 150ms, the interval is cleared and restarted. Once the interval finally finishes, the ajax request is made.

This is identical to using an abort other than the fact that the server isn't bombarded with ajax requests that you would have aborted anyway, and a 150ms latency is added to the request.

There are libraries out there that will handle this even better such as http://benalman.com/projects/jquery-throttle-debounce-plugin/ (it doesn't require jQuery)

Using the plugin you could have it instead send no more than 1 request per timer ms, resulting in the first key always sending a request, and another not being sent until timer ms has passed, giving you a more real-time looking result.

Kevin B
  • 94,570
  • 16
  • 163
  • 180
0

Here I am sharing some overview of what I did in my stencilsjs project to achieve this scenario,

firstly I have created the separate xmlApi.ts common file for my project and wrote following code in that

// common XMLHttpRequest for handling fetch request 
// currently using this XMLHttpRequest in search component to fetch the data
let xmlApi
// Create the XHR request
const request = new XMLHttpRequest()
const fetchRequest = (url: string, params: any) => {
// Return it as a Promise
 return new Promise((resolve, reject) => {
// Setup our listener to process compeleted requests
request.onreadystatechange = () => {

  // Only run if the request is complete
  if (request.readyState !== 4) { return }

  // Process the response
  if (request.status >= 200 && request.status < 300) {
    // If successful
    resolve(request)
  } else {
    // If failed
    reject({
      status: request.status,
      statusText: request.statusText
    })
  }
}
// If error
request.onerror = () => {
  reject({
    status: request.status,
    statusText: request.statusText
  })
}
// Setup our HTTP request
request.open(params.method, url, true)

// Setup our HTTP request headers
if (params.headers) {
  Object.keys(params.headers).forEach(key => {
    request.setRequestHeader(key, params.headers[key])
  })
}

   // Send the request
   request.send(params.body)
 })
}
xmlApi = {
// exporting XMLHttpRequest object to use in search component to abort the previous fetch calls
  request,
  fetchRequest
}
export default xmlApi

Second I have passed the event object with onTextInput method to get the input value using event.target.value

HTMl:

<input id="search_box" type="text" placeholder="type to search..." 
      onInput={event => { this.onTextInput(event) }}/>

Sample HTML for suggestion :

Here based on the showSuggestionListFlag ,i have shown the search suggestionlist, also used css to align div and input tag properly

<div class={'search-result ' + (this.showSuggestionListFlag ? 'show' : '')}>
      <ul class="dropdown-list">
        {this.responseData && this.responseData.map((item, index) => (
          <li class="list-element">{item} </li>
        ))}
      </ul>
    </div>

Third in my ts code, i imported the my xmlApi

Here i have just written some logic code from my code, I have also used async and await to handle promise/reject in my project code, according to your code you can handle your own promise/reject code:

import xmlApi from './xmlApi'
onTextInput (event) { // first created bodydata here using `event.target.value`
const opts = {
  method: 'POST',
  body: bodyData,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
  }
}
try {
  // aborting the previous xhr request call
  if (xmlApi.request) {
    xmlApi.request.abort()
  }
  const responseData = xmlApi.fetchRequest(endPointUrl, opts)
    .then(data => {
      consolep.log(`xhr request success`)
      return JSON.parse(data['response'])
    })
    .catch(error => {
      console.log.debug(`xhr request cancelled/failed : ${JSON.stringify(error)}`)
    }) //use responseData
if(responseData){this.showSuggestionListFlag = true}
} catch (e) {
  console.log(`fetch failed`, e)
}
}

This is my first answer on Stack Overflow. Thanks!!