jQuery 事件处理程序如何排队和执行?

How are jQuery event handlers queued and executed?

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

所以我在我的表格中添加了以下 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() 中会影响它们的执行行为吗?

在您的代码段中,我添加了一些可能有用的日志。由于你问的问题不止一个,我会一一回答。

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>

Can anyone explain why sometimes the submit events are fired immediately one after the other... and sometimes it is not the case?

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

I can never hit a break point on e.preventDefault();

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

Does putting the event handler inside .each(), affects their execution behavior?

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

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

http://jsfiddle.net/2vugwyfe/

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

示例:

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>

我知道你想要什么,但你把它搞得很复杂。不要插入提交按钮,只需添加一个简单的 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');
}

我只是展示一下,你可以做得更好

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

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

现在,如果您的真实表单实际上并没有进行正常的表单提交,并且您使用的是 ajax 请求、网络套接字连接等,那么您可以将按钮设置为禁用(或设置忙标志)在请求之前并在 ajax 请求回调、网络套接字事件等中取消设置

例如:

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

如果您在执行 $("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 没用。