Pourquoi la validation avancée des formulaires compte

Bien que les attributs HTML5 fournissent une base de référence solide, ils sont souvent insuffisants lorsque vous devez appliquer des règles commerciales complexes, fournir des retours en temps réel ou créer une expérience utilisateur fluide sur différents navigateurs. La validation basée sur JavaScript vous donne un contrôle complet sur le moment et la manière de procéder de la validation, vous permettant d'appliquer des règles personnalisées telles que les compteurs de force de mot de passe, l'appariement de patrons pour les noms d'utilisateur, la logique de champ dépendante et des vérifications asynchrones comme si un nom d'utilisateur est déjà pris.

La validation côté client réduit également la charge du serveur et accélère la boucle de rétroaction pour les utilisateurs. Au lieu de soumettre un formulaire et d'attendre une réponse du serveur, les erreurs sont capturées immédiatement. Cependant, il est essentiel de se rappeler que la validation côté client est une convenience[, pas une mesure de sécurité.

Dans ce guide, nous allons vous aider à construire un système de validation complet et avancé en utilisant le JavaScript vanille. Vous apprendrez à définir des règles personnalisées, afficher des messages d'erreur dynamiques, valider plusieurs champs ensemble et garder votre code à jour. Chaque section comprend des extraits de code prêts à la production que vous pouvez adapter immédiatement.

Comprendre les limites de la validation HTML5

Les attributs HTML5 comme , et sont excellents pour les vérifications simples, mais ils manquent de flexibilité.

  • Aucune validation de champ croisé – Vous ne pouvez pas confirmer qu'un mot de passe et un champ de confirmation correspondent en utilisant HTML5 seul.
  • Message d'erreur limité – Les outils intégrés du navigateur sont incohérents dans tous les navigateurs et ne peuvent pas être conçus entièrement.
  • Aucune mise à jour en temps réel – La validation HTML5 ne s'allume habituellement que sur la soumission de formulaire, et non sur chaque frappe.
  • Constraint Validation API[ – Bien que puissant, il repose toujours sur les règles intégrées du navigateur et manque de la capacité de définir une logique personnalisée complexe facilement.

JavaScript comble ces lacunes. Avec des auditeurs d'événements et des fonctions personnalisées, vous pouvez créer une validation à la fois expressive et conviviale.

Configuration de la structure du formulaire HTML

Commencez par un formulaire sémantique propre. Voici un exemple qui inclut des champs pour nom d'utilisateur, courriel, mot de passe et confirmer le mot de passe. Nous avons ajouté des attributs pour l'accessibilité et des éléments vides qui vont contenir des messages d'erreur.

<form id="registrationForm" novalidate>
 <div class="form-group">
 <label for="username">Username</label>
 <input type="text" id="username" name="username" placeholder="e.g. johndoe" required>
 <span id="usernameError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="email">Email address</label>
 <input type="email" id="email" name="email" placeholder="[email protected]" required>
 <span id="emailError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="password">Password</label>
 <input type="password" id="password" name="password" placeholder="At least 8 characters" required>
 <span id="passwordError" class="error-message" role="alert"></span>
 </div>

 <div class="form-group">
 <label for="confirmPassword">Confirm Password</label>
 <input type="password" id="confirmPassword" name="confirmPassword" placeholder="Repeat password" required>
 <span id="confirmPasswordError" class="error-message" role="alert"></span>
 </div>

 <button type="submit">Register</button>
</form>

Notez l'attribut sur le formulaire. Ceci indique au navigateur d'éteindre sa propre validation intégrée afin que nous puissions gérer tout avec JavaScript. Nous utilisons CSS pour afficher ou masquer des messages d'erreur et ajouter des retours visuels (par exemple, bordures rouges) aux champs invalides.

Conception d'une logique de validation centrale

Au lieu de diffuser le code de validation dans les gestionnaires d'événements, nous créons un objet central qui tient des règles personnalisées. Chaque règle est une fonction qui renvoie (valable) ou (invalide) avec un message d'erreur optionnel. Cette approche facilite l'extension et la maintenance du code.

const validators = {
 required: (value) => value.trim() !== '' || 'This field is required.',
 email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || 'Please enter a valid email address.',
 minLength: (min) => (value) => value.length >= min || `Must be at least ${min} characters.`,
 passwordStrength: (value) => {
 const errors = [];
 if (value.length < 8) errors.push('At least 8 characters.');
 if (!/[A-Z]/.test(value)) errors.push('One uppercase letter.');
 if (!/[0-9]/.test(value)) errors.push('One number.');
 if (!/[!@#$%^&*]/.test(value)) errors.push('One special character.');
 return errors.length === 0 || errors.join(' ');
 },
 match: (otherFieldId) => (value, formData) => {
 const otherValue = formData.get(otherFieldId);
 return value === otherValue || 'Passwords do not match.';
 }
};

Notez que les validateurs peuvent être paramétrés – retourne une fonction qui attend la valeur. Le validateur reçoit l'id d'un autre champ et utilise (un objet ) pour comparer les valeurs. Ce motif est très réutilisable.

Création de règles personnalisées pour chaque champ

Définissez quels validateurs s'appliquent à chaque champ. Ce mappage est un objet simple où les clés sont des noms de champ et les valeurs sont des tableaux de fonctions de validateur.

const fieldRules = {
 username: [
 validators.required,
 validators.minLength(3),
 (value) => /^[a-zA-Z0-9_]+$/.test(value) || 'Usernames can only contain letters, numbers, and underscores.'
 ],
 email: [
 validators.required,
 validators.email
 ],
 password: [
 validators.required,
 validators.passwordStrength
 ],
 confirmPassword: [
 validators.required,
 validators.match('password')
 ]
};

Vous pouvez facilement ajouter une nouvelle règle personnalisée, telle qu'une vérification que le nom d'utilisateur n'est pas déjà pris (validation async), en ajoutant une fonction async à la liste. Pour l'instant, nous allons garder toutes les règles synchrones.

Validation en cours sur la présentation de formulaire

Joindre un auditeur d'événement au formulaire qui empêche la soumission, exécute tous les validateurs et recueille les erreurs. Si aucune erreur n'est trouvée, le formulaire est autorisé à soumettre.

const form = document.getElementById('registrationForm');

form.addEventListener('submit', (event) => {
 event.preventDefault();
 const formData = new FormData(form);
 const errors = validateForm(formData);

 if (Object.keys(errors).length === 0) {
 // All valid – you can submit programmatically or send via fetch
 console.log('Form is valid. Submitting...');
 // form.submit();
 } else {
 displayErrors(errors);
 }
});

La fonction itère sur chaque champ, exécute ses validateurs et recueille les messages d'erreur. Voici l'implémentation:

function validateForm(formData) {
 const errors = {};
 for (const [fieldName, rules] of Object.entries(fieldRules)) {
 const value = formData.get(fieldName) || '';
 for (const rule of rules) {
 const result = rule(value, formData);
 if (result !== true) {
 errors[fieldName] = result; // result is the error string
 break; // stop after first failure for this field
 }
 }
 }
 return errors;
}

Affichage de la rétroaction de validation en temps réel

Les utilisateurs profitent de la lecture d'erreurs telles qu'elles sont saisies, et non seulement sur soumission. Ajoutez les auditeurs d'événements pour et sur chaque champ. Pour éviter les utilisateurs accablants, un bon modèle est de valider sur flou (lorsqu'ils quittent le champ) et ensuite de valider à nouveau sur chaque entrée subséquente jusqu'à ce que l'erreur soit résolue.

document.querySelectorAll('#registrationForm input').forEach((input) => {
 input.addEventListener('blur', () => {
 validateField(input.id, input.value);
 });
 input.addEventListener('input', () => {
 // If the field currently has an error, re-validate on each keystroke
 const errorSpan = document.getElementById(input.id + 'Error');
 if (errorSpan.textContent !== '') {
 validateField(input.id, input.value);
 }
 });
});

La fonction ne vérifie que les règles d'un seul champ et met à jour la plage d'erreur correspondante.

function validateField(fieldId, value) {
 const formData = new FormData(form);
 formData.set(fieldId, value);
 const rules = fieldRules[fieldId];
 if (!rules) return;
 for (const rule of rules) {
 const result = rule(value, formData);
 if (result !== true) {
 setFieldError(fieldId, result);
 return;
 }
 }
 clearFieldError(fieldId);
}

Erreurs de style et états de réussite

Utilisez CSS pour modifier l'apparence visuelle des champs valides/invalides. Les fonctions et ajoutent ou suppriment des classes CSS et mettent à jour l'attribut pour faciliter l'accès.

function setFieldError(fieldId, message) {
 const input = document.getElementById(fieldId);
 const errorSpan = document.getElementById(fieldId + 'Error');
 input.classList.add('is-invalid');
 input.classList.remove('is-valid');
 input.setAttribute('aria-invalid', 'true');
 errorSpan.textContent = message;
}

function clearFieldError(fieldId) {
 const input = document.getElementById(fieldId);
 const errorSpan = document.getElementById(fieldId + 'Error');
 input.classList.remove('is-invalid');
 input.classList.add('is-valid');
 input.setAttribute('aria-invalid', 'false');
 errorSpan.textContent = '';
}

La CSS correspondante pourrait être :

.is-invalid {
 border-color: #dc3545;
}
.is-valid {
 border-color: #28a745;
}
.error-message {
 color: #dc3545;
 font-size: 0.875rem;
 min-height: 1.2em;
}

Construire un compteur de force de mot de passe

Un compteur de force de mot de passe fournit une rétroaction visuelle qui encourage les utilisateurs à créer des mots de passe plus forts. Plutôt que de faire un simple pass/échec, vous pouvez calculer un score et afficher une barre de progression.

function passwordStrengthScore(password) {
 let score = 0;
 if (password.length >= 8) score += 1;
 if (password.length >= 12) score += 1;
 if (/[A-Z]/.test(password)) score += 1;
 if (/[a-z]/.test(password)) score += 1;
 if (/[0-9]/.test(password)) score += 1;
 if (/[^A-Za-z0-9]/.test(password)) score += 1;
 return score; // 0-6
}

Joindre un événement au champ de mot de passe qui met à jour un élément de compteur. Vous pouvez mapper la partition sur une étiquette comme Weak (0-2), Fair (3-4), Strong (5-6).

const passwordMeter = document.getElementById('passwordStrengthMeter');
const passwordInput = document.getElementById('password');

passwordInput.addEventListener('input', () => {
 const score = passwordStrengthScore(passwordInput.value);
 const percentage = (score / 6) * 100;
 passwordMeter.value = percentage;
 passwordMeter.style.accentColor = score < 3 ? '#dc3545' : score < 5 ? '#ffc107' : '#28a745';
});

Règles personnalisées asynchrones

Certaines vérifications de validation nécessitent une excursion vers le serveur, comme la vérification si un nom d'utilisateur ou un courriel est déjà enregistré. Pour gérer cela sans bloquer l'interface utilisateur, vous pouvez faire de votre validateur une fonction async et ajuster la logique de validation pour attendre le résultat.

const asyncValidators = {
 uniqueUsername: async (value) => {
 try {
 const response = await fetch(`/api/check-username?username=${encodeURIComponent(value)}`);
 const data = await response.json();
 return data.available || 'Username is already taken.';
 } catch {
 return 'Could not verify username availability.';
 }
 }
};

Puis mettre à jour pour prendre en charge les validateurs asynchrones. Vous devez également gérer l'état de chargement pour éviter plusieurs requêtes simultanées (dénoncer la requête).

Meilleures pratiques pour les formulaires de production

  • Validez toujours le côté serveur. La validation côté client est une commodité, jamais une couche de sécurité. Utilisez les mêmes règles sur le moteur de réception pour rejeter les données malveillantes ou malformées.
  • Débonce async checks. Si vous validez sur un clavier pour les appels de serveur, utilisez un minuteur de débonflage (p. ex. 300 ms) pour éviter de frapper le serveur.
  • Utilisez l'API de validation des contraintes lorsque c'est possible. Vous pouvez mélanger JavaScript personnalisé avec les méthodes intégrées et pour une approche hybride. Ceci est utile pour les champs qui ont des contraintes standard mais qui ont besoin de messages personnalisés.
  • Faire la validation accessible.Utilisez pour associer des messages d'erreur avec des entrées.Mise à jour et ] régions afin que les lecteurs d'écran annoncent des erreurs.
  • Test sur les navigateurs et les appareils. La manipulation des événements tactiles, des claviers virtuels et des différentes tailles d'écran est essentielle.
  • Gardez votre performance à l'esprit. Pour les formulaires complexes avec de nombreux champs, la validation par lots ou l'utilisation pour les mises à jour de l'interface utilisateur peut empêcher jank.
  • Fournir des messages d'erreur clairs et constructifs[. Dites à l'utilisateur exactement ce qui ne va pas et comment le corriger. Évitez les messages génériques comme «Input invalide».

Aller plus loin : Validation modulaire avec les bibliothèques

Si votre projet nécessite un niveau élevé de règles personnalisées et de validation en temps réel, vous pouvez considérer une bibliothèque légère comme Validate.js ou l'API de validation Constraint. Cependant, l'approche JavaScript vanillée présentée dans cet article vous donne un contrôle total et aucune dépendance, ce qui est idéal pour les applications sensibles aux performances ou lorsque vous devez suivre des lignes directrices spécifiques de conception.

Vous pouvez également combiner validation personnalisée avec des cadres modernes comme React ou Vue, mais les concepts de base de séparation des règles de présentation restent les mêmes.

Pièges fréquents à éviter

  • Validation uniquement sur submit. Les utilisateurs préfèrent une rétroaction immédiate. Au minimum, valider sur flou pour chaque champ.
  • Ne pas réinitialiser les erreurs lorsque les champs deviennent valides.Une fois qu'un utilisateur corrige une erreur, effacez le message. La logique en temps réel de cet article gère automatiquement cela.
  • Si l'utilisateur obéit à la validation. JavaScript peut être désactivé entièrement. Sauvegardez toujours chaque vérification avec la logique côté serveur.
  • Règles de surcomplication. Gardez les validateurs simples et concentrés. Une fonction doit tester une préoccupation. Combinez-les pour faire appliquer plusieurs critères.

Conclusion

En structurant votre code autour des fonctions de validation réutilisables et des retours en temps réel, vous pouvez tout gérer, des champs obligatoires de base aux compteurs complexes de résistance aux mots de passe et aux comparaisons entre champs. Les exemples fournis dans cet article vous donnent une base solide pour vous adapter à vos propres projets.

N'oubliez pas que la validation est une partie critique de l'expérience utilisateur. Testez soigneusement vos formulaires, écoutez les commentaires des utilisateurs et joignez toujours la validation côté client à des vérifications côté serveur robustes. Pour plus de détails, consultez le W3C Web Accessibility Initiative Guide on form validation et l'article MDN sur la validation du formulaire.

En suivant les modèles et les meilleures pratiques décrits ici, vous créerez des formulaires à la fois sûrs et agréables à utiliser. Commencez par expérimenter les extraits de code, personnaliser les règles pour correspondre à votre logique d'affaires, et itérer en fonction des interactions réelles avec les utilisateurs.