Как обработчики событий jQuery ставятся в очередь и выполняются?

У меня есть форма ввода с кнопкой отправки. Я не хочу, чтобы пользователь мог дважды щелкнуть кнопку отправки и дважды отправить форму...

Итак, я добавил следующий 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() функцию preventFromBeingDoubleSubmitted(), то я смогу дважды отправить форму, дважды щелкнув кнопку отправки.

Может кто-нибудь объяснить, почему иногда события отправки запускаются сразу одно за другим... а иногда это не так? Влияет ли размещение обработчика событий внутри .each() на их поведение при выполнении?

Ответ 1

Форма при представлении по умолчанию перейдите к заданному URL action. В случае, если он не установлен явно, URL является текущей страницей. Первый запрос на отправку в конечном итоге запускает процесс навигации. При этом загруженный в данный момент код JavaScript выгружается. Это включает в себя обработчики событий. Следовательно, почему вы получаете несоответствие возможности двойной подачи или нет. Если сетевой запрос и другие страницы обрабатываются, URL-адрес действия происходит быстрее, чем скорость, необходимая для повторного нажатия на обработчики событий, и ваша точка останова больше не будет вызываться/достигаться, поскольку они уже выгружены. И наоборот, если сетевой запрос медленнее, вы сможете вызвать вызов обработчика и достижение точки останова (если она еще не выгружена).

Вы говорите, что не хотите навсегда отключить кнопку отправки, но даже если вы отключите ее, отправка формы вызовет изменение страницы, и в вашем примере это просто загрузит ту же страницу с новой кнопкой отправки, которая будет больше не будет отключен, потому что это новая страница. Таким образом, он никогда не будет отключен навсегда.

Теперь, если ваша настоящая форма на самом деле не выполняет обычную отправку формы, и вы используете что-то вроде запроса 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));
});

Ответ 2

В ваш фрагмент я добавил несколько журналов, которые могут быть полезны. Поскольку вы задаете более одного вопроса, я отвечу один за другим.

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>

Ответ 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>

Ответ 4

Если вы добавите e.preventDefault(); непосредственно перед выполнением $("form").valid() вы увидите там ошибку.

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)

Эта ошибка сначала не была видна, потому что отправка фактически изменяет страницу (в данном случае обновляет страницу), если ничего не реализовано.

Однако в целом практика заключается в переходе на другую страницу после отправки формы. Если вы по-прежнему хотите придерживаться своего подхода и ограничить число отправляемых, я предлагаю сохранить отправленное состояние в локальной переменной и изменить его в соответствии с проверкой на стороне сервера.

И последнее... Я не понимаю итерацию по формам, так как у вас есть только одна в вашем HTML → $('form').each них бесполезна.

Ответ 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');
}

Я просто показываю идею, вы можете сделать лучше.