Evitar o envio duplo de formulários no jQuery

Eu tenho um formulário que demora um pouco para o servidor processar. Eu preciso garantir que o usuário espere e não tente reenviar o formulário clicando no botão novamente. Eu tentei usar o seguinte código jQuery:

 $(document).ready(function() { $("form#my_form").submit(function() { $('input').attr('disabled', 'disabled'); $('a').attr('disabled', 'disabled'); return true; }); });  

Quando tento isso no Firefox, tudo fica desativado, mas o formulário não é enviado com nenhum dos dados POST que ele deve include. Não posso usar o jQuery para enviar o formulário porque preciso que o botão seja enviado com o formulário, pois há vários botões de envio e determino qual foi usado pelo valor de um deles incluído no POST. Preciso que o formulário seja enviado como normalmente é e preciso desativar tudo logo depois disso.

Obrigado!

   

Eu acho que o seu problema é essa linha:

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

Você está desabilitando TODAS as inputs, incluindo, eu acho, aquelas cujos dados o formulário deve enviar.

Para desabilitar apenas o (s) botão (ões) de envio, você pode fazer isso:

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

No entanto, não creio que o IE envie o formulário, mesmo que esses botões estejam desativados. Eu sugeriria uma abordagem diferente.

Um plugin jQuery para resolvê-lo

Nós apenas resolvemos este problema com o seguinte código. O truque aqui é usar os data() do jQuery data() para marcar o formulário como já enviado ou não. Dessa forma, não temos que mexer com os botões de envio, que assustam o 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; }; 

Use assim:

 $('form').preventDoubleSubmission(); 

Se houver formulários AJAX que devem receber permissão para enviar várias vezes por carregamento de página, você pode dar a eles uma class indicando isso e excluí-los do seu seletor da seguinte forma:

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

A abordagem de tempo está errada – como você sabe quanto tempo a ação levará no navegador do cliente?

Como fazer isso

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

Quando o formulário é enviado, ele desativa todos os botões de envio.

Lembre-se, no Firefox, quando você desativa um botão, esse estado será lembrado quando você voltar ao histórico. Para evitar que você tenha que habilitar botões no carregamento da página, por exemplo.

Eu acho que a resposta de Nathan Long é o caminho a percorrer. Para mim, estou usando a validação do lado do cliente, então acabei de adicionar uma condição de que o formulário seja válido.

EDIT : Se isso não for adicionado, o usuário nunca poderá enviar o formulário se a validação do lado do cliente encontrar um erro.

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

event.timeStamp não funciona no Firefox. Retornar false não é padrão, você deve chamar event.preventDefault() . E enquanto estamos nisso, sempre use chaves com uma construção de controle .

Para resumir todas as respostas anteriores, aqui está um plugin que faz o trabalho e funciona em vários navegadores.

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

Para abordar o Kern3l, o método de temporização funciona para mim simplesmente porque estamos tentando parar um clique duplo no botão de envio. Se você tiver um tempo de resposta muito longo para um envio, recomendo replace o botão ou formulário de envio por um controle giratório.

Completamente bloqueando submissões subseqüentes do formulário, como a maioria dos exemplos acima, tem um efeito colateral ruim: se houver uma falha de rede e eles quiserem tentar reenviar, eles seriam incapazes de fazê-lo e perderiam as alterações que eles feito. Isso definitivamente faria um usuário irritado.

Por favor, verifique o plugin jquery-safeform .

Exemplo de uso:

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

… mas o formulário não é enviado com nenhum dos dados POST que deve include.

Corrigir. Nomes / valores de elementos de formulários desativados não serão enviados para o servidor. Você deve defini-los como elementos somente leitura .

Além disso, âncoras não podem ser desativadas assim. Você precisará remover seus HREFs (não recomendado) ou impedir seu comportamento padrão (melhor maneira), por exemplo:

  

Existe a possibilidade de melhorar a abordagem de Nathan Long. Você pode replace a lógica para detecção de um formulário já enviado por este:

 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 

O código de Nathan, mas para o plugin jQuery Validate

Se acontecer de você usar o plugin jQuery Validate, eles já terão o manipulador de envio implementado e, nesse caso, não há razão para implementar mais de um. O código:

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

Você pode expandi-lo facilmente dentro do } else { -block para desabilitar inputs e / ou botão de envio.

Felicidades

Acabei usando ideias deste post para chegar a uma solução que é bastante semelhante à versão da 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; }); }; 

Usando assim:

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

Descobri que as soluções que não incluíam algum tipo de tempo limite, mas apenas elementos de formulário desativado ou de formulário desativado, causavam problemas porque, depois que o bloqueio é acionado, não é possível enviá-lo novamente até que você atualize a página. Isso causa alguns problemas para mim quando faço coisas ajax.

Isso provavelmente pode ser enfeitado um pouco como não é tão chique.

Se estiver usando o AJAX para postar um formulário, defina async: false deve evitar submissões adicionais antes que o formulário seja limpo:

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

Modificou a solução de Nathan um pouco para o Bootstrap 3. Isso irá definir um texto de carregamento para o botão de envio. Além disso, o tempo limite será atingido após 30 segundos e permitirá que o formulário seja reenviado.

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

Use dois botões de envio.

   

E jQuery:

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

Eu tenho tido problemas semelhantes e minha (s) solução (ões) é a seguinte.

Se você não tem nenhuma validação do lado do cliente, então você pode simplesmente usar o método jquery one () como documentado aqui.

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

Isso desativa o manipulador após ser invocado.

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

Se você está fazendo a validação do lado do cliente como eu estava fazendo, é um pouco mais complicado. O exemplo acima não permitiria que você enviasse novamente após a validação com falha. Tente esta abordagem

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

aqui está como eu faço:

 $(document).ready(function () { $('.class_name').click(function () { $(this).parent().append(''); $(this).hide(); }); }); 

créditos: https://github.com/phpawy/jquery-submit-once

este código exibirá o carregamento no label do botão e o botão

desative o estado, depois do processamento, reative e retorne o texto do botão original **

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

);

Minha solução:

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

No meu caso o onsubmit do formulário tinha algum código de validação, então eu incremento Nathan Long resposta incluindo um checkpoint 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; }; 

Alterar botão de envio:

  

Com botão normal:

  

Então use a function click:

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

E lembre-se de reativar o botão quando for necessário:

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

Use contador simples no envio.

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

E chame a function monitor() ao enviar o formulário.

  
....

Eu resolvi um problema muito semelhante usando:

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