⌘I

Délai à la soumission ou double envoi

Updated April 11, 2024

Si vous avez déjà cliqué sur « Envoyer » dans un formulaire HTML, vous avez probablement ressenti ce moment d’incertitude qui précède la redirection vers la page de remerciement. Même s’il ne dure que quelques instants, il soulève de nombreuses questions : « Le formulaire ne fonctionne-t-il pas ? », « Mon clic a-t-il bien été pris en compte ? », voire « Ma connexion Internet est-elle coupée ? ». Parfois, pris de panique, les utilisateurs cliquent une deuxième fois sur le bouton d’envoi, ce qui génère des soumissions en double.

Ce délai nuit à l’expérience utilisateur. En plus de cela, un deuxième clic entraîne une requête en double vers votre formulaire Formspree (comptabilisée dans votre quota d’utilisation) et un enregistrement en double dans votre liste de soumissions (que vous devrez supprimer ultérieurement). C’est pourquoi nous avons rédigé ce guide pour vous aider à comprendre pourquoi cela se produit et comment y remédier grâce à quelques lignes de JavaScript.

Pourquoi cela se produit-il ?

Le délai que vos utilisateurs ressentent après avoir cliqué sur « Envoyer » n’est pas dû à un problème avec Formspree ; c’est une conséquence naturelle du fonctionnement des formulaires web. Voici ce qui se passe : lorsqu’un utilisateur clique sur « Envoyer », le navigateur initie une requête HTTP vers le serveur avec les données du formulaire. Cette requête transite par Internet et peut subir une latence due à des connexions réseau lentes.

Une fois la requête parvenue au serveur, elle doit être traitée : validation des informations, enregistrement en base de données, et éventuellement envoi d’e-mails de confirmation. Ce traitement prend également du temps, généralement moins que le transfert réseau. Une fois terminé, le serveur renvoie une confirmation et redirige l’utilisateur vers la page de remerciement.

Le temps de trajet aller-retour de la requête génère un délai côté utilisateur après le clic sur « Envoyer ». Les connexions lentes, comme les réseaux mobiles, amplifient encore davantage ce phénomène.

Voici à quoi ressemble ce délai :

9Yt3xqi.gif

Comment y remédier ?

Vous ne pouvez pas agir sur le temps de trajet de la requête HTTP, mais vous pouvez en revanche améliorer l’expérience d’attente de vos utilisateurs. Si vous développez un projet basé sur React (ou Next/Gatsby), vous pouvez utiliser l’objet state retourné par le hook useForm de la bibliothèque Formspree React pour savoir si le formulaire est en cours de soumission et désactiver le bouton d’envoi. En JavaScript natif, vous devez implémenter cette vérification vous-même.

Pour ce faire, vous devez écouter l’événement submit du formulaire, déclenché chaque fois qu’un formulaire est soumis. Dès la réception de cet événement, désactivez le bouton d’envoi et/ou affichez un élément d’interface (comme un indicateur de chargement) informant l’utilisateur que les données sont en cours de traitement.

REMARQUE : Vous pourriez implémenter la même logique en écoutant l’événement click sur le bouton d’envoi, mais l’événement submit sur le formulaire est plus fiable car il capture toutes les façons possibles de soumettre un formulaire : clic sur le bouton, appui sur Entrée dans un champ, ou soumission programmatique via form.requestSubmit().

Désactiver le bouton à la soumission

La façon la plus simple d’informer l’utilisateur qu’un traitement est en cours est de désactiver le bouton d’envoi et d’afficher éventuellement un curseur d’attente au survol. Voici le résultat :

VueV1eF.gif

Pour l’implémenter, définissez un fond grisé et le curseur wait pour l’état désactivé du bouton via CSS. Ensuite, écoutez l’événement submit sur le formulaire HTML et passez l’attribut disabled du bouton à true lorsque cet événement se déclenche.

Voici à quoi ressemble le fichier 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>

Cette méthode est simple et tire parti des curseurs de chargement natifs selon les plateformes. Par exemple, voici à quoi ressemble le curseur de chargement pour un utilisateur Windows :

mUg5eOm.gif

Le curseur n’est pas visible sur les appareils mobiles. Toutefois, le bouton d’envoi reste grisé, indiquant à l’utilisateur que son action a bien été enregistrée et qu’il ne peut pas appuyer une seconde fois :

fU5hbNa.gif

Remplacer le bouton par un autre élément d’interface à la soumission

Vous pouvez également masquer entièrement le bouton d’envoi et afficher à la place un élément d’interface tel qu’un indicateur de chargement :

Vi8x3Oc.gif

Le code de ce formulaire est également simple :

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

Vous ajoutez deux balises <div>, l’une imbriquée dans l’autre, juste en dessous du bouton d’envoi, vous ajoutez le CSS pour centrer l’indicateur dans son conteneur, et vous définissez les keyframes pour l’animation. Enfin, vous ajoutez le script JS qui écoute l’événement de soumission et masque le bouton d’envoi tout en affichant l’indicateur de chargement.

Cette approche vous donne plus de contrôle sur le design de l’indicateur de chargement et vous permet de maintenir une apparence cohérente quelle que soit la plateforme.

Le script d’écoute de l’événement de soumission doit accéder au DOM HTML pour configurer l’écouteur. Vous devez donc vous assurer que ce code s’exécute uniquement après le chargement complet du DOM. Si vous utilisez un script inline, placez-le juste avant la balise fermante </body>, comme dans les exemples ci-dessus. Si vous utilisez un script externe, assurez-vous de différer son exécution à l’aide de l’attribut defer dans la balise <script>, ou d’écouter l’événement DOMContentLoaded dans le script avant d’ajouter l’écouteur.

Offrir une expérience utilisateur fluide avec vos formulaires

Comme vous l’avez vu, le délai de retour après un clic sur « Envoyer » et les soumissions en double peuvent facilement être résolus en implémentant des éléments d’interface qui informent l’utilisateur du traitement en arrière-plan. Nous espérons que ce guide vous a aidé à comprendre les mécanismes sous-jacents et vous a fourni des extraits de code utiles pour démarrer rapidement.