防止在 jQuery 中重复提交表单

我有一个表单,需要一点时间服务器处理。我需要确保用户等待并且不会再次单击按钮重新提交表单。我尝试使用以下 jQuery 代码:

<script type="text/javascript">
$(document).ready(function() {
$("form#my_form").submit(function() {
$('input').attr('disabled', 'disabled');
$('a').attr('disabled', 'disabled');
return true;
});
});
</script>

当我在 Firefox 中尝试这个功能时,所有功能都被禁用,但是表单没有提交它应该包含的任何 POST 数据。我不能使用 jQuery 来提交表单,因为我需要与表单一起提交的按钮,因为有多个提交按钮,我要确定哪个按钮被哪个值包含在 POST 中。我需要提交的形式,因为它通常是,我需要禁用一切后,立即发生。

谢谢!

219904 次浏览

... 但是表格没有提交 任何 POST 数据 包括。

正确。禁用的表单元素名称/值不会发送到服务器。您应该将它们设置为 只读元素。

此外,锚不能像那样禁用。你需要删除他们的 HREF (不推荐)或者阻止他们的默认行为(更好的方法) ,例如:

<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>

2018年更新 : 我只是为这个旧的答案得到了一些分数,只是想补充一下,最好的解决方案将使操作幂等,这样重复提交是无害的。

例如,如果表单创建了一个顺序,在表单中放置一个唯一的 ID。当服务器第一次看到具有该 id 的订单创建请求时,它应该创建该请求并响应“成功”。随后的提交也应该响应“成功”(如果客户端没有得到第一个响应) ,但不应该改变任何东西。

应该通过数据库中的唯一性检查来检测重复,以防止竞态条件。


我觉得你的问题在于这句台词:

$('input').attr('disabled','disabled');

您正在禁用所有的输入,包括,我猜,那些表单应该提交的数据。

为了禁用提交按钮,你可以这样做:

$('button[type=submit], input[type=submit]').prop('disabled',true);

然而,我不认为 IE 会提交表单,即使这些按钮是禁用的。我会建议一个不同的方法。

一个 jQuery 插件来解决这个问题

我们刚用下面的代码解决了这个问题。这里的技巧是使用 jQuery 的 data()来标记表单是否已经提交。这样的话,我们就不用去搞乱提交按钮,这会让 IE 崩溃的。

// 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;
};

像这样使用它:

$('form').preventDoubleSubmission();

如果有一些 AJAX 表单允许 应该在每次页面加载时多次提交,你可以给它们一个类来指示,然后像下面这样从你的选择器中排除它们:

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();

我用以下方法解决了一个非常类似的问题:

$("#my_form").submit(function(){
$('input[type=submit]').click(function(event){
event.preventDefault();
});
});

有可能改进内森 · 朗的 接近。 您可以用下面的逻辑来替换检测已提交表单的逻辑:

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

我最终用这篇文章中的想法想出了一个与 AtZako 的版本非常相似的解决方案。

 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;
});
};

像这样使用:

$('#my-form').preventDoubleSubmission();

我发现那些没有包含某种超时的解决方案,只是禁用了提交或禁用了表单元素,这会导致问题,因为一旦锁定被触发,在刷新页面之前你不能再次提交。这给我在做 Ajax 的时候带来了一些问题。

这个也许可以装饰一下,因为它没那么花哨。

计时方法是错误的——你怎么知道客户端浏览器的操作需要多长时间?

怎么做

$('form').submit(function(){
$(this).find(':submit').attr('disabled','disabled');
});

当表单提交时,它将禁用内部的所有提交按钮。

请记住,在 Firefox 中,当你禁用一个按钮时,这种状态会在你回到历史时被记住。为了防止必须在页面加载时启用按钮,例如。

在 Firefox 中无法使用 event.timeStamp。返回 false 是不标准的,你应该调用 event.preventDefault()

总结一下以前的答案,这里有一个插件可以完成这项工作,并且可以跨浏览器工作。

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;
});
};

为了解决 Kern3l 的问题,计时方法对我来说很简单,因为我们试图停止双击提交按钮。如果对提交的响应时间很长,我建议用微调器替换提交按钮或表单。

正如上面的大多数例子所做的那样,完全阻止表单的后续提交有一个不好的副作用: 如果出现网络故障,他们想尝试重新提交,他们将无法这样做,并将失去他们所做的更改。这肯定会让用户很生气。

如果使用 AJAX发布表单,则设置 async: false应防止在表单清除之前提交其他表单:

$("#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;
}
});

此代码将在按钮标签上显示加载,并将按钮设置为

禁用状态,然后处理后,重新启用并返回原来的按钮文本 * *

$(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
});

对内森的解决方案进行了一点改进。这将为提交按钮设置一个加载文本。此外,它将超时后30秒,并允许重新提交的形式。

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);
};

我认为 Nathan Long 的答案是正确的。对我来说,我使用的是客户端验证,所以我只是添加了一个条件,表单是有效的。

EDIT : 如果没有添加这个,那么如果客户端验证遇到错误,用户将永远无法提交表单。

        // 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;
};

请查看 Jquery-safe 格式插件。

用法例子:

$('.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;
}
});

使用两个提交按钮。

<input id="sub" name="sub" type="submit" value="OK, Save">
<input id="sub2" name="sub2" type="submit" value="Hidden Submit" style="display:none">

还有 jQuery:

$("#sub").click(function(){
$(this).val("Please wait..");
$(this).attr("disabled","disabled");
$("#sub2").click();
});

内森的代码,但 jQuery 验证插件

如果您碰巧使用的是 jQueryValidateplugin,那么它们已经实现了提交处理程序,在这种情况下没有理由实现多个提交处理程序。密码:

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;
}
}
});

您可以很容易地在 } else {块中展开它,以禁用输入和/或提交按钮。

干杯

我的解决办法是:

// 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;
};

在我的例子中,表单的 on 提交有一些验证代码,所以我增加了 Nathan Long的应答,包括一个 on 提交检查点

$.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;
};

更改提交按钮:

<input id="submitButtonId" type="submit" value="Delete" />

普通按钮:

<input id="submitButtonId" type="button" value="Delete" />

然后使用点击功能:

$("#submitButtonId").click(function () {
$('#submitButtonId').prop('disabled', true);
$('#myForm').submit();
});

必要时请记住重新启用按钮:

$('#submitButtonId').prop('disabled', false);

提交时使用简单计数器。

    var submitCounter = 0;
function monitor() {
submitCounter++;
if (submitCounter < 2) {
console.log('Submitted. Attempt: ' + submitCounter);
return true;
}
console.log('Not Submitted. Attempt: ' + submitCounter);
return false;
}

并在提交表单时调用 monitor()函数。

    <form action="/someAction.go" onsubmit="return monitor();" method="POST">
....
<input type="submit" value="Save Data">
</form>

我简直不敢相信老式的 CSS 指针事件技巧: 还没有人提到过。我有同样的问题,添加了一个禁用的属性,但这不回发。尝试以下操作,并将 # SubmitButton 替换为提交按钮的 ID。

$(document).on('click', '#SubmitButton', function () {
$(this).css('pointer-events', 'none');
})

为什么不只是这个,这个会提交表单但也会禁用提交按钮,

   $('#myForm').on('submit', function(e) {
var clickedSubmit = $(this).find('input[type=submit]:focus');
$(clickedSubmit).prop('disabled', true);
});

另外,如果您正在使用 jQueryValidate,您可以将这两行放在 if ($('#myForm').valid())下面。

您可以通过以下命令停止第二次提交

$("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
});
});

只要不导入 jQuery 验证插件,所有这些解决方案都是可以通过的。

例如,如果客户端输入无效输入并提交表单,则无论 jQuery 验证是否检测到无效输入,按钮都将被禁用。然后客户端将无法在修复他们的输入之后重新提交表单。

这个问题的解决办法只需要一个 if 声明:

$('form').submit(function () {
if ($(this).valid()) {
$(this).find("input[type='submit']").prop('disabled', true);
}
});

在将这段代码添加到我的全球导入 JavaScript 文件后,我试图在我的网站上双发表单,但是没有成功。

我发现最好的方法就是在点击时立即关闭按钮:

$('#myButton').click(function() {
$('#myButton').prop('disabled', true);
});

并在需要时重新启动它,例如:

  • 验证失败
  • 在服务器处理表单数据时出现错误,然后在使用 jQuery 进行错误响应之后出现错误