Formulaire partie 2 - Soumettre son formulaire
Thématiques associées :- Web
- Intermédiaire
- Composant
Date de parution
Mise à jour de l'article du
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 pour les utilisateurs et mettre en place un système de gestion des erreurs lors de la validation du formulaire.
Nous continuerons à baser nos exemples sur le 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
requiredouaria-required="true"dans la baliseinputde 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 ajouter 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.
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 <span class="important">*</span></label>
<input type="text" class="form-control" id="email"/>
</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-outline-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>
<p id="passwordHelpBlock" class="form-text mb-0">
Votre mot de passe doit contenir au moins 6 caractères.
</p>
<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'erreur explicites pour tous les autres utilisateurs
- Si besoin, suggérer des corrections
Si des messages d'erreur empêchent la validation du formulaire, plutôt que de lister les erreurs dans une bannière au début du formulaire, on peut avertir l’utilisateur pour chaque champ en erreur.
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écisez quel champ est invalide et, si possible, en quoi il est invalide)
- Soyez précis et pertinent
- Donnez des pistes de corrections et moyens de corriger
- Assurez-vous que les erreurs sont sous forme de texte et évitez l'utilisation systématique des majuscules.
- Ne vous contentez pas d'utiliser des indicateurs visuels ou seulement la couleur pour signaler les erreurs.
- Laissez 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 le 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
Dans l'exemple ci-dessus :
- Les champs obligatoires qui ne sont pas remplis ont des messages d'erreur 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'erreur sont liés au champ grâce à l'attribut
aria-describedbyouaria-labelledby, ce qui permettra aux technologies d'assistance 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-outline-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>
<p id="passwordHelpBlock" class="form-text mb-0">
Votre mot de passe doit contenir au moins 6 caractères.
</p>
<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>Déjà client ?</legend>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="oui" value="oui" checked>
<label class="form-check-label" for="oui">Oui</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="non" value="non">
<label class="form-check-label" for="non">Non</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éussie.</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,}))$/
);
};
});