Formulaire partie 2 - Soumettre son formulaire

Thématiques associées :
  • Web
  • Intermédiaire
  • Composant

Introduction #

Nous avons vu dans la première partie comment structurer son formulaire en respectant les critères d'accessibilité.

Dans cette deuxième partie, nous allons faciliter la saisie dans les champs par l'utilisateur et mettre en place d'un système de gestion des erreurs lors de la validation de notre formulaire.

Nous continuerons de baser nos exemples sur notre formulaire d'inscription de la partie 1.

Dans cet exemple, nous utilisons la librairie Boosted qui est la déclinaison Web de notre Orange Design System (ODS). Celle-ci permet d’obtenir des formulaires dont le design est conforme à la charte Orange.

Soumission d'un formulaire #

Champ obligatoire #

Dans les formulaires, il est fréquent que des champs soient obligatoires. Ces champs doivent être indiqués clairement aux utilisateurs.

Pour réaliser ceci, plusieurs solutions existent :

  • de manière programmatique, il faut utiliser l'attribut required ou aria-required="true" dans la balise input de nos champs, l'utilisation de ces attributs permettra aux technologies d'assistance (AT) d'indiquer que le champ est obligatoire.
  • il faut aussi informer les utilisateurs n'utilisant pas de technologies d'assistance que le champ est obligatoire; il faut donc rajouter une identification visuelle qui ne repose pas uniquement sur la couleur (par exemple, en mentionnant explicitement "obligatoire"). Si cette identification n'est pas réalisée via un texte explicite, par exemple, un astérisque (*), il faut en expliquer la signification, comme par exemple, "Tous les champs obligatoires sont marqués d'un *", qui sera placée au début du formulaire.

Exemple #

Dans notre exemple d'inscription, plusieurs champs peuvent être considérés comme obligatoires afin de valider son inscription : l'email, le mot de passe, le prénom et le nom.

Il faut donc les spécifier aux utilisateurs.

Tous les champs obligatoires sont marqués d'un *

Votre mot de passe doit contenir minimum 6 caractères.

Exemple de code :

  
  <div class="col-md-8">
    <form id="formulaire" class="border border-secondary p-3 my-2">
      <p>Tous les champs obligatoires sont marqués d'un *</p>
      <div class="mb-2">
        <label for="email" class="form-label">Email * </label>
        <input type="text" class="form-control" id="email" required/>
      </div>
      <label for="password" class="form-label">Mot de passe *</label>
      <div class="mb-2 input-group">
        <input type="password" class="form-control" id="password" required aria-describedby="passwordHelpBlock"/>
        <span class="input-group-text">
          <button type="button" class="btn btn-icon btn-secondary btn-sm" id="password_visibility" title="Afficher le mot de passe" >
            <svg aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 1000 1000"></svg>
          </button>
        </span>
      </div>
      <div id="passwordHelpBlock" class="form-text">
          Votre mot de passe doit contenir minimum 6 caractères.
      </div>
      <div class="mb-2">
        <label for="name" class="form-label">Nom *</label>
        <input type="text" class="form-control" id="name" required/>
      </div>
      <div class="mb-2">
        <label for="firstname" class="form-label">Prénom * </label>
        <input type="text" class="form-control" id="firstname" required/>
      </div>
      <button type="submit" class="btn btn-primary">Soumettre</button>
    </form>
  </div>
  

Gestion des erreurs #

Lors de la validation, si des champs obligatoires ne sont pas renseignés, ou si le format de la donnée saisie n'est pas valide, il faut prévenir l'utilisateur.

Pour réaliser ceci, il faut 

  • Utiliser l'attribut aria-invalid="true" pour indiquer une erreur de saisie aux utilisateurs d'AT
  • Afficher des messages d'erreurs explicites pour tous les autres utilisateurs
  • Si besoin, suggérer des corrections

Si des messages d'erreurs empêchent la validation du formulaire, plutôt que de lister les erreurs au début du formulaire dans une bannière, on peut, pour chaque champ en erreur avertir l’utilisateur (voir ci-dessus).

Pour chaque champ en erreur, il faut que les messages soient explicites, ce qui signifie :

  • Soyez clair et non ambigu ("champ invalide" ne suffit pas, préciser quel champ est invalide et, si possible, en quoi il est invalide)
  • Soyez précis et pertinent
  • Donner des pistes de corrections et moyens de corriger
  • Assurez-vous que les erreurs sont sous forme de texte, éviter les majuscules.
  • Ne vous contentez pas d'utiliser des indicateurs visuels ou seulement la couleur pour signaler les erreurs.
  • Laisser actif en toute circonstance le bouton d'envoi. Certains sites Web activent le bouton d'envoi que si le formulaire est correctement rempli, c'est une mauvaise idée.
  • Fournissez les instructions nécessaires et soyez aussi précis que possible sur les erreurs commises afin de faciliter remplissage des champs par les utilisateurs.
  • Assurez-vous que les erreurs sont visuellement identifiables sur la page Web.

Exemple #

Dans notre exemple d'inscription, plusieurs champs peuvent être en erreur :

  • Pour les champs obligatoires, il faudra préciser quel champ obligatoire est en erreur
  • Pour les champs nécessitant un format de donnée précis, comme l'email, dans le message d'erreur, nous préciserons le champ en erreur et fournirons une aide à la correction

Tous les champs obligatoires sont marqués d'un *

Erreur

Le champ email est obligatoire

Erreur

Veuillez renseigner un Email valide (nomprenom@gmail.com)

Erreur

Le champ Mot de passe est obligatoire

Erreur

Veuillez renseigner un Mot de passe valide (6 caractères minimum)

Votre mot de passe doit contenir minimum 6 caractères.
Erreur

Le champ Nom est obligatoire

Erreur

Le champ Prénom est obligatoire

Genre

Dans l'exemple ci-dessus :

  • Les champs obligatoires qui ne sont pas remplis ont des messages d'erreurs pertinents et uniques (exemple : Le champ email est obligatoire)
  • Les champs avec une entrée invalide ont un message précis qui donne des suggestions de correction (exemple : Veuillez renseigner un Email valide (nomprenom@gmail.com))
  • Les messages d'erreurs sont liés au champ grâce à l'attribut aria-describedby ou aria-labelledby, ce qui permettra aux technologies d'assistances de restituer l'information
  • Le focus clavier est mis sur le premier champ en erreur afin de pouvoir rebalayer tout le formulaire

Utilisation de l'attribut autocomplete #

L'attribut autocomplete permet de faciliter le remplissage des champs qui contiennent une information personnelle. Tous les champs dont le type est listé dans 7. InputPurposes for User Interface Components doivent contenir l'attribut autocomplete.

Dans notre exemple les champs ci-dessous devront avoir un attribut autocomplete :

  • Email avec autocomplete="email"
  • Mot de passe avec autocomplete="new-password"
  • Nom avec autocomplete="name"
  • Prénom avec autocomplete="given-name"

Exemple complet #

Le code HTML et Javascript complet qui nous a permis de réaliser ce formulaire d'inscription accessible.

  
  <div class="col-md-8">
    <form id="formulaire" class="border border-secondary p-3 my-2">
      <p>Tous les champs obligatoires sont marqués d'un *</p>
      <div class="mb-2">
        <label for="email" class="form-label">Email *</label>
        <input type="text" class="form-control" id="email" autocomplete="email" required/>
        <div id="erroremailDiv" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="erroremail1">Le champ email est obligatoire</p>
        </div>
        <div id="erroremailDiv2" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="erroremail2">Veuillez renseigner un Email valide (nomprenom@gmail.com)</p>
        </div>
      </div>
      <label for="password" class="form-label">Mot de passe *</label>
      <div class="mb-2 input-group">
        <input type="password" class="form-control" id="password" aria-describedby="passwordHelpBlock" autocomplete="new-password" required/>
        <span class="input-group-text">
          <button type="button" class="btn btn-icon btn-secondary btn-sm" id="password_visibility" title="Afficher le mot de passe" >
            <svg aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 1000 1000"></svg>
          </button>
        </span>
      </div>
      <div id="errorpasswordDiv" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="errorpassword1">Le champ Mot de passe est obligatoire</p>
        </div>
        <div id="errorpasswordDiv2" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="errorpassword2">Veuillez renseigner un Mot de passe valide (6 caractères minimum)</p>
        </div>
      <div id="passwordHelpBlock" class="form-text">
          Votre mot de passe doit contenir minimum 6 caractères.
      </div>
      <div class="mb-2">
        <label for="name" class="form-label">Nom *</label>
        <input type="text" class="form-control" id="name" autocomplete="name" required/>
        <div id="errorname" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="errorname1">Le champ Nom est obligatoire</p>
        </div>
      </div>
      <div class="mb-2">
        <label for="firstname" class="form-label">Prénom *</label>
        <input type="text" class="form-control" id="firstname" autocomplete="given-name" required/>
        <div id="errorfirstname" class="alert alert-danger alert-sm d-none">
          <span class="alert-icon"><span class="visually-hidden">Erreur</span></span>
          <p id="errorfirstname1">Le champ Prénom est obligatoire</p>
        </div>
      </div>
      <fieldset>
        <legend>Genre</legend>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" name="inlineRadioOptions" id="M" value="M" selected>
          <label class="form-check-label" for="M">M</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" name="inlineRadioOptions" id="Mme" value="Mme">
          <label class="form-check-label" for="Mme">Mme</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" name="inlineRadioOptions" id="Non-binaire" value="Non-binaire" >
          <label class="form-check-label" for="Non-binaire">Non-binaire</label>
        </div>
      </fieldset>
      <div class="mb-2">
        <label for="adresse" class="form-label">Adresse</label>
        <input type="text" class="form-control" id="adresse"/>
      </div>
      <div class="mb-2">
        <label for="adresse2" class="form-label">Complément d'adresse</label>
        <input type="text" class="form-control" id="adresse2"/>
      </div>
      <div class="mb-2">
        <label for="ville" class="form-label">Ville</label>
        <input type="text" class="form-control" id="ville"/>
      </div>
      <div class="mb-2">
        <label for="cp" class="form-label">Code postal</label>
        <input type="text" class="form-control" id="cp"/>
      </div>
      <button id="submit" class="btn btn-primary">Soumettre</button>
      <div id="alertsucces" class="alert alert-success d-none" role="alert">
        <span class="alert-icon"><span class="visually-hidden">Succès</span></span>
        <p>La validation du formulaire est réussi.</p>
      </div>
    </form>
  </div>
  
  
  document.addEventListener("DOMContentLoaded", function(event) {

    document.getElementById("password_visibility").onclick = function (e){
        let password = document.getElementById("password");
        if(password.type=="password"){
            password.type="text";
            this.title ="Cacher le mot de passe";
        }
        else{
            password.type="password";
            this.title ="Afficher le mot de passe";
        }
    }

    document.getElementById("submit").onclick = function (e){
        e.preventDefault();

        let error=false;

        let email = document.getElementById("email");
        let password = document.getElementById("password");
        let name = document.getElementById("name");
        let firstname = document.getElementById("firstname");

        if(firstname.value==""){
            error=invalid(firstname,"errorfirstname1");
            document.getElementById("errorfirstname").classList.remove("d-none");

        }
        else{
            valid(firstname);
            document.getElementById("errorfirstname").classList.add("d-none")
        }

        if(name.value==""){
            error=invalid(name,"errorname1");
            document.getElementById("errorname").classList.remove("d-none");
        }
        else{
            valid(name);
            document.getElementById("errorname").classList.add("d-none")
        }

        if(password.value==""){
            error=invalid(password,"errorpassword1 passwordHelpBlock");
            document.getElementById("errorpasswordDiv").classList.remove("d-none");
        }
        else{
            if(password.value.length>=6){
                valid(password);
                document.getElementById("errorpasswordDiv").classList.add("d-none")
                document.getElementById("errorpasswordDiv2").classList.add("d-none")
                password.setAttribute("aria-describedby", "passwordHelpBlock");
            }
            else{
                error=invalid(password,"errorpassword2 passwordHelpBlock");
                document.getElementById("errorpasswordDiv").classList.add("d-none")
                document.getElementById("errorpasswordDiv2").classList.remove("d-none");
            }   
        }
        if(email.value==""){
            error=invalid(email,"erroremail1");
            document.getElementById("erroremailDiv").classList.remove("d-none");
        }
        else{
            if(validateEmail(email.value)){
                valid(email);
                document.getElementById("erroremailDiv").classList.add("d-none")
                document.getElementById("erroremailDiv2").classList.add("d-none")
            }
            else{
                error=invalid(email,"erroremail2");
                document.getElementById("erroremailDiv").classList.add("d-none")
                document.getElementById("erroremailDiv2").classList.remove("d-none");
            }
            
        }

        if(error){
            document.getElementById("alertsucces").classList.add("d-none")
        }
        else{
            document.getElementById("alertsucces").classList.remove("d-none")
        }
    }

    function valid (element){
        element.setAttribute("aria-invalid", false)
        element.classList.remove("is-invalid");
        element.removeAttribute("aria-describedby")
    }

    function invalid(element,errorDiv){
        element.setAttribute("aria-invalid", true);
        element.classList.add("is-invalid");
        element.setAttribute("aria-describedby", errorDiv);
        element.focus();
        return true;
    }

    const validateEmail = (email) => {
        return String(email)
          .toLowerCase()
          .match(
            /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
          );
      };
})
  

Lien vers la première partie #

Formulaire partie 1 - Structurer son formulaire