Reputation: 44959
I have a form that takes a little while for the server to process. I need to ensure that the user waits and does not attempt to resubmit the form by clicking the button again. I tried using the following jQuery code:
<script type="text/javascript">
$(document).ready(function() {
$("form#my_form").submit(function() {
$('input').attr('disabled', 'disabled');
$('a').attr('disabled', 'disabled');
return true;
});
});
</script>
When I try this in Firefox everything gets disabled but the form is not submitted with any of the POST data it is supposed to include. I can't use jQuery to submit the form because I need the button to be submitted with the form as there are multiple submit buttons and I determine which was used by which one's value is included in the POST. I need the form to be submitted as it usually is and I need to disable everything right after that happens.
Thanks!
Upvotes: 187
Views: 228943
Reputation: 6737
The absolute best way I've found is to immediately disable the button when clicked:
$('#myButton').click(function() {
$('#myButton').prop('disabled', true);
});
And re-enable it when needed, for example:
Upvotes: 0
Reputation: 173
All of these solutions are passable as long as you're not importing the jQuery validation plugin.
For example, if the client enters invalid input and submits the form, the button will become disabled regardless of whether jQuery validation detects the invalid input. Then the client won't be able to re-submit the form after fixing their input.
The workaround for this issue only took one if statement:
$('form').submit(function () {
if ($(this).valid()) {
$(this).find("input[type='submit']").prop('disabled', true);
}
});
After adding this code to my globally-imported JavaScript file, I was unsuccessful in trying to double-post forms on my website.
Upvotes: 1
Reputation: 2200
this code will display loading on the button label, and set button to
disable state, then after processing, re-enable and return back the original button text**
$(function () {
$(".btn-Loading").each(function (idx, elm) {
$(elm).click(function () {
//do processing
if ($(".input-validation-error").length > 0)
return;
$(this).attr("label", $(this).text()).text("loading ....");
$(this).delay(1000).animate({ disabled: true }, 1000, function () {
//original event call
$.when($(elm).delay(1000).one("click")).done(function () {
$(this).animate({ disabled: false }, 1000, function () {
$(this).text($(this).attr("label"));
})
});
//processing finalized
});
});
});
// and fire it after definition
});
Upvotes: 0
Reputation: 899
You can stop the second submit by this
$("form").submit(function() {
// submit more than once return false
$(this).submit(function() {
return false;
});
// submit once return true
return true; // or do what you want to do
});
});
Upvotes: 3
Reputation: 12044
Why not just this -- this will submit the form but also disable the submitting button,
$('#myForm').on('submit', function(e) {
var clickedSubmit = $(this).find('input[type=submit]:focus');
$(clickedSubmit).prop('disabled', true);
});
Also, if you're using jQuery Validate, you can put these two lines under if ($('#myForm').valid())
.
Upvotes: 0
Reputation: 1262
I can't believe the good old fashioned css trick of pointer-events: none hasn't been mentioned yet. I had the same issue by adding a disabled attribute but this doesn't post back. Try the below and replace #SubmitButton with the ID of your submit button.
$(document).on('click', '#SubmitButton', function () {
$(this).css('pointer-events', 'none');
})
Upvotes: 0
Reputation: 126112
Update in 2018: I just got some points for this old answer, and just wanted to add that the best solution would be to make the operation idempotent so that duplicate submissions are harmless.
Eg, if the form creates an order, put a unique ID in the form. The first time the server sees an order creation request with that id, it should create it and respond "success". Subsequent submissions should also respond "success" (in case the client didn't get the first response) but shouldn't change anything.
Duplicates should be detected via a uniqueness check in the database to prevent race conditions.
I think that your problem is this line:
$('input').attr('disabled','disabled');
You're disabling ALL the inputs, including, I'd guess, the ones whose data the form is supposed to submit.
To disable just the submit button(s), you could do this:
$('button[type=submit], input[type=submit]').prop('disabled',true);
However, I don't think IE will submit the form if even those buttons are disabled. I'd suggest a different approach.
We just solved this problem with the following code. The trick here is using jQuery's data()
to mark the form as already submitted or not. That way, we don't have to mess with the submit buttons, which freaks IE out.
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function() {
$(this).on('submit',function(e){
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
e.preventDefault();
} else {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
}
});
// Keep chainability
return this;
};
Use it like this:
$('form').preventDoubleSubmission();
If there are AJAX forms that should be allowed to submit multiple times per page load, you can give them a class indicating that, then exclude them from your selector like this:
$('form:not(.js-allow-double-submission)').preventDoubleSubmission();
Upvotes: 338
Reputation: 867
Use simple counter on submit.
var submitCounter = 0;
function monitor() {
submitCounter++;
if (submitCounter < 2) {
console.log('Submitted. Attempt: ' + submitCounter);
return true;
}
console.log('Not Submitted. Attempt: ' + submitCounter);
return false;
}
And call monitor()
function on submit the form.
<form action="/someAction.go" onsubmit="return monitor();" method="POST">
....
<input type="submit" value="Save Data">
</form>
Upvotes: 2
Reputation: 2036
Change submit button:
<input id="submitButtonId" type="submit" value="Delete" />
With normal button:
<input id="submitButtonId" type="button" value="Delete" />
Then use click function:
$("#submitButtonId").click(function () {
$('#submitButtonId').prop('disabled', true);
$('#myForm').submit();
});
And remember re-enable button when is necesary:
$('#submitButtonId').prop('disabled', false);
Upvotes: 0
Reputation: 1166
I've been having similar issues and my solution(s) are as follows.
If you don't have any client side validation then you can simply use the jquery one() method as documented here.
This disables the handler after its been invoked.
$("#mysavebuttonid").on("click", function () {
$('form').submit();
});
If you're doing client side validation as I was doing then its slightly more tricky. The above example would not let you submit again after failed validation. Try this approach instead
$("#mysavebuttonid").on("click", function (event) {
$('form').submit();
if (boolFormPassedClientSideValidation) {
//form has passed client side validation and is going to be saved
//now disable this button from future presses
$(this).off(event);
}
});
Upvotes: 1
Reputation: 109
In my case the form's onsubmit had some validation code, so I increment Nathan Long answer including an onsubmit checkpoint
$.fn.preventDoubleSubmission = function() {
$(this).on('submit',function(e){
var $form = $(this);
//if the form has something in onsubmit
var submitCode = $form.attr('onsubmit');
if(submitCode != undefined && submitCode != ''){
var submitFunction = new Function (submitCode);
if(!submitFunction()){
event.preventDefault();
return false;
}
}
if ($form.data('submitted') === true) {
/*Previously submitted - don't submit again */
e.preventDefault();
} else {
/*Mark it so that the next submit can be ignored*/
$form.data('submitted', true);
}
});
/*Keep chainability*/
return this;
};
Upvotes: 0
Reputation:
My solution:
// jQuery plugin to prevent double submission of forms
$.fn.preventDoubleSubmission = function () {
var $form = $(this);
$form.find('[type="submit"]').click(function () {
$(this).prop('disabled', true);
$form.submit();
});
// Keep chainability
return this;
};
Upvotes: 0
Reputation: 3107
If you happen to use jQuery Validate plugin, they already have submit handler implemented, and in that case there is no reason to implement more than one. The code:
jQuery.validator.setDefaults({
submitHandler: function(form){
// Prevent double submit
if($(form).data('submitted')===true){
// Previously submitted - don't submit again
return false;
} else {
// Mark form as 'submitted' so that the next submit can be ignored
$(form).data('submitted', true);
return true;
}
}
});
You can easily expand it within the } else {
-block to disable inputs and/or submit button.
Cheers
Upvotes: 4
Reputation: 159
Use two submit buttons.
<input id="sub" name="sub" type="submit" value="OK, Save">
<input id="sub2" name="sub2" type="submit" value="Hidden Submit" style="display:none">
And jQuery:
$("#sub").click(function(){
$(this).val("Please wait..");
$(this).attr("disabled","disabled");
$("#sub2").click();
});
Upvotes: 3
Reputation: 466
I think Nathan Long's answer is the way to go. For me, I am using client-side validation, so I just added a condition that the form be valid.
EDIT: If this is not added, the user will never be able to submit the form if the client-side validation encounters an error.
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function () {
$(this).on('submit', function (e) {
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
alert('Form already submitted. Please wait.');
e.preventDefault();
} else {
// Mark it so that the next submit can be ignored
// ADDED requirement that form be valid
if($form.valid()) {
$form.data('submitted', true);
}
}
});
// Keep chainability
return this;
};
Upvotes: 21
Reputation: 2598
Please, check out jquery-safeform plugin.
Usage example:
$('.safeform').safeform({
timeout: 5000, // disable next submission for 5 sec
submit: function() {
// You can put validation and ajax stuff here...
// When done no need to wait for timeout, re-enable the form ASAP
$(this).safeform('complete');
return false;
}
});
Upvotes: 9
Reputation: 254
Modified Nathan's solution a little for Bootstrap 3. This will set a loading text to the submit button. In addition it will timeout after 30 seconds and allow the form to be resubmitted.
jQuery.fn.preventDoubleSubmission = function() {
$('input[type="submit"]').data('loading-text', 'Loading...');
$(this).on('submit',function(e){
var $form = $(this);
$('input[type="submit"]', $form).button('loading');
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
e.preventDefault();
} else {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
$form.setFormTimeout();
}
});
// Keep chainability
return this;
};
jQuery.fn.setFormTimeout = function() {
var $form = $(this);
setTimeout(function() {
$('input[type="submit"]', $form).button('reset');
alert('Form failed to submit within 30 seconds');
}, 30000);
};
Upvotes: 2
Reputation: 4172
Timing approach is wrong - how do you know how long the action will take on client's browser?
$('form').submit(function(){
$(this).find(':submit').attr('disabled','disabled');
});
When form is submitted it will disable all submit buttons inside.
Remember, in Firefox when you disable a button this state will be remembered when you go back in history. To prevent that you have to enable buttons on page load, for example.
Upvotes: 54
Reputation: 73
If using AJAX to post a form, set async: false
should prevent additional submits before the form clears:
$("#form").submit(function(){
var one = $("#one").val();
var two = $("#two").val();
$.ajax({
type: "POST",
async: false, // <------ Will complete submit before allowing further action
url: "process.php",
data: "one="+one+"&two="+two+"&add=true",
success: function(result){
console.log(result);
// do something with result
},
error: function(){alert('Error!')}
});
return false;
}
});
Upvotes: 2
Reputation: 7534
event.timeStamp
doesn't work in Firefox. Returning false is non-standard, you should call event.preventDefault()
. And while we're at it, always use braces with a control construct.
To sum up all of the previous answers, here is a plugin that does the job and works cross-browser.
jQuery.fn.preventDoubleSubmission = function() {
var last_clicked, time_since_clicked;
jQuery(this).bind('submit', function(event) {
if(last_clicked) {
time_since_clicked = jQuery.now() - last_clicked;
}
last_clicked = jQuery.now();
if(time_since_clicked < 2000) {
// Blocking form submit because it was too soon after the last submit.
event.preventDefault();
}
return true;
});
};
To address Kern3l, the timing method works for me simply because we're trying to stop a double-click of the submit button. If you have a very long response time to a submission, I recommend replacing the submit button or form with a spinner.
Completely blocking subsequent submissions of the form, as most of the above examples do, has one bad side-effect: if there is a network failure and they want to try to resubmit, they would be unable to do so and would lose the changes they made. This would definitely make an angry user.
Upvotes: 10
Reputation: 2779
I ended up using ideas from this post to come up with a solution that is pretty similar to AtZako's version.
jQuery.fn.preventDoubleSubmission = function() {
var last_clicked, time_since_clicked;
$(this).bind('submit', function(event){
if(last_clicked)
time_since_clicked = event.timeStamp - last_clicked;
last_clicked = event.timeStamp;
if(time_since_clicked < 2000)
return false;
return true;
});
};
Using like this:
$('#my-form').preventDoubleSubmission();
I found that the solutions that didn't include some kind of timeout but just disabled submission or disabled form elements caused problems because once the lock-out is triggered you can't submit again until you refresh the page. That causes some problems for me when doing ajax stuff.
This can probably be prettied up a bit as its not that fancy.
Upvotes: 2
Reputation: 321
There is a possibility to improve Nathan Long's approach. You can replace the logic for detection of already submitted form with this one:
var lastTime = $(this).data("lastSubmitTime");
if (lastTime && typeof lastTime === "object") {
var now = new Date();
if ((now - lastTime) > 2000) // 2000ms
return true;
else
return false;
}
$(this).data("lastSubmitTime", new Date());
return true; // or do an ajax call or smth else
Upvotes: 4
Reputation: 49
I solved a very similar issue using:
$("#my_form").submit(function(){
$('input[type=submit]').click(function(event){
event.preventDefault();
});
});
Upvotes: -1
Reputation: 342765
...but the form is not submitted with any of the POST data it is supposed to include.
Correct. Disabled form element names/values will not be sent to the server. You should set them as readonly elements.
Also, anchors cannot be disabled like that. You will need to either remove their HREFs (not recommended) or prevent their default behaviour (better way), e.g.:
<script type="text/javascript">
$(document).ready(function(){
$("form#my_form").submit(function(){
$('input').attr('readonly', true);
$('input[type=submit]').attr("disabled", "disabled");
$('a').unbind("click").click(function(e) {
e.preventDefault();
// or return false;
});
});
</script>
Upvotes: 4