JQuery'de formların iki kez gönderilmesini önleme


170

Sunucunun işlemesi için biraz zaman alan bir form var. Kullanıcı bekler ve tekrar düğmesini tıklatarak formu yeniden göndermeyi denemek gerekir. Aşağıdaki jQuery kodunu kullanarak denedim:

<script type="text/javascript">
$(document).ready(function() {
    $("form#my_form").submit(function() {
        $('input').attr('disabled', 'disabled');
        $('a').attr('disabled', 'disabled');
        return true;
    });
});
</script>

Bunu Firefox'ta denediğimde her şey devre dışı kalıyor, ancak formun içermesi gereken POST verileriyle gönderilmiyor. Formu göndermek için jQuery kullanamıyorum çünkü birden çok gönderme düğmesi olduğu gibi form ile gönderilmesi gerekiyor ve hangi POST değerinin dahil olduğu tarafından hangisinin kullanıldığını belirler. Formun genellikle olduğu gibi gönderilmesi gerekir ve bundan sonra her şeyi devre dışı bırakmam gerekir.

Teşekkürler!


Yardımlarınız için herkese teşekkürler!
Adam

Yanıtlar:


315

2018'de güncelleme : Bu eski cevap için bazı puanlar aldım ve sadece en iyi çözümün, işlemlerin ikna edici olmasını sağlamak, böylece yinelenen gönderimlerin zararsız olmasını sağlamak istedim .

Örneğin, form bir sipariş oluşturuyorsa, forma benzersiz bir kimlik ekleyin. Sunucu bu kimliğe sahip bir sipariş oluşturma isteği ilk gördüğünde, onu oluşturmalı ve "başarılı" yanıt vermelidir. Sonraki gönderimler de "başarılı" yanıt vermelidir (müşterinin ilk yanıtı almaması durumunda), ancak hiçbir şeyi değiştirmemelidir.

Yarış koşullarını önlemek için, veri tabanındaki bir teklik kontrolü ile kopyalar tespit edilmelidir.


Bence senin sorunun şu satır:

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

Tahmin ediyorum ki, verileri formun göndermesi gereken girişler de dahil olmak üzere TÜM girdileri devre dışı bırakıyorsunuz.

Yalnızca gönder düğmelerini devre dışı bırakmak için şunları yapabilirsiniz :

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

Ancak, bu düğmeler bile devre dışı olsa bile IE formu göndereceğini sanmıyorum. Farklı bir yaklaşım öneririm.

Bunu çözmek için bir jQuery eklentisi

Bu sorunu aşağıdaki kodla çözdük. Buradaki hile, jQuery's'i kullanarak data()formu önceden gönderilmiş veya gönderilmemiş olarak işaretlemektedir. Bu şekilde, IE'yi korkutacak gönderme düğmeleriyle uğraşmak zorunda kalmıyoruz.

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

Şöyle kullanın:

$('form').preventDoubleSubmission();

Sayfa yüklemesi başına birden çok kez gönderilmesine izin verilmesi gereken AJAX formları varsa, onlara bunu gösteren bir sınıf verebilir, ardından bunları seçicinizden hariç tutabilirsiniz:

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();

2
Sadece bir işaretçi - önbelleğe daha iyi olmaz $(this)a var that = $(this)?
Stuart.Sklinar

5
@ Stuart.Sklinar - iyi fikir. $formYine de kullandım - 'form' çünkü daha açıklayıcı ve öncü $çünkü benim kuralımla, bu bir jQuery nesnesi anlamına geliyor.
Nathan Long

21
Jquery kullanırken formun geçerli olup olmadığını kontrol etmek için göze batmayan doğrulamayı daha iyi doğrulayın. if ($ (this) .valid)) {$ form.data ('gönderildi', doğru);}
cck

1
@cck Harika, bunu kullandım. Kaldırdığım ekstra bir kapanış pareniniz vardı: if ($ (this) .valid) {$ form.data ('gönderildi', doğru);
Brian MacKay

1
@BrianMacKay form geçerli değilse ve gönder düğmesine basılırsa bu jquery eklenti işaretleri gönderilen öznitelik doğru ancak göze batmayan doğrulama, doğrulama hataları nedeniyle göndermeyi engeller. Hataları düzelttikten sonra gönderilen özellik doğru olduğundan formu gönderemezsiniz!
cck

44

Zamanlama yaklaşımı yanlış - işlemin istemcinin tarayıcısında ne kadar süreceğini nasıl bilebilirsiniz?

Nasıl yapılır

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

Form gönderildiğinde içindeki tüm gönderme düğmelerini devre dışı bırakır.

Unutmayın, Firefox'ta bir düğmeyi devre dışı bıraktığınızda bu durum geçmişe geri döndüğünüzde hatırlanır. Örneğin, sayfa yüklenirken düğmeleri etkinleştirmeniz gerekir.


2
Firefox ipucu nedeniyle +1, ancak sayfa yüklemede düğmeleri etkinleştirmenin dışında başka bir seçenek var mı?
Raphael Isidro

1
Bu en temiz yol. Bir tıklama olay işleyicisi ile gönder düğmesini devre dışı bırakmaya çalışıyordum, ancak yaklaşımım kromla ilgili sorunlara neden oluyordu. Bu çözüm güzel çalışıyor.
Anthony To

1
Bu yaklaşıma çok dikkat edin, gönder düğmesinde bir değeriniz varsa ve buna ihtiyacınız varsa form göndermez.
Fabien Ménager

En son jQuery belgelerine göre .attr yerine .prop kullanmalısınız. Başvuru: api.jquery.com/prop/#entry-longdesc-1 ".atrop () yöntemi yerine devre dışı bırakılmış ve işaretlenmiş .prop () yöntemi kullanılmalıdır."
J Plana

19

Bence Nathan Long'un cevabı yoludur. Benim için, istemci tarafı doğrulama kullanıyorum, bu yüzden sadece formun geçerli olması için bir koşul ekledim.

DÜZENLE : Bu eklenmezse, istemci tarafı doğrulama bir hatayla karşılaşırsa kullanıcı formu asla gönderemez.

        // 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.timeStampFirefox'ta çalışmıyor. False döndürmek standart değildir, aramalısınız event.preventDefault(). Biz işteyken , daima bir kontrol yapısıyla parantez kullanın .

Önceki tüm cevapları özetlemek için, işi yapan ve tarayıcılar arası çalışan bir eklenti.

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'e hitap etmek için, zamanlama yöntemi benim için işe yarıyor çünkü gönder düğmesine çift tıklamayı durdurmaya çalışıyoruz. Bir gönderime çok uzun bir yanıt süreniz varsa, gönder düğmesini veya formunu bir döndürücü ile değiştirmenizi öneririz.

Yukarıdaki örneklerin çoğunun yaptığı gibi, formun sonraki gönderimlerinin tamamen engellenmesinin kötü bir yan etkisi vardır: bir ağ hatası varsa ve yeniden göndermeyi denemek isterse, bunu yapamazlar ve değişiklikleri kaybederlerdi yapılmış. Bu kesinlikle öfkeli bir kullanıcı olur.


2
Haklısın. Diğer yoldan gidebilirsiniz - anında devre dışı bırakın ve uzun bir zaman aşımından sonra tekrar etkinleştirin. Çift tıklamayı önler, yeniden göndermeye izin verir, ancak yine de geciken kullanıcılar için geçersiz yeniden göndermelere izin verir.
Ctrl-C

8

Lütfen jquery-safeform'a göz atın eklentisine .

Kullanım örneği:

$('.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

... ancak form, içermesi gereken POST verileriyle birlikte gönderilmez.

Doğru. Devre dışı bırakılmış form öğesi adları / değerleri sunucuya gönderilmez. Onları salt okunur olarak ayarlamalısınız öğeler .

Ayrıca, çapalar böyle devre dışı bırakılamaz. HREF'lerini kaldırmanız (önerilmez) veya varsayılan davranışlarını (daha iyi bir şekilde) önlemeniz gerekir, örneğin:

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

@Adam, evet, bağlı tıklama işleyicileri tetiklenir. Sadece değiştirilmelerini durdurmak istediğini sanıyordum?
karim79

@ karim79 Tıklama işleyicilerinin tetiklenip işlenmediğini umursamıyorum, yalnızca formun başka bir gönder düğmesini tıklatarak kullanıcı tarafından yeniden gönderilmediğinden emin olmak istiyorum.
Adam

@Adam - hayır, bu olmayacak. Ben örneğim, ekledim $('input[type=submit]').attr("disabled", "disabled");ama en sevdiğim yol, onclick="this.disabled = 'disabled'"gönderimin onclick içine koymaktır .
karim79

@ karim79 Bunu denedim, ancak tıkladığım gönder düğmesi POST'a dahil değil. Hangi düğmenin tıklandığını belirlemek için POST'a dahil edilmesine ihtiyacım var
Adam

Ben sadece 'a' için tıklama olayı unbid biliyorum ama neden sadece $ ('a') kullanın. Click (function (e) {e.preventDefault (); // veya return false;}); ?
Fabio

4

Nathan Long'un yaklaşımını iyileştirme olasılığı var . Zaten gönderilen formun tespiti mantığını bununla değiştirebilirsiniz:

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
Bu yaklaşım için neden yeryüzünde bir zamanlayıcı kullanasınız?
Bobort

4

Nathan'ın kodu ancak jQuery Validate eklentisi için

JQuery Validate eklentisini kullanırsanız, zaten gönderme işleyicisi uygulanmıştır ve bu durumda birden fazla uygulama yapmak için bir neden yoktur. Kod:

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 {Girişleri devre dışı bırakmak ve / veya gönder düğmesini devre dışı bırakmak için kolayca -block içinde genişletebilirsiniz .

Şerefe


2

AtZako'nun sürümüne oldukça benzer bir çözüm bulmak için bu yazıdan fikirler kullandım.

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

Bunun gibi kullanma:

$('#my-form').preventDoubleSubmission();

Bir tür zaman aşımı içermeyen, ancak yalnızca devre dışı bırakılmış gönderme veya devre dışı bırakılmış form öğelerini içermeyen çözümlerin sorunlara neden olduğunu fark ettim, çünkü kilitleme tetiklendiğinde sayfayı yenileyene kadar tekrar gönderemezsiniz. Ajax şeyler yaparken bu benim için bazı sorunlara neden olur.

Bu muhtemelen o kadar fantezi değil gibi biraz prettied olabilir.


2

Form göndermek için AJAX kullanılıyorsa , set async: falseformun temizlenmesinden önce ek gönderimleri engellemelidir:

$("#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> ayarı kötü bir yaklaşımdır, çünkü istek tamamlanana kadar tarayıcıyı dondurur, bu da AJAX'ın özünü yener
Edwin M

2

Bootstrap 3 için Nathan'ın çözümü biraz değiştirildi. Bu, gönder düğmesine bir yükleme metni koyacaktır. Ayrıca 30 saniye sonra zaman aşımına uğrar ve formun yeniden gönderilmesine izin verir.

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

İki gönderme düğmesi kullanın.

<input id="sub" name="sub" type="submit" value="OK, Save">
<input id="sub2" name="sub2" type="submit" value="Hidden Submit" style="display:none">

Ve jQuery:

$("#sub").click(function(){
  $(this).val("Please wait..");
  $(this).attr("disabled","disabled");
  $("#sub2").click();
});

2

Gönderimde basit sayaç kullanın.

    var submitCounter = 0;
    function monitor() {
        submitCounter++;
        if (submitCounter < 2) {
            console.log('Submitted. Attempt: ' + submitCounter);
            return true;
        }
        console.log('Not Submitted. Attempt: ' + submitCounter);
        return false;
    }

Ve monitor()formu göndermek üzerine çağrı fonksiyonu.

    <form action="/someAction.go" onsubmit="return monitor();" method="POST">
        ....
        <input type="submit" value="Save Data">
    </form>

1

Benzer sorunlar yaşıyorum ve çözümlerim aşağıdaki gibidir.

Herhangi bir istemci tarafı doğrulamanız yoksa, burada belgelendiği gibi jquery one () yöntemini kullanabilirsiniz.

http://api.jquery.com/one/

Bu, çağrıldıktan sonra işleyiciyi devre dışı bırakır.

$("#mysavebuttonid").on("click", function () {
  $('form').submit();
});

Eğer yaptığım gibi istemci tarafı doğrulama yapıyorsanız, o zaman biraz daha zor. Yukarıdaki örnek, başarısız doğrulama işleminden sonra tekrar göndermenize izin vermez. Bunun yerine bu yaklaşımı deneyin

$("#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

İkinci gönderimi bununla durdurabilirsin

$("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

Çözümüm:

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

Benim durumumda formun onubmit bazı doğrulama kodu vardı, bu yüzden bir onsubmit kontrol noktası dahil Nathan Long cevap artırın

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

Gönder düğmesini değiştir:

<input id="submitButtonId" type="submit" value="Delete" />

Normal düğme ile:

<input id="submitButtonId" type="button" value="Delete" />

Ardından tıklama işlevini kullanın:

$("#submitButtonId").click(function () {
        $('#submitButtonId').prop('disabled', true);
        $('#myForm').submit();
    });

Gerektiğinde yeniden etkinleştirme düğmesini unutmayın:

$('#submitButtonId').prop('disabled', false);

0

İşaretçi olayların eski moda css hilesine inanamıyorum: henüz hiç bahsedilmedi. Devre dışı bırakılmış bir öznitelik ekleyerek de aynı sorunu yaşadım, ancak bu geri göndermiyor. Aşağıdakileri deneyin ve #SubmitButton öğesini gönder düğmenizin kimliğiyle değiştirin.

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

Bu, kullanıcı Enteranahtara bastığında form gönderimlerini durdurmaz .
reform

0

Neden sadece bu değil - bu formu gönderecek, aynı zamanda gönderme düğmesini de devre dışı bırakacak,

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

Ayrıca, jQuery Validate kullanıyorsanız, bu iki satırı altına koyabilirsiniz if ($('#myForm').valid()).


0

bu kod düğme etiketine yükleme gösterir ve düğmeyi

durumu devre dışı bırakın, ardından işledikten sonra orijinal düğme metnini yeniden etkinleştirin ve geri gönderin **

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

Ben çok benzer bir sorunu kullanarak çözdü:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.