In my web application, I'm using an $.ajax() request to load data from a database and display it in the browser. During the execution of the request, I display a Loading Results ... message like this:
$.ajax({
// ...
beforeSend: function() {
$('#loading-results-message').show();
},
complete: function() {
$('#loading-results-message').hide();
}
});
This works fine. However, if there is not much data to load, the request takes only a fraction of a second. In this case, the message is only displayed for a fraction of a second as well. This animation happens so quickly that it's difficult to recognize it. Therefore, it would be great if it was possible to display the message only if the request takes a certain amount of time, i.e. some seconds at least but not only a fraction of a second. Is that possible somehow? By the way, I'm using Django on the server side, if that should matter.
Use setTimeout to establish a timer, cancel the timer when the request completes:
var desired_delay = 2000;
var message_timer = false;
$.ajax({
// ...
beforeSend: function() {
message_timer = setTimeout(function () {
$('#loading-results-message').show();
message_timer = false;
}, desired_delay);
},
complete: function() {
if (message_timer)
clearTimeout(message_timer);
message_timer = false;
$('#loading-results-message').hide();
}
});
Documentation
setTimeout on MDN - https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout
clearTimeout on MDN - https://developer.mozilla.org/en-US/docs/DOM/window.clearTimeout
I am with #chris on this solution. But it might also be worth your while to look into http://api.jquery.com/jQuery.ajaxSetup/
This will make it so you won't have to write the timeout for every single request.
Related
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/
I was following a video tutorial on how to do long polling, and it seems I got it done, but I'm having one issue, for some reason, though I get one response from server, my alert shows up 3-6 times. I thought "success" only happens once if we got one response.
2nd question is, how can I make this javascript code cancel the ajax call every 30 seconds, and restart it? I've put in setInterval with .abort() in there while experimenting with no luck, probably wrong placement.
Thank you for the wisdom and help!
var timestamp = null;
var imp = null;
var annk = null;
function waitForMsg(){
$.ajax(
{
type: "GET",
url: "/test?timestamp=" + timestamp + "&imp=" +imp + "&annk=" +annk,
dataType : 'json',
async: true,
cache: false,
success: function(data)
{
alert("hello");
if(data.annkr > "0"){
$("#myidone").css("background-color", "#cccccc");
}else{
$("#myidone").css("background-color", "#cccccc");
}
if(data.impr > 0){
$("#myidtwo").css("background-color", "#000000");
}else{
$("#myidtwo").css("background-color", "#000000");
}
annk = data.annkr;
imp = data.impr;
timestamp = data.timestamp;
setTimeout('waitForMsg()',2000);
}
});
}
$(document).ready(function(){
waitForMsg();
});
I read stuff on Stackoverflow about readystates, but how do I make sure it's ready only once and does it after it's ready?
If the code is exactly the one you posted, there's no reason to show the alert more than 1 and then after 2s for the rescheduling.
Can you make a jsFiddle for that showing the problem?
The second question is more interesting. You can use the timeout option in the .ajax call and then, in the error handler, just reschedule the call.
My personal suggestion is to refactor your code to use the new JQuery Ajax style base on .done .fail and .always.
And that setTimeout should be written as setTimeout(waitForMsg,2000). Using the string parameter you evaluate that string instead of just calling the function and it's a performance penalty (so small that's hardly noticeable but that is ).
You might be seeing the alert multiple times due to the setTimeout() in your success function, you keep calling the waitForMsg() function. Just a guess.
To abort the request you could do something like this:
var timer = null;
function waitForMessage() {
var req = $.ajax(YOUR_CODE);
// The 30 second timeout
timer = setTimeout(function() {
req.abort();
waitForMessage();
}, 30000);
}
Or slightly better maybe:
function waitForMsg() {
$.ajax({
timeout: 30000,
error: function(err) {
if(err === 'timeout') {
waitForMsg();
}
}
})
}
Actually you should use setInterval instead of setTimeout, using this approach if your ajax call fails, you will forcelly abort the loop.
You could also, cancel or change the timeout timings depending on the usage of your service for network resource sanity.
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).
I have an app built in HTML that loads fullscreen inside a modal with $.get()
I have a loading screen that is triggered when someone clicks the icon to load the app.
I want the loading screen to appear for a minimum of 2 seconds before clearing out and showing the app.
Is there a good, safe way to start a timer that runs for 2000 milliseconds, checks a variable that is populated by the callback to see if it is null, and then proceeds with a certain action only when the variable is loaded?
I know a while loop will do the trick, but might cycle 1000 times before the content loads on a slow day, and this seems like an inefficient way to do things.
Answer
$('#testApp').click(function() {
var twoSecMin = $.Deferred();
setTimeout(function () { twoSecMin.resolve(); }, 2000);
$('#appModal').fadeIn(400);
$.when($.get("/usm/portal/_layouts/monkeysphere/test.aspx"),twoSecMin).then(function (data) {
$('#appContainer').html(data[0]);
$('#appContainer').show();
$('#appModal').fadeOut(200);
});
});
If you're using a recent version of jQuery (1.5 or later), you can create a $.Deferred and resolve it with a fixed timeout:
var twoSecMin = $.Deferred();
setTimeout(function () { twoSecMin.resolve(); }, 2000);
Also, save the jqXHR returned by $.get, which is an extended Deferred object:
var ajaxReq = $.get("/usm/test.aspx", ...);
Then wait for both:
$.when(twoSecMin, ajaxReq).then(function (_, res) {
// ready...
var data = res[0];
// ...
});
You could probably just use a setTimeout(fn, 2e3) to do this.
For testing if your variable is null, you could use yourVariable === null.
I think this fairly basic but I can't seem to find one on-line. This can be in JavaScript or jquery.
I need to create a timer for about a 1-2 seconds.
Meanwhile another function is using ajax to pass data to a server side php file. When the response text gets back it displays it on the page.
At the moment I have the ajax function running and the time taken for the function to complete is about 0.1 seconds. But this makes the page look really jumpy as the content changes css styles while the ajax is waiting for a response and then back to the original on return (hope that makes sense).
Anyway to combat this I would like the function to check if the timer has ended before displaying the response text.
The only way I can get it at the moment is by creating a interval timer for a second and running the ajax function when that completes, but this is not ideal as the viewer MUST wait the extra second or 2 even if the request to the server takes over that time to complete.
Hope All Of That Makes Sense & Thanks Very Much For Your Time.
Chris
You're better off attaching your function as a "success" handler to your AJAX call rather than using a fixed timer. How you attach it depends on which library, if any:
jQuery 1.4 style (still works in 1.5)
$.ajax({
// your AJAX options
success: yourFunc
});
jQuery 1.5 style
$.ajax({
// your AJAX options
}).done(yourFunc);
DOM style
// after creating your XHR
xhr.onreadystatechange = function() {
if (this.readyState === 4) { // 4 means the request has completed
if (this.status !== 200) { // 200 is success, so anything else...
// log or report error
return;
}
// call your other function, which uses the AJAX data
yourFunc(this.responseText);
}
};
I would use a setTimeout or the jQuery .delay() if a timer is your only option.
$.ajax({
success: function() {
setTimout(function() {
// styling code goes here
}, 1000);
}
});