【问题标题】:How are jQuery event handlers queued and executed?jQuery 事件处理程序是如何排队和执行的?
【发布时间】:2019-04-04 06:14:30
【问题描述】:

我有一个带有提交按钮的输入表单。我不希望用户能够双击提交按钮并再次提交表单...

所以我在我的表单中添加了以下 jQuery:

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

上面的代码存储了提交时间并防止了第二次提交,如果太早(小于2秒),我不想永久禁用提交按钮,以防出现服务器端错误。 .

这段代码可以满足我的要求,但是在调试代码时,我永远无法在e.preventDefault(); 上遇到断点...即使我双击提交按钮。

看起来第二个提交事件正在等待第一个提交事件完成后再触发。

但是,如果我删除 preventFromBeingDoubleSubmitted() 函数,那么我可以通过双击提交按钮来双重提交表单。

谁能解释为什么有时提交事件会一个接一个地立即触发……而有时情况并非如此?将事件处理程序放在.each() 中会影响它们的执行行为吗?

【问题讨论】:

  • 旁注:您不需要.each() 部分,$('form').submit(...) 将在内部进行迭代
  • @PatrickEvans:非常感谢您的解释。
  • 我用你的代码和一些控制台日志创建了一个小提琴,正如其他人所说,问题是你正在离开页面。如果您在 chrome 中限制页面,您将看到您实际上正在执行您的功能。 jsfiddle.net/vz84j6ak/3

标签: javascript jquery html form-submit


【解决方案1】:

表单在默认情况下提交时导航到设置的action url。在没有明确设置的情况下,url 是当前页面。第一次提交调用将最终启动导航过程。在此期间,当前加载的 javascript 代码被卸载。这包括事件处理程序。因此,为什么您会出现能否双重提交的不一致。如果网络请求和其他页面进程对操作 url 的发生速度快于您再次单击事件处理程序的速度,并且您的断点将不会被再次调用/到达,因为它们已经被卸载。反之亦然,如果网络请求较慢,您将能够调用处理程序并到达断点(如果尚未卸载)。

您说您不想永久禁用提交按钮,但即使您禁用它,表单提交也会导致页面更改,在您的示例中,这只会加载具有新提交的同一页面按钮将不再被禁用,因为它是一个新页面。因此,它从来没有真正永久地被禁用。

现在,如果您的真实表单实际上并没有进行正常的表单提交,并且您使用的是 ajax 请求、Web 套接字连接等,那么您可以在在 ajax 请求回调、web socket 事件等中请求和取消设置。

例如:

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

【讨论】:

    【解决方案2】:

    在您的 sn-p 中,我添加了一些可能有用的日志。由于您提出的问题不止一个,我将一一回答。

    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>

    谁能解释为什么有时会触发提交事件 紧接着一个接一个……有时不是这样?

    我想你自己已经回答了这个问题。您正在添加代码以检查您第一次单击提交按钮与第二次单击提交按钮的时间之间是否存在差异。如果存在时间差,则停止第二次表单提交。

    我永远无法在 e.preventDefault(); 上打断点

    您无法获得控制台的原因是,当您单击提交按钮时,您正在重定向离开该页面。所以控制台被清除了。如果要查看控制台,请使用 ajax 函数提交表单。在返回时,您可能可以将页面重定向到某个地方。

    是否将事件处理程序放在 .each() 中,会影响它们的执行 行为?

    没有。它只是一个迭代器。它不会影响提交功能。

    我添加了指向 jsfiddle 的链接。在 preventDefault 之前添加警报将暂时停止页面重定向。这将证明执行发生了。

    http://jsfiddle.net/2vugwyfe/

    【讨论】:

    • 编辑了答案。如果有帮助,请告诉我。
    • 非常感谢,代码 sn-p 中有一个小错误...我已将 jQuery.Validate CDN 添加到您的代码中,以修复错误...现在使用您的示例,我可以看到:console.log('A small time difference. So I am stopping') 行是在双击提交按钮时执行的。
    【解决方案3】:

    您的解决方案过于复杂。防止重复提交的最简单方法是在提交时禁用提交按钮。

    例子:

    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>

    【讨论】:

    • 感谢您的回答,此解决方案工作正常,我只是好奇为什么事件以这种方式触发...顺便说一句,我确实尝试禁用提交按钮 see here,但它导致一些问题......我也不想永久禁用提交按钮,因为可能存在一些服务器端错误,用户应该能够重新提交。
    • @HoomanBahreini 没问题!我更新了我的代码,向您展示了如何根据任意条件的成功来切换 submit 按钮。
    【解决方案4】:

    如果您在执行$("form").valid() 之前添加e.preventDefault();,您将看到抛出错误。

    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)
    

    这个错误一开始是不可见的,因为如果没有执行任何其他操作,提交实际上会更改页面(在这种情况下刷新页面)。

    但是,一般来说,这种做法是在提交表单后导航到另一个页面。 如果您仍然想使用您的方法并限制提交次数,我建议将 submitted 状态保留在局部变量中,并根据服务器端的验证进行更改。

    最后一件事.. 我不理解表单的迭代,因为您的 HTML 中只有一个 -> $('form').each 没用。

    【讨论】:

    • 非常感谢@Mihai,你是对的,我忘记在问题中包含 jQuery.validate(但我在真实示例中使用了它)......我已经用我的真实更新了这个问题示例代码。
    【解决方案5】:

    我知道你想要什么,但你把它弄得很复杂。而不是插入提交按钮,只需添加一个简单的div 并在其上添加一个点击处理程序。

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

    和:

    function myform_submit() {
        if ($('#submit').hasClass('busy')) { return; }
        $('#submit').addClass('busy');
    
        // do the tasks then remove the `busy` class :
        $('#submit').removeClass('busy');
    }
    

    我只是展示想法,你可以做得更好。

    【讨论】:

    • 通常不鼓励使用点击事件,因为还有其他方法可以提交表单,例如在文本框中按 Enter 键。在您的示例中,我可以通过单击按钮然后在文本框内按 Enter 来导致两个或更多提交。后者不会触发点击事件。使用 submit 事件将捕获所有提交方式并确保执行检查。
    • 其实提交表单的方式有很多种,我只是想在这里展示一个想法。就我个人而言,我从不使用表单元素,而是创建了一个带有 form 类的 div 元素并手动控制所有内容。实际上,我已经为此目的创建了一个 github 存储库。 (AminAdel/ErkinForm.js) ...但还不够高级。
    猜你喜欢
    • 2012-02-20
    • 2014-01-25
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多