Prérequis
- Niveau : bases JavaScript et formulaires HTML.
- Outils : VS Code + Live Server, navigateur moderne (DevTools onglet Network pour observer la soumission).
- Temps estimé : 1 h.
Pourquoi valider en JavaScript ?
La validation JS améliore l’UX : retour immédiat sans attendre le serveur. Mais elle ne remplace JAMAIS la validation serveur — un attaquant peut désactiver le JS en 2 clics depuis les DevTools. Règle d’or : validez côté client pour la fluidité, et systématiquement côté serveur pour la sécurité.
Pourquoi valider côté client ?
La validation JavaScript améliore l’expérience utilisateur en signalant les erreurs instantanément, avant l’envoi au serveur. Attention : la validation côté serveur reste obligatoire pour la sécurité.
Validation HTML5 native (le minimum)
<form id="inscription">
<input type="text" name="nom" required minlength="2" maxlength="50"
placeholder="Votre nom">
<input type="email" name="email" required
placeholder="votre@email.com">
<input type="tel" name="telephone"
pattern="[0-9]{9,12}" placeholder="771234567">
<input type="password" name="motdepasse" required minlength="8"
placeholder="Mot de passe (8 caractères min)">
<button type="submit">S'inscrire</button>
</form>
Les attributs required, minlength, pattern et type="email" font déjà beaucoup de travail sans JavaScript !
Validation JavaScript personnalisée
const form = document.getElementById('inscription');
form.addEventListener('submit', function(e) {
// Empêcher l'envoi si invalide
e.preventDefault();
// Récupérer les valeurs
const nom = form.nom.value.trim();
const email = form.email.value.trim();
const tel = form.telephone.value.trim();
const mdp = form.motdepasse.value;
// Tableau d'erreurs
const erreurs = [];
// Validation du nom
if (nom.length < 2) {
erreurs.push('Le nom doit contenir au moins 2 caractères');
}
// Validation email avec regex
const regexEmail = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!regexEmail.test(email)) {
erreurs.push('Adresse email invalide');
}
// Validation téléphone sénégalais
const regexTel = /^(70|75|76|77|78)[0-9]{7}$/;
if (tel && !regexTel.test(tel)) {
erreurs.push('Numéro de téléphone invalide (format : 77XXXXXXX)');
}
// Validation mot de passe robuste
if (mdp.length < 8) {
erreurs.push('Le mot de passe doit contenir au moins 8 caractères');
}
if (!/[A-Z]/.test(mdp)) {
erreurs.push('Le mot de passe doit contenir une majuscule');
}
if (!/[0-9]/.test(mdp)) {
erreurs.push('Le mot de passe doit contenir un chiffre');
}
// Afficher les erreurs ou soumettre
if (erreurs.length > 0) {
afficherErreurs(erreurs);
} else {
form.submit(); // Tout est OK, on envoie
}
});
Afficher les erreurs de manière visuelle
function afficherErreurs(erreurs) {
// Supprimer les anciens messages
document.querySelectorAll('.erreur').forEach(el => el.remove());
document.querySelectorAll('.champ-erreur').forEach(el => {
el.classList.remove('champ-erreur');
});
erreurs.forEach(erreur => {
const div = document.createElement('div');
div.className = 'erreur';
div.textContent = '⚠️ ' + erreur;
form.insertBefore(div, form.querySelector('button'));
});
}
// CSS pour les erreurs
const style = document.createElement('style');
style.textContent = `
.erreur {
background: #ffebee;
color: #c62828;
padding: 10px 15px;
border-radius: 5px;
margin: 5px 0;
border-left: 4px solid #c62828;
font-size: 14px;
}
.champ-erreur {
border: 2px solid #c62828 !important;
background: #fff5f5;
}
input:valid { border: 2px solid #4caf50; }
input:focus:invalid { border: 2px solid #ff9800; }
`;
document.head.appendChild(style);
Validation en temps réel (pendant la saisie)
// Valider chaque champ pendant la saisie
form.email.addEventListener('input', function() {
const regexEmail = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (this.value === '') {
this.style.borderColor = '#ddd';
} else if (regexEmail.test(this.value)) {
this.style.borderColor = '#4caf50'; // Vert = valide
supprimerErreur(this);
} else {
this.style.borderColor = '#f44336'; // Rouge = invalide
}
});
// Indicateur de force du mot de passe
form.motdepasse.addEventListener('input', function() {
const mdp = this.value;
let force = 0;
if (mdp.length >= 8) force++;
if (mdp.length >= 12) force++;
if (/[A-Z]/.test(mdp)) force++;
if (/[0-9]/.test(mdp)) force++;
if (/[^A-Za-z0-9]/.test(mdp)) force++;
const barre = document.getElementById('force-mdp');
const niveaux = ['Très faible', 'Faible', 'Moyen', 'Fort', 'Très fort'];
const couleurs = ['#f44336', '#ff9800', '#ffeb3b', '#8bc34a', '#4caf50'];
barre.style.width = (force * 20) + '%';
barre.style.background = couleurs[force - 1] || '#eee';
barre.textContent = niveaux[force - 1] || '';
});
⚠️ Règles de sécurité importantes
- Ne jamais faire confiance au client : la validation JS peut être contournée (DevTools)
- Toujours revalider côté serveur (PHP, Node.js, Python)
- Échapper les données avant de les afficher (prévenir les attaques XSS)
- Utiliser des requêtes préparées pour les bases de données (prévenir les injections SQL)
Regex utiles pour la validation
| Type | Regex | Exemple valide |
|---|---|---|
/^[\w.-]+@[\w.-]+\.\w{2,}$/ |
user@mail.com | |
| Tél Sénégal | /^(70|75|76|77|78)\d{7}$/ |
771234567 |
| URL | /^https?:\/\/[\w.-]+/ |
https://site.sn |
| Code postal FR | /^\d{5}$/ |
75001 |
Erreurs fréquentes
Validation contournée par DevTools
Cause : on ne valide qu’en JS, sans copie côté serveur.
Solution : validez TOUJOURS côté serveur (PHP/Node/Python). Le JS est une couche de confort, pas de sécurité.
Regex email trop stricte ou trop laxiste
Cause : regex faite « à la main » qui rejette des emails valides (TLD > 6 lettres, plus, accents).
Solution : en réalité, type="email" + un simple /.+@.+\..+/ + envoi d’un email de confirmation est plus fiable que toute regex maison.
Validation en temps réel agressive
Cause : on affiche « invalide » dès la 1ère lettre tapée.
Solution : validez sur blur (sortie du champ) la 1ère fois, puis sur input ensuite. Pattern UX standard.
Identifiants/noms de champ accentués
Cause : name="téléphone" + accès form.téléphone en JS — fragile selon les navigateurs et le bundler.
Solution : ASCII pour les attributs name et id (telephone), texte du label en français.
Exercice pratique
🎯 Défi : Formulaire d’inscription complet
- Créez un formulaire avec : nom, prénom, email, téléphone, mot de passe, confirmation mot de passe
- Ajoutez la validation en temps réel sur chaque champ
- Vérifiez que les 2 mots de passe correspondent
- Ajoutez un indicateur de force du mot de passe
- Affichez un résumé des erreurs au-dessus du bouton submit
Validation HTML5 native vs validation JavaScript : quand utiliser laquelle
Avant de coder de la validation JavaScript, vérifiez si HTML5 ne couvre pas déjà votre besoin. Les attributs required, type="email", type="tel", pattern="...", minlength et maxlength sur les inputs déclenchent une validation native gérée par le navigateur — sans une ligne de JS. Pour 70 % des formulaires PME (contact, inscription newsletter, demande de devis), HTML5 seul suffit largement.
La validation JavaScript devient nécessaire dans trois cas. Premier cas : règles métier complexes que les attributs HTML5 ne couvrent pas (par exemple, un numéro fiscal sénégalais avec un format spécifique, ou un mot de passe avec contraintes croisées). Deuxième cas : feedback utilisateur en temps réel pendant la saisie (afficher la force du mot de passe au fur et à mesure des frappes). Troisième cas : validation cross-field (la date de fin doit être après la date de début, le total doit correspondre à la somme des lignes).
Pour ces cas, JavaScript prend le relais avec l’API HTMLFormElement.checkValidity() et l’événement invalid qui s’intègrent harmonieusement avec la validation HTML5 native. Pas besoin d’écrire un système from scratch — réutilisez l’infrastructure du navigateur.
Validation côté client ne remplace jamais la validation côté serveur
Une erreur fréquente chez les développeurs juniors est de considérer la validation JavaScript comme une protection suffisante. Ce n’est pas le cas. Un attaquant peut désactiver JavaScript dans son navigateur, bypasser le formulaire en envoyant directement la requête HTTP avec curl, ou modifier le code via la console DevTools. Toute validation côté client est purement cosmétique et orientée UX — elle améliore l’expérience utilisateur mais ne sécurise rien.
La validation côté serveur reste obligatoire pour trois objectifs : protéger l’intégrité des données stockées en base, prévenir les attaques par injection (SQL, XSS, headers), et bloquer les abus (rate limiting, captcha). Pour un formulaire de contact PHP, la combinaison standard est : validation JavaScript pour l’UX, validation PHP côté serveur pour la sécurité, plus une vérification CAPTCHA si le formulaire est public et exposé. Voir le tutoriel d’envoi d’email avec PHP pour la partie serveur.
Bibliothèques de validation modernes en 2026
Pour les projets sérieux qui dépassent quelques formulaires simples, plusieurs bibliothèques de validation JavaScript dominent en 2026. Zod (90k+ étoiles GitHub) est devenu le standard de facto dans l’écosystème TypeScript. Il permet de définir un schéma de validation type-safe qui sert à la fois côté client et côté serveur (Node.js, Deno, Bun), avec inférence automatique des types TypeScript. Pour une équipe full-stack TypeScript, Zod élimine la duplication des règles entre frontend et backend.
Pour les projets vanilla ou jQuery legacy, jQuery Validation Plugin reste utilisé mais ne reçoit plus de mises à jour majeures depuis 2023. Just Validate est une alternative moderne sans dépendance jQuery, légère (≈ 8 KB) et bien documentée. Valibot (lancé en 2024) est une alternative à Zod plus légère pour les contextes edge runtime où chaque KB compte.
Pour un POC ou un site vitrine simple, restez sur la validation native HTML5 + quelques lignes de JS personnalisé. Pour une application produit avec dizaines de formulaires, investir dans Zod ou Valibot dès le départ paie en cohérence et en maintenabilité.
Accessibilité : afficher les erreurs sans casser les lecteurs d’écran
Une validation qui affiche un texte rouge en dessous du champ peut être totalement invisible pour un utilisateur de lecteur d’écran. La conformité WCAG 2.2 niveau AA exige que les messages d’erreur soient annoncés correctement via les ARIA live regions. Trois patterns techniques sont à connaître. Premier pattern : associer chaque champ à son message d’erreur via aria-describedby qui pointe vers l’ID du span d’erreur. Le lecteur d’écran lit alors automatiquement le message quand le champ est focusé.
Deuxième pattern : marquer le champ invalide avec aria-invalid="true" dynamiquement quand la validation échoue. Cela donne une signalisation supplémentaire aux technologies d’assistance, indépendamment du style visuel. Troisième pattern : utiliser une zone role="alert" ou aria-live="assertive" pour annoncer les erreurs critiques (par exemple échec de soumission du formulaire entier). Pour les erreurs ponctuelles par champ, aria-live="polite" suffit et reste moins intrusif.
Comparaison vanilla JS, jQuery et frameworks modernes
Pour les formulaires simples (3-5 champs), du JavaScript vanilla avec validation native HTML5 et quelques événements blur personnalisés est imbattable en simplicité et en performance. Pour les projets jQuery legacy maintenus, jQuery Validation Plugin reste pertinent même en 2026 — pas la peine de réécrire tout pour ajouter de la validation. Pour les applications React, Vue ou Angular modernes, basculer vers React Hook Form, VeeValidate ou Angular Reactive Forms qui s’intègrent profondément avec le système de gestion d’état du framework et donnent une expérience développeur supérieure.
Adaptation au contexte ouest-africain
Pour un développeur ou une PME qui livre des formulaires à des utilisateurs basés à Dakar, Abidjan, Bamako ou Cotonou, deux contraintes locales pèsent sur les choix de validation. Première contrainte : la connectivité 4G inégale. Une validation entièrement côté serveur (chaque blur de champ déclenche une requête HTTP de validation) devient pénible quand le réseau oscille à 100 ms de latence. Préférez une validation côté client maximale, et n’envoyez au serveur que pour les vérifications réellement nécessaires (unicité d’email en base, par exemple).
Deuxième contrainte : la diversité des formats locaux. Les numéros de téléphone ouest-africains suivent le format international avec préfixe pays (+221 Sénégal, +225 Côte d’Ivoire, +223 Mali) et 8 à 9 chiffres derrière. Validez avec une regex permissive qui accepte les variantes (+221 77 123 45 67, +221771234567, 0077-12-34-567) plutôt qu’un format strict qui rejette des numéros valides. Idem pour les codes postaux et adresses qui n’ont pas de format universel comme en France ou aux US.
Troisième conseil pratique : tester vos formulaires sur un téléphone Android à 200 EUR avec une connexion 4G dégradée, pas seulement sur un Mac avec fibre. C’est le contexte réel d’usage de la majorité de vos utilisateurs locaux.
Patterns regex utiles pour formulaires ouest-africains
Quelques regex testées et fiables pour les cas d’usage récurrents en contexte ouest-africain. Première regex : numéro de téléphone à format international tolérant. /^\+?[0-9\s().-]{8,20}$/ accepte la plupart des variantes sans bloquer les utilisateurs qui mettent des espaces ou tirets. Pour valider strictement un numéro sénégalais, utiliser /^(?:\+221|00221)?[\s.-]?[7][0-9]{8}$/ qui couvre les formats avec ou sans préfixe pays.
Deuxième regex : code de transaction Wave ou Orange Money. Ces codes suivent généralement un format alphanumérique de 10 à 16 caractères : /^[A-Z0-9]{10,16}$/i. Troisième regex : numéro fiscal NINEA (Sénégal). Format à 7 chiffres puis 4 chiffres : /^\d{7}[A-Z]?\d{4}$/. Quatrième regex : registre de commerce RCCM ouest-africain. Format SN-DKR-2024-B-12345 avec le code pays, ville, année et numéro : /^[A-Z]{2}-[A-Z]{3}-\d{4}-[A-Z]-\d+$/.
Toujours tester ces regex avec une vingtaine d’exemples réels avant de les déployer en production. Un faux négatif (numéro valide rejeté) frustre vos utilisateurs et fait fuir des prospects. Un faux positif (numéro invalide accepté) pollue votre base de données et complique les relances commerciales.
Tester sa validation sans déployer
Plutôt que de tester la validation en cliquant manuellement à chaque modification, écrivez quelques tests automatisés dès le départ. Vitest ou Jest pour Node.js, ou Cypress / Playwright pour les tests end-to-end dans le navigateur. Un test type vérifie qu’un formulaire avec un email valide est accepté, qu’un email malformé est rejeté avec le bon message d’erreur, et que les messages d’accessibilité sont correctement annoncés. Cinq tests suffisent pour couvrir 80 % des régressions possibles sur un formulaire complexe.
Pour les équipes qui ont des dizaines de formulaires à maintenir, centraliser les règles de validation dans un fichier schemas.ts partagé entre frontend et backend économise des heures de débuggage et de divergence accidentelle entre les deux couches du stack.