Запобігайте подвійному надсиланню форм у jQuery


170

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

Дякую!


Дякуємо всім за вашу допомогу!
Адам

Відповіді:


315

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

Наприклад, якщо форма створює замовлення, введіть у форму унікальний ідентифікатор. Перший раз, коли сервер бачить запит на створення замовлення з цим ідентифікатором, він повинен створити його та відповісти на "успіх". Подальші подання також повинні відповідати "успіхом" (у випадку, якщо клієнт не отримав першої відповіді), але не повинні нічого змінювати.

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


Я думаю, що ваша проблема полягає в цій лінії:

$('input').attr('disabled','disabled');

Ви вимикаєте ВСІ вхідні дані, включаючи, напевно, ті, дані яких форма повинна надати.

Щоб вимкнути лише кнопки надсилання, ви можете зробити це:

$('button[type=submit], input[type=submit]').prop('disabled',true);

Однак я не думаю, що IE подасть форму, якщо навіть ці кнопки відключені. Я б запропонував інший підхід.

Плагін jQuery для його вирішення

Ми просто вирішили цю проблему за допомогою наступного коду. Трюк тут полягає у використанні jQuery's 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();

2
Просто вказівник - чи не було б краще кешувати в $(this)a var that = $(this)?
Stuart.Sklinar

5
@ Stuart.Sklinar - хороша ідея. Я використовував, $formхоча - "форму", тому що вона є більш описовою, а провідна, $оскільки, за моєю конвенцією, це означає, що це об'єкт jQuery.
Натан Лонг

21
Під час використання jquery перевіряйте ненав’язливу перевірку, краще перевірити, чи форма дійсна. if ($ (this) .valid)) {$ form.data ('відправлено', правда);}
cck

1
@cck Awesome, я це використав. У вас був додатковий закриваючий батьків, який я видалив: if ($ (this) .valid) {$ form.data ('відправлено', true);
Брайан Маккей

1
@BrianMacKay, якщо форма не вірна і натиснута кнопка надсилання, цей плагін jquery відмічає доданий атрибут true, але ненав'язлива перевірка запобігає надсиланню через помилки перевірки. Після виправлення помилок ви не можете подати форму, оскільки представлений атрибут є істинним!
cck

44

Підхід до термінів невірний - як ви знаєте, скільки часу буде тривати дія браузера клієнта?

Як це зробити

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

Після надсилання форми вона відключить всі кнопки для надсилання всередині.

Пам'ятайте, що в Firefox при відключенні кнопки цей стан запам'ятається, коли ви повернетесь в історію. Для запобігання, наприклад, потрібно активувати кнопки при завантаженні сторінки.


2
+1 через наконечник Firefox, але чи є інший варіант, крім включення кнопок при завантаженні сторінки?
Рафаель Ісідро

1
Це найчистіший шлях. Я намагався відключити кнопку подання за допомогою обробника подій клацання, але мій підхід викликав проблеми з хромом. Це рішення прекрасно працює.
Ентоні

1
Будьте дуже обережні з таким підходом, якщо у вас є значення кнопки подання, і вона вам потрібна, форма не подаватиме її.
Фабієн Менеджер

Відповідно до останньої документації jQuery, ви повинні використовувати .prop замість .attr. Довідка: api.jquery.com/prop/#entry-longdesc-1 "Метод .prop () слід використовувати для встановлення вимкнених і перевірених замість методу .attr ()."
J Plana,

19

Я думаю, що відповідь Натана Лонга - це шлях. Для мене я використовую перевірку на стороні клієнта, тому я просто додав умову, щоб форма була дійсною.

EDIT : Якщо це не додано, користувач ніколи не зможе надіслати форму, якщо перевірка на стороні клієнта виявить помилку.

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

event.timeStampне працює у Firefox. Повернення помилки нестандартне, вам слід зателефонувати 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, метод синхронізації працює для мене просто тому, що ми намагаємось зупинити подвійне клацання кнопки подання. Якщо у вас дуже довгий час відповіді на подання, я рекомендую замінити кнопку або форму подання на спінер.

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


2
Ти маєш рацію. Можна піти навпаки - миттєво відключити, а потім, через деякий тривалий час, знову включити. Запобігає подвійним клацанням, дозволяє повторно надсилати, але все ж дозволяє недійсні повторні подання для відстаючих користувачів.
Ctrl-C

8

Перевірте плагін 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

... але форма не подається з жодним із даних POST, які вона повинна містити.

Правильно. Відключені імена / значення елементів форми не надсилаються на сервер. Ви повинні встановити їх як елементи, що читаються тільки .

Також анкери не можна відключити так. Вам потрібно буде або видалити їхні 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>

@ Так, будь-який доданий обробник кліків запустить. Я думав, ти просто хочеш не допустити їх модифікації?
karim79

@ karim79 Мені все одно, чи звільняються обробники кліків, я просто хочу переконатися, що форма не буде повторно відправлена ​​користувачем, натиснувши іншу кнопку подання.
Адам

@Adam - ні, цього не станеться. Я, наприклад, додав, $('input[type=submit]').attr("disabled", "disabled");але мій улюблений спосіб - це помістити onclick="this.disabled = 'disabled'"в onclick подання.
karim79

@ karim79 Я спробував це, але потім кнопка для надсилання, яку я натискаю, не входить до пошти. Мені потрібно, щоб воно було включене в пошту, щоб визначити, на яку кнопку було натиснуто
Адам,

Я знаю, що ви просто скасували подію натискання для 'a', але чому б не просто використати $ ('a'). Click (функція (e) {e.preventDefault (); // або повернути false;}); ?
Фабіо

4

Є можливість вдосконалити підхід Натана Лонга . Ви можете замінити логіку виявлення вже поданої форми на цю:

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

1
Чому б на землі ви використовували таймер для такого підходу?
Бобборт

4

Код Натана, але для 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

Я в кінцевому підсумку використовував ідеї цього поста, щоб придумати рішення, яке досить схоже на версію 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

Якщо для розміщення форми використовується 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;
   }
});

3
Налаштування <code> asnyc: true </code> - це поганий підхід, оскільки він заморожує браузер, поки запит не буде завершений, що перемагає суть AJAX
Edwin M

2

Трохи модифікував рішення Натана для 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);
};

2

Використовуйте дві кнопки подання.

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

2

Використовуйте простий лічильник при подачі.

    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>

1

У мене виникли подібні проблеми, і моє рішення:

Якщо у вас немає ніякої перевірки на стороні клієнта, ви можете просто скористатися методом 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);
   }
});

1

Ви можете зупинити друге подання цим

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

0

Моє рішення:

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

0

У моєму випадку форма подання форми мала деякий код перевірки, тому я збільшував відповідь Натана Лонга, включаючи контрольну точку підписки

$.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;
    };

0

Змінити кнопку подання:

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

0

Я не можу повірити в старомодний фокус-потік покажчиків: жоден досі не згадувався. У мене виникла та сама проблема, додавши відключений атрибут, але цей показник не надсилається назад. Спробуйте нижче та замініть #SubmitButton ідентифікатором кнопки подання.

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

Це не зупиняє подання форми, коли користувач натискає Enterключ.
реформовано

0

Чому б не просто це - це надіслати форму, а й відключити кнопку подання,

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

Крім того, якщо ви використовуєте jQuery Validate, ви можете помістити ці два рядки під if ($('#myForm').valid()).


0

цей код відобразить завантаження на мітці кнопки та встановить кнопку на

вимкнути стан, після обробки знову включити та повернути початковий текст кнопки **

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

-1

Я вирішив дуже подібне питання, використовуючи:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.