Mencegah pengiriman ganda formulir di jQuery

170

Saya memiliki formulir yang memerlukan sedikit waktu agar server dapat memproses. Saya perlu memastikan bahwa pengguna menunggu dan tidak mencoba mengirim ulang formulir dengan mengklik tombol lagi. Saya mencoba menggunakan kode jQuery berikut:

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

Ketika saya mencoba ini di Firefox semuanya dinonaktifkan tetapi formulir tidak dikirimkan dengan data POST yang seharusnya dimasukkan. Saya tidak dapat menggunakan jQuery untuk mengirimkan formulir karena saya perlu tombol untuk dikirimkan dengan formulir karena ada beberapa tombol kirim dan saya menentukan mana yang digunakan oleh mana nilai seseorang dimasukkan dalam POST. Saya perlu formulir yang dikirimkan seperti biasanya dan saya harus menonaktifkan semuanya setelah itu terjadi.

Terima kasih!

Adam
sumber
Terima kasih semuanya atas semua bantuan Anda!
Adam

Jawaban:

315

Pembaruan pada tahun 2018 : Saya baru saja mendapat beberapa poin untuk jawaban lama ini, dan hanya ingin menambahkan bahwa solusi terbaik adalah membuat operasi idempoten sehingga pengiriman duplikat tidak berbahaya.

Misalnya, jika formulir membuat pesanan, masukkan ID unik di formulir. Pertama kali server melihat permintaan pembuatan pesanan dengan id itu, ia harus membuatnya dan merespons "sukses". Pengajuan selanjutnya juga harus merespons "sukses" (jika klien tidak mendapatkan tanggapan pertama) tetapi tidak boleh mengubah apa pun.

Duplikat harus dideteksi melalui pemeriksaan keunikan di database untuk mencegah kondisi balapan.


Saya pikir masalah Anda adalah baris ini:

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

Anda menonaktifkan SEMUA input, termasuk, saya kira, orang-orang yang datanya harus dikirimkan oleh formulir.

Untuk menonaktifkan hanya tombol kirim, Anda dapat melakukan ini:

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

Namun, saya tidak berpikir IE akan mengirimkan formulir jika tombol-tombol itu dinonaktifkan. Saya menyarankan pendekatan yang berbeda.

Plugin jQuery untuk menyelesaikannya

Kami baru saja menyelesaikan masalah ini dengan kode berikut. Caranya di sini adalah menggunakan jQuery's data()untuk menandai formulir yang sudah dikirimkan atau belum. Dengan begitu, kita tidak perlu dipusingkan dengan tombol kirim, yang membuat IE ketakutan.

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

Gunakan seperti ini:

$('form').preventDoubleSubmission();

Jika ada formulir AJAX yang harus diizinkan untuk mengirimkan beberapa kali per halaman memuat, Anda bisa memberi mereka kelas yang menunjukkan itu, lalu mengecualikannya dari pemilih Anda seperti ini:

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();
Nathan Long
sumber
2
Hanya sebuah pointer - bukankah akan lebih baik untuk cache $(this)ke var that = $(this)?
Stuart.Sklinar
5
@ Stuart.Sklinar - ide bagus Saya menggunakan $form- 'form' karena lebih deskriptif, dan yang utama $karena menurut konvensi saya, itu berarti objek jQuery.
Nathan Long
21
Saat menggunakan jquery memvalidasi validasi tidak mencolok lebih baik untuk memeriksa apakah formulir valid. if ($ (this) .valid)) {$ form.data ('dikirimkan', true);}
cck
1
@cck Keren, saya menggunakan itu. Anda memiliki paren penutupan tambahan yang saya hapus: if ($ (this) .valid) {$ form.data ('dikirimkan', true);
Brian MacKay
1
@BrianMacKay jika formulir tidak valid dan tombol kirim ditekan, plugin jquery ini menandai atribut yang diajukan benar tetapi validasi yang tidak mencolok mencegah pengiriman karena kesalahan validasi. Setelah memperbaiki kesalahan, seseorang tidak dapat mengirimkan formulir karena atribut yang dikirimkan benar!
cck
44

Pendekatan pengaturan waktu salah - bagaimana Anda tahu berapa lama tindakan akan dilakukan pada browser klien?

Bagaimana cara melakukannya

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

Ketika formulir dikirimkan, ini akan menonaktifkan semua tombol kirim di dalam.

Ingat, di Firefox saat Anda menonaktifkan tombol, status ini akan diingat ketika Anda kembali ke riwayat. Untuk mencegahnya, Anda harus mengaktifkan tombol pada pemuatan halaman, misalnya.

Ctrl-C
sumber
2
+1 karena ujung firefox, tetapi apakah ada opsi lain selain mengaktifkan tombol pada pemuatan halaman?
Raphael Isidro
1
Ini cara terbersih untuk ditempuh. Saya mencoba untuk menonaktifkan tombol kirim dengan pengendali event klik tetapi pendekatan saya menyebabkan masalah dengan chrome. Solusi ini bekerja dengan baik.
Anthony To
1
Berhati-hatilah dengan pendekatan ini, jika Anda memiliki nilai pada tombol kirim dan Anda membutuhkannya, formulir tidak akan mengirimkannya.
Fabien Ménager
Menurut dokumentasi jQuery terbaru, Anda harus menggunakan .prop alih-alih .attr. Referensi: api.jquery.com/prop/#entry-longdesc-1 "Metode .prop () harus digunakan untuk mengatur dinonaktifkan dan diperiksa daripada metode .attr ()."
J Plana
19

Saya pikir jawaban Nathan Long adalah cara untuk pergi. Bagi saya, saya menggunakan validasi sisi klien, jadi saya hanya menambahkan syarat bahwa formulir tersebut valid.

EDIT : Jika ini tidak ditambahkan, pengguna tidak akan pernah bisa mengirimkan formulir jika validasi sisi klien menemukan kesalahan.

        // 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;
        };
PTK
sumber
9

event.timeStamptidak berfungsi di Firefox. Mengembalikan salah adalah tidak standar, Anda harus menelepon event.preventDefault(). Dan sementara kita melakukannya, selalu gunakan kawat gigi dengan konstruk kontrol .

Untuk meringkas semua jawaban sebelumnya, berikut adalah plugin yang berfungsi dan bekerja lintas browser.

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

Untuk mengatasi Kern3l, metode pewaktuan bekerja untuk saya hanya karena kami mencoba menghentikan klik dua kali tombol kirim. Jika Anda memiliki waktu respons yang sangat lama untuk pengiriman, saya sarankan mengganti tombol kirim atau formulir dengan pemintal.

Benar-benar memblokir pengiriman formulir berikutnya, seperti yang dilakukan sebagian besar contoh di atas, memiliki satu efek samping yang buruk: jika ada kegagalan jaringan dan mereka ingin mencoba mengirim ulang, mereka tidak akan dapat melakukannya dan akan kehilangan perubahan yang mereka lakukan. terbuat. Ini pasti akan membuat pengguna marah.

Robin Daugherty
sumber
2
Kamu benar. Anda dapat sebaliknya - langsung menonaktifkan dan kemudian, setelah beberapa lama habis, aktifkan lagi. Mencegah klik ganda, memungkinkan pengiriman ulang, tetapi masih memungkinkan pengiriman ulang yang tidak valid untuk pengguna yang tertinggal.
Ctrl-C
8

Silakan, periksa plugin jquery-safeform .

Contoh penggunaan:

$('.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;
    }
});
Max Kamenkov
sumber
4

... tetapi formulir tidak dikirimkan dengan data POST apa pun yang seharusnya dimasukkan.

Benar. Nama / nilai elemen bentuk yang dinonaktifkan tidak akan dikirim ke server. Anda harus mengaturnya sebagai elemen hanya baca .

Juga, jangkar tidak dapat dinonaktifkan seperti itu. Anda harus menghapus HREF mereka (tidak disarankan) atau mencegah perilaku default mereka (cara yang lebih baik), misalnya:

<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
sumber
@ Adam, ya, penangan klik mana pun yang terpasang akan diaktifkan. Saya pikir Anda hanya ingin menghentikannya agar tidak dimodifikasi?
karim79
@ karim79 Saya tidak peduli jika penangan klik dipecat, saya hanya ingin memastikan bahwa formulir tidak dikirim kembali oleh pengguna dengan mengklik tombol kirim yang lain.
Adam
@ Adam - tidak, itu tidak akan terjadi. Saya contoh saya, saya telah menambahkan $('input[type=submit]').attr("disabled", "disabled");tetapi cara favorit saya adalah dengan memasukkan onclick="this.disabled = 'disabled'"klik kirim.
karim79
@ karim79 Saya mencobanya, tapi kemudian tombol kirim saya mengklik tidak termasuk dalam POST. Saya perlu dimasukkan dalam POST untuk menentukan tombol mana yang diklik
Adam
Saya tahu bahwa Anda baru saja membatalkan klik pada acara klik untuk 'a' tetapi mengapa tidak hanya menggunakan $ ('a') .klik (fungsi (e) {e.preventDefault (); // atau mengembalikan false;}); ?
Fabio
4

Ada kemungkinan untuk meningkatkan pendekatan Nathan Long . Anda dapat mengganti logika untuk mendeteksi formulir yang sudah dikirimkan dengan yang ini:

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
Mikhail Fiadosenka
sumber
1
Kenapa Anda akan menggunakan timer untuk pendekatan ini?
Bobort
4

Kode Nathan tetapi untuk plugin Validasi jQuery

Jika Anda menggunakan plugin jQuery Validate, mereka sudah mengirimkan handler diimplementasikan, dan dalam hal ini tidak ada alasan untuk mengimplementasikan lebih dari satu. Kode:

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

Anda dapat dengan mudah memperluasnya di dalam } else {-blok untuk menonaktifkan input dan / atau tombol kirim.

Bersulang

Alph.Dev
sumber
2

Saya akhirnya menggunakan ide dari posting ini untuk menghasilkan solusi yang sangat mirip dengan versi 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;
  });   
};

Gunakan seperti ini:

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

Saya menemukan bahwa solusi yang tidak menyertakan semacam batas waktu tetapi hanya menonaktifkan pengiriman atau elemen formulir yang dinonaktifkan menyebabkan masalah karena setelah penguncian dipicu, Anda tidak dapat mengirim lagi hingga Anda menyegarkan halaman. Itu menyebabkan beberapa masalah bagi saya ketika melakukan hal-hal ajax.

Ini mungkin bisa dipoles sedikit karena tidak semewah itu.

jacklin
sumber
2

Jika menggunakan AJAX untuk mengirim formulir, set async: falseharus mencegah pengiriman tambahan sebelum formulir dihapus:

$("#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;
   }
});
Al Kari
sumber
3
Pengaturan <code> asnyc: true </code> adalah pendekatan yang buruk karena pendekatan ini membekukan browser hingga permintaan selesai yang mengalahkan inti AJAX
Edwin M
2

Solusi Nathan yang dimodifikasi sedikit untuk Bootstrap 3. Ini akan mengatur pemuatan teks ke tombol kirim. Selain itu akan habis setelah 30 detik dan memungkinkan formulir untuk dikirim kembali.

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);
};
AlexZ
sumber
2

Gunakan dua tombol kirim.

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

Dan jQuery:

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

Gunakan penghitung sederhana saat kirim.

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

Dan monitor()fungsi panggilan pada mengirimkan formulir.

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

Saya mengalami masalah yang serupa dan solusi saya adalah sebagai berikut.

Jika Anda tidak memiliki validasi sisi klien maka Anda cukup menggunakan metode jquery one () seperti yang didokumentasikan di sini.

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

Ini menonaktifkan pawang setelah dipanggil.

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

Jika Anda melakukan validasi sisi klien seperti yang saya lakukan maka itu sedikit lebih rumit. Contoh di atas tidak akan membiarkan Anda mengirim lagi setelah validasi gagal. Coba pendekatan ini sebagai gantinya

$("#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);
   }
});
mattbloke
sumber
1

Anda dapat menghentikan pengiriman kedua dengan ini

$("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
    });
});
Mohammed Zayan
sumber
0

Solusi saya:

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

sumber
0

Dalam kasus saya, pengiriman form memiliki beberapa kode validasi, jadi saya menambah jawaban Nathan Long termasuk pos pemeriksaan onsubmit

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

Ubah tombol kirim:

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

Dengan tombol normal:

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

Kemudian gunakan fungsi klik:

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

Dan ingat tombol aktifkan kembali ketika diperlukan:

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

Saya tidak percaya trik css kuno yang baik dari pointer-events: belum ada yang disebutkan. Saya memiliki masalah yang sama dengan menambahkan atribut yang dinonaktifkan tetapi ini tidak memposting kembali. Coba yang di bawah ini dan ganti #SubmitButton dengan ID tombol kirim Anda.

$(document).on('click', '#SubmitButton', function () {
    $(this).css('pointer-events', 'none');
})
Tom McDonough
sumber
Ini tidak menghentikan pengiriman formulir ketika pengguna menekan Enterkunci.
direformasi
0

Mengapa tidak hanya ini - ini akan mengirimkan formulir tetapi juga menonaktifkan tombol pengiriman,

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

Juga, jika Anda menggunakan jQuery Validate, Anda dapat meletakkan kedua baris ini di bawah if ($('#myForm').valid()).

gen b.
sumber
0

kode ini akan menampilkan pemuatan pada label tombol, dan mengatur tombol ke

nonaktifkan keadaan, lalu setelah pemrosesan, aktifkan kembali dan kembalikan teks tombol asli **

$(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
});
Mohamed.Abdo
sumber
-1

Saya memecahkan masalah yang sangat mirip menggunakan:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Nolan
sumber