¿Cómo evitar que el formulario se envíe varias veces desde el lado del cliente?

96

A veces, cuando la respuesta es lenta, se puede hacer clic en el botón enviar varias veces.

¿Cómo evitar que esto suceda?

Dios mio
fuente
4
Espero que la razón por la que coloque "lado del cliente" en el título de su pregunta es porque sabe que cualquier solución en el cliente para tratar de evitar envíos engañados es 100% insegura y siempre debe realizar la validación en el lado del servidor.
Paolo Bergantino
51
Paolo: Sí, si está interesado en la seguridad completa de los datos, debe hacerlo en el lado del servidor. Pero a veces solo desea evitar que la abuela haga doble clic en el botón Enviar cuando solo necesita un solo clic. En estos casos, javascript está perfectamente bien.
Simon East
1
Hacerlo solo del lado del servidor tampoco es 100% seguro, ya que su primera solicitud puede tardar lo suficiente en completarse para que la segunda sepa que no debe continuar.
igorsantos07
¿El patrón de cookies de doble envío para evitar ataques CSRF también evita el envío de varios formularios?
Martin Thoma

Respuestas:

99

Use javascript discreto para deshabilitar el evento de envío en el formulario después de que ya se haya enviado. Aquí hay un ejemplo usando jQuery.

EDITAR: Se solucionó el problema al enviar un formulario sin hacer clic en el botón Enviar. Gracias, ichiban.

$("body").on("submit", "form", function() {
    $(this).submit(function() {
        return false;
    });
    return true;
});
Starx
fuente
4
¿Qué pasa si el formulario se envía presionando Intro en un cuadro de texto?
ichiban
2
¿Incluso necesitas el return true? Creo que el envío inicial debería funcionar sin eso.
Simon East
Creo que return trueestá implícito si se omite.
Derek
15
Probé esta solución con validación discreta y MVC. Primero se llama al JS, que siempre devuelve falso y luego se llama a la validación. Entonces, si mi formulario tiene algún error de validación, ¡el formulario nunca se envía!
bjan
6
funciona muy bien. También me gusta desactivar el botón de envío y reemplazar el texto del botón de envío para mostrar la pista del usuario. $(this).find("input[type='submit']").attr('disabled', 'disabled').val('submiting'); return true; });
Cam Song
42

Lo intenté la solución de vanstee junto con ASP MVC 3 de validación discreto, y si falla la validación del cliente, el código es todavía corro, y formulario de envío está desactivada para siempre. No puedo volver a enviar después de corregir los campos. (ver el comentario de bjan)

Así que modifiqué el guión de vanstee de esta manera:

$("form").submit(function () {
    if ($(this).valid()) {
        $(this).submit(function () {
            return false;
        });
        return true;
    }
    else {
        return false;
    }
});
m irina
fuente
¿Algún otro efecto secundario a tener en cuenta, o funcionó para que lo aplique en todas partes?
Ciaran Gallagher
24

El control de envío de formularios del lado del cliente se puede lograr con bastante elegancia haciendo que el controlador onsubmit oculte el botón de envío y lo reemplace con una animación de carga. De esa manera, el usuario obtiene retroalimentación visual inmediata en el mismo lugar donde ocurrió su acción (el clic). Al mismo tiempo, evita que el formulario se envíe en otro momento.

Si envía el formulario a través de XHR, tenga en cuenta que también debe manejar los errores de envío, por ejemplo, un tiempo de espera. Debería volver a mostrar el botón de envío porque el usuario debe volver enviar el formulario.

En otra nota, llimllib trae un punto muy válido. Toda la validación del formulario debe ocurrir en el servidor. Esto incluye múltiples comprobaciones de envío. ¡Nunca confíes en el cliente! Este no es solo un caso si JavaScript está desactivado. Debe tener en cuenta que todo el código del lado del cliente se puede modificar. Es algo difícil de imaginar, pero el html / javascript que habla con su servidor no es necesariamente el html / javascript que ha escrito.

Como sugiere llimllib, genere el formulario con un identificador que sea único para ese formulario y colóquelo en un campo de entrada oculto. Almacene ese identificador. Al recibir datos del formulario, solo procese cuando el identificador coincida. (También vincular el identificador a la sesión del usuario y hacer coincidir eso, también, para mayor seguridad). Después del procesamiento de datos, elimine el identificador.

Por supuesto, de vez en cuando, necesitaría limpiar los identificadores para los que nunca se envió ningún dato de formulario. Pero lo más probable es que su sitio web ya emplee algún tipo de mecanismo de "recolección de basura".

Robar
fuente
15
<form onsubmit="if(submitted) return false; submitted = true; return true">
caos
fuente
6
Uso <form onsubmit="return (typeof submitted == 'undefined') ? (submitted = true) : !submitted">, o escribir var submitted = false;en el punto correcto de la secuencia de comandos, para evitar el error siguiente: Uncaught ReferenceError: submitted is not defined.
Tamás Bolvári
15

Esta es una forma sencilla de hacerlo:

<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>
Ichibán
fuente
con la validación discreta de jQuery, el script de bloqueo se llama primero y luego la validación, lo que no produce ningún envío si hay errores de validación
bjan
8

Desactive el botón enviar poco después de hacer clic. Asegúrese de manejar las validaciones correctamente. También mantenga una página intermedia para todo el procesamiento o las operaciones de la base de datos y luego redirija a la página siguiente. Esto asegura que la actualización de la segunda página no realice otro procesamiento.

Shoban
fuente
Sí, gran consejo Shoban, estoy de acuerdo. Debería serform page ---submits to---> form_submission_script.php ---after saving, redirects to---> form_thankyou.html
Simon East
6

Hash la hora actual, conviértela en una entrada oculta en el formulario. En el lado del servidor, verifique el hash de cada envío de formulario; si ya recibió ese hash, entonces tiene un envío repetido.

editar: confiar en javascript no es una buena idea, por lo que todos pueden seguir votando esas ideas, pero algunos usuarios no lo tendrán habilitado. La respuesta correcta es no confiar en la entrada del usuario en el lado del servidor.

llimllib
fuente
¿No se ve esto "demasiado"? ;-)
Shoban
1
Eso suena a que evitaría que un usuario envíe el formulario más de una vez por segundo. Si envío el formulario en 2009-05-29 12:13:37, el servidor no me permitirá enviar otro formulario con el mismo hash, pero si hice clic en Enviar en 2009-05-29 12:13:38, pasaría. Además, su técnica evitaría que 2 usuarios diferentes envíen simultáneamente dos formularios diferentes: uno de ellos sería expulsado, aunque posiblemente sea el primer envío de él.
Sarah Vessels
2
@moneypenny, su primera objeción es falsa, el hash sería del momento en que se envió el formulario al usuario, no del momento en que lo envió. Si está realmente preocupado por el segundo, tiene muchas opciones. Agregue su ID de sesión a la cadena que hash y / o verifique todos los campos del formulario para verificar que sean idénticos; dos usuarios probablemente no enviaron exactamente el mismo formulario.
llimllib
@Shoban lo siento, no sigo lo que estás diciendo
llimllib
2
"Excesivo" está en el ojo del espectador. Si realmente necesita evitar el envío de formularios duplicados, al menos parte de la solución debe ser del lado del servidor. "No se puede confiar en los usuarios".
Ben Blank
5

También puede mostrar una barra de progreso o una ruleta para indicar que el formulario se está procesando.

Tony Borf
fuente
5

Usando JQuery puedes hacer:

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

Y

   $('input:submit').keypress( function(e) {
     if (e.which == 13) {
        this.disabled = true 
     } 
    }
   );
Boris Guéry
fuente
1
¿Qué pasa si el formulario se envía presionando ENTER en un cuadro de texto después de que el botón está deshabilitado?
ichiban
Deshabilitar el botón de esta manera evitará que el usuario presione ENTER. Pero, bien, debe vincular una pulsación de tecla en la entrada de envío
Boris Guéry
Esto evita que el botón se envíe. Por lo tanto, deshabilitar la validación del lado del servidor.
Marko Aleksić
@Marko, cierto, pero ese es el OP solicitado, de todos modos, usar un token evitaría que el formulario se envíe dos veces.
Boris Guéry
1
Cuando probé este tipo de técnica, descubrí que el formulario no se enviaría en absoluto (al menos no en Chrome 14), presumiblemente porque deshabilita el botón antes de que el navegador inicie el envío del formulario.
Simon East
4

Sé que etiquetó su pregunta con 'javascript', pero aquí hay una solución que no depende en absoluto de javascript:

Es un patrón de aplicación web llamado PRG , y aquí hay un buen artículo que lo describe

Pablo fernandez
fuente
4

Puede evitar el envío múltiple simplemente con:

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
fuente
El problema con su respuesta es que hay una fracción de segundo entre el momento en que el usuario hace clic y el momento Workin =true;. Aún es posible realizar envíos dobles, pero es probable que haya menos.
enviado
4

La respuesta más simple a esta pregunta es: "A veces, cuando la respuesta es lenta, uno puede hacer clic en el botón enviar varias veces. ¿Cómo evitar que esto suceda?"

Simplemente desactive el botón de envío del formulario, como el siguiente código.

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

Deshabilitará el botón enviar, en el primer clic para enviar. Además, si tiene algunas reglas de validación, funcionará bien. Espero que te ayude.

Imran Khan
fuente
Creo que te falta algo, publica tu código.
Imran Khan
3

En el lado del cliente , debe deshabilitar el botón de envío una vez que el formulario se envía con código javascript como el método proporcionado por @vanstee y @chaos.

Pero existe un problema de retraso en la red o situación de inhabilitación de JavaScript en la que no debe confiar en JS para evitar que esto suceda.

Entonces, en el lado del servidor , debe verificar el envío repetido de los mismos clientes y omitir el repetido que parece un intento falso del usuario.

Steveyang
fuente
Sé que esto fue respondido hace mucho tiempo, pero ¿alguien puede explicar cómo el retraso de la red podría prevenir la desactivación? Estoy lidiando con este problema en este momento y estoy muy confundido en cuanto a cómo se envió un formulario por duplicado a pesar de que desactivo el botón. Ni siquiera pensé en JavaScript desactivado. No creo que ese sea el problema en mi caso, pero de todos modos es una idea interesante.
davidatthepark
También estaba considerando limitar el controlador de clic.
davidatthepark
3

Puede probar el complemento jquery de 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
fuente
1

La solución más simple y elegante para mí:

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>

Enlace para más ...

Solis
fuente
1

Use este código en su formulario. Manejará múltiples clics.

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

seguro que funcionará.

Bachas
fuente
1

Esto permite enviar cada 2 segundos. En caso de validación 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
fuente
0

la mejor manera de evitar el envío de varios es simplemente pasar la identificación del botón en el método.

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

Hacer esto usando javascript es un poco fácil. A continuación se muestra el código que proporcionará la funcionalidad deseada:

$('#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
fuente
0

Las soluciones más simples son deshabilitar el botón al hacer clic y habilitarlo después de que se complete la operación. Para verificar una solución similar en jsfiddle:

[click here][1]

Y puede encontrar otra solución en esta respuesta .

Kalyani
fuente
1
Si bien un enlace es excelente, proporcione contexto a su enlace (s) , ya que una respuesta debería contener una respuesta. Tenga en cuenta que ser apenas más que un enlace a un sitio externo es una posible razón de ¿Por qué y cómo se eliminan algunas respuestas? .
enero
0

Esto funciona muy bien para mí. Envía la granja y hace que el botón se desactive y después de 2 segundos active el botón.

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

En ECMA6 Syntex

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

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

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

Solo para agregar a las posibles respuestas sin omitir la validación de entrada del navegador

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

Una alternativa a lo propuesto anteriormente es:

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