Pergunta

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

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

Quando tento isso no Firefox, tudo fica desativado, mas o formulário não é enviado com nenhum dos dados postais que ele deve incluir. Não posso usar o jQuery para enviar o formulário, porque preciso que o botão seja enviado com o formulário, pois existem vários botões de envio e determinei qual foi usado pelo valor de que você está incluído na postagem. Preciso que o formulário seja enviado como normalmente é e preciso desativar tudo logo depois disso.

Obrigado!

Foi útil?

Solução

Atualização em 2018: Acabei de receber alguns pontos para essa resposta antiga e só queria acrescentar que o melhor A solução seria tornar a operação idempotente para que os envios duplicados sejam inofensivos.

Por exemplo, se o formulário criar uma ordem, coloque um ID exclusivo no formulário. A primeira vez que o servidor vê uma solicitação de criação de pedidos com esse ID, ele deve criá -lo e responder "sucesso". Os envios subsequentes também devem responder "sucesso" (caso o cliente não tenha recebido a primeira resposta), mas não deve mudar nada.

As duplicatas devem ser detectadas por meio de uma verificação de exclusividade no banco de dados para evitar condições de corrida.


Eu acho que o seu problema é esta linha:

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

Você está desativando todos os insumos, incluindo, eu acho, aqueles cujos dados o formulário deve enviar.

Para desativar apenas o (s) botão (s) Enviar (s), você poderia fazem isto:

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

No entanto, acho que o IE não enviará o formulário se mesmo esses botões estiverem desativados. Eu sugeriria uma abordagem diferente.

Um plugin jQuery para resolvê -lo

Acabamos de resolver esse problema com o seguinte código. O truque aqui está usando o jQuery data() Para marcar o formulário como já enviado ou não. Dessa forma, não precisamos mexer com os botões de envio, o que enlouquece.

// 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 de Ajax que deve Pode enviar várias vezes por carga de página, você pode dar a eles uma aula indicando que, em seguida, exclui -os do seu seletor como este:

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

Outras dicas

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

Como fazer isso

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

Quando o formulário for enviado, ele desativará todos os botões de envio dentro.

Lembre -se, no Firefox, quando você desativar um botão, esse estado será lembrado quando você voltar à história. Para evitar que você precise ativar os botões na carga 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.

EDITAR: 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 é não padrão, você deve ligar event.preventDefault(). E enquanto estamos nisso, sempre Use aparelhos com uma construção de controle.

Para resumir todas as respostas anteriores, aqui está um plug-in que faz o trabalho e trabalha com navegador cruzado.

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 tempo funciona para mim simplesmente porque estamos tentando parar um clique duas vezes do botão Enviar. Se você tiver um tempo de resposta muito longo a um envio, recomendo substituir o botão Enviar ou formulário por um girador.

Bloqueando completamente os envios subsequentes do formulário, como a maioria dos exemplos acima tem, tem um efeito colateral ruim: se houver uma falha na rede e eles desejam tentar reenviar, eles seriam incapazes de fazê-lo e perderiam as mudanças que eles feito. Isso definitivamente faria um usuário irritado.

Por favor verifique JQuery-Safeform plugar.

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 postais que ele deve incluir.

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

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

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

Existe a possibilidade de melhorar o Nathan Long abordagem. Você pode substituir a lógica para a detecção do 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

Código de Nathan, mas para o plug -in JQuery Validate

Se você usar o plug -in do JQuery Validate, eles já enviaram o manipulador 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 {-Clock para desativar os insumos e/ou enviar o botão.

Felicidades

Acabei usando idéias deste post para encontrar uma solução bem semelhante à versão de 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 o envio desativado ou os elementos de formulário desativados causaram problemas, porque uma vez acionados o bloqueio, você não pode enviar novamente até que você atualize a página. Isso causa alguns problemas para mim ao fazer coisas de Ajax.

Provavelmente isso pode ser bonito como não é tão chique.

Se estiver usando Ajax Para postar um formulário, defina async: false deve evitar submetimentos adicionais antes que o formulário limpe:

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

Solução de Nathan modificada um pouco para o Bootstrap 3. Isso definirá um texto de carregamento no botão Enviar. Além disso, o tempo limite 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.

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

E jQuery:

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

Eu tenho tido problemas semelhantes e minhas solução (s) são as seguintes.

Se você não tiver nenhuma validação do lado do cliente, basta usar o método jQuery One (), conforme documentado aqui.

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

Isso desativa o manipulador depois que foi 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ê envie novamente após a falha na validação. Tente esta abordagem em vez disso

$("#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('<img src="data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==" />');
    $(this).hide();
  });
});

Créditos: https://github.com/phpawy/jquery-submit-nce-nce

Use um 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 ligue monitor() função em enviar o formulário.

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

Este código exibirá carregamento no rótulo do botão e defina o botão como

Desative o estado, depois de processar, reativável e devolva o texto original do botão **

$(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 submit do formulário tinha algum código de validação, então eu incremento Nathan Long Resposta, incluindo um ponto de verificação 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;
    };

Botão de envio de alteração:

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

Com o botão normal:

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

Em seguida, use a função de clique:

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

E lembre-se do botão reencenável, quando é necessário:

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

Não acredito que o bom truque do CSS antiquado de eventos de ponteiro: nenhum ainda não foi mencionado. Eu tive o mesmo problema adicionando um atributo desativado, mas isso não publica novamente. Experimente o abaixo e substitua #subMitButton pelo ID do seu botão Enviar.

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

Por que não apenas isso - isso enviará o formulário, mas também desativará o botão Enviar,

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

Além disso, se você estiver usando o JQuery Validate, poderá colocar essas duas linhas em if ($('#myForm').valid()).

Você pode parar o segundo envio por este

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

Eu resolvi um problema muito semelhante usando:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top