Запретить двойную отправку форм в jQuery - PullRequest
158 голосов
/ 14 мая 2010

У меня есть форма, которая требует немного времени для обработки сервера. Мне нужно убедиться, что пользователь ждет и не пытается повторно отправить форму, нажав кнопку еще раз. Я попытался использовать следующий код 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. Мне нужно, чтобы форма была отправлена, как обычно, и мне нужно отключить все сразу после этого.

Спасибо!

Ответы [ 23 ]

297 голосов
/ 17 декабря 2010

Обновление в 2018 : я только что получил несколько баллов за этот старый ответ и просто хотел добавить, что решение best состоит в том, чтобы сделать операцию идемпотентной, чтобы дублированные представления безвредны.

Например, если форма создает заказ, введите в форму уникальный идентификатор. Когда сервер впервые видит запрос на создание заказа с таким идентификатором, он должен создать его и ответить «успех». Последующие представления также должны отвечать «успех» (в случае, если клиент не получил первый ответ), но не должны ничего менять.

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


Я думаю, что ваша проблема в этой строке:

$('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();
42 голосов
/ 25 июля 2012

Неправильный подход - как узнать, сколько времени займет действие в браузере клиента?

Как это сделать

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

Когда форма отправлена, она отключит все кнопки отправки внутри.

Помните, что в Firefox, когда вы отключаете кнопку, это состояние будет запомнено, когда вы вернетесь в историю. Чтобы предотвратить это, вы должны включить кнопки при загрузке страницы, например.

19 голосов
/ 16 июня 2014

Я думаю, что ответ Натана Лонга - это путь. Для меня я использую проверку на стороне клиента, поэтому я просто добавил условие, чтобы форма была действительной.

РЕДАКТИРОВАТЬ : Если это не добавлено, пользователь никогда не сможет отправить форму, если проверка на стороне клиента обнаружит ошибку.

        // 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 голосов
/ 22 февраля 2013

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, метод синхронизации работает для меня просто потому, что мы пытаемся остановить двойной щелчок по кнопке отправки. Если у вас очень много времени отклика на отправку, я рекомендую заменить кнопку или форму отправки на бланку.

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

8 голосов
/ 20 января 2015

Пожалуйста, проверьте 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 голосов
/ 30 октября 2011

Существует возможность улучшить подход Натана Лонга . Вы можете заменить логику обнаружения уже отправленной формы на следующую:

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
4 голосов
/ 14 мая 2010

... но форма не отправлена ​​с любые из данных POST, которые он должен включают в себя.

Правильно. Имена / значения отключенных элементов формы не будут отправлены на сервер. Вы должны установить их как readonly elements.

Кроме того, якоря не могут быть отключены таким образом. Вам нужно будет либо удалить их 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>
4 голосов
/ 21 апреля 2015

код Натана, но для плагина 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 {, чтобы отключить входы и / или кнопку отправки.

Приветствия

2 голосов
/ 12 июня 2012

В итоге я воспользовался идеями из этого поста, чтобы найти решение, очень похожее на версию 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 голосов
/ 08 июня 2014

Немного изменил решение Натана для Bootstrap 3. Это установит текст загрузки для кнопки отправки Кроме того, он будет отключен через 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);
};
...