I need to fire a function on completion of an already running ajax on the page. The function I want to update will update wishlist item counter and the function which is previously running saves item in wishlist.
The problem is (what I've figured out) - after making (initializing) ajax request, while waiting for success msg, the function again executes itself.
Bottom line, I want ajaxComplete function part to run only once ever. Please point me in right direction
jQuery(document).ready( function() {
var token = false;
jQuery( '.add_to_wishlist' ).on( 'click', function() {
if( token == false ) {
jQuery(document).ajaxComplete(function() {
console.log('Entered Click!');
token = true;
jQuery.ajax({
url: wishajax.ajax_url,
data: {
action: 'vg_inject_wish',
},
success: function( response ) {
console.log('Entered Success!');
jQuery( '.wishlist-container' ).html( response );
console.log('After Success!');
token = true;
}
});
});
}
});
});
jQuery(document).ajaxComplete is an independent event. Why are you combining it inside click event and writing again jQuery.ajax inside it? Separate the concerns as below:
jQuery(document).ajaxComplete(function() {
token = true;//If you need this only in success then no need to put it here as this
//will get executed irrespective of ajax result
});
jQuery( '.add_to_wishlist' ).on( 'click', function() {
if( token == false ) {
jQuery.ajax({
url: wishajax.ajax_url,
data: {
action: 'vg_inject_wish',
},
success: function( response ) {
console.log('Entered Success!');
jQuery( '.wishlist-container' ).html( response );
console.log('After Success!');
token = true;
}
});
}
});
maybe this can help you to
function once(fn, context) {
var result;
return function() {
if(fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
// Usage
var canOnlyFireOnce = once(function() {
console.log('Fired!');
});
canOnlyFireOnce(); // "Fired!"
canOnlyFireOnce(); // nada
taken from the blog of David Walsh.
Related
Attached Event handler to callback like :
$("someSelector").on('click',callBackHandler);
function callBackHandler(){
//Some code
$.ajax({
//Ajax call with success methods
})
}
My success method is manipulating some object properties. Since ajax is involved, it will not wait for the completion and next event handling will start. How can I make sure next click event handling starts only when previous handling is done.
Cannot think of a way of using deferred manually on this because I am triggering event manually on base of some condition in for loop (Not a clean style of coding, but has no other option in particular use case).
$('someSelector').trigger('click');
$("someSelector").on('click', function(event) {
event.preventDefault();
var urAjax = $.ajax({
// Ajax Call here...
});
urAjax.always(function(response) {
$.when( callBackHandler() ).done(function() {
// Handle your ajax response in here!
});
});
});
function callBackHandler() {
// Do Stuff
}
callBackHandler function will fire, and when it's done, your ajax response for .always will fire directly after that. This allows for your ajax to load while the callBackHandler function is running also, but doesn't fire the response until after the function is done! Hopefully I'm understanding what you are asking for here.
You can see an example jsfiddle located here: https://jsfiddle.net/e39oyk8q/11/
Try clicking the submit button multiple times before the AJAX request is finished, you will notice that it will loop over and over again the total amount of clicks you give it on the Submit button. You can see this by the amount of times the Alert box pops up, and also, it adds 100 to the len (that gets outputted on the page) during each call to the callBackHandler function. So, I do believe this is what you asked for.
And, ofcourse, you can still use: $('someSelector').trigger('click');
EDIT
Another approach is to return a json object that can be used within the ajax call or wherever you need it within the click event, like so:
$("someSelector").on('click', function(event) {
event.preventDefault();
var myfunc = callBackHandler();
var urAjax = $.ajax({
type: 'POST',
url: myfunc['url'],
data: myfunc['data']
});
urAjax.always(function(response) {
$.when( myfunc ).done(function() {
console.log(myfunc['time']);
// Handle your ajax response in here!
});
});
});
function callBackHandler() {
var timestamp = new Date().getTime();
return { url: 'my_ajax_post_url', data: {data1: 'testing', data2: 'testing2'}, time: timestamp }
}
fiddle example here: https://jsfiddle.net/e39oyk8q/15/
You can remove the binding on the call of function and bind again when the ajax is done().
function callBackHandler(){
$("someSelector").off('click');
//Some code
$.ajax({
//Ajax call with success methods
}).done(function(){
$("someSelector").on('click',callBackHandler);
})
}
var requestDataButton = document.querySelector('.js-request-data');
var displayDataBox = document.querySelector('.js-display-data');
var displayOperationsBox = document.querySelector('.js-display-operations');
var displayNumberOfRequestsToDo = document.querySelector('.js-display-number-of-requests-to-do');
var isAjaxCallInProgress = false;
var numberOfAjaxRequestsToDo = 0;
var requestUrl = 'http://jsonplaceholder.typicode.com/photos';
requestDataButton.addEventListener('click', handleClick);
function handleClick() {
displayNumberOfRequestsToDo.innerText = numberOfAjaxRequestsToDo;
numberOfAjaxRequestsToDo++;
if(!isAjaxCallInProgress) {
isAjaxCallInProgress = true;
requestData();
}
}
function handleResponse(data) {
displayData(data);
displayOperationsBox.innerHTML = displayOperationsBox.innerHTML + 'request handled <br>';
numberOfAjaxRequestsToDo--;
displayNumberOfRequestsToDo.innerText = numberOfAjaxRequestsToDo;
if(numberOfAjaxRequestsToDo) {
requestData();
} else {
isAjaxCallInProgress = false;
}
}
function displayData(data) {
displayDataBox.textContent = displayDataBox.textContent + JSON.stringify(data[0]);
}
function requestData() {
var request = new XMLHttpRequest();
request.open('GET', requestUrl, true);
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
var data = JSON.parse(this.response);
handleResponse(data);
} else {
// on error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
displayOperationsBox.innerHTML = displayOperationsBox.innerHTML + 'request sent <br>';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="js-request-data">Request Data</button>
<div>
<h2>Requests wainting in a queue to be sent:
<span class="js-display-number-of-requests-to-do">
0
</span>
</h2>
</div>
<div>
<h2>Operations:</h2>
<div class="js-display-operations"></div>
</div>
<div>
<h2>Response Data:</h2>
<div class="js-display-data"></div>
</div>
edit
here's a utility function that takes similar arguments as $.ajax, and then returns an augmented $.ajax function that keeps an eye on its previous calls and waits for all preceding requests to finish before dispatching another ajax call (are fired sequentially):
function sequentialize(requestData, responseHandler) {
let inProgress = false;
let deferredRequests = 0;
const makeRequest = () => {
$.ajax(requestData).then(processManager);
};
const processManager = (data) => {
responseHandler(data);
if (deferredRequests) {
deferredRequests--;
makeRequest();
} else {
inProgress = false;
}
};
return function () {
if (!inProgress) {
inProgress = true;
makeRequest();
} else {
deferredRequests++;
}
};
}
I've done a web page that has to make the use wait a loooong time before getting the answer.
When the user clicks on "Generate" (complex stuff), i do a slow slideUp() of the main div and immediately after that, I launch my "background" AJAX call:
$('#div-lol-generate-result').slideUp(4000);
$('#div-lol-generate-form').slideUp(3000);
$.ajax({
url: '/long/api/call/that/takes/between/1/and/10/seconds',
data: data,
dataType: 'json',
method: 'POST'
})
.done(function(result) {
console.log('ok :');
console.log(result);
var monp=$('<p />');
if (typeof(result.error)!='undefined') {
for (var i in result.error) {
monp.append(result.error[i]);
monp.append('<br />');
}
} else if (typeof(result.story)!='undefined') {
console.log(result.story.length);
for (var i in result.story) {
monp.append(result.story[i]);
monp.append('<br />');
}
}
monp.last().remove();
$('#div-lol-generate-result').empty().append(monp).slideDown();
});
})
.error(function(result) {
console.log('Erreur :');
console.log(result);
})".
Everything works fine... only when the answer takes longer than the "hide" animation. If the answer is fast, the we can see the content of the maindiv being replaced.
How do you deal with that?
You make sure both the animation and the ajax call has completed before you replace the content
var promise1 = $('#maindiv').slideUp(4000).promise();
var promise2 = $.ajax({
url : '/complexstuff',
data : data,
dataType : 'json',
method : 'POST'
});
$.when.apply($, [promise1, promise2]).done(function(elem, data) {
$('#maindiv').html(data.result).slideDown();
});
This way the ajax call starts right away without having to wait for a callback, and the promises makes sure both have completed before the callback for $.when is called.
Here's my final working code, thanks to adeneo
$(document).ready(function() {
$('#div-lol-generate-result').hide();
var sub=function() { return lol_submit(); };
var lol_submit = function() {
var data=$('#lol-generate-form').serialize();
$('#lol-generate-form :input')
.prop('disabled', 'disabled');
$('#lol-generate')
.unbind('click')
.click(function(e) {
e.preventDefault();
});
$.when(
$('#div-lol-generate-result').slideUp(4000),
$('#div-lol-generate-form').slideUp(3000),
$.ajax({
url: '/lol/json/story',
data: data,
dataType: 'json',
method: 'POST'
})
)
.then(function(a, b, c) {
result=c[0];
var monp=$('<p />');
if (typeof(result.error)!='undefined') {
for (var i in result.error) {
monp.append(result.error[i]);
monp.append('<br />');
}
}
else if (typeof(result.story)!='undefined') {
console.log(result.story.length);
for (var i in result.story) {
monp.append(result.story[i]);
monp.append('<br />');
}
}
monp.last().remove();
$('#div-lol-generate-result')
.empty()
.append(monp)
.slideDown();
}, function(a, b, c) {
/* should never happen
* TODO: hide and all ask refresh
*/
// a=xhr
// b='failure'
// c='Not Found'
})
.always(function(result) {
$('#lol-generate-form :input').removeAttr('disabled');
$('#lol-generate').click(sub);
$('#lol-generate-form-input-summoner-name').focus().select();
$('#div-lol-generate-form').slideDown();
});
return false;
}
$('#lol-generate-form').submit(sub);
$('#lol-generate').click(sub);
});
Im'm loading posts in a div #reviewspostscont, the AJAX code works and the posts are loaded when the scrollbar gets to the end but I can't reinitialise JScrollPane to show them.
I tried different codes but nothing works, this is what i have so far.
Thanks in advance, Matt
jQuery(document).ready(function($) {
$(function()
{
$('#reviewspostscont').each(
function()
{
$(this).jScrollPane(
{
horizontalDragMaxWidth : 100
}
);
var api = $(this).data('jsp');
var throttleTimeout;
$(window).bind(
'resize',
function()
{
if (!throttleTimeout) {
throttleTimeout = setTimeout(
function()
{
api.reinitialise();
throttleTimeout = null;
},
50
);
}
}
);
$(this).bind(
'jsp-scroll-x',
function(event, scrollPositionX, isAtLeft, isAtRight)
{
var count = 2;
if (isAtRight == true) {
loadArticle(count);
var api = $(this).data('jsp');
api.reinitialise();
count++;
}
}
);
}
)
});
function loadArticle(pageNumber){
$.ajax({
url: "<?php bloginfo('wpurl') ?>/wp-admin/admin-ajax.php",
type:'POST',
data: "action=infinite_scroll&page_no="+ pageNumber + '&loop_file=loop-reviews',
success: function(html){
$("#reviewspostscont").append(html); // This will be the div where our content will be loaded
}
});
return false;
}
});
You can use autoReinitialise. Here's a demo showing a similar example. And another one. And another.
I have a js which is showing error in javascript console Cannot call method 'appendChild' of null in this file:
(function(d) {
if( typeof jQuery === 'undefined' ) {
var srsr = d.createElement('script');
srsr.src = '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
d.body.appendChild(srsr);
setTimeout(function(){ doThat(); }, 5000);
} else {
doThat();
}
function getHost( url ) {
var a = document.createElement('a');
a.href = url;
var host = a.hostname;
var t = host.split(".");
if( t.length == 2 ) {
var host = "www."+host;
}
return host;
}
function doThat() {
$.ajax({
url: 'http://mobilesplashpro.com/app/assets/php/tRaCk_loAdInG_PoPUp.php',
type: 'GET',
data: 'adrs='+getHost( document.domain ),
dataType: 'jsonp',
jsonp: false,
jsonpCallback: 'methodCallback',
success: function( data ) {
if( data.message == "yes" ) {
$.getScript("http://mobilesplashpro.com/app/assets/js/popup/PoPUp_txt_CoDE.js",function() { iPhoneAlert(); });
} else {
}
},
error: function( error ) {
console.log( error );
}
});
}
})( document );
have used Appendchild code in another file which is working perfectly but not in it..
Thanks
First of all, place this code in the end of your body (if this is in your head, the body doest not exists), if you must place it in the head, you should make some sort of interval untill you simulated a DomReady event (That garantees you have a document.body to traverse).
Example:
var intervalDomReady = setInterval(function() {
if (document.body) {
clearInterval(intervalDomReady);
intervalDomReady =null;
DomReady();
}
},500);
function DomReady() { /*you code*/ }
You only have the reference to jquery from google CDN once it is loaded. So you will not have sure that It will happen in 5 secs, must first bind to the onload event of the script an then try to use jquery functions.
Example of solution:
var d = document;
var srsr = d.createElement('script');
srsr.src = '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
srsr.src.onload = function() { doThat(); }
Because this is running when the document loads, it's a valid use-case for document.write.
It'll also simplify things if you use document.write to load the script, since it'll load the script synchronously.
As a result, you would eliminate the need for the doThat() function, and the setTimeout.
So first put your jQuery check and script loading code in a separate script at the top...
<script type="text/javascript">
if( typeof jQuery === 'undefined' ) {
document.write('<scr' + 'ipt src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"><\/scr' + 'ipt>');
}
</script>
Then put your main script in a separate script below the one above...
<script>
(function(d) {
function getHost( url ) {
var a = document.createElement('a');
a.href = url;
var host = a.hostname;
var t = host.split(".");
if( t.length == 2 ) {
var host = "www."+host;
}
return host;
}
$.ajax({
url: 'http://mobilesplashpro.com/app/assets/php/tRaCk_loAdInG_PoPUp.php',
type: 'GET',
data: 'adrs='+getHost( document.domain ),
dataType: 'jsonp',
jsonp: false,
jsonpCallback: 'methodCallback',
success: function( data ) {
if( data.message == "yes" ) {
$.getScript("http://mobilesplashpro.com/app/assets/js/popup/PoPUp_txt_CoDE.js",function() { iPhoneAlert(); });
} else {
}
},
error: function( error ) {
console.log( error );
}
});
})( document );
</script>
Now if jQuery isn't loaded, the new script will be written and loaded below the first, and above the second, and we never had to perform any sort of DOM selection.
I want after click on link show alert box with tow option ok and cancel, if user click on button ok return it function is true and if click on button cancel return it function is false, problem is here that after click on link always return is true. How can fix it?
Example: http://jsfiddle.net/MaGyp/
function myalert() {
var result = true;
var $alertDiv = $('<div class="alert">Do you want to delete this item?<button class="ok">ok</button><button class="cancel">cancel</button></div>');
$('body').append($alertDiv);
$('.ok').click(function () {
$('.alert').fadeOut(100);
$('#appriseOverlay').css('display', 'none');
result = true;
});
$('.cancel').click(function () {
$('.alert').fadeOut(100);
$('#appriseOverlay').css('display', 'none');
result = false;
});
$alertDiv.fadeIn(100);
$('#appriseOverlay').css('display', 'block');
return result;
};
$('.iu').click(function () {
alert(myalert());
if (myalert() == true) {
alert('ok')
} else {
alert('no')
}
});
Update:
...
$('.iu').click(myalert)
function callback(result) {
//
if(result){
alert(result);
$('.image_upbg').each(function(){$(this).removeClass().addClass(unique())});
var get_class = '.'+$(this).closest('div').attr('class');
var get_val = $(this).closest('a').find('input').attr('value');
//alert(get_val);
var val = 'val_upimg1=' + get_val;
$(get_class).fadeOut('slow');
$.ajax({
type: "POST",
dataType: 'json',
url: 'delete_upimg',
data: val,
cache: false,
success: function (data) {
$(get_class).fadeOut('slow');
},
"error": function (x, y, z) {
// callback to run if an error occurs
alert("An error has occured:\n" + x + "\n" + y + "\n" + z);
}
});
}else{
alert('no')
}
}
If you want to keep it structured like this, you could use a callback after the user responds.
http://jsfiddle.net/MaGyp/2/
function myalert() {
...do stuff here
$('.ok').click(function () {
callback(true); // callback when user clicks ok
});
$('.cancel').click(function () {
callback(false); // callback when user clicks cancel
});
}
$('.iu').click(myalert);
function callback(result) {
alert(result);
}
As suggested by Ben you could improve this by making the callback function a parameter to the first function to remove the tight coupling.
myalert() returns before result is set to true or false. To fix it I suggest having myalert() take a callback function as a parameter, and calling it inside the click() handlers within myalert(). The .iu event handler will then need to be split into two functions, one of which is the callback passed into myalert().
you are not waiting for the ok and cancel clicks so would always return true.
Modified the fiddle - http://jsfiddle.net/MaGyp/3/
function myalert() {
var result = true;
//var hide = $('.alert').fadeOut(100);
//var css = $('#appriseOverlay').css('display','none');
var $alertDiv = $('<div class="alert">Do you want to delete this item?<button class="ok">ok</button><button class="cancel">cancel</button></div>');
$('body').append($alertDiv);
$alertDiv.fadeIn(100);
$('#appriseOverlay').css('display','block');
return result;
};
$(document).ready(function(){
$('.ok').live('click',function () {
$('.alert').fadeOut(100);
$('#appriseOverlay').css('display','none');
alert('ok');
});
$('.cancel').live('click',function () {
$('.alert').fadeOut(100);
$('#appriseOverlay').css('display','none');
alert('cancel');
});
$('.iu').click(function() {
myalert();
});
})