Un efecto bastante indeseable en una página web es que los usuarios puedan enviar al servidor dos o más veces una misma información.

Esto suele ocurrir porque existe una latencia entre el cliente y el servidor que hace que, si el servidor es lento o si hay mucha información que enviar o recibir en el proceso, la página tarde varios segundos en desaparecer del navegador.  Por lo tanto un usuario impaciente puede pulsar varias veces el botón de envío, recibiéndose varias veces la información en el servidor.

Esto, según sea el sistema que hemos diseñado, puede tener resultados desastrosos para nuestra aplicación.

Evitarlo par aun botón concreto es muy sencillo. Basta con deshabilitarlo usando un código como este:

   1: <input type="submit" value="Enviar!" onclick="this.disabled=true;">

De esta forma cuando se pulse el botón, aparte de efectuar su función habitual de enviar el formulario (es un botón de tipo “submit”) conseguiremos que éste se deshabilite y por lo tanto que el usuario no pueda pulsarlo más que la primera vez.

El aspecto del botón deshabilitado es como este:

Submit_Disabled

Fíjate en que el botón aparece plano y desdibujado, y no es posible pulsarlo ni interactuar con él.

Hacerlo automáticamente en cualquier página y cualquier botón

Lo anterior es muy sencillo y rápido de conseguir, pero ¿no sería estupendo poder conseguir lo mismo de manera automática en todos los formularios que haya en una página y sin necesidad de ir botón por botón?

Además existen otros problemas asociados con lo anterior. Por ejemplo, si queremos hacer más cosas antes de enviar el formulario (por ejemplo validarlo) y no siempre se va a enviar al servidor por el mero hecho de pulsar el botón, entonces tendremos que complicar el código un poco más y no sería tan directo. Por otro lado los formularios se pueden enviar también solo con pulsar la tecla ENTER al estar dentro de cualquier campo de texto, en cuyo caso nuestro botón no se enteraría y podría ser pulsado también.

He escrito un pequeño código contenido en un archivo .js que usa otra técnica para interceptar el envío del los formularios y en caso de enviar desactiva el botón submit correspondiente. Esto lo hace de manera automática con tan solo incluirlo en cualquier página. Su contenido es el siguiente:

   1: var tmrReady = setInterval(isPageFullyLoaded, 100);
   2:  
   3: function isPageFullyLoaded() {
   4:     if (document.readyState == "loaded" || document.readyState == "complete") {
   5:         subclassForms();
   6:         clearInterval(tmrReady);
   7:     }
   8: }
   9:  
  10: function submitDisabled(_form, currSubmit) {
  11:     return function () {
  12:         var mustSubmit = true;
  13:         if (currSubmit != null)
  14:             mustSubmit = currSubmit();
  15:  
  16:         var els = _form.elements;
  17:         for (var i = 0; i < els.length; i++) {
  18:             if (els[i].type == "submit")
  19:                 if (mustSubmit)
  20:                     els[i].disabled = true;
  21:         }
  22:         return mustSubmit;
  23:     }
  24: }
  25:  
  26: function subclassForms() {
  27:     for (var f = 0; f < document.forms.length; f++) {
  28:         var frm = document.forms[f];
  29:         frm.onsubmit = submitDisabled(frm, frm.onsubmit);
  30:     }
  31: }

La primera parte del código (hasta la línea 8) se encarga de llamar al método subclassForms una vez que la página esté lista para ser manipulada desde JavaScript. Puedes ver cómo funciona esta técnica en mi anterior post.

Lo interesante aquí es que sub-clasificamos el evento onsubmit de los formularios de nuestra página de modo que le añadimos código propio. La sub-clasificación de un método o un evento consiste en sustituir el código original (de haberlo) por uno propio pero al mismo tiempo conservando toda la funcionalidad inicial.

Lo que hace el método subclassForms es recorrer todos los formularios que haya en la página e interceptar su evento onsubmit para que cuando se envíe el formulario al servidor se ejecute nuestro código. Para ello definimos una función submitDiabled que es una clausura o closure. Éstas nos permiten crear funciones genéricas pero que conservan parámetros internos para una ejecución posterior (ver mi post al respecto).

Como parámetros para la función le pasamos el formulario actual y el código del evento onsubmit de éste (que puede que tenga o no). El objeto de este segundo parámetro es conservar toda la funcionalidad del método onsubmit existente aunque nosotros vayamos a hacer más cosas. Es la sub-clasificación de la que hablaba antes.

Si vemos el código de la clausura, nuestra función lo que hace es, antes de nada suponer que el formulario se va a enviar (que es la acción por defecto), por eso se define una variable mustSubmit que por defecto vale true.

Si originalmente existía un código específico para ejecutar en el evento de envío del formulario (línea 13) entonces lo ejecutamos llamándolo con los paréntesis, y guardamos el resultado en la variable mustSubmit anterior. Como nota de cultura general diré que si el resultado de un evento onsubmit es true es que debe enviarse el formulario. Si por el contrario devuelve un false entonces el formulario no se envía.

Bien. Ahora recorremos todos los elementos del formulario en cuestión y verificamos si hay alguno que sea del tipo “submit”, o sea, un botón de envío de formulario. En caso de encontrarlo, si realmente se va a enviar el formulario (es decir, si mustSubmit es true, línea 19) entonces deshabilitamos el botón para evitar que pueda pulsarse más de una vez.

Finalmente (línea 22) se devuelve el resultado apropiado para enviar o no el formulario.

Con este código conseguimos el desactivado de los botones evitando los problemas que mencionábamos antes y además de forma completamente transparente ya que no se ve afectada la funcionalidad del evento onsubmit.

Podríamos incluso ir un paso más allá y hacerlo más seguro detectando si se ha enviado el formulario ya o no, evitando así un doble envío directamente en el código de nuestro evento onsubmit sub-clasificado (devolviendo siempre false si el evento se llama más de una vez). Pero con esto debería ser suficiente para la mayor parte de los casos.

El archivo con este código lo puedes descargar desde aquí: disableSubmits.zip (ZIP, 514 bytes). Para usar la funcionalidad lo único que tienes que hacer es copiar el .js a la misma carpeta que tu página e incluir una línea como esta en la cabecera de la misma:

   1: <script language="javascript" type="text/javascript" src="disableSubmits.js"></script>

¡Espero que te resulte útil!

Para saber más: Programación avanzada con JavaScript y ECMAScript

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor