Como evitar que o formulário seja enviado várias vezes do lado do cliente?

96

Às vezes, quando a resposta é lenta, pode-se clicar no botão enviar várias vezes.

Como evitar que isso aconteça?

AMD
fonte
4
Espero que a razão de você colocar "lado do cliente" no título da pergunta seja porque você está ciente de que qualquer solução no cliente para tentar evitar envios enganosos é 100% insegura e você deve sempre fazer a validação no lado do servidor.
Paolo Bergantino
51
Paolo: Sim, se você estiver interessado em segurança de dados completa, isso deve ser feito no lado do servidor. Mas às vezes você quer apenas evitar que a vovó dê um clique duplo no botão enviar, quando ele só precisa de um clique. Nestes casos, javascript está perfeitamente correto.
Simon East,
1
Fazê-lo apenas no lado do servidor também não é 100% seguro, pois sua primeira solicitação pode demorar o suficiente para ser concluída, então a segunda saberá que não deve prosseguir.
igorsantos07
O padrão de cookie de envio duplo para evitar ataques CSRF também impede o envio de vários formulários?
Martin Thoma

Respostas:

99

Use javascript discreto para desativar o evento de envio no formulário após ele já ter sido enviado. Aqui está um exemplo usando jQuery.

EDIT: Corrigido o problema com o envio de um formulário sem clicar no botão de envio. Obrigado, Ichiban.

$("body").on("submit", "form", function() {
    $(this).submit(function() {
        return false;
    });
    return true;
});
Starx
fonte
4
E se o formulário for enviado pressionando Enter em uma caixa de texto?
ichiban
2
Você ainda precisa do return true? Acho que o envio inicial deve funcionar sem isso.
Simon East
Eu acredito que return trueestá implícito se omitido.
Derek
15
Eu tentei esta solução com validação discreta e MVC. O JS é chamado primeiro, o que sempre retorna falso e, em seguida, a validação é chamada. Portanto, se meu formulário tiver algum erro de validação, o formulário nunca será enviado !!
bjan
6
funciona muito bem. Eu também gosto de desabilitar o botão de envio e substituir o texto do botão de envio para mostrar a dica do usuário. $(this).find("input[type='submit']").attr('disabled', 'disabled').val('submiting'); return true; });
Cam Song de
42

Tentei a solução de vanstee junto com a validação discreta do asp mvc 3 e, se a validação do cliente falhar, o código ainda será executado e o envio do formulário será desativado para sempre. Não consigo reenviar após corrigir os campos. (veja o comentário de bjan)

Então eu modifiquei o script de vanstee assim:

$("form").submit(function () {
    if ($(this).valid()) {
        $(this).submit(function () {
            return false;
        });
        return true;
    }
    else {
        return false;
    }
});
m irina
fonte
Algum outro efeito colateral a ser observado, ou funcionou para você aplicar em todos os lugares?
Ciaran Gallagher
24

O controle de envio do formulário do lado do cliente pode ser alcançado de forma bastante elegante, fazendo com que o manipulador onsubmit oculte o botão de envio e o substitua por uma animação de carregamento. Dessa forma, o usuário obtém feedback visual imediato no mesmo local onde sua ação (o clique) aconteceu. Ao mesmo tempo, você evita que o formulário seja enviado em outra ocasião.

Se você enviar o formulário via XHR, lembre-se de que você também deve lidar com erros de envio, por exemplo, tempo limite. Você teria que exibir o botão de envio novamente porque o usuário precisa reenviar o formulário.

Por outro lado, llimllib traz um ponto muito válido. Toda validação de formulário deve acontecer no lado do servidor. Isso inclui várias verificações de envio. Nunca confie no cliente! Este não é o caso apenas se o javascript estiver desativado. Você deve ter em mente que todo código do lado do cliente pode ser modificado. É um pouco difícil de imaginar, mas o html / javascript falando com o seu servidor não é necessariamente o html / javascript que você escreveu.

Como o llimllib sugere, gere o formulário com um identificador exclusivo para esse formulário e coloque-o em um campo de entrada oculto. Armazene esse identificador. Ao receber dados do formulário, processe-os apenas quando o identificador corresponder. (Também vincular o identificador à sessão do usuário e corresponder a isso, também, para segurança extra.) Após o processamento de dados, exclua o identificador.

Obviamente, de vez em quando, você precisa limpar os identificadores para os quais nunca foram enviados dados de formulário. Mas provavelmente seu site já emprega algum tipo de mecanismo de "coleta de lixo".

Roubar
fonte
15
<form onsubmit="if(submitted) return false; submitted = true; return true">
caos
fonte
6
Use <form onsubmit="return (typeof submitted == 'undefined') ? (submitted = true) : !submitted">, ou escrever var submitted = false;no ponto certo do seu script, para evitar o seguinte erro: Uncaught ReferenceError: submitted is not defined.
Tamás Bolvári
15

Esta é uma maneira simples de fazer isso:

<form onsubmit="return checkBeforeSubmit()">
  some input:<input type="text">
  <input type="submit" value="submit" />
</form>

<script type="text/javascript">
  var wasSubmitted = false;    
    function checkBeforeSubmit(){
      if(!wasSubmitted) {
        wasSubmitted = true;
        return wasSubmitted;
      }
      return false;
    }    
</script>
Ichiban
fonte
com a validação discreta do jQuery, o script de bloqueio é chamado primeiro e a validação depois, o que resulta em nenhum envio se houver erros de validação
bjan
8

Desative o botão de envio logo após um clique. Certifique-se de lidar com as validações corretamente. Além disso, mantenha uma página intermediária para todas as operações de processamento ou banco de dados e, em seguida, redirecione para a próxima página. Isso garante que Atualizar a segunda página não faça outro processamento.

Shoban
fonte
Sim, ótima dica Shoban, eu concordo. Deveria serform page ---submits to---> form_submission_script.php ---after saving, redirects to---> form_thankyou.html
Simon East,
6

Hash a hora atual, torne-a uma entrada oculta no formulário. No lado do servidor, verifique o hash de cada envio de formulário; se você já recebeu esse hash, então você repetiu o envio.

editar: depender de javascript não é uma boa ideia, então todos vocês podem continuar votando nessas ideias, mas alguns usuários não os habilitarão. A resposta correta é não confiar na entrada do usuário no lado do servidor.

llimllib
fonte
Isso não parece "demais"? ;-)
Shoban
1
Parece que apenas impediria o usuário de enviar o formulário mais de uma vez por segundo. Se eu enviar o formulário em 2009-05-29 12:13:37, o servidor não me permitirá enviar outro formulário com o mesmo hash, mas se eu clicar em Enviar em 2009-05-29 12:13:38, iria passar. Além disso, sua técnica evitaria que 2 usuários diferentes enviassem simultaneamente dois formulários diferentes: um deles seria expulso, embora fosse possivelmente o primeiro envio dele.
Sarah Vessels
2
@moneypenny sua primeira objeção é falsa, o hash seria da hora em que o formulário foi enviado ao usuário, não da hora em que ele foi enviado. Se você estiver realmente preocupado com o segundo, terá muitas opções. Adicione seu ID de sessão à string que você hash e / ou verifique todos os campos do formulário quanto à mesmice exata; dois usuários provavelmente não enviaram exatamente o mesmo formulário.
llimllib
@Shoban, desculpe, não entendi o que você está dizendo
llimllib
2
"Excessivo" está nos olhos de quem vê. Se vocês realmente precisa evitar o envio de formulários duplicados, pelo menos parte da solução deve ser do lado do servidor. "Você não pode confiar nos usuários."
Ben Blank
5

Você também pode exibir uma barra de progresso ou um botão giratório para indicar que o formulário está sendo processado.

Tony Borf
fonte
5

Usando JQuery, você pode fazer:

$('input:submit').click( function() { this.disabled = true } );

E

   $('input:submit').keypress( function(e) {
     if (e.which == 13) {
        this.disabled = true 
     } 
    }
   );
Boris Guéry
fonte
1
E se o formulário for enviado pressionando ENTER em uma caixa de texto após o botão ser desabilitado?
ichiban
Desativar o botão como este impedirá que o usuário pressione ENTER. Mas, certo, ele precisa pressionar uma tecla na entrada de envio
Boris Guéry
Isso evita que o botão seja enviado. Conseqüentemente, desabilitando a validação do lado do servidor.
Marko Aleksić
@Marko, certo, mas esse é o OP solicitado, de qualquer maneira, usar um token evitaria que o formulário fosse enviado duas vezes.
Boris Guéry
1
Quando tentei esse tipo de técnica, descobri que o formulário não enviava de todo (pelo menos não no Chrome 14), provavelmente porque você desabilitou o botão antes de o navegador iniciar o envio do formulário.
Simon East
4

Sei que você marcou sua pergunta com 'javascript', mas aqui está uma solução que não depende de javascript de forma alguma:

É um padrão de webapp chamado PRG , e aqui está um bom artigo que o descreve

Pablo Fernandez
fonte
4

Você pode evitar o envio múltiplo simplesmente com:

var Workin = false;

$('form').submit(function()
{
   if(Workin) return false;
   Workin =true;

   // codes here.
   // Once you finish turn the Workin variable into false 
   // to enable the submit event again
   Workin = false;

});
Alfa
fonte
O problema com sua resposta é que há uma fração de segundo entre o momento em que o usuário clica e o momento Workin =true;. Submissões duplas ainda são possíveis, mas menos são prováveis.
sent1nel
4

A resposta mais simples para esta pergunta: "Às vezes, quando a resposta é lenta, pode-se clicar no botão enviar várias vezes. Como evitar que isso aconteça?"

Apenas desative o botão de envio do formulário, como o código abaixo.

<form ... onsubmit="buttonName.disabled=true; return true;">
  <input type="submit" name="buttonName" value="Submit">
</form>

Isso irá desabilitar o botão enviar, no primeiro clique para enviar. Além disso, se você tiver algumas regras de validação, funcionará bem. Espero que ajude.

Imran Khan
fonte
Acho que está faltando alguma coisa, por favor poste seu código.
Imran Khan
3

Do lado do cliente , você deve desabilitar o botão de envio assim que o formulário for enviado com o código javascript, como o método fornecido por @vanstee e @chaos.

Mas há um problema de atraso de rede ou situação de javascript desabilitado em que você não deve confiar no JS para evitar que isso aconteça.

Portanto, no lado do servidor , você deve verificar o envio repetido dos mesmos clientes e omitir o repetido que parece uma tentativa falsa do usuário.

Steveyang
fonte
Eu sei que isso foi respondido há muito tempo, mas alguém pode explicar como o atraso da rede pode impedir a desativação? Estou lidando com esse problema agora e estou muito confuso sobre como um formulário foi submetido duas vezes, embora eu tenha desabilitado o botão. Nunca pensei em desabilitar o JavaScript. Não acho que seja esse o problema no meu caso, mas é um insight interessante.
davidatthepark
Eu também estava pensando em limitar o gerenciador de cliques.
davidatthepark
3

Você pode tentar o plugin jquery do safeform .

$('#example').safeform({
    timeout: 5000, // disable form on 5 sec. after submit
    submit: function(event) {
        // put here validation and ajax stuff...

        // no need to wait for timeout, re-enable the form ASAP
        $(this).safeform('complete');
        return false;
    }
})
Max Kamenkov
fonte
1

A solução mais simples e elegante para mim:

function checkForm(form) // Submit button clicked
{
    form.myButton.disabled = true;
    form.myButton.value = "Please wait...";
    return true;
}

<form method="POST" action="..." onsubmit="return checkForm(this);">
    ...
    <input type="submit" name="myButton" value="Submit">
</form>

Link para mais ...

Solis
fonte
1

Use este código em seu formulário. Ele lidará com vários cliques.

<script type="text/javascript">
    $(document).ready(function() {
        $("form").submit(function() {
            $(this).submit(function() {
                return false;
            });
            return true;
        });     
    }); 
</script>

vai funcionar com certeza.

Bachas
fonte
1

Isso permite o envio a cada 2 segundos. Em caso de validação frontal.

$(document).ready(function() {
    $('form[debounce]').submit(function(e) {
        const submiting = !!$(this).data('submiting');

        if(!submiting) {
            $(this).data('submiting', true);

            setTimeout(() => {
                $(this).data('submiting', false);
            }, 2000);

            return true;
        }

        e.preventDefault();
        return false;
    });
})
Aurel
fonte
0

a melhor forma de evitar o envio de múltiplos é passando o id do botão no método.

    function DisableButton() {
        document.getElementById("btnPostJob").disabled = true;
    }
    window.onbeforeunload = DisableButton; 
user2427182
fonte
0

Fazer isso usando javascript é um pouco fácil. A seguir está o código que dará a funcionalidade desejada:

$('#disable').on('click', function(){
    $('#disable').attr("disabled", true);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="disable">Disable Me!</button>

Rish
fonte
0

As soluções mais simples são desabilitar o botão ao clicar e habilitá-lo após a conclusão da operação. Para verificar uma solução semelhante no jsfiddle:

[click here][1]

E você pode encontrar alguma outra solução para esta resposta .

Kalyani
fonte
1
Embora um link seja excelente, forneça contexto para seu (s) link (s) , pois uma resposta deve conter uma resposta. Esteja ciente de que o fato de ser pouco mais do que um link para um site externo é uma possível razão para Por que e como algumas respostas são excluídas? .
janeiro,
0

Isso funciona muito bem para mim. Ele envia a fazenda e desativa o botão e, após 2 segundos, ativa o botão.

<button id="submit" type="submit" onclick="submitLimit()">Yes</button>

function submitLimit() {
var btn = document.getElementById('submit')
setTimeout(function() {
    btn.setAttribute('disabled', 'disabled');
}, 1);

setTimeout(function() {
    btn.removeAttribute('disabled');
}, 2000);}

Em ECMA6 Syntex

function submitLimit() {
submitBtn = document.getElementById('submit');

setTimeout(() => { submitBtn.setAttribute('disabled', 'disabled') }, 1);

setTimeout(() => { submitBtn.removeAttribute('disabled') }, 4000);}
Awais Jameel
fonte
0

Apenas para adicionar às respostas possíveis sem burlar a validação de entrada do navegador

$( document ).ready(function() {
    $('.btn-submit').on('click', function() {
        if(this.form.checkValidity()) {
            $(this).attr("disabled", "disabled");
            $(this).val("Submitting...");
            this.form.submit();
        }
    });
});
Roi
fonte
0

Uma alternativa ao que foi proposto antes é:

jQuery('form').submit(function(){
     $(this).find(':submit').attr( 'disabled','disabled' );
     //the rest of your code
});
Kisded Szabi - CodeRevolution
fonte