⌘I

Retraso al enviar o envíos duplicados

Updated April 11, 2024
Also available in:

Si alguna vez has hecho clic en “submit” en un formulario HTML, probablemente hayas experimentado un momento de incertidumbre antes de ser redirigido a la página de “gracias”. Aunque dura solo unos instantes, puede generar muchas preguntas como “¿no funciona el formulario?”, “¿se registró mi clic?” o incluso “¿se cayó el internet?”. A veces los usuarios, presa del pánico, terminan haciendo clic en el botón de enviar nuevamente, lo que provoca envíos duplicados.

El retraso crea una experiencia de usuario poco agradable. Además de eso, un segundo clic resulta en una solicitud duplicada a tu formulario de Formspree (que cuenta para tu cuota de uso) y un registro duplicado en tu lista de envíos (que tendrás que limpiar después). Por eso hemos preparado esta breve guía para ayudarte a entender por qué sucede esto y cómo puedes solucionarlo con un pequeño fragmento de JS.

¿Por qué sucede esto?

El retraso que experimentan tus usuarios después de hacer clic en el botón “submit” en un formulario de Formspree no se debe a un problema con Formspree en sí; es un resultado natural de cómo funcionan los formularios web. Aquí tienes un resumen rápido del proceso: cuando un usuario hace clic en el botón “submit”, el navegador inicia una solicitud HTTP al backend con los datos del formulario. Esta solicitud viaja a través de internet para llegar al backend, durante lo cual puede experimentar latencia debido a conexiones de red lentas.

Una vez que la solicitud llega a los servidores, debe ser procesada. Esto puede involucrar validar la información, almacenarla en la base de datos y potencialmente enviar correos de confirmación. Este procesamiento también toma tiempo, aunque normalmente menos que la transferencia de red. Una vez hecho, el backend responde con una confirmación y redirige al usuario a la página de “gracias”.

El tiempo que toma la solicitud para viajar hacia y desde el servidor causa un retraso del lado del usuario después de hacer clic en el botón “submit”. Además, conexiones más lentas como las redes de datos móviles pueden agregar latencia a la solicitud y agravar el problema.

Así se ve el retraso:

9Yt3xqi.gif

¿Cómo puedes solucionarlo?

Si bien no hay nada que puedas hacer respecto al tiempo invertido durante la solicitud HTTP, sí puedes implementar una mejor experiencia de espera para tus usuarios. Si estás desarrollando un proyecto basado en React (o Next/Gatsby), puedes usar el objeto state que devuelve el hook useForm de la biblioteca Formspree React para saber si el formulario está siendo enviado actualmente y deshabilitar el botón de enviar. Sin embargo, en JS puro, necesitas implementar esta verificación por tu cuenta.

Para hacer esto, necesitas escuchar el evento submit del formulario que se dispara cada vez que se envía un formulario en una página web. Una vez que recibas este evento, necesitas deshabilitar el botón de enviar y/o mostrar un elemento de UI (como un spinner) que informe al usuario que los datos del formulario se están procesando en segundo plano.

NOTA: Aunque podrías implementar la misma lógica usando un evento click en el botón “submit” también, el evento “submit” en el formulario es una forma más confiable de capturar todas las posibles maneras de enviar el formulario, que incluyen hacer clic en el botón “submit”, presionar Enter al editar un campo y enviar el formulario programáticamente llamando al método form.requestSubmit().

Deshabilitar el botón al enviar

La forma más simple de informar al usuario sobre el procesamiento en segundo plano es deshabilitar el botón de enviar y opcionalmente mostrar un signo de espera en el cursor al pasar sobre el botón. Así se vería:

VueV1eF.gif

Para implementar esto, necesitarás establecer un fondo grisáceo y el cursor wait para el estado deshabilitado del botón mediante CSS. Luego, necesitas escuchar el evento submit en el formulario HTML y establecer el atributo disabled del botón de enviar en true cuando se observa el evento submit.

Así se vería el archivo HTML:

<html>

<head>
    <style>
        /* Other styles omitted... */
        
        /* 1. Set the background and cursor for the disabled state of the button */
        #submitButton:disabled {
            cursor: wait;
            background: #dfdfdf;
        }

    </style>
</head>

<body>
    <form id="contact-form" name="simple-contact-form" accept-charset="utf-8" action="https://formspree.io/f/{form_id}"
        method="post">
        <label for="full-name">Full Name</label>
        <input type="text" name="name" id="full-name" placeholder="First and Last" required="">
        <label for="email-address">Email Address</label>
        <input type="email" name="_replyto" id="email-address" placeholder="email@domain.tld" required="">
        <label for="message">Message</label>
        <textarea rows="5" name="message" id="message"
            placeholder="Your message here"
            required=""></textarea>
        <input id="submitButton" type="submit" value="Submit">
    </form>

    <!-- 2. Add the following script to listen to the submit event -->
    <script>
        const form = document.getElementById('contact-form');
        const submitButton = document.getElementById('submitButton');
	
	
        form.addEventListener('submit', function (event) {
	
            // Sets the submit button's disabled attribute as true
            submitButton.disabled = true;
        });
    </script>
</body>

</html>

Este método es directo y aprovecha los cargadores de cursor nativos en todas las plataformas. Por ejemplo, así se vería el cursor de carga para un usuario de Windows:

mUg5eOm.gif

El cursor no es visible en dispositivos móviles. Sin embargo, el botón de enviar sigue estando grisáceo, así que el usuario sabrá que su toque fue registrado y no podrá tocar por segunda vez:

fU5hbNa.gif

Reemplazar el botón con otro elemento de UI al enviar

Alternativamente, podrías ocultar el botón de enviar por completo y mostrar un elemento de UI como un spinner en su lugar cuando se envía un formulario:

Vi8x3Oc.gif

El código para este formulario también es bastante directo:

<html>

<head>
    <style>
        /* 1. Add the following styles */
        #loader-container {
            width: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        #loader {
            border: 4px solid #e5122e; 
            border-bottom: 4px solid #fff;
            border-radius: 50%;
            width: 16px;
            height: 16px;
            animation: spin 1s linear infinite;
        }
          
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>

<body>
    <form id="contact-form" name="simple-contact-form" accept-charset="utf-8" action="<https://formspree.io/f/{form_id}>"
        method="post">
        <fieldset id="fs-frm-inputs">
            <div id="inputs">
                <label for="full-name">Full Name</label>
                <input type="text" name="name" id="full-name" placeholder="First and Last" required="">
                <label for="email-address">Email Address</label>
                <input type="email" name="_replyto" id="email-address" placeholder="email@domain.tld" required="">
                <label for="message">Message</label>
                <textarea rows="5" name="message" id="message"
                    placeholder="Your message here"
                    required=""></textarea>
                <input type="hidden" name="_subject" id="email-subject" value="Contact Form Submission">
            </div>
        </fieldset>
        <input id="submitButton" type="submit" value="Submit">
        
        <!-- 2. Add the following div tags -->
        <div id="loader-container" >
            <div id="loader" hidden="hidden"></div>
        </div>
        
    </form>

	<!-- 3. Add the following script to listen to the submit event -->
	<script>
	    const form = document.getElementById('contact-form');
	    const submitButton = document.getElementById('submitButton');
	    const loader = document.getElementById('loader')
	
	
	    form.addEventListener('submit', function (event) {
	    
	        // Removes the hidden attribute from the loader element
	        loader.hidden = ""
	        
	        // Sets the submit button to be hidden
	        submitButton.hidden = "hidden";
	    });
	</script>
</body>

</html>

Agregas dos <div>, uno anidado dentro del otro, justo debajo del botón de enviar, agregas el CSS para centrar el loader dentro del contenedor y configuras los keyframes para la animación del loader. Finalmente, agregas el script JS que escucha el evento submit del formulario y oculta el botón de enviar mientras revela el loader.

Este enfoque te da más control sobre el diseño de UI del elemento loader. Además, puedes mantener un diseño consistente para tu loader entre plataformas.

El script para agregar el listener del evento submit del formulario necesita acceder al DOM HTML para configurar el listener del evento. Por lo tanto, necesitas asegurarte de que el código para agregar el listener del evento submit del formulario (form.addEventListener('submit', function (event) {}) se ejecute solo después de que el DOM haya terminado de cargar. Esto significa que si estás usando un script en línea, asegúrate de agregarlo justo antes de la etiqueta </body> de cierre, tal como en los ejemplos mostrados arriba. Alternativamente, si estás usando un script externo, asegúrate de o bien defer su ejecución hasta después de que el DOM HTML sea analizado usando el atributo defer en la etiqueta <script>, o escuches el evento DOMContentLoaded en el script antes de agregar el listener del evento submit del formulario al documento.

Ofreciendo una UX fluida con tus formularios

Como has visto, el problema del retraso en la retroalimentación después de hacer clic en el botón de enviar y los incidentes de envíos duplicados pueden resolverse fácilmente implementando elementos de UI que informen al usuario sobre el procesamiento en segundo plano que se está realizando para manejar su envío de formulario. Esperamos que esta guía te haya ayudado a entender cómo funciona detrás de escena y te haya proporcionado algunos fragmentos de código útiles para empezar de inmediato.