防止在jQuery中重复提交表单


170

我的表单需要服务器处理一些时间。我需要确保用户等待并且不会再次单击按钮来尝试重新提交表单。我尝试使用以下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包含哪个值使用了哪个。我需要像平常一样提交表单,并且在发生这种情况后我需要立即禁用所有内容。

谢谢!


感谢大家的帮助!
亚当

Answers:


315

在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();

2
只是一个指针-将其缓存$(this)到a 会更好var that = $(this)吗?
Stuart.Sklinar 2012年

5
@ Stuart.Sklinar-好主意。我使用$form了-“ form”,因为它更具描述性,而前导$是因为按照我的约定,这意味着它是一个jQuery对象。
弥敦道(Nathan Long)

21
使用jquery验证不干扰验证时,最好检查表单是否有效。if($(this).valid)){$ form.data('submitted',true);}
cck

1
@cck太好了,我用过。您还删除了一个额外的结束语句:if($(this).valid){$ form.data('submitted',true);
Brian MacKay 2013年

1
@BrianMacKay,如果表单无效并且按下了提交按钮,则此jQuery插件将submitted属性标记为true,但由于验证错误,强制性验证会阻止提交。纠正错误后,由于提交属性为true,因此无法提交表单!
cck

44

计时方法是错误的-您如何知道该操作将在客户端的浏览器上执行多长时间?

怎么做

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

提交表单后,它将禁用其中的所有提交按钮。

请记住,在Firefox中,当您禁用按钮时,在返回历史记录时将记住该状态。例如,为防止这种情况,您必须启用页面加载按钮。


2
由于Firefox提示而+1,但是除了启用页面加载按钮之外,还有其他选择吗?
拉斐尔·伊西德罗

1
这是最干净的方法。我试图使用点击事件处理程序禁用“提交”按钮,但是我的方法导致了chrome问题。该解决方案效果很好。
安东尼到

1
使用这种方法要非常小心,如果您在“提交”按钮上有一个值并且需要它,则表单将不会提交它。
法比恩·梅纳格(FabienMénager)

根据最新的jQuery文档,您应该使用.prop而不是.attr。参考:api.jquery.com/prop/#entry-longdesc-1 “应该使用.prop()方法设置禁用和检查的功能,而不是.attr()方法。”
J Plana

19

我认为内森·朗的答案是正确的方法。对我来说,我正在使用客户端验证,所以我只是添加了一个条件,要求该表单有效。

编辑:如果未添加,如果客户端验证遇到错误,则用户将永远无法提交表单。

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

9

event.timeStamp在Firefox中不起作用。返回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的问题,计时方法对我有用,仅因为我们试图停止双击“提交”按钮。如果您对提交的响应时间很长,建议您使用微调框替换提交按钮或表单。

就像上面的大多数示例一样,完全阻止后续提交该表单具有一个不良的副作用:如果网络出现故障并且他们想要尝试重新提交,则他们将无法这样做,并且将丢失所做的更改制作。这肯定会使用户生气。


2
你是对的。您可以采取其他方法-立即停用,然后在长时间超时后再次启用。防止双击,允许重新提交,但仍然允许落后的用户进行无效的重新提交。
Ctrl-C

8

请检查jquery-safeform插件。

用法示例:

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

4

...但是表格未提交任何应该包含的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>

@Adam,是的,所有附加的单击处理程序都会触发。我以为您只是想阻止它们被修改?
karim79'5

@ karim79我不在乎单击处理程序是否被触发,我只是想确保用户单击另一个提交按钮不会重新提交该表单。
亚当

@亚当-不,那不会发生。在我的示例中,我已经添加了,$('input[type=submit]').attr("disabled", "disabled");但是我最喜欢的方法是放入onclick="this.disabled = 'disabled'"提交的onclick中。
karim79

@ karim79我尝试过,但是我单击的提交按钮不包含在POST中。我需要将其包含在POST中,以确定单击了哪个按钮
亚当(Adam)2010年

我知道您只是取消了'a'的click事件,但为什么不只使用$('a')。click(function(e){e.preventDefault(); //或返回false;}); ?
法比奥

4

有可能改善内森·朗的方法。您可以用以下代码替换检测已提交表单的逻辑:

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

1
为什么在地球上您会为此使用计时器?
Bobort

4

Nathan的代码,但适用于jQuery Validate插件

如果您碰巧使用jQuery Validate插件,则它们已经实现了提交处理程序,在这种情况下,没有理由实现多个。代码:

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 {-block 中将其展开以禁用输入和/或提交按钮。

干杯


2

我最终根据这篇文章中的想法提出了与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时给我带来一些问题。

这可能有点麻烦,因为它不那么花哨。


2

如果使用AJAX发布表单,则set 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;
   }
});

3
设置<code> asnyc:true </ code>是一种不好的方法,因为它会冻结浏览器,直到请求完成为止,这超出了AJAX的本质
Edwin M

2

稍微修改了Nathan针对Bootstrap 3的解决方案。这会将加载文本设置为Submit按钮。此外,它将在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);
};

2

使用两个提交按钮。

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

2

在提交时使用简单的计数器。

    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>

1

我遇到过类似的问题,我的解决方案如下。

如果您没有任何客户端验证,则只需使用此处记录的jquery one()方法即可。

http://api.jquery.com/one/

这将在调用处理程序后将其禁用。

$("#mysavebuttonid").on("click", function () {
  $('form').submit();
});

如果您像我一样在进行客户端验证,那么它会有些棘手。上面的示例不允许您在验证失败后再次提交。尝试这种方法

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

1

您可以以此停止第二次提交

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

0

我的解决方案:

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

0

就我而言,表单的onsubmit有一些验证代码,因此我增加了Nathan Long的答案,包括一个onsubmit检查点

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

0

更改提交按钮:

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

0

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

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

当用户Enter按下键时,这不会停止表单提交。
进行了改革

0

为什么不这样做-这将提交表单,但也禁用提交按钮,

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

另外,如果您使用的是jQuery Validate,则可以将这两行放在下if ($('#myForm').valid())


0

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

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

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

-1

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

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.