I want to serialize 2 events in my web application. I have change event tied to an input field and click event tied to a button. User sometimes enters value is input field and presses button immediately.
I want that change event should be called before click event.
Both of the events have AJAX calls and I have declared change event AJAX call as synchronous, but this does not help.
EDIT:
Other important details are as follows
I am using jQuery UI autocomplete feature related change event for input field.
Here is the portion of html code along with related javascript code
<!-- I have more than one input fields with class set as code -->
<input type="text" name="code_1" id="code_1" class="code" value=""/>
<input type="text" name="code_2" id="code_2" class="code" value=""/>
<input type="text" name="code_3" id="code_3" class="code" value=""/>
<!-- Here is the clickable button -->
<input type="button" value="Save" class="formButton" id="postCharge"/>
// Here is the javascript code for these items
$(".code").autocomplete({
source: jsHrefRoot+"/ajax/cpt_autocomp.php",
minLength: 3,
delay: 500, // milliseconds - 1s = 1000ms
select: function (event, ui) {
$(this).val(ui.item.id);
return false;
},
change: function (event, ui) {
// Following function has an AJAX call
getCodeDetail($(this));
return false;
}
}).each(function(){
// This part is for rendering each item and not relevant to my question
});
$( "#postCharge" ).click(function(){
// Again there is an AJAX call in this fuction after long logic of input validation
})
Here is one (async) way:
var $codeInputs = $('.code');
var $button = $('#postCharge');
function changeHandler (cb) {
$.ajax({
// ...
complete: function () {
$(this).data('changeHandled', true);
cb();
}
});
}
function clickHandler () {
$.ajax({
// ...
});
}
function checkChanges (cb) {
var $unhandled = $codeInputs.filter(function () {
var hasChanged = $(this).val() !== $(this).data('initialValue');
return hasChanged && !$(this).data('changeHandled');
});
if (!$unhandled.length) return cb();
changeHandler.call($unhandled.first().get(), checkChanges.bind(this, cb));
}
$codeInputs.each(function () {
$(this).data('initialValue', $(this).val());
})
.autocomplete({
// ...
change: function (event, ui) {
changeHandler.call(this.get(), function () {
// Ajax call complete
});
}
});
$button.click(function (e) {
e.preventDefault();
checkChanges(clickHandler.bind(this));
});
I delayed click event to wait for completion of autocomplete change.
function postCharges() {
...
}
$( "#postCharge" ).click(function(e){
e.preventDefault();
setTimeout(postCharges, 1000);
});
change event always fire before click.
var cansubmit =true;
$('text').on('change',function(){
cansubmit = false;
$.ajax({
...
complete:function(){
cansubmit = true;
}
})
}
buildPromise=function(){
new Promise(function(resolve,reject){
var timer = null;
function checkCanSubmit(){
if(cansubmit){
clearInterval(timer);
resolve();
}
}
if(cansubmit)
resolve();
else
timer = setInterval(checkCanSubmit,10);
})
}
$('btn').on('click',function(){
buildPromise().then(function(){
//todo::
})
})
Related
Suppose there is a textbox in my webpage and I have attached an 'change' event on this textbox using jQuery.
$('.myFormClass').on('change', '.amount', function () {
// Some AJAX call here, and the response is assigned to a variable
});
And I have a form submit event as well,
$('.myFormClass').on('submit', 'form', function (e) {
// some code in which I use the response from the earlier AJAX call returned against 'change' event
});
The issue is that the 'change' event works fine individually but when form submitted they are fired almost simultaneously and till the time when AJAX call against the 'change' event is returned back (supposed to be), the form had already been submitted by then so I can't use the AJAX response, which is needed before the form submission.
Is there something built-in jQuery for this situation? If no, any alternate efficient solution?
Store the ajax promise and wait for its resolve in the form submit handler
let amountReady = null;
$('.testForm').on('change', '.amount', function(ev) {
amountReady = $.ajax({
method: 'POST',
url: 'https://httpbin.org/post',
data: {
amount: $(this).val()
}
});
});
$('.testForm').on('submit', 'form', function(e) {
e.preventDefault();
if (!amountReady) return;
amountReady.then((amountAjaxResult) => {
console.log('submit now', amountAjaxResult.form.amount);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="testForm">
<form>
<input type="number" class="amount" />
<button type="submit">Submit</button>
</form>
</div>
Add a button. When the user click the button a boolean is set to tru and if it is true then only submit will happen otherwise only onchange function will work not the submit function.
var sub=false;
$(document).ready(function(){
$('button').click(function(){sub=true;console.log(sub)})
$('.myFormClass').on('change', '.amount', function () {
// Some AJAX call here, and the response is assigned to a variable
console.log('changed')
});
$('.myFormClass').keypress((e)=>{if(e.keyCode=='13'){e.preventDefault();console.log('not submitted on enter')}})
$('.myFormClass').click( function (e) {
e.preventDefault();
console.log("submit cancelled")
if(sub)
$(this).submit();
// some code in which I use the response from the earlier AJAX call returned against 'change' event
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form class="myFormClass">
aa
<input class="amount">
<input type="submit">
</form>
<button>Want to submit the form?</button>
You can use:
function method1(){
// some code
}
function method2(){
// some code
}
$.ajax({
url:method1(),
success:function(){
method2();
}
})
Or nested function after a function is done with:
function test () {
console.log('test');
}
function test2 (callback) {
console.log('test2');
callback();
}
test2(test);
Might be related
So what I am trying to do is to call a function with a callback inside a submit event. What happens is that callback functions finishes after the submit event, and I want it to be vice versa.
stripeResponseHandler = function(status, response) {;
var $form = $('#new-card-details-form');
if (response.error) {
addErrorMessage(response.error.message);
return false;
} else {
var token = response.id;
$form.append($('<input id="token" type="hidden" name="token">').val(token));
}
};
$(document).ready(function() {
$('#new-card-details-form *').removeAttr('name');
Stripe.setPublishableKey('key');
var $form = $('#new-card-details-form');
$(document).on('submit', '#new-card-details-form', function(e) {
if (!$('#token').val()) {
Stripe.card.createToken($form, stripeResponseHandler);
if (!$('#token').val()) {
e.preventDefault();
}
}
});
});
So what happens here is that form submits on second click, because on the first click when I check for token existance it doesn't yet exist because the stripeResponseHandler function will complete after the submit event. Is there a way in which I can make it work with the first click? I've tried submitting the form in the stripeResponseHandler with unbind() but that didn't work too.
How do I run a function only once? I read about jquery .one but it seems to only work with click functions. I need my audio play function to activate only once otherwise I have two audio players when the user clicks on the same link again.
How would I do this? Here is piece of code I need running once:
$(function() {
$('audio').audioPlayer();
});
and here the full code:
$('ul li:nth-child(2)').click(function(e) {;
e.preventDefault();
link1.reverse()
link2.reverse()
link3.reverse()
$('#div').uncomment( /* recurse */ true);
setTimeout(function() {
link4.play()
$(function() {
$('audio').audioPlayer();
});
picturefill({
reevaluate: true
});
},
You could simply use a variable to track whether or not it has been executed. And you don't need the $(function() {...} inside the setTimeout function as this binding is already going to be created after the DOM is ready.
E.g.
HTML:
<ul>
<li>test</li>
<li>click me</li>
<li>test</li>
</ul>
JS:
var hasPlayed = false;
$(function () {
$('ul li:nth-child(2)').click(function (e) {;
e.preventDefault();
link1.reverse();
link2.reverse();
link3.reverse();
$('#div').uncomment( /* recurse */ true);
if (!hasPlayed) {
hasPlayed = true;
setTimeout(function () {
link4.play();
$('audio').audioPlayer();
picturefill({
reevaluate: true
});
}, 1000);
}
});
});
Fiddle: http://jsfiddle.net/BenjaminRay/2eaf28jh/
Try using data attributes,
var playAudio = function() {
if ($('.audio').data('once') != true) {
$('.audio').append('Once'); // replace with $('audio').audioPlayer();
$('.audio').data('once', true)
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="audio"></div>
Click Me
You could use a guard condition. I'm not that familiar with this audioPlayer(), but if there is a way to ask "is it playing" you could do
if (!audioPlayer.isPlaying()) {
$('audio').audioPlayer();
}
Otherwise you could do something like
//Outside the click handler
var isFirstRun = true;
...
//Inside the click handler
if (isFirstRun) {
isFirstRun = false;
$('audio').audioPlayer();
}
I have two buttons, approve (visible) and undo(not visible). Clicking approve hides the button and shows the undo button (meant as a fail safe). Clicking approve sets a 20 second delay which will submit a form if realized. However I want the un-approve button to cancel the delay. How should I best do this without removing the event handler on the form entirely?
$("button.approve").on('click', function(e){
$(this).hide();
$(this).siblings('button.undo').show();
$(this).siblings("form.update-comment.approve").delay(20000).trigger('submit');
});
$("button.undo").on('click', function(e){
$(this).hide();
$(this).siblings('button.approve').show();
$(this).siblings("form.update-comment.approve").off('submit');
});
Did you see the yellow box in the documentation ?
The .delay() method is best for delaying between queued jQuery effects. Because it is limited—it doesn't, for example, offer a way to cancel the delay—.delay() is not a replacement for JavaScript's native setTimeout function, which may be more appropriate for certain use cases.
Use a setTimeout instead
var approveTimeout;
$("button.approve").on('click', function(e){
var self = $(this);
self.hide().siblings('button.undo').show();
approveTimeout = setTimeout(function(){
self.siblings("form.update-comment.approve").trigger('submit');
}, 20000);
});
$("button.undo").on('click', function(e){
$(this).hide().siblings('button.approve').show();
clearTimeout( approveTimeout );
});
You can do it with setTimeout and clearTimeout:
function onSubmit(form) {
form.submit();
};
var delayedSubmit = null;
$("button.approve").on('click', function(e){
$(this).hide();
$(this).siblings('button.undo').show();
var thisForm = $(this).siblings("form.update-comment.approve");
delayedSubmit = setTimeout(function() {
onSubmit( thisForm );
}, 2000);
});
$("button.undo").on('click', function(e){
$(this).hide();
$(this).siblings('button.approve').show();
clearTimeout( delayedSubmit );
});
Try
var form = $("form.update-comment.approve")
, approve = $("button.approve")
, undo = $("button.undo");
form.on("submit.approved", function (e) {
// do stuff
undo.trigger("click", ["reset"])
});
approve.on('click', function (e) {
approve.hide();
undo.show();
form
.delay(20000, "approval")
.queue("approval", function () {
form.trigger('submit.approved');
}).dequeue("approval");
});
undo.hide().on('click', function (e, reset) {
form
.queue("approval", []);
undo.hide();
approve.show();
console.log(reset || "cancelled");
});
See .delay() , .queue()
var form = $("form.update-comment.approve")
, approve = $("button.approve")
, undo = $("button.undo");
form.on("submit.approved", function (e) {
e.preventDefault();
e.stopPropagation();
alert(e.namespace);
undo.trigger("click", ["reset"])
});
approve.on('click', function (e) {
approve.hide();
undo.show();
form
.delay(20000, "approval")
.queue("approval", function () {
form.trigger('submit.approved');
}).dequeue("approval");
});
undo.hide().on('click', function (e, reset) {
form
// clear queue
.queue("approval", []);
undo.hide();
approve.show();
console.log(reset || "cancelled");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<form method="POST" class="update-comment approve"></form>
<button class="approve">approve</button>
<button class="undo">undo</button>
When I click a chat on my site I want the messages to be grabbed from the server so I use an $.post request like so :
$("#friendsDiv").on("click", "#aFriend", function(event){
retrieveMessages();
}
and this is what is in the retrieveMessages function
$.post("PHP/chat.php",
{
action:'retrieveMessages',
last_message: last_message,
conversation_id:conversation_id
},
function(data){
$("#messages").append(data);
last_message = $("#messages").find(".aMessage:last").attr("id");
$("#messages").animate({ scrollTop: $("#messages")[0].scrollHeight}, 1000);
}
);
The issue is that if the button is clicked very quickly multiple post requests will begin before the last_message is updated, this results in many copies of the same messages being displayed. Is there a way to prevent the button being clicked quickly or stop the post request being processed if another of the same request is already being processed?
EDIT
The #aFreind element is a DIV not a button
Typically in such situation you just disable a button until request is complete. For this you will need to provide a callback function. For example:
$("#friendsDiv").on("click", "#aFriend", function (event) {
// reference the button
var button = this;
// disable the button
this.disabled = true;
// provide a callback to be invoked when post is done
retrieveMessages(function() {
button.disabled = false;
});
});
function retrieveMessages(callback) {
$.post("PHP/chat.php", {
action: 'retrieveMessages',
last_message: last_message,
conversation_id: conversation_id
}, function (data) {
$("#messages").append(data);
last_message = $("#messages").find(".aMessage:last").attr("id");
$("#messages").animate({
scrollTop: $("#messages")[0].scrollHeight
}, 1000);
// execute callback which enables button again
callback();
});
}
Demo: http://jsfiddle.net/9t8fLdjn/
Your best bet would be to disable the button and then enable it after $.post
$("#friendsDiv").on("click", "#aFriend", function(event) {
$(this).prop('disabled', true); // disable
retrieveMessages();
});
and the retrieveMessage function
$.post("PHP/chat.php", {
action: 'retrieveMessages',
last_message: last_message,
conversation_id: conversation_id
}, function(data) {
$("#messages").append(data);
last_message = $("#messages").find(".aMessage:last").attr("id");
$("#messages").animate({
scrollTop: $("#messages")[0].scrollHeight
}, 1000);
$(this).prop('disabled', false); // enable it again
});
Instead of using on you could use the one jQuery function and bind the button again in the callback. Se http://api.jquery.com/one/
$("#friendsDiv").one("click", "#aFriend", retrieveMessages });
var retrieveMessages = function(){
$.post("PHP/chat.php", {
...
}).done(function(){
$("#friendsDiv").one("click", "#aFriend", retrieveMessages });
});
};