Executing current ajax call and aborting all previous ones - javascript

I am trying to build an auto-complete UI. There is an input whose on keyup function does an ajax call to server to fetch the most relevant data. But if user types a word which is, say 10 character long, so for each keyup one ajax call is made and my dialogue box refreshes 10 times.
I have tried using abort() for the ajax call. When I do an abort to previous ajax call, the call is not made but still it waits for 10 calls before executing the last one, which makes the user experience very bad.
So is there a way to execute just the current ajax call without any delay from the previous ones?
A part of my code:
var request_autocomplete=jQuery.ajax({});
$('.review_autocomplete').keyup(function() {
request_autocomplete.abort();
request_autocomplete=jQuery.ajax({
// DO something
});
});

OP, there are two parts to this. The first is your abort, which it seems that you already have.
The second is to introduce forgiveness into the process. You want to fire when the user stops typing, and not on every key press.
You need to use both keyUp and keyDown. On keyUp, set a timeout to fire your submit. Give it perhaps 700ms. On KeyDown, clear the timeout.
var request_autocomplete=jQuery.ajax({});
var forgiveness;
// first your AJAX routine as a function
var myServiceCall = function() {
request_autocomplete.abort();
request_autocomplete=jQuery.ajax({
// DO something
}
// keyup
$('.review_autocomplete').keyup(function() {
forgiveness = window.setTimeout(myServiceCall, 700);
});
});
// key down
$('.review_autocomplete').keydown(function() {
window.clearTimeout(forgiveness);
});
});
What this will do is constantly set a timeout to fire every time a key is up, but each time a key is down it will cancel that timeout. This will have the effect of keeping your service call from firing until the user has stopped typing, or paused too long. The end result is that you will wind up aborting a much smaller percentage of your calls.

you can implement the way you asked in your question is preventing for example 3 calls as below :
var calls = 0;
$('.review_autocomplete').keyup(function() {
if (calls >3) {
request_autocomplete.abort();
request_autocomplete=jQuery.ajax({
// DO something
});
calls = 0;
}
calls++;
});
but this way not recommended because when user wants to types sample while user types samp at p ajax call fire up. and when user type l and e nothing happen !
If you are using jquery Autocomplete
you can using
minLenght so you can check current lenght of text box and when user typed at least 3 character then you must call the ajax request.
delay (between last keystroke and ajax call. Usually 2-300ms should do)
and using AjaxQueue
after a quick search about this issue I have found this link that shows another way to prevent multiple ajax calls for autocomplete by using cache

You could use a globalTimeout variable that you reset with setTimeout() and clearTimeout().
var globalTimeout;
$('.review_autocomplete').keydown(function(){
if(globalTimeout)clearTimeout(globalTimeout);
}).keyup(function(){
globalTimeout = setTimeoout(function(){
$.ajax({/* you know the drill */});
}, 10);
});
This way the timeout is cleared whenever your Client pushes a keydown, yet the timeout is set again as soon as the your Client releases a key onkeyup, therefore $.ajax() will only be called if there's no key action, after 10 milliseconds in this case. I admit that this won't stop an $.ajax() call that has already been made, however it probably won't matter because they happen pretty fast, and because this example prevents future $.ajax() calls as long as the Client keeps typing.

Try
var count = {
"start": 0,
// future , by margin of `count.timeout`
"complete": 0,
// if no `keyup` events occur ,
// within span of `count.timeout`
// `request_autocomplete()` is called
// approximately `2` seconds , below ,
// adjustable
"timeout" : 2
};
$('.review_autocomplete')
.focus()
.on("keyup", function (e) {
elem = $(this);
window.clearInterval(window.s);
window.s = null;
var time = function () {
var t = Math.round($.now() / 1000);
count.start = t;
count.complete = t + count.timeout;
};
time();
var request_autocomplete = function () {
return jQuery.ajax({
url: "/echo/json/",
type: "POST",
dataType: "json",
data: {
json: JSON.stringify({
"data": elem.val()
})
}
// DO something
}).done(function (data) {
window.clearInterval(s);
console.log("request complete", data);
$("body").append("<br /><em>" + data.data + "</em>");
elem.val("");
count.start = count.complete = 0;
console.log(count.start, count.complete);
});
};
window.s = setInterval(function () {
if (Math.round($.now() / 1000) > count.complete) {
request_autocomplete();
console.log("requesting data");
};
// increased to `1000` from `501`
}, 1000);
});
jsfiddle http://jsfiddle.net/guest271314/73yrndwy/

Related

Stopping a recurring ajax call

I'm making an app where there's loads of products, each with a button. My plan is the user clicks one of the buttons and I have a javascript (jquery) function which starts making an ajax request every second using the id of the button as a parameter to identify the product.
The function I'm planning on using is the 2nd answer here Execute an Ajax request every second
The idea is to check the status of the product (which can change constantly) every second while the user is interested in it.
When the user clicks the button again I want to stop checking this particular product's status but I can't figure out how to do this. In my head I imagine the user might have clicked 3 buttons so there's 3 ajax requests every second happening, each with a different product id. How can I stop the recurring request which has the id of the product the user has clicked stop?
I did something similar recently, where I had an interval running every few seconds and, when some event occurred, I stopped the process altogether. I'm assuming you're using Javascript, so something like the below. You can check "someVar" in the timer call if you wish, up to you...
var someVar = false, intervalId;
$(function () {
// Attach event handler to button
$('#ButtonId').click(function (e) {
if (!someVar) {
e.preventDefault();
someVar = true;
//Start interval
intervalId = setInterval(CheckStatus, 3000);
} else {
// Stop the interval!
clearInterval(intervalId);
someVar = false;
}
});
});
function CheckStatus() {
// I used MVC, but you can use whatever you need to generate the URL
var checkUrl = '#Url.Action("CheckStatus", "SomeController", new { intervalId = "_intervalId_" })'.replace('_intervalId_', intervalId).replace(/&/g, "&");
$.ajax({
url: checkUrl,
type: 'GET',
contentType: 'application/json; charset=utf-8',
success: function (data) {
// do something with the status - data.Status perhaps?
},
error: function () {
//handle error
}
});
}

Combining multiple keyups into single event to reduce number of ajax requests

I have a keyup event on a search box that produces suggestions by fetching data from db. It is working fine. But the problem arises when i press keyboard buttons quickly and for every keyup event it gets me the result which produce duplicates.
I tried using
$("#search").on("keyup", function() {
setTimeout(getLocationFromDb, 1000);
});
But that still is producing same result (sending request to server for every keyup event).
I was looking for solutions but couldn't find one. Thanks for help.
EDIT
I am clearing the results beforeSend and appending the results in success.
Your approach isn't that bad - just make sure to clear all old timouts before setting a new one. You of course have to store the timeout somwhere - you could, for example, create a variable inside a closure.
This code only calls your callback when there was no new input for 1 second:
$("#search").on("keyup", (function () {
var timeout;
return function (e) {
window.clearTimeout(timeout);
timeout = window.setTimeout(getLocationFromDb, 1000);
}
})());
Fiddle: http://jsfiddle.net/uq4x9/
You can use boolean variable to detect if ajax call is finished already before send new
var isAjaxSent = false;
$("#search").on("keyup", function() {
isAjaxSent = true;
setTimeout(getLocationFromDb, 1000);
});
function getLocationFromDb( .... ) {
if (!isAjaxSent) {
//body of function
// in ajax success function set isAjaxSent to false
}
}

Delaying click event

I'm wondering whether there's a simple way to delay the click event from being processed for a specified period of time. For example we could have
$('#someElement').on('click', 'a', function(event) {
var duration = 1000;
someAsynchronousFunction(); // Start as soon as click occurs
... // Code to delay page transition from taking place for duration specified
});
So in this case the asynchronous function would be guaranteed some amount of time to run. If it hasn't completed it's work in this time I wouldn't care and would just like to continue with the page transition. I know that it's possible to accomplish something close with
event.preventDefault();
...
setTimeout(function(){
window.location = $(this).attr('href');
}, duration);
But this only works when the link being clicked goes to a full page. I want to be able to deal with links that are used for ajax calls (which don't change the url) as well.
I noticed that the mixpanel library has a function track_links which seems to accomplish the delay on the page transition, though that function doesn't seem to work well with the support for ajax links that I mentioned.
Any help would be great! Thanks.
Edit: So I suppose my question wasn't exactly clear, so I'll try to provide some more details below.
I don't care if the async function finishes running! I only want to give it the guarantee that it has some set amount of time to execute, after which I don't care if it finishes, and would prefer to go ahead with the page transition.
i.e. I want to delay not the start of the async function, but the start of the page transition. The async function would start running as soon as the click occured.
Hopefully this is a bit more clear!
I figured out a way to solve the problem:
var secondClick = false;
var duration = 1000;
$('#someElement').on('click', 'a', function(event) {
var that = $(this);
if(!secondClick) {
event.stopPropagation();
setTimeout(function(){
secondClick = true;
that.click();
}, duration);
someAsynchronousFunction();
} else {
secondClick = false;
}
});
When the user clicks the link, it internally prevents that click from actually having any effect, and gives the asynchronous function a set amount of time to do it's work before doing a second click on the link which behaves normally.
setTimeout allows you to delay running code by however many ms you want
setTimeout(function(){
console.log('Stuff be done'); //This will be delayed for one second
}, 1000);
In reality, if you're dealing with ajax you want to respond when the ajax call is complete. It may take more or less than 1000ms. $.ajax allows you do this with the .done() method. This is an example from the docs:
$.ajax({
url: "test.html",
context: document.body
}).done(function() {
$(this).addClass("done");
});
window.setTimeout will execute any given function after a specified delay.
You'd call it like this:
$('yourElement').click(function (event) {
setTimeout(function () { console.log('hi'); }, 1000);
});
But I have to wonder why you need to do this. What's the problem you're trying to solve? Usually delaying stuff doesn't really solve anything.
jQuery's ajax functionality provides exactly what you are looking for. You can define a callback function to run after your ajax request.
Something like this:
$('#someElement').click(function(event){
event.preventDefault();
var loc = $(this).attr('href');
$.ajax({
url: "test.html",
complete: function(){
// Handle the complete event
loc = $(this).attr('href');
window.location.href = loc;
}
});
});
You may want to use ajaxStop instead of complete, it seems like your motivation for delaying navigation is because you have a bunch of asynchronous stuff going on and you want to make sure all your ajax stuff is complete before you navigate to that page.
Regardless I would recommend looking at http://api.jquery.com/Ajax_Events/ (a very useful page of documentation).

Javascript: Do processing when user has stopped typing

I have a text box on a web page, whose value I want to send to a XMLHttpRequest. Now I want the user to just type the value, without pressing a button. But If i just send the request int he keyboard events, it will fire every time a key is pressed.
So basically I want something liek this
function KeyUpEvent()
{
if (user is still typing)
return;
else
//do processing
}
It would be great if the solution could come from plain javascript or mootools. I dont want to use any other library.
The way this is usually done is by restarting a timer on the keyup event. Something like this:
var keyupTimer;
function keyUpEvent(){
clearTimeout(keyupTimer);
keyupTimer = setTimeout(sendInput,1000); // will activate when the user has stopped typing for 1 second
}
function sendInput(){
alert("Do AJAX request");
}
Basically, you want to start a timer on KeyUp, and when KeyUp starts again, reset the timer. When the user stops typing, the timer runs out, and your request can go at that point.
Example:
var timout_id;
function keyup_handler(event) {
if (timout_id) {
clearTimeout(timout_id);
}
timout_id = setTimeout(function(){
alert('sending data: \n' + event.target.value)
}, 800);
}
Just attach the function to the input using your preferred method, and replace the alert with your preferred action.
Of course there are many ways you could generalize this approach and make it more reusable, etc, but I think this illustrates the basic idea.
I always use this simple function to handle a timer, that will fire a callback function, after the user has stopped typing for a specified amount of time:
var typewatch = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
}
})();
Usage (example with MooTools):
$('textInput').addEvent('keyup', function(e){
typewatch(function () {
// executed only 500 ms after the last keyup event
// make Ajax request
}, 500);
});
The main difference between this solution and solutions from other answers is that all the timer logic is handled by the typewatch function itself, the event handler doesn't need to know anything about the timer, it just invokes the function. Also, there are no global variables to take care (the timer id is not stored on a global variable).
You never know when a user is really "finished" typing. The user might take a sneeze break, or a stretch break, or a coffee break, and then continue typing.
However, if you're implementing something like an autocomplete mechanism, you can set a timer (cf. window.setTimeout(...)) to see if the user hasn't typed anything in a certain amount of time. If you get another key-up event while the timer is running, you can start the timer over.
var keyTimer;
function onKeyUp(){
clearTimeout(keyTimer);
setTimeout(stoppedTyping,1500);
}
function stoppedTyping(){
// Profit! $$$
}
EDIT: Damn ninjas
I wrote a custom jQuery event because I use this logic a lot:
jQuery.event.special.stoppedtyping = {
setup: function(data, namespaces) {
jQuery(this).bind('keyup', jQuery.event.special.stoppedtyping.keyuphandler);
},
teardown: function(namespaces) {
jQuery(this).bind('keyup', jQuery.event.special.stoppedtyping.keyuphandler);
},
keyuphandler: function(e) {
var interval = 1000;
var el = this;
if (jQuery.data(this, 'checklastkeypress') != null) {
clearTimeout(jQuery.data(this, 'checklastkeypress'));
}
var id = setTimeout(function() {
jQuery(el).trigger('stoppedtyping');
}, interval);
jQuery.data(this, 'checklastkeypress', id);
}
};
You can use it like this:
$('input.title').bind('stoppedtyping', function() {
// run some ajax save operation
});
For some reason I could never get it to work with .live( ... ). I'm not sure why...
Use onBlur and maybe an onKeyDown to check for the user pressing the return/enter key.

Javascript - Which event to use for multiselect change

I'm using YUI as javascript framework, and can successfully react when the user changes the value of basic input fields, the reaction being to sent an Ajax query.
However, I'm not so lucky with multiselect dropdown lists:
listening to "change" would send my query each time the user adds/removes an item to his selection
listening to "blur" requires the user to click elsewhere in order to loose the focus and send the query (not very usable), plus it would send the query if the user only scrolls on the list without changing anything (useless, confusing).
Any idea (with YUI), that would use a clever behavior?
Or should I really listen to change and implement a timeout (to wait for subsequent changes before sending a query)?
I use the same kind of timeout you want on key events, to detect when the user have finished typing, the same approach can be used on your problem:
// helper function
var timeout = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
Usage:
// YUI 2
YAHOO.util.Event.addListener(oElement, "change", function () {
timeout(function () {
// one second since the last selection change
}, 1000);
});
// YUI 3
Y.on("click", function () {
timeout(function () {
// one second since the last selection change
}, 1000);
}, oElement);
Basically in this timeout function, I reset the timer if the function is called before the specified delay.
you could run a setTimeout after the onChange event and keep track of a number of changes to determine whether or not a change had been made since the event was fired. if no changes were made within that time, then the query could be sent.
e.g., something like:
var changes = 0;
function myOnChangeHandler(e)
{
changes++;
var local_change = changes;
setTimeout(function() {
if (local_change === changes) {
sendRequest();
}
}, 500);
}

Categories