jQuery Ajax call performed multiple times - Chrome browser - javascript

I am running into a situation in Chrome where a single Ajax call is being performed multiple times (20 times to be exact).
I have an input field that gets triggered when you enter in 5 digits into the zip field.
I am trying to perform a zip code / state validation in my ajax call.
I am crossing domains.
The code performs correctly in Firefox. It makes the single call, and performs the proper maintenance.(see code). The maintenance is nothing significant. It's assigns a title value or changes a class. Nothing that should cause my ajax call to perform multiple times.
I have checked my code. It is not being called 20 times.
I have put counters in my code to see if the function is being called multiple times and it's only being done once.
The reason for the onKeyUp and onInput was to handle if someone typed into the field, or double-clicked on it and used a cached value.
So in Chrome Version 60.0.3112.113, the Ajax call is performed 20 times, and on the 21st, it cancels itself.
I am also using jQuery instead of $ because there is other code being used that I don't have any control over.
Any and all help would be appreciated. If I am missing something, please let me know.
Here's the input field code:
<input id="zipPostal" name="zipPostal" type="text" placeholder="Postal Code" onkeyup="zipValid1()" oninput="zipValid1()" />
Here's the jquery code:
function zipValid1(){
var x = jQuery("#zipPostal").val();
if (x != '00000' && x.length == 5){
jQuery.ajax({
url: 'https://myurl/getzip.cfm',
type:'POST',
crossDomain:true,
data: {zipcode: x},
success: function(data){
if (data.trim() == 'Invalid'){
(perform CSS and JS maintenance)
}
else{
(perform CSS and JS maintenance)
}
} // closes Success
}); // ajax
}
else{
(perform CSS and JS maintenance)
}
};

Your function zipValid1 gets called on every keyup and input, which when typing it is called twice for each character you type into the input.
A standard approach to fix this issue is to "debounce" the function called until after the user has stopped typing.
This is a popular jQuery library to help with this issue. http://benalman.com/projects/jquery-throttle-debounce-plugin/
Here is a simple example of debouncing the function and waiting until 500ms after the user has stopped typing.
var timeout;
function zipValid1() {
clearTimeout(timeout);
timeout = setTimeout(function() {
var x = jQuery("#zipPostal").val();
if (x != '00000' && x.length == 5) {
jQuery.ajax({
url: 'https://myurl/getzip.cfm',
type: 'POST',
crossDomain: true,
data: {
zipcode: x
},
success: function(data) {
if (data.trim() == 'Invalid') {
// (perform CSS and JS maintenance)
} else {
// (perform CSS and JS maintenance)
}
} // closes Success
}); // ajax
} else {
// (perform CSS and JS maintenance)
}
}, 500);
}

Related

How to execute multiple, chained $.post() calls in consecutive order?

What I'm Trying To Accomplish
I need to trigger 1 to 3 different $.post() requests, and whether it's 1 call, 2 consecutive calls, or 3 consecutive calls is decided by some basic user selection. Each call must only start after the previous is completely finished.
I am dealing with 3 simple behavior cases -- the user, being presented with "Checkbox 1," "Checkbox 2," and a "Continue" button," opts to
Select nothing and then press "Continue" button, which makes an XHR call to '/remote.php',
The user opts to only select "Checkbox 1" or "Checkbox 2," and then presses "Continue" button, which calls $.post() Function 1 that is bound to Checkbox 1, or $.post() Function 2 that is bound to Checkbox 2, and then makes an XHR call to '/remote.php',
Or the user selects both Checkbox 1 + 2 and then presses Continue, which calls $.post() Function 1, then calls $.post() Function 2, and then makes an XHR call to '/remote.php'.
I need to make sure that the Continue-button $.post() function does not fire until the Checkbox-bound $.post() functions fire and complete.
The Problem
The problem is that if Checkbox 1 is selected and Checkbox 2 is selected, and then the Continue button is pressed, as I understand it, the loading order should be:
Checkbox 1 bound $.post() request fires and completes, then
Checkbox 2 bound $.post() request fires and completes, then
Continue button bound $.post() fires, and pages is changed via AJAX at the end of the function tied to "Continue" button bound function.
So, where the result should look like:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
XHR finished loading: POST "/remote.php".
It instead often comes out like this:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
So when the page changes due to the AJAX at the end of the "last"/"Continue-button function, either neither of the Checkbox 1 or Checkbox 2 actions have taken place, or one of the two or both do register in the backend (ie, added to cart) but do not reflect in the AJAXified DOM as they should as the final AJAX fires and completes before the previous $.post() calls have completed.
My Code
The HTML
The HTML is basic:
<form method="post" action="#" onsubmit="newChooseShippingProvider(); return false;">
<label for="delSigCheck" class="del-sig-text"><input id="delSigCheck" type="checkbox" onchange="addDelSigToCart();" title="Add Delivery Signature"></label>
<label for="addInsCheck" class="ins-add-calc"><input id="addInsCheck" type="checkbox" onchange="addInsToCart();" title="Add Delivery Signature" data-ins-id="1284"></label>
<input type="submit" value="Continue" class="btn Small">
</form>
The Javascript/jQuery
This is my latest--4th or 5th--attempt, and still does not work:
function addDelSigToCart() {
$('#delSigCheck').toggleClass('checked');
}
function addInsToCart() {
$('#addInsCheck').toggleClass('checked');
}
function newChooseShippingProvider() {
var originalCheckout = ExpressCheckout.ChooseShippingProvider();
if ($('.ShippingProviderList .radio span').hasClass('checked')) {
var addInsCheck = $('#addInsCheck').hasClass('checked');
var delSigCheck = $('#delSigCheck').hasClass('checked');
var insId = $('#addInsCheck').attr('data-ins-id');
var addDelSigUrl = '/cart.php?action=add&product_id=1280';
var addInsUrl = '/cart.php?action=add&product_id=' + insId;
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl, function() {
$.post(addInsUrl, function() {
originalCheckout;
});
});
} else if (!delSigCheck && !addInsCheck) {
originalCheckout;
} else if (delSigCheck && !addInsCheck) {
$.post(addDelSigUrl, function() {
originalCheckout;
});
} else if (!delSigCheck && addInsCheck) {
$.post(addInsUrl, function() {
originalCheckout;
});
}
} else {
originalCheckout;
}
What I've Tried
I've gone through several version of chaining the $.post() calls, but nothing seems to work consistently.
What I am using now and what seems to work the best for me with extensive testing is using setTimeout to chain the function with some delay, like this:
...
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl);
setTimeout(function() {
$.post(addInsUrl);
setTimeout(function() {
ExpressCheckout.ChooseShippingProvider();
}, 1300);
}, 1300);
} else if ...
And this version above is what I'm using now, as it seems to give the most consistent results, seeing the scripts load typically as 1,2,3, followed by a DOM AJAXified with appropriate changes based on function 1 and 2. However, I don't think the setTimeout is working as even when I increase it to 5000 or 10000, the action is performed "instantaneously" and no delay takes place (at least certainly nothing close to 5-10 seconds).
I've also tried putting the functions inside $.post()'s success callback:
...
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl, function() {
setTimeout(function() {
$.post(addInsUrl, function() {
setTimeout(function() {
originalCheckout;
}, 1300);
});
}, 1300);
});
} else if ...
And finally I've also tried:
$.when($.post(addDelSigUrl)).then(originalCheckout);
as well as .done and success: but none of it works, and the $.posts()'s load in an unexpected order, failing.
The Question
What am I doing wrong?
How can I make it so 1 loads fully, then 2 loads fully, and only then 3 fires and loads?
UPDATE 1:
I just tried jfriend00's answer:
$.post(addDelSigUrl, { cache: false }).then(function(data1) {
//CONSOLE.LOGing HERE
console.log(data1);
return $.post(addInsUrl, { cache: false });
}).then(function(data2) {
//CONSOLE.LOGing HERE
console.log(data2);
return originalCheckout;
});
But it still resulted in:
XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284".
and both console.logs fire immediately after the first "XHR ...", THEN /remote.php fires (though it should fire last as part of originalCheckout), THEN the 3rd XHR fires.
UPDATE 2
Now that we got the XHRs firing and loading in the correct order via .then(), the second part of the problem I am having is that the 3rd XHR to /remote.php updates the DOM via AJAX with data from the backend. Part of that data is the 1st and 2nd $.posts.
I think the 3rd AJAX call is firing and completing milliseconds before some action is taken on the backend via server-side PHP, and because of this more than 50% of the time, the DOM update via the 3rd AJAX call is missing the data from the 1st and/or 2nd call (most often the DOM changes include Checkbox 1/AJAX call 1, but not 2).
How can I fix this? I've tried setTimeout but it doesn't seem to work as even when I set it to like 30000, the 3rd AJAX fires as soon as the 1st/2nd complete.
Latest front-end code:
function newChooseShippingProvider() {
if ($('.ShippingProviderList .radio span').hasClass('checked')) {
var addInsCheck = $('#addInsCheck').hasClass('checked');
var delSigCheck = $('#delSigCheck').hasClass('checked');
var insId = $('#addInsCheck').attr('data-ins-id');
var addDelSigUrl = '/cart.php?action=add&product_id=1280';
var addInsUrl = '/cart.php?action=add&product_id=' + insId;
if (delSigCheck && addInsCheck) {
$.post(addDelSigUrl).then(function(data1) {
return $.post(addInsUrl);
}).then(function(data2) {
return ExpressCheckout.ChooseShippingProvider();
});
} else if (!delSigCheck && !addInsCheck) {
ExpressCheckout.ChooseShippingProvider();
} else if (delSigCheck && !addInsCheck) {
$.post(addDelSigUrl).then(function(data1) {
return ExpressCheckout.ChooseShippingProvider();
});
} else if (!delSigCheck && addInsCheck) {
$.post(addInsUrl).then(function(data1) {
return ExpressCheckout.ChooseShippingProvider();
});
}
} else {
ExpressCheckout.ChooseShippingProvider();
}
}
The simplest way to sequence jQuery ajax operations is to use the built-in promises:
$.post(...).then(function(data1) {
return $.post(...);
}).then(function(data2) {
return $.post(...);
}).then(function(data3) {
// everything done here
});
Working demo that shows you the precise sequencing: http://jsfiddle.net/jfriend00/zcfr2xy0/
OK, it appears that the problem is that you're doing this:
var originalCheckout = ExpressCheckout.ChooseShippingProvider();
And, then you think that sometime later, you can just do:
originalCheckout;
and that will somehow execute the former. That's not the case. Your ExpressCheckout.ChooseShippingProvider() function is executed immediately and the return result from executing that function is assigned to originalCheckout.
You simply can't do it that way. When you have () after a function name, that means to execute it NOW. I would suggest that you just replace all instances of originalCheckout; with ExpressCheckout.ChooseShippingProvider();.
If you "chain" AJAX post operations (meaning that you do your process once receiving the data from the previous call) then nothing strange should happen.
From the symptom I'd think more to some cache-related problem. Adding an extra random value to the query is a quick'n dirty way to get rid of whoever is caching the result and responding instead of who should. Also using a POST request instead of a GET (if possible) may help on this issue and better conveys the idea that the operation is a mutation that should not be skipped or done out of order.
Note that a stale response problem could be at several levels: browser, proxy, web server, cms plugin...

Using setInterval to test for a condition and when found, clearing interval...any alternatives?

What I need is for javascript to keep checking for a condition (similar to a while loop). I am doing this by doing a setInterval and if the condition is met, I clear the interval which then exits or if it has run X amount of times, in the case I have below I have it set to 100.
I don't like this that much, I threw it together because it works. I am checking data from a server and this is sluggish. My main reason why I want an alternative is that I have a loading gif that runs when an AJAX call is started, I'll paste this code at the bottom just to show. It is running sluggish so I believe while the javascript is in the interval, it isn't able to render the gif so the moving image just stops.
Anyone know of an alternative to do what I want?
I hope this makes sense?
function checkCondition(name,action){
var timesRun = 0;
var success = false;
var interval = setInterval(function() {
timesRun++;
var container = {};
container = mapSystem["desktop"];
if(action === "delete"){
var isExisting = name in container ;
if ( !isExisting || (timesRun === 100)) {
//break out of this interval
clearInterval(interval);
renderCheckList();
success = true;
} else {
// Update mapNodeData with fresh data
updateData('desktop', false); // Just updates data
}
}
}, 2000);
}
// Code that I have to render a loading GIF. Works great but seems
// to stop during the interval
$('#loading').hide().ajaxStart(function() {
$(this).show();
}).ajaxStop(function() {
$(this).hide();
});
If you use .ajax() instead of ajaxStart, you can set a function to execute when the action has completed:
From the docs:
complete
Type: Function( jqXHR jqXHR, String textStatus )
A function
to be called when the request finishes (after success and error
callbacks are executed). The function gets passed two arguments: The
jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object and a string
categorizing the status of the request ("success", "notmodified",
"error", "timeout", "abort", or "parsererror"). As of jQuery 1.5, the
complete setting can accept an array of functions. Each function will
be called in turn. This is an Ajax Event.
You also can specify a different function for success or failure using the success and error attributes.

jQuery: Submit a form only if no AJAX is currently running on the page

When the city input field is blurred I get somnething via an ajax request and set that as the value of a hidden field in the same form that the city field resides in.
$('input#city').on('blur', function() {
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
}
});
});
If the user submits the form immediately after blurring off the city field sometimes due to latency the hidden field is not populated because the SQL on the other end is taking too long.
The form that both these fields are in is also submitted via ajax:
$('form#find-users').on('submit', function() {
if(NO_AJAX_CURRENTLY_RUNNING_ON_PAGE) {
// do stuff
}
});
How to detect if no ajax is running on the page? This will ensure that the city ajax was completed and the hidden field populated before the form is processed.
EDIT
Actually it won't, it will only prevent the form from being submitted. But if I can detect that then I can use a setInterval and keep trying to run that code until it runs because ajax is complete. Ideally there will be something in jQuery that waits until other ajax is complete and then submits.
Use jQuery's Ajax Events. As long as all of your Ajax calls are generated using jQuery, you have a way of knowing if any Ajax calls are outstanding.
$(document).ready(function() {
var ajaxBusy = false;
$(document).ajaxStart( function() {
ajaxBusy = true;
}).ajaxStop( function() {
ajaxBusy = false;
});
});
Edit:
So that answers your direct question about "How do I know if there is any Ajax call running."
Alternatively, you could disable the form's submit buttons when run your blur handler, and then re-enable it when you're done.
$('input#city').on('blur', function() {
var submit = $(this).closest('form').find(':submit:enabled');
submit.prop('disabled', true);
$.ajax('get/something?param=val').done(function(response) {
$('input:hidden[name="something"]').val(response);
}).always(function() {
submit.prop('disabled', false);
});
});
Edit 2:
So now we're at the point where we would like to delay the form submission until all current Ajax calls have completed. We let people click on the submit button, but if there are pending Ajax calls we don't do anything right away.
We can use a Deferred object to help us with this.
$(document).ready(function() {
var ajaxDefer = $.Deferred().resolve();
$(document).ajaxStart( function() {
ajaxDefer = $.Deferred();
}).ajaxStop( function() {
ajaxDefer.resolve();
});
$('form#find-users').on('submit', function() {
ajaxDefer.always(function() {
// Code here will always be executed as soon as there are no
// Ajax calls running.
// this points to the deferred object (ajaxDefer), so use the closure
// to carry over any variables you need.
});
});
});
When we're just starting out, we set up our ajaxDefer object in a resolved state. That means any functions attached using .always() will execute immediately.
When the first Ajax call starts, we replace the old ajaxDefer object with a new one that has not been resolved. Any new functions attached using ajaxDefer.always() will be deferred until later.
When the last Ajax call completes, we call ajaxDefer.resolve(), which causes any unexecuted deferred functions to execute. Now we're back to our initial state, where any newly-attached functions will execute immediately.
When somebody tries to submit the form, create an anonymous function that does the work and attach it to ajaxDefer. It will get executed when appropriate, depending on if there are any outstanding Ajax requests or not. Be mindful of your closures.
Use this to check if AJAX calls are currently in-progress using JQuery:
if ($.active == 0) {
...
}
you can put a variable in the global namespace, perhaps named ajaxLock and toggle it on when AJAX starts and off when the response comes. Then check it before allowing submit.
something like
var ajaxLock = 1;
$('input#city').on('blur', function() {
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
ajaxLock = 0;
}
});
});
Use a lock variable like you suggested:
$('input#city').on('blur', function() {
window.AJAX_CURRENTLY_RUNNING_ON_PAGE = true;
$.ajax({
url: 'get/something?param=val',
success: function(response) {
$('input:hidden[name="something"]').val(response);
},
complete: function() { window.AJAX_CURRENTLY_RUNNING_ON_PAGE = false; }
});
});
$('form#find-users').on('submit', function() {
if(window.AJAX_CURRENTLY_RUNNING_ON_PAGE) {
return;
}
//dostuff
});
What i could have done on this circumstances is to use plugin like block ui or disable the form submit button,the reason is you need to be interactive in your design,you may well able to lock the form submission,but its better to give a message or have a modal gray out

Efficient AutoSuggest with jQuery?

I'm working to build a jQuery AutoSuggest plugin, inspired by Apple's spotlight.
Here is the general code:
$(document).ready(function() {
$('#q').bind('keyup', function() {
if( $(this).val().length == 0) {
// Hide the q-suggestions box
$('#q-suggestions').fadeOut();
} else {
// Show the AJAX Spinner
$("#q").css("background-image","url(/images/ajax-loader.gif)");
$.ajax({
url: '/search/spotlight/',
data: {"q": $(this).val()},
success: function(data) {
$('#q-suggestions').fadeIn(); // Show the q-suggestions box
$('#q-suggestions').html(data); // Fill the q-suggestions box
// Hide the AJAX Spinner
$("#q").css("background-image","url(/images/icon-search.gif)");
}
});
}
});
The issue I want to solve well & elegantly, is not killing the sever. Right now the code above hits the server every time you type a key and does not wait for you to essentially finish typing. What's the best way to solve this?
A. Kill previous AJAX request?
B. Some type of AJAX caching?
C. Adding some type of delay to only submit .AJAX() when the person has stopped typing for 300ms or so?
Try using Ben Alman's Throttle & Debounce plugin
Lets you "delay" things till the user is done.
For an example on how to use it check out his example of debouncing with a pretend autocomplete
Your code would basically become
var qinput = $('#q').bind('keyup', $.debounce( 250, function() {
if( $(this).val().length == 0) {
// Hide the q-suggestions box
$('#q-suggestions').fadeOut();
} else {
// Show the AJAX Spinner
qinput.addClass('loading');
$.ajax({
url: '/search/spotlight/',
data: {"q": $(this).val()},
success: function(data) {
$('#q-suggestions')
.fadeIn() // Show the q-suggestions box
.html(data); // Fill the q-suggestions box
// Hide the AJAX Spinner
qinput.removeClass('loading').addClass('search');
}
});
}
}));
CSS
.loading{
background: url('/images/ajax-loader.gif');
}
.search{
background: url('/images/icon-search.gif');
}
You will note that I removed your background-image css and replaced them with addClass/removeClass. Much easier to manage css stuff in css files.
I'd go for a variant of C. Don't wait for users to stop typing, but wait some time (200ms?) after the first keystroke. Then after that time, you will in many cases have received additional keystrokes and then you use the typed characters to get the autosuggest. If another key is pressed after submitting the request, you start counting again.
And you should definitely do some caching too; people will use backspace and you'll have to show the name list again.
I don't know, what DB are you using OR are you using hardcoded file!?
anyway a good starting point is wait for a least a TOT NUMS of chars for
es.: after 3 ( that is a min word lenght for search mysql in most cases ) chars
then you can start to search your DB or json file!
I think you must give to PHP or others the hard job like FILTERING etc, etc..
before send back the responce!
btw i think one of the best AutoSuggest is the one from brandspankingnew
The autocomplete plugin has a timeout option you can set to do this.
http://docs.jquery.com/Plugins/AutoComplete/autocomplete

Unexpected JavaScript Actions reported by my Users

My users keep complaining that a link does not show up for them. For me, I have tested this on several browsers and it works for me.
What should happen is that a process is started via AJAX using JQuery and once that is done I keep checking with the server via AJAX how much of the process has been done and once the process is complete I show them a link. But a lot of users tell me that it shows them the link and it quickly disappears back to showing 100.0%!
I can't see how I can fix this and I was hoping you guys could help me write something fool proof so that the link is always shown!
Here is the code concerned (its been shortened).
var startTime;
var continueTime;
var done = false;
function convertNow(validURL){
startTime = setTimeout('getStatus();', 6000);
$.ajax({
type: "GET",
url: "main.php",
data: 'url=' + validURL + '&filename=' + fileNameTxt,
success: function(msg){
done = true;
$("#loading").hide("slow");
$("#done").html("LINK SHOWN HERE");
}//function
});//ajax
}//function convertNow
function getStatus()
{
if(done==false){
$.ajax({
type: "POST",
url: "fileReader.php",
data: 'textFile=' + fileNameTxt,
success: function(respomse){
textFileResponse = respomse.split(" ");
$("#done").html("PROGRESS SHOWN HERE IN PERCENTAGES");
}
});//ajax
continueTime = setTimeout('getStatus();', 3000);
}
}
Thanks all
P.S. I have this question before and was given an idea of using a conditional in the function but that didn't work when it should have!!
UPDATE
I have some of my users what OS and browsers they are using and they usually say a Mac Os and firefox or safari. Not sure if that help with the solution.
The behaviour described by the users suggests that the success callback of your getStatus function is called after the one in convertNow. You should test done variable in this callback
function getStatus(){
if(done==false){
$.ajax({
type: "POST",
url: "fileReader.php",
data: 'textFile=' + fileNameTxt,
success: function(respomse){
// FIX : Already done, just ignore this callback
if (done) return;
textFileResponse = respomse.split(" ");
$("#done").html("PROGRESS SHOWN HERE IN PERCENTAGES");
// BONUS : call getStatus only when previous ajax call is finished
continueTime = setTimeout('getStatus();', 3000);
}
});//ajax
}
}
EDIT : This solution should prevent the bug from appearing most of the time, but there is still a chance. The only way to be sure is to remove the callback from convertNow and let the one in getStatus set the link when the processing is done (don't forget to allow only one call to getStatus at a time, see "BONUS" modification above).
If done is never set back to false then the reported behavior would be expected upon the second call to convertNow.
Since the ajax call in convertNow uses GET instead of POST, it is possible that a browser is returning a cached result whenever parameters are identical to a previous call.

Categories

Resources