How to avoid conflict between 2 jQuery "on form submit" scripts? - javascript

What I want to achieve is to track form submits.
But because of the many variations that we use for the submit button I want to change my current code:
$(document).on('click','input[type="submit"], button[type="submit"]',function(){
to something that is universal. And I believe the best approach is the $("form")-annotation.
The problem is that for example if a form has an ajax script on it, it gets blocked by my additional script code.
Basically what I want to achieve is to have both worlds.
So the first one is what the website currently has (not every websites though):
$("form").submit(function () {
// ....do something, maybe ajax submit of the form.....
});
and my additional that I want to add without editing any current scripts already found in the website:
$("form").submit(function () {
$.getJSON("....");
});
The solution for me should be that the second script (the additional) will not interfere with any other form scripts.
AN IDEA
To add a class by using jQuery addClass to the forms of current page.
What is a solution for this?

I created a little Snippet to demonstrate the issue:
$(document).ready(function() {
// Registering form-submission as the first would be a possibility
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My first callback is executing');
// Do some stuff here, but don't mess with the event-object
// (like preventing default or stopping the event-chain)
});
// Then afterwards everything else that *might* catch the event
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My second callback is executing');
// Usually some Ajax-Handler-Callback, that handles sending the form,
// will preventDefault() and stopImmediatePropagation() - that is why
// your general first listener must be registered before any of these callbacks
console.warn('Second callback stops the event from bubbling/propagating');
e.stopImmediatePropagation();
e.preventDefault();
});
// This will never happen
$('form').on('submit', function(e) {
console.log(e.target);
console.info('My third callback will never execute');
});
// Using a delegated event-listener with `useCapture` lets this execute first
document.addEventListener('submit', function(e) {
console.info('Capturing the event natively');
}, true);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>My Website with a multi-handled form</h1>
<form class="" action="" method="post">
<input type="text" name="test" value="">
<button type="submit" name="button">Send</button>
</form>
Output of the Snippet, when submitting the form:
Capturing the event natively
<form class action method="post">…</form>
My first callback is executing
<form class action method="post">…</form>
My second callback is executing
Second callback stops the event from bubbling/propagating
What did just happened?
By pressing the submit-button, our form emits the submit-event. The Browser starts with the event-propagation in a specified event-order. There are two phases of event-propagation: event-capturing and event-bubbling.
Now our first called event-listener is the one with the useCapture-directive.
This is during the capture-phase of the event-propagation.
Explanation for useCapture taken from MDN:
capture: A Boolean that indicates that events of this type will be
dispatched to the registered listener before being dispatched to any
EventTarget beneath it in the DOM tree.
When done, the Browser starts with the bubbling-phase of the event-propagation.
This is where all $('element').on() and element.addEventListener() (without the useCapture option) registered listeners are called in their appearing order.
During this phase our second listener is not only preventing default (not submitting the form the standard-way), but also stopping the event-propagation by calling e.stopImmediatePropagation().
After that the event-chain/event-propagation stops immediately.
That is why our third listener will never execute.
On a side note: When using jQuery and exiting an event-callback with
return false, jQuery will execute e.preventDefault() and
e.stopPropagation() automatically.
See: http://api.jquery.com/on/
Conclusion
You basically have two possibilities for your scenario:
Register your default general event-listener before anything else (first event-registration in Snippet).
Register an event-listener during the capture-phase, to capture the event and handle things before the other listeners from the bubbling-phase get called (last event-registration in Snippet).
With both methods you should be able to do your stuff without interfering with other event-listeners.

Use this:
$(document).on("submit", "form", function (e){
Complete example:
<form id="form1"><input type="text" /><input type="submit" /></form>
<form id="form2"><input type="text" /><input type="submit" /></form>
Js:
$(document).on("submit", "form", function (e) {
var oForm = $(this);
var formId = oForm.attr("id");
var firstValue = oForm.find("input").first().val();
alert("Form '" + formId + " is being submitted, value of first input is: " + firstValue);
return false;
})
[JS fiddle]: http://jsfiddle.net/pZ3Jn/

What I want to achieve is to track form submits.
Why not just use $(document).on('submit', 'form', function(){});?
It will be triggered on every form submit, no matter how it is being submitted.
$(document).ready(function() {
// Some already existing event handler
$('#bir').on('submit', function(e) {
console.log($(this).attr('id'));
e.preventDefault();
});
// Your universal form event handler
$(document).on('submit', 'form', function(e) {
console.log('Additional functionality for: ' + $(this).attr('id'));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="bir">
<input type="submit" />
</form>
<form id="ikki">
<input type="submit" />
</form>

I've ran into this issue a few times before and my solution was to capture all form nodes and associate them with a special action . This may not be practical but is a possible solution for you also .
Example
//Getting all form elements
var formNodes = document.getElementsByTagName('form');
//loop through nodelist and add submit event and special class to each.
for(var i = 0; i < formNodes.length; i++){
formNodes[i].addEventListener('submit' , registerAction)
formNodes[i].className += "form-" + i;
}
/*This function captures the submitted form and determines
the action to carry out based off class name .
e.preventDefault will stop page from reloading in case of
making ajax requests.
*/
function registerAction(e){
e.preventDefault();
var formTarget = $(e.target).attr('class');
switch(formTarget){
case "form-0" :
// Do something ...
break;
case "form-1" :
// Do something else...
break;
default:
break;
}
return false;
}
Keep in mind that the logic inside registerAction can be alter to fit your needs
in this situation I used "case statement" because I feel it makes the most sense .
This is not perfect but I hope it gives you an idea..

The problem is that for example if a form has an ajax script on it, it
gets blocked by my additional script code.
No, it doesn't. You can bind many handlers on one element.
For rare cases, see the other suggestions, but If I got you right, your basic assumption was that binding a handler on an element cancel the previous one. Well, it doesn't.

Related

How are jQuery event handlers queued and executed?

I have an input form, with a submit button. I don't want the user to be able to double click the submit button and double submit the form...
So I have added the following jQuery to my Form:
var prevSubmitTime = new Date('2000-01-01');
function preventFromBeingDoubleSubmitted() {
$('form').each(function () {
$(this).submit(function (e) {
if ($("form").valid()) {
var curSubmitTime = new Date($.now());
// prevent the second submit if it is within 2 seconds of the first submit
if (curSubmitTime - prevSubmitTime < 2000) {
e.preventDefault();
}
prevSubmitTime = new Date($.now());
}
});
});
}
$(document).ready(function () {
preventFromBeingDoubleSubmitted();
});
The above code stores the submit time and prevents the second submit, if it is too early (less than 2 seconds), I don't want to permanently disable the submit button, in case there is a server side error...
This code does what I want, but when debugging the code, I can never hit a break point on e.preventDefault();... even if I double click the submit button.
It looks like the second submit event is waiting for the first submit event to complete before firing.
But, if I remove preventFromBeingDoubleSubmitted() function, then I would be able to double submit the form, by double clicking the submit button.
Can anyone explain why sometimes the submit events are fired immediately one after the other... and sometimes it is not the case? Does putting the event handler inside .each(), affects their execution behavior?
Form's when submited by default navigate to the set action url. In the case it isn't explicitly set the url is the current page. The first submit call is going end up starting the navigation process. During this the currently loaded javascript code gets unloaded. This includes event handlers. Hence why you get the inconsistency of being able to double submit or not. If the network request, and other page processes, to the action url happens faster than the speed it takes you to click again the event handlers and your break point won't be called/reached again because they are already unloaded. And vise versa if the network request is slower you would be able to cause the handler to be called and the break point to be reached (if it hasnt already been unloaded).
You say you don't want to permanently disable the submit button, but even if you disable it the form submission is going to cause a page change, and in your example's case this will just load the same page with a new submit button which will not be disabled anymore because its a new page. Thus it is never really permanetly disabeled in the first place.
Now if your real form isn't actually doing a normal form submit, and you are using something like an ajax request, web socket connection, etc then you would set the button to disabled(or set a busy flag) before the request and unset it in the ajax request callback, web socket event,etc.
For example:
jQuery('form').on('submit',function(e){
e.preventDefault();
var fd = new FormData(this);
jQuery('yourbutton').prop('disabled',true);
fetch('url',{method:"post",body:fd}).then(()=>jQuery('yourbutton').prop('disabled',false));
});
In your snippet I've added a few logs that might be helpful. As you are asking more than one question, I'll answer one by one.
var prevSubmitTime = new Date('2000-01-01');
function preventFromBeingDoubleSubmitted() {
$('form').each(function () {
$(this).submit(function (e) {
console.log('I am going to submit form');
if ($("form").valid()) {
var curSubmitTime = new Date($.now());
console.log('I am a valid form')
// prevent the second submit if it is within 2 seconds of the first submit
if (curSubmitTime - prevSubmitTime < 2000) {
console.log('A small time difference. So I am stopping')
e.preventDefault();
}
prevSubmitTime = new Date($.now());
}
});
});
}
$(document).ready(function () {
preventFromBeingDoubleSubmitted();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>
<form id="myform">
<input type="text" name="q" required="required" />
<br />
<input type="text" name="bar" required="required" />
<br />
<br />
<input type="submit" />
</form>
Can anyone explain why sometimes the submit events are fired
immediately one after the other... and sometimes it is not the case?
I think you've answered this question yourself. You are adding the code to check if there a difference between time you clicked the submit button the first time versus the second time. If the time difference exists, then you stop the second form submit.
I can never hit a break point on e.preventDefault();
The reason you're not able to get the console is, you're redirecting away from that page when you click the submit button. So the console is cleared. If you want to see the console, use an ajax function to submit the form. And on return, you can probably redirect the page somewhere.
Does putting the event handler inside .each(), affects their execution
behavior?
No. It is just an iterator. It will not affect the submit functionality.
I've added a link to the jsfiddle. Adding the alert before preventDefault will stop page from redirecting momentarily. This will prove that the execution happened.
http://jsfiddle.net/2vugwyfe/
You solution is way too overcomplicated. The easiest way to prevent a double submit would be to disable the submit button on submission.
Example:
var submittable = false;
$('form').submit(function (e) {
if (!submittable) {
e.preventDefault();
var $this = $(this);
var $submitButton = $this.find('button[type="submit"]');
$submitButton.attr('disabled', true);
if (CONDITION_SATISFIED) {
submittable = true;
$this.submit()
} else {
$submitButton.attr('disabled', false);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<button type="submit">Submit</button>
</form>
If you add e.preventDefault(); just before doing $("form").valid(), you will see there's an error thrown.
script.js:7 Uncaught TypeError: $(...).valid is not a function
at HTMLFormElement.<anonymous> (script.js:7)
at HTMLFormElement.dispatch (jquery.min.js:2)
at HTMLFormElement.y.handle (jquery.min.js:2)
This error wasn't visible at first because the submit actually changes the page (refreshes the page in this case) if nothing else is implemented.
However, in general the practice is navigating to another page after a form submission.
If you still want to go with your approach and limit the number of submitting, I suggest keeping the submitted state in a local variable and change it according to the validation on the server side.
Last thing.. I don't understand the iteration through the forms since you have only one in your HTML -> $('form').each is useless.
I know what you want, but you made it very complicated. instead of inserting a submit button just add a simple div and add a click handler on that.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myform">
<input type="text" name="myInput" />
<div id="submit" onclick="myform_submit()" />
</form>
and :
function myform_submit() {
if ($('#submit').hasClass('busy')) { return; }
$('#submit').addClass('busy');
// do the tasks then remove the `busy` class :
$('#submit').removeClass('busy');
}
I just show the idea, you can do better.

jQuery one function not working as expected

I have the following jsp:
...
<script type="text/javascript">
$(function() {
// prevent multiple submissions
$('#saveCallListBtn').one("click", function() {
$('#callListForm').submit();
});
});
...
</script>
...
<form:form id="callListForm" commandName="callList" action="${contextPath}/calllist/save" method="POST" htmlEscape="true">
...
<td colspan="2" style="text-align: center">
<input id="saveCallListBtn" type="submit" value="Save" class="button-med"/>
</td>
...
</form:form>
The behavior I am looking for is to only all the form to be submitted once no matter how many times the save button is clicked. Using the jQuery .one function, I can get the above code to correctly work. As the form will submit multiple times if I click more than once.
The following code will work fine:
$('#saveCallListBtn').on("click", function() {
$(this).prop("disabled", true);
$('#callListForm').submit();
});
But I am interested to know what I am doing wrong with the .one function.
Note the type here:
<input id="saveCallListBtn" type="submit" value="Save" class="button-med"/>
A submit button in a form will submit the form, no JavaScript required. So when your handler is automatically removed, on the next click the default handling (submitting the form) occurs, courtesy of the browser.
The only reason you're not seeing the form submitted twice on first click, I suspect, is that the act of submitting the form begins the process of tearing down the page to make room for the result of the submission.
FWIW, I would suggest that you not have a click handler on the button, but rather a submit handler on the form that, if all is well and it's going to allow submission to occur, disables the button and sets a flag to prevent future form submission, since forms can be submitted in multiple ways. (On some forms, pressing Enter in a text field will do it, for instance.)
E.g.:
$("#callListForm").on("submit", function(e) {
var $btn = $("#saveCallListBtn");
var valid = !$btn.prop("disabled");
if (valid) {
// ...do any other validity checks you may want, set `valid` to false
// if problems encountered...
}
if (valid) {
$btn.prop("disabled", true);
} else {
e.preventDefault();
}
});
The jQuery one function will execute the event handler only once. However, the default behaviour of the element clicked will execute indefinitely.
Change the type of the button to button, such that it has no default behaviour:
<input id="saveCallListBtn" type="button" value="Save" class="button-med"/>

How to prevent submitting form based on text in an html element?

I have a form in that I have User Id availability check. So if Id is already in DB it will show a message "Id is already in use". In that case I have to avoid submitting the form. For that my html is as follow,
<div>
<label><strong>Teacher Id:</strong></label>
<input type="text" name="teacherId" id="teacherId" placeholder="Enter Teacher Id" >
</div><span class="status" id="status"></span>
Here span will have the text about availability,
The value to span comes form jquery post call,
$.post('<%=request.getContextPath()%>/controller/TeacherIdCheckController',
{'teacherId':teacherId},
function(data)
{
$('.status').html(data);
});
}
This works fine, to prevent submitting I wrote javascript function as,
function checkTeacherId(){
alert(" in checkTecherId()");
var status=$("#status").text();
alert(status);
if(status=="Id in use try another")
preventDefault();
else
return true;
}
Everything works fine but this javascript function is not working fine so I cant able to prevent submit in case of Id already exist in DB. So please anyone help me in this.
Just because you need to pass the event in the function's arg:
function checkTeacherId(e){ // <---pass the event here
.....
if(status=="Id in use try another")
e.preventDefault(); // and stop it here using dot notation
else
return true;
}
As per your comment you can pass the event to your function in your onclick handler:
onclick="checkTeacherId(event);"
Fiddle
Okay! As #Sanjeev tried commenting on best approach for this work then as you are using jQuery then you can just do this as per best approach like Unobrusive Javascript (removing this inliner scripts just like above posted):
function checkTeacherId(e){ // <---pass the event here
.....
if(status=="Id in use try another")
e.preventDefault(); // and stop it here using dot notation
else
return true;
}
$(function(){
$('#yourformid').on('submit', function(e){
checkTeacherId(e);
});
});
Use this approach if you want to externalize your scripts as declare the function in global scope and put your event handler in doc ready with submit event.
Updated fiddle with unobtrusive way.
Solution as per best practice for form validation:
You have implemented form submit via Submit button and not through js like document.getElementById("myForm").submit();
I don't see any point in using onclick handler on submit button for validation, use the native onsubmit Event Attribute, else you will keep on breaking submit flow.
onsubmit is made for validating form and stopping form submission if validation fails.
This will work sure shot in all browsers and is the correct approach for form validation
Example:
<form action="demo_form.asp" onsubmit="return checkTeacherId()">
function checkTeacherId(){
var status=$("#status").text();
if(status==="Id in use try another"){
return false
}
else{
return true;
}
}

Preventing a form submit works... too well

I have a form that I don't want to be submitted the first time submit is clicked, but the second time it should work like a normal submit. So I added a not-submittable class to the form on load, then after the first click remove that class... which should (I think) make it submit normally. But, this doesn't happen. The first click works as expected, removes the class and changes the button text. The second click, however, does the exact same thing. So, what am I missing here?
jQuery:
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').addClass('not-submittable');
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE.not-submittable').click(function(event) {
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').removeClass('not-submittable');
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').val('Continue');
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').removeAttr('disabled');
return false;
});
Pre-javascript button:
<input type="submit" class="Button" value="Submit Survey" id="ACTION_SUBMIT_SURVEY_RESPONSE" name="ACTION_SUBMIT_SURVEY_RESPONSE">
Quote OP: "I have a form that I don't want to be submitted the first
time submit is clicked, but the second time it should work like a
normal submit."
Use jQuery .one() to block the submit on first click only.
http://api.jquery.com/one/
$('#ACTION_SUBMIT_SURVEY_RESPONSE').one('submit', function(e) {
e.preventDefault();
// do what you need to do on first click
}
Alternatively...
$('#ACTION_SUBMIT_SURVEY_RESPONSE').one('submit', function(e) {
e.preventDefault();
// do what you need to do on first click
if ( some-condition ) { // under certain conditions allow submit on first click
$(this).submit();
}
}
Instead of using .click(), try using the .on() and .off() methods to bind and unbind the event. In your case:
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE.not-submittable').on("click.stopSubmit", function(event) {
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').removeClass('not-submittable');
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').val('Continue');
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE').removeAttr('disabled');
if (...conditions are met.....) {
$('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE.not-submittable').off("click.stopSubmit");
}
return false;
});
You may notice that the first parameter of the .on() method is the string representation of the handler, but that I appended the namespace ".stopSubmit". Namespacing your handlers allows you to unbind one specific click handler, rather than all click handlers. The best part about this is that if there is code in your original handler that you still want to use you can make a separate click handler to run that code, and it will not be unbound when you unbind the ".stopSubmit" handler.
Please note that .on() and .off() are the recommended bind/unbind methods - jQuery no longer recommends .bind() and .unbind().
UPDATE
After reading your comment about not unbinding until after certain conditions are met, I would would like to point out that you can insert the .off() call in a conditional. I have updated the code to reflect this.
You can do something like this
$(document).ready(function () {
$('#ACTION_SUBMIT_SURVEY_RESPONSE').click(function (event) {
if (!$('#ACTION_SUBMIT_SURVEY_RESPONSE').hasClass(".not-submittable")) {
//do all conditions you wish on first click
//if condidition meets add this class to button
$('#ACTION_SUBMIT_SURVEY_RESPONSE').addClass(".not-submittable");
//stop form submit
event.preventDefault();
}
else {
//calls when button have .not-submittable class may be second or any no of clicks
$('#ACTION_SUBMIT_SURVEY_RESPONSE').removeClass('not-submittable');
$('#ACTION_SUBMIT_SURVEY_RESPONSE').val('Continue');
$('#ACTION_SUBMIT_SURVEY_RESPONSE').removeAttr('disabled');
//commented return false so form submits normally
}
});
});
If there are certain criteria that must match use this where submitable contains your logic what makes it possible to send the form:
var submit = $('form#survey_7042 #ACTION_SUBMIT_SURVEY_RESPONSE');
submit.addClass('not-submittable');
submit.click(function(event) {
if (true == submitable) {
submit.removeClass('not-submittable').val('Continue').removeAttr('disabled');
submit.unbind();
event.preventDefault();
}
});

2 jQuery events on same action seem to cancel each other

**Update: I have pasted working code in order to erase any ambiguity about what is going on. I have also tried to remove the preventDefault on both handlers, does not help*
I have a form where upon the button click, a JS event needs to happen, and the form needs to submit.
As per the code below, what I thought would happen is: alert(button), then alert(form), or vice versa. I do not care about sequence.
If i run it however, the alert(button) will show up, but the alert(form) will not.
If i comment out the code for the button, the form alert comes up.
Do i have some fundamental misunderstanding of how this is supposed to work?
jQuery(document).ready(function(){
$("form.example").submit(function(event){
event.preventDefault();
alert("form submitted");
});
$("form.example button").click(function(event){
event.preventDefault();
alert("button clicked");
});
)};
<form class="example" action="/v4test">
<button type="submit">Meow!</button>
</form>
After edit of OP
You do not need to preventDefault of the click.... only the submit... here is you working code:
jsFiddle example
jQuery(document).ready(function(){
$('form.example').submit(function(event){
event.preventDefault();
alert("form submitted");
// stop submission so we don't leave this page
});
$('form.example button').click(function() {
alert("button clicked");
});
});​
old answer
You can simply put your .click() and .submit() handlers in series, and they should not cancel out. You have some syntax errors in your pseudo code.... maybe those are causing problems?
Another potential problem is that $("form button") targets the HTML <button> tags. If you use <input type="button" /> you should use $("form:button") and note that <input type="submit" /> is not a button. Anyway, I'll assume you are in fact using the <button> tags.
Usually return false is used inside .submit(function() { ... });. This stops the form from being submited through HTML. s**[topPropagation][6]** is very different. It deals with stopping events "bubbling up" to the parents of elements....... But I don't see how this would effect your case.
If you are doing a true HTML submission, make sure to put your .click() handler first, since a true HTML submission will cause you to leave the page.
If you use return false inside .submit(), the form will not be submitted through the HTML, and you'll have to handle the submission with jQuery / Javascript / AJAX.
Anyway, here is a demonstration of both the .click() and .submit() events firing in series... the code is below:
jsFiddle Example
$(function() {
$('form button').click(function() {
// Do click button stuff here.
});
$('form').submit(function(){
// Do for submission stuff here
// ...
// stop submission so we don't leave this page
// Leave this line out, if you do want to leave
// the page and submit the form, but then the results of your
// click event will probably be hard for the user to see.
return false;
});
});​
The above will trigger both handlers with the following HTML:
<button type="submit">Submit</button>
As a note, I suppose you were using pseudo code, but even then, it's much easier to read, and one is sure you're not writing syntax errors, if you use:
$('form').submit(function() { /*submits form*/ });
$('form button').click(function() { /*does some action*/ });
If you put a return false on the click, it should cancel the default behavior. If you want to execute one then the other, call $('form').submit() within the click function. e.g.
$('form').submit { //submits form}
$('form button').click {
// does some action
$('form').submit();
}
There seems to be a bit of confusion about propagation here. Event propagation (which can be disabled by stopPropagation) means that events "bubble up" to parent elements; in this case, the click event would register on the form, because it is a parent of the submit button. But of course the submit handler on the form will not catch the click event.
What you are interested in is the default action, which in the case of clicking a submit button is to submit the form. The default action can be prevented by either calling preventDefault or returning false. You are probably doing the latter.
Note that in Javascript functions which do not end with an explicit return do still return a value, which is the result of the last command in the function. You should end your click handler with return; or return true;. I have no idea where I got that from. Javascript functions actually return undefined when there is no explicit return statement.
Does clicking the button submit the form? If so:
// Disable the submit action
$("form").submit(function(){
return false;
});
$("form button").click(function(){
// Do some action here
$("form").unbind("submit").submit();
});
If you don't unbind the submit event when you click the button, the submit will just do nothing.

Categories