IE9 does not seem to recognise $.ajax's 'complete' event - javascript

I am using ajax to allow the user to filter content that appears in a target div (var target).
While the content loads, I show a div containing a loader image ('#loader').
However, when the ajax call is done, IE doesn't re-hide the loader as other browsers do.
It also indentifies the the setTimeout() call (in the ajax callback) as an 'invalid argument'.
If I didn't find this so baffling I wouldn't ask here. Thanks!
CODE:
function run_ajax() {
$.ajax({
url: 'artworks_ajax',
beforeSend: function(){
target.empty();
$('#loader').fadeIn();
},
complete: function() {
$('#loader').fadeOut('fast')
},
data: {
'select' : 'artworks',
'artwork-filter': JSON.stringify(filter)
},
success: function(data) {
target.hide();
target.html(data);
fireMasonry();
reloadMasonry(); // masonry needs reminding how big its div is
setTimeout(
fadeUp()
, 1000); // pause necessary to give masonry time to fix itself in place
}
});
}

There is a semicolon missing at the end here:
$('#loader').fadeOut('fast')
Also, the first argument to setTimeout should be a function, while here you are calling the function and using its return value. Assuming that fadeUp is a free function, it should be like this:
setTimeout(fadeUp, 1000);

If for whatever reason there is still an issue, you can move complete to be after the ajax call like so: $.ajax({ajax_stuff_goes_here}).complete(function() {$('#loader').fadeOut('fast');});

Related

Controlling an $.ajax function within a js "for" loop?

Here's my issue. I have a js function that performs an $.ajax call to fetch some data from a server. When it gets that data back, I need to pass control back to the browser in order to show an update to a div.
The js function is itself within a for loop, and I need to ensure that the for loop does not advance until the js function has updated the div and allowed the Browser to display that update, at which point the for loop advances and the js function (with its ajax call) is called again, continuing until the for loop test causes the loop to end.
I've tried many different approaches - callbacks, promises etc, but to date I can't seem to get a handle on ensuring that the loop doesn't advance until the js function gets its server data, updates the div, causes the browser to display that update and fully completes.
Here's a simple stripped-down version of the function:
function myFunction (email) {
var request = $.ajax( {
url: 'getit.php',
cache: false,
async: false,
method: "post",
timeout: 1000,
data: "requesttype=getemailname&email="+encodeURIComponent(email)
});
request.done(function(response) {
$("#myDiv").html(response);
});
}
and here's part of the js that calls it:
.....
var emailscount = emails.length;
for(var i=0;i<emailscount;i++) {
myFunction (emails[i]);
}
.....
So, my issues are:
1) myFunction must allow the browser to display the updated div html - I'm not sure how to achieve that?
2) the for loop should only proceed when myFunction has received the data back from the server, updated the div html, AND allowed the browser to display that div.
At the moment, I have set the $.ajax call async flag set to "false" to stop execution until the data comes back, but how do I ensure the browser displays the new div content, and that the for loop does not proceed to call myFunction again until the previous myFunction call fully completes?
Any help you can give me would be very welcome, as right now I can't get this all to work!
Sounds like you need a recursive function, not a for loop with synchronous ajax calls
(function myFunction(i) {
$.ajax({
url: 'getit.php',
method: "post",
timeout: 1000,
data: {
requesttype : 'getemailname',
email : emails[i]
}
}).done(function(response) {
$("#myDiv").html(response);
if (emails[++i]) myFunction(i); // continue when this one is done
});
})(0);
Thanks for everyone's help! I'm making good progress (including taking care of JQuery deprecations!) but have run into a further problem. As I need to hand control back to the browser in order to show the refreshed div as I recurse, I'm calling a setTimeout as follows:
var nextBitOfWork = function () {
return myFunction(email);
};
setTimeout(nextBitOfWork, 0);
where myFunction (which recurses) now returns a promise when it's done doing it's $.ajax call.
If I simply call:
return myFunction(email);
without the setTimeout function construct above, the promise is passed through and all my promises are captured and allow me to get the array output I need and everything works great. But without the setTimeout I don't get the browser refresh. Using it as above I get the div update refresh displaying, but seem to lose the promise and so the script continues and I don't get to fill the array I use to capture values as I recurse.
Any thoughts on how to make sure the setTimeout passes on the promise?
Thanks

Send ajax request at a given interval after on click event

I am sending an ajax request when the user hit the search button in the following manner:
$('#search').on('click',function(){
$('#searchResponse').hide();
$('#searchResponse').html('<img src="assets/img/loading.gif">');
$('#searchResponse').show();
$.ajax({type:'POST',url:'assets/php/handler.php',data:$('#form').serialize(),success:function(response){
$('#searchResponse').html(response);
}});
return false;
});
Everything is working fine but I want to have something like an automatic update after the above happens. This means I have to set up something like a timeout after the request is completed so the ajax is fired again. I've tried the following but with no success unfortunately:
$('#search').on('click',function(){
$('#searchResponse').hide();
$('#searchResponse').html('<img src="assets/img/loading.gif">');
$('#searchResponse').show();
$.ajax({type:'POST',url:'assets/php/handler.php',data:$('#form').serialize(),success:function(response){
$('#searchResponse').html(response);
},complete:function(){
setTimeout(this, 5000);
}});
return false;
});
I guess that the selector isn't right but what alternative should I use to suits my needs? Any help of guidance is more than welcomed.
You are not providing a suitable method for the setTimeout call. this is the ajax context. As you want to call the same upload a second time after 5 seconds, try like this:
$('#search').on('click', function () {
$('#searchResponse').hide();
$('#searchResponse').html('<img src="assets/img/loading.gif">');
$('#searchResponse').show();
var doAjax = function () {
// return the ajax promise
return $.ajax({
type: 'POST',
url: 'assets/php/handler.php',
data: $('#form').serialize(),
success: function (response) {
$('#searchResponse').html(response);
}
});
});
// Call once then again on success
doAjax().done(function(){setTimeout(doAjax, 5000);});
return false;
});
Notes: jQuery.Ajax returns a deferred's promise that you can use to chain together functionality. Although promises are initially more confusing than say callbacks they are far more powerful and worth learning. You will change the way you write your code once you try them :)
Side-issue:
As #Peter Herdenborg points out, these three lines hiding and showing the response are not all required. The reason is that they all happen on the same render cycle, so you will not see a visual flash.
e.g. this:
$('#searchResponse').html('<img src="assets/img/loading.gif">');
will do the same as this:
$('#searchResponse').hide();
$('#searchResponse').html('<img src="assets/img/loading.gif">');
$('#searchResponse').show();
You need to extract out the ajax bits to a function which either calls itself with a delay or that is simply called using setInterval(). I also don't see a point in hiding #searchResponse before changing its contents, so I've removed that and the related .show().
$('#search').on('click',function(){
$('#searchResponse').html('<img src="assets/img/loading.gif">');
loadResults();
setInterval(loadResults, 5000);
return false;
});
function loadResults(){
$.ajax({
type:'POST',
url:'assets/php/handler.php',
data: $('#form').serialize(),
success: function(response){
$('#searchResponse').html(response);
}
});
}

How to implement loading display with ajax

I am returning data from a php file using ajax. But it would look good if a loading display came up, like an animated gif or something. I have had a go using text but noting shows up. Is there a better way of doing it than I have.
function small_loader(t){
if(t === 'show'){
$('#sub_menu').html('Loading');
}else{
$('#sub_menu').html('');
}
}
function loadtools(t){
var tools = 'tools/'+t+'.php';
$.ajax({
url:tools,
type:'POST',
beforeSend: function(){
small_loader('show');
},
success: function(e){
$('#sub_menu').html(e);
},
complete:function(){
small_loader('hide');
}
});
}
At the moment the php file just has a for loop on it echoing out numbers with kine breaks for testing purposes. This does work if I take the small_loader function off. Why is this?
You set #sub_menu to 'Loading...' then put the AJAX stuff there, then at the end overwrite it with '' when you disable the loader, overwriting the ajax response.
Try using a different div for the loader, and maybe hide the #sub_menu and display it when it's finished.
You should add your loader in your HTML (at the right place, say beside the button launching the ajax request), hide it in css (display:none), show it before sending your request and hide it when response comes back :
function loadtools(t){
var tools = 'tools/'+t+'.php';
$.ajax({
url:tools,
type:'POST',
beforeSend: function(){
$("#your_loader_wrapper_id").show();
},
success : ...,
error : ...,
complete: function(e){
$("#your_loader_wrapper_id").hide();
$('#sub_menu').html(e);
}
});
}

Use Ajax variables in jQuery function

I've been working on a site built by someone else, and every time there is an Ajax call in the site there's a div showing a progress bar. In once instance though I would like to hide this bar (or better: not show it), but I don't know how to get my Ajax variables in this function.
The Ajax call is a very simple:
$.ajax({url: url, ...
and somwhere else in the code the function is added:
jQuery(function ($) {
$(document).ajaxStart(function () {
$('#progressbar').modal('show');
});
I would love to add something to the ajax call like
$.ajax({url: url, hideProgressBar: true, ...
and then use the false to stop the progressbar from showing. Anyone?
Set the global option to false in your AJAX properties for that call:
global: false,
Use ajaxSend instead of ajaxStart...
$(document).ajaxSend(function (e, jqXHR, options) {
if (options.showProgressBar) {
$('#progressbar').modal('show');
}
});
Then make your ajax calls like this...
$.ajax({
url: "http://etc..",
showProgressBar: false
});
You can put any options you like in the ajax call and they'll be accessible in the send event handler, in the options object.
Note: I know I've used showProgressBar and you were talking about hiding it, but that's just me. Change that, if needed, to suit :)

How do you make javascript code execute *in order*

Okay, so I appreciate that Javascript is not C# or PHP, but I keep coming back to an issue in Javascript - not with JS itself but my use of it.
I have a function:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
}
Thing is, owing to Javascript's burning desire to jump ahead in the code, the loader never appears because the 'hideLoader' function runs straight after.
How can I fix this? Or in other words, how can I make a javascript function execute in the order I write it on the page...
The problem occurs because AJAX is in its nature asynchronus. This means that the updateStatus() calls are indeed executed in order but returns immediatly and the JS interpreter reaches hideLoader() before any data is retreived from the AJAX requests.
You should perform the hideLoader() on an event where the AJAX calls are finished.
You need to think of JavaScript as event based rather than procedural if you're doing AJAX programming. You have to wait until the first call completes before executing the second. The way to do that is to bind the second call to a callback that fires when the first is finished. Without knowing more about the inner workings of your AJAX library (hopefully you're using a library) I can't tell you how to do this, but it will probably look something like this:
showLoader();
updateStatus('cron1', function() {
updateStatus('cron2', function() {
updateStatus('cron3', function() {
updateStatus('cronEmail', function() {
updateStatus('cronHourly', function() {
updateStatus('cronDaily', funciton() { hideLoader(); })
})
})
})
})
})
});
The idea is, updateStatus takes its normal argument, plus a callback function to execute when it's finished. It's a reasonably common pattern to pass a function to run onComplete into a function which provides such a hook.
Update
If you're using jQuery, you can read up on $.ajax() here: http://api.jquery.com/jQuery.ajax/
Your code probably looks something like this:
function updateStatus(arg) {
// processing
$.ajax({
data : /* something */,
url : /* something */
});
// processing
}
You can modify your functions to take a callback as their second parameter with something like this:
function updateStatus(arg, onComplete) {
$.ajax({
data : /* something */,
url : /* something */,
complete : onComplete // called when AJAX transaction finishes
});
}
I thinks all you need to do is have this in your code:
async: false,
So your Ajax call would look like this:
jQuery.ajax({
type: "GET",
url: "something.html for example",
dataType: "html",
async: false,
context: document.body,
success: function(response){
//do stuff here
},
error: function() {
alert("Sorry, The requested property could not be found.");
}
});
Obviously some of this need to change for XML, JSON etc but the async: false, is the main point here which tell the JS engine to wait until the success call have returned (or failed depending) and then carry on.
Remember there is a downside to this, and thats that the entire page becomes unresponsive until the ajax returns!!! usually within milliseconds which is not a big deals but COULD take longer.
Hope this is the right answer and it helps you :)
We have something similar in one of our projects, and we solved it by using a counter. If you increase the counter for each call to updateStatus and decrease it in the AJAX request's response function (depends on the AJAX JavaScript library you're using.)
Once the counter reaches zero, all AJAX requests are completed and you can call hideLoader().
Here's a sample:
var loadCounter = 0;
function updateStatuses(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
}
function updateStatus(what) {
loadCounter++;
//perform your AJAX call and set the response method to updateStatusCompleted()
}
function updateStatusCompleted() {
loadCounter--;
if (loadCounter <= 0)
hideLoader(); //hide the 'loader.gif' in the UI
}
This has nothing to do with the execution order of the code.
The reason that the loader image never shows, is that the UI doesn't update while your function is running. If you do changes in the UI, they don't appear until you exit the function and return control to the browser.
You can use a timeout after setting the image, giving the browser a chance to update the UI before starting rest of the code:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
// start a timeout that will start the rest of the code after the UI updates
window.setTimeout(function(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
},0);
}
There is another factor that also can make your code appear to execute out of order. If your AJAX requests are asynchronous, the function won't wait for the responses. The function that takes care of the response will run when the browser receives the response. If you want to hide the loader image after the response has been received, you would have to do that when the last response handler function runs. As the responses doesn't have to arrive in the order that you sent the requests, you would need to count how many responses you got to know when the last one comes.
As others have pointed out, you don't want to do a synchronous operation. Embrace Async, that's what the A in AJAX stands for.
I would just like to mention an excellent analogy on sync v/s async. You can read the entire post on the GWT forum, I am just including the relevant analogies.
Imagine if you will ...
You are sitting on the couch watching
TV, and knowing that you are out of
beer, you ask your spouse to please
run down to the liquor store and
fetch you some. As soon as you see
your spouse walk out the front door,
you get up off the couch and trundle
into the kitchen and open the
fridge. To your surprise, there is no
beer!
Well of course there is no beer, your
spouse is still on the trip to the
liquor store. You've gotta wait until
[s]he returns before you can expect
to have a beer.
But, you say you want it synchronous? Imagine again ...
... spouse walks out the door ... now,
the entire world around you stops, you
don't get to breath, answer the
door, or finish watching your show
while [s]he runs across town to
fetch your beer. You just get to sit
there not moving a muscle, and
turning blue until you lose
consciousness ... waking up some
indefinite time later surrounded by
EMTs and a spouse saying oh, hey, I
got your beer.
That's exactly what happens when you insist on doing a synchronous server call.
Install Firebug, then add a line like this to each of showLoader, updateStatus and hideLoader:
Console.log("event logged");
You'll see listed in the console window the calls to your function, and they will be in order. The question, is what does your "updateStatus" method do?
Presumably it starts a background task, then returns, so you will reach the call to hideLoader before any of the background tasks finish. Your Ajax library probably has an "OnComplete" or "OnFinished" callback - call the following updateStatus from there.
move the updateStatus calls to another function. make a call setTimeout with the new function as a target.
if your ajax requests are asynchronous, you should have something to track which ones have completed. each callback method can set a "completed" flag somewhere for itself, and check to see if it's the last one to do so. if it is, then have it call hideLoader.
One of the best solutions for handling all async requests is the 'Promise'.
The Promise object represents the eventual completion (or failure) of an asynchronous operation.
Example:
let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});
Promise
If you have 3 async functions and expect to run in order, do as follows:
let FirstPromise = new Promise((resolve, reject) => {
FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {
});
let ThirdPromise = new Promise((resolve, reject) => {
});
FirstPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("First! ");
SecondPromise.resolve("Second!");
},
error: function() {
//handle your error
}
});
});
SecondPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Second! ");
ThirdPromise.resolve("Third!");
},
error: function() {
//handle your error
}
});
});
ThirdPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Third! ");
},
error: function() {
//handle your error
}
});
});
With this approach, you can handle all async operation as you wish.

Categories

Resources