【问题标题】:Prevent double submission of forms in jQuery防止在jQuery中重复提交表单
【发布时间】:2011-02-19 07:22:52
【问题描述】:

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

谢谢!

【问题讨论】:

标签: javascript jquery double-submit-prevention forms http-post


【解决方案1】:

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

【讨论】:

  • 只是一个指针 - 将$(this) 缓存到var that = $(this) 不是更好吗?
  • @Stuart.Sklinar - 好主意。虽然我使用了$form - 'form' 因为它更具描述性,而领先的$ 因为按照我的惯例,这意味着它是一个 jQuery 对象。
  • 当使用 jquery validate 时,不显眼的验证更好地检查表单是否有效。 if ($(this).valid)) {$form.data('submitted', true);}
  • @cck 太棒了,我用过。你有一个额外的结束括号,我删除了: if ($(this).valid) {$form.data('submitted', true);
  • @BrianMacKay 如果表单无效并且按下了提交按钮,则此 jquery 插件将提交属性标记为 true,但由于验证错误,不显眼的验证会阻止提交。更正错误后无法提交表单,因为提交的属性为真!
【解决方案2】:

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

怎么做

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

当表单被提交时,它会禁用里面的所有提交按钮。

请记住,在 Firefox 中,当您禁用按钮时,当您返回历史记录时会记住此状态。例如,为了防止您必须在页面加载时启用按钮。

【讨论】:

  • +1 因为 firefox 提示,但是除了在页面加载时启用按钮之外还有其他选项吗?
  • 这是最干净的方法。我试图使用单击事件处理程序禁用提交按钮,但我的方法导致 chrome 出现问题。这个解决方案效果很好。
  • 这种方法要非常小心,如果提交按钮上有值并且需要它,表单将不会提交。
  • 根据最新的 jQuery 文档,您应该使用 .prop 而不是 .attr。参考:api.jquery.com/prop/#entry-longdesc-1“.prop() 方法应该用于设置禁用和检查,而不是 .attr() 方法。”
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    event.timeStamp 在 Firefox 中不起作用。返回 false 是非标准的,您应该调用 event.preventDefault()。当我们在做的时候,always use braces with a control construct

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

    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,计时方法对我很有效,因为我们试图停止双击提交按钮。如果您对提交的响应时间很长,我建议您将提交按钮或表单替换为微调器。

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

    【讨论】:

    • 你是对的。您可以反过来 - 立即禁用,然后在长时间超时后再次启用。防止双击,允许重新提交,但仍然允许滞后用户重新提交无效。
    【解决方案5】:

    请查看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;
        }
    });
    

    【讨论】:

      【解决方案6】:

      ...但是表单没有提交 它应该的任何 POST 数据 包括。

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

      此外,不能像那样禁用锚点。您将需要删除它们的 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 我不在乎点击处理程序是否被触发,我只是想确保用户点击另一个提交按钮时不会重新提交表单。
      • @Adam - 不,这不会发生。我以我为例,我添加了$('input[type=submit]').attr("disabled", "disabled");,但我最喜欢的方式是将onclick="this.disabled = 'disabled'" 放入提交的onclick 中。
      • @karim79 我试过了,但是我点击的提交按钮没有包含在 POST 中。我需要将其包含在 POST 中以确定单击了哪个按钮
      • 我知道你刚刚取消了 'a' 的点击事件,但为什么不直接使用 $('a').click(function(e) { e.preventDefault(); //或返回 false; }); ?
      【解决方案7】:

      有可能改进 Nathan Long 的approach。 您可以用这个替换检测已提交表单的逻辑:

      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
      

      【讨论】:

      • 你到底为什么要在这种方法中使用计时器?
      【解决方案8】:

      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 中轻松展开它以禁用输入和/或提交按钮。

      干杯

      【讨论】:

        【解决方案9】:

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

        这可能有点美化,因为它不是那么花哨。

        【讨论】:

          【解决方案10】:

          如果使用 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;
             }
          });
          

          【讨论】:

          • 设置 asnyc:true 是一种不好的方法,因为它会冻结浏览器直到请求完成,这超出了 AJAX 的本质
          【解决方案11】:

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

          【讨论】:

            【解决方案12】:

            使用两个提交按钮。

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

            【讨论】:

              【解决方案13】:

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

                  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>
              

              【讨论】:

                【解决方案14】:

                你可以通过这个停止第二次提交

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

                【讨论】:

                  【解决方案15】:

                  我一直有类似的问题,我的解决方案如下。

                  如果您没有任何客户端验证,那么您可以简单地使用此处记录的 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);
                     }
                  });
                  

                  【讨论】:

                    【解决方案16】:

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

                    例如,如果客户端输入了无效输入并提交了表单,那么无论 jQuery 验证是否检测到无效输入,按钮都会被禁用。那么客户在修正输入后将无法重新提交表单。

                    这个问题的解决方法只需要一个 if 语句:

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

                    将此代码添加到我全局导入的 JavaScript 文件后,我未能成功尝试在我的网站上重复发布表单。

                    【讨论】:

                      【解决方案17】:

                      我的解决方案:

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

                      【讨论】:

                        【解决方案18】:

                        在我的情况下,表单的 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;
                            };
                        

                        【讨论】:

                          【解决方案19】:

                          更改提交按钮:

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

                          【讨论】:

                            【解决方案20】:

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

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

                            【讨论】:

                            • 这不会在用户点击Enter 键时停止表单提交。
                            【解决方案21】:

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

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

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

                            【讨论】:

                              【解决方案22】:

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

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

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

                              【讨论】:

                                【解决方案23】:

                                我发现的绝对最好的方法是在单击时立即禁用按钮:

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

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

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

                                【讨论】:

                                  【解决方案24】:

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

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

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2011-11-23
                                    • 2011-06-04
                                    • 2016-12-07
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多