Maîtrisez la création de formulaires interactifs et accessibles : inputs, validation, design responsive et expérience utilisateur optimale.
Les formulaires HTML permettent de collecter et envoyer des données utilisateur avec une structure claire et des contrôles appropriés.
Points clés :
label[for]
↔ input[id]
name
pour l'envoi des donnéesDécouvrez tous les types d'inputs et éléments disponibles pour créer des formulaires riches et interactifs.
Utilisez les attributs de validation natifs pour améliorer l'expérience utilisateur et la qualité des données.
Rend un champ obligatoire
Expression régulière pour valider le format
Contraintes de valeur et de longueur
Message d'aide pour l'utilisateur
Validation avec setCustomValidity()
Styles pour les états de validation
Créez des formulaires accessibles, ergonomiques et sécurisés qui offrent une excellente expérience utilisateur.
label[for] ↔ input[id]
aria-describedby
⚠️ La validation HTML5 peut être contournée ! Toujours valider côté serveur.
Créez des formulaires professionnels et interactifs avec ces exercices progressifs couvrant tous les aspects.
Créez un formulaire de contact complet avec validation, accessibilité et design responsive.
Créez un formulaire d'inscription complexe avec tous les types d'inputs et une validation poussée.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exercices - Formulaires HTML</title>
<style>
/* Styles de base pour les formulaires */
* { box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background: #f5f7fa;
}
/* Conteneurs */
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
/* Formulaires */
form { margin-bottom: 30px; }
fieldset {
border: 2px solid #e1e8ed;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
legend {
font-weight: bold;
color: #2c3e50;
padding: 0 10px;
}
/* Labels et inputs */
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #34495e;
}
input, textarea, select {
width: 100%;
padding: 12px;
border: 2px solid #bdc3c7;
border-radius: 6px;
font-size: 16px;
transition: border-color 0.3s, box-shadow 0.3s;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52,152,219,0.2);
}
/* États de validation */
input:valid { border-color: #27ae60; }
input:invalid:not(:focus):not(:placeholder-shown) { border-color: #e74c3c; }
/* Types spéciaux */
input[type="checkbox"], input[type="radio"] {
width: auto;
margin-right: 8px;
}
input[type="file"] {
border: none;
padding: 8px 0;
}
/* Boutons */
button {
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover { background: #2980b9; }
button:disabled {
background: #bdc3c7;
cursor: not-allowed;
}
/* Layout */
.form-group { margin-bottom: 20px; }
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
/* Messages d'aide */
.help-text {
font-size: 14px;
color: #7f8c8d;
margin-top: 5px;
}
.error-message {
color: #e74c3c;
font-size: 14px;
margin-top: 5px;
}
/* Responsive */
@media (max-width: 768px) {
.form-row { grid-template-columns: 1fr; }
.container { margin: 10px; padding: 20px; }
}
</style>
</head>
<body>
<div class="container">
<h1>Exercices - Formulaires HTML</h1>
<!-- EXERCICE 1 : FORMULAIRE DE CONTACT -->
<section>
<h2>Exercice 1 : Formulaire de contact professionnel</h2>
<form action="/contact" method="POST" novalidate>
<!-- TODO: Champ token CSRF -->
<input type="hidden" name="_token" value="csrf_token_here">
<!-- TODO: Fieldset informations personnelles -->
<fieldset>
<legend>Informations personnelles</legend>
<div class="form-row">
<div class="form-group">
<label for="lastname">Nom *</label>
<input
type="text"
id="lastname"
name="lastname"
required
minlength="2"
maxlength="50"
autocomplete="family-name"
placeholder="Votre nom de famille"
>
</div>
<!-- TODO: Champ prénom -->
<div class="form-group">
<label for="firstname">Prénom</label>
<input
type="text"
id="firstname"
name="firstname"
maxlength="50"
autocomplete="given-name"
placeholder="Votre prénom"
>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="email">Email *</label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
placeholder="nom@exemple.com"
>
</div>
<!-- TODO: Champ téléphone -->
<div class="form-group">
<label for="phone">Téléphone</label>
<input
type="tel"
id="phone"
name="phone"
pattern="[0-9]{10}"
autocomplete="tel"
placeholder="0123456789"
title="Numéro de téléphone à 10 chiffres"
>
<div class="help-text">Format: 10 chiffres sans espaces</div>
</div>
</div>
</fieldset>
<!-- TODO: Sujet du message -->
<div class="form-group">
<label for="subject">Sujet *</label>
<select id="subject" name="subject" required>
<option value="">Choisissez un sujet</option>
<option value="info">Demande d'information</option>
<option value="support">Support technique</option>
<!-- TODO: Ajoutez d'autres options -->
<option value="commercial">Question commerciale</option>
<option value="partnership">Partenariat</option>
<option value="other">Autre</option>
</select>
</div>
<!-- TODO: Message -->
<div class="form-group">
<label for="message">Message *</label>
<textarea
id="message"
name="message"
required
minlength="10"
maxlength="1000"
rows="5"
placeholder="Décrivez votre demande en détail..."
></textarea>
<div class="help-text">Minimum 10 caractères, maximum 1000</div>
</div>
<!-- TODO: Cases à cocher -->
<div class="form-group">
<label>
<input type="checkbox" name="newsletter" value="1">
Je souhaite recevoir la newsletter
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="terms" required>
J'accepte les <a href="/terms" target="_blank">conditions d'utilisation</a> *
</label>
</div>
<!-- TODO: Boutons d'action -->
<div class="form-group">
<button type="submit">Envoyer le message</button>
<button type="reset" style="margin-left: 10px; background: #95a5a6;">Effacer</button>
</div>
</form>
</section>
<hr style="margin: 40px 0;">
<!-- EXERCICE 2 : FORMULAIRE D'INSCRIPTION -->
<section>
<h2>Exercice 2 : Formulaire d'inscription avancé</h2>
<form action="/register" method="POST" enctype="multipart/form-data" novalidate>
<!-- TODO: Upload d'avatar -->
<div class="form-group" style="text-align: center;">
<label for="avatar">Photo de profil</label>
<div id="avatar-preview" style="width: 100px; height: 100px; border: 2px dashed #bdc3c7; border-radius: 50%; margin: 10px auto; display: flex; align-items: center; justify-content: center; background: #ecf0f1;">
📷
</div>
<input
type="file"
id="avatar"
name="avatar"
accept="image/jpeg,image/png,image/gif"
>
<div class="help-text">Formats acceptés: JPG, PNG, GIF (max 2MB)</div>
</div>
<!-- TODO: Informations de compte -->
<div class="form-row">
<div class="form-group">
<label for="username">Nom d'utilisateur *</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="20"
pattern="[a-zA-Z0-9_-]{3,20}"
autocomplete="username"
placeholder="mon_nom_utilisateur"
>
<div class="help-text">3-20 caractères, lettres, chiffres, _ et - autorisés</div>
</div>
<div class="form-group">
<label for="register-email">Email *</label>
<input
type="email"
id="register-email"
name="email"
required
autocomplete="email"
placeholder="nom@exemple.com"
>
</div>
</div>
<!-- TODO: Mots de passe -->
<div class="form-row">
<div class="form-group">
<label for="password">Mot de passe *</label>
<input
type="password"
id="confirm-password"
name="confirm_password"
required
autocomplete="new-password"
>
<div id="password-match" class="help-text"></div>
</div>
</div>
<!-- TODO: Informations personnelles -->
<fieldset>
<legend>Informations personnelles</legend>
<div class="form-row">
<div class="form-group">
<label for="birthdate">Date de naissance</label>
<input
type="date"
id="birthdate"
name="birthdate"
min="1900-01-01"
max="2010-12-31"
autocomplete="bday"
>
</div>
<div class="form-group">
<label>Genre</label>
<div style="display: flex; gap: 15px; margin-top: 5px;">
<label>
<input type="radio" name="gender" value="M">
Homme
</label>
<label>
<input type="radio" name="gender" value="F">
Femme
</label>
<label>
<input type="radio" name="gender" value="O">
Autre
</label>
</div>
</div>
</div>
</fieldset>
<!-- TODO: Préférences et intérêts -->
<fieldset>
<legend>Préférences</legend>
<div class="form-group">
<label>Centres d'intérêt (plusieurs choix possibles)</label>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;">
<label>
<input type="checkbox" name="interests[]" value="tech">
Technologie
</label>
<label>
<input type="checkbox" name="interests[]" value="design">
Design
</label>
<label>
<input type="checkbox" name="interests[]" value="business">
Business
</label>
<label>
<input type="checkbox" name="interests[]" value="marketing">
Marketing
</label>
<label>
<input type="checkbox" name="interests[]" value="education">
Éducation
</label>
<label>
<input type="checkbox" name="interests[]" value="health">
Santé
</label>
</div>
</div>
<div class="form-group">
<label for="experience">Niveau d'expérience</label>
<input
type="range"
id="experience"
name="experience"
min="0"
max="10"
value="5"
oninput="document.getElementById('experience-value').textContent = this.value"
>
<div class="help-text">
Années d'expérience: <span id="experience-value">5</span>
</div>
</div>
<div class="form-group">
<label for="theme-color">Couleur de thème préférée</label>
<input
type="color"
id="theme-color"
name="theme_color"
value="#3498db"
style="width: 60px; height: 40px; padding: 2px;"
>
</div>
</fieldset>
<!-- TODO: Acceptation des conditions -->
<div class="form-group">
<label>
<input type="checkbox" name="newsletter_signup" value="1">
Je souhaite recevoir des notifications par email
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="terms_register" required>
J'accepte les <a href="/terms" target="_blank">conditions d'utilisation</a> et la <a href="/privacy" target="_blank">politique de confidentialité</a> *
</label>
</div>
<!-- TODO: Boutons d'action -->
<div class="form-group">
<button type="submit" style="background: #27ae60;">Créer mon compte</button>
<button type="button" onclick="resetFormWithConfirm()" style="margin-left: 10px; background: #95a5a6;">Réinitialiser</button>
</div>
</form>
</section>
<!-- Instructions pour les étudiants -->
<section style="margin-top: 40px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h2>Instructions pour les exercices</h2>
<h3>Exercice 1 - Points à améliorer :</h3>
<ul>
<li>Ajouter une validation en temps réel avec JavaScript</li>
<li>Implémenter des messages d'erreur personnalisés</li>
<li>Améliorer l'accessibilité avec aria-describedby
</li>
<li>Ajouter un indicateur de progression</li>
<li>Tester la soumission et gérer les états de chargement</li>
</ul>
<h3>Exercice 2 - Défis avancés :</h3>
<ul>
<li>Implémenter la prévisualisation d'avatar en temps réel</li>
<li>Créer un indicateur de force de mot de passe dynamique</li>
<li>Ajouter la vérification de disponibilité du nom d'utilisateur</li>
<li>Implémenter une sauvegarde automatique en brouillon</li>
<li>Créer des animations de feedback pour les validations</li>
</ul>
<h3>Bonnes pratiques à appliquer :</h3>
<ul>
<li>Accessibilité : Labels, roles ARIA, navigation clavier</li>
<li>Performance : Debounce des validations, lazy loading</li>
<li>Sécurité : Validation côté serveur, protection CSRF</li>
<li>UX : Messages clairs, progression visuelle, états de chargement</li>
</ul>
</section>
</div>
<!-- JavaScript pour les fonctionnalités avancées -->
<script>
// Fonction de validation de démonstration
function showValidationDemo() {
const result = document.getElementById('validation-result');
result.className = 'mt-4 p-3 rounded bg-green-100 text-green-800';
result.textContent = '✅ Formulaire validé avec succès !';
result.classList.remove('hidden');
setTimeout(() => {
result.classList.add('hidden');
}, 3000);
}
// Fonction de réinitialisation avec confirmation
function resetFormWithConfirm() {
if (confirm('Êtes-vous sûr de vouloir effacer tous les champs ?')) {
document.querySelector('form[action="/register"]').reset();
// Réinitialiser les éléments personnalisés
document.getElementById('experience-value').textContent = '5';
document.getElementById('password-strength').innerHTML = 'Force du mot de passe: <span style="color: #e74c3c;">●○○○</span> Faible';
document.getElementById('password-match').textContent = '';
}
}
// Validation en temps réel pour les mots de passe
document.addEventListener('DOMContentLoaded', function() {
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirm-password');
const strengthIndicator = document.getElementById('password-strength');
const matchIndicator = document.getElementById('password-match');
if (password && strengthIndicator) {
password.addEventListener('input', function() {
const value = this.value;
let strength = 0;
let indicators = ['○', '○', '○', '○'];
let label = 'Très faible';
let color = '#e74c3c';
if (value.length >= 8) strength++;
if (/[a-z]/.test(value)) strength++;
if (/[A-Z]/.test(value)) strength++;
if (/[0-9]/.test(value)) strength++;
if (/[^A-Za-z0-9]/.test(value)) strength++;
for (let i = 0; i < strength && i < 4; i++) {
indicators[i] = '●';
}
if (strength >= 4) { label = 'Forte'; color = '#27ae60'; }
else if (strength >= 3) { label = 'Moyenne'; color = '#f39c12'; }
else if (strength >= 2) { label = 'Faible'; color = '#e67e22'; }
strengthIndicator.innerHTML = `Force du mot de passe: <span style="color: ${color}">${indicators.join('')}</span> ${label}`;
});
}
if (confirmPassword && matchIndicator) {
function checkPasswordMatch() {
const pass1 = password ? password.value : '';
const pass2 = confirmPassword.value;
if (pass2.length > 0) {
if (pass1 === pass2) {
matchIndicator.textContent = '✅ Les mots de passe correspondent';
matchIndicator.style.color = '#27ae60';
} else {
matchIndicator.textContent = '❌ Les mots de passe ne correspondent pas';
matchIndicator.style.color = '#e74c3c';
}
} else {
matchIndicator.textContent = '';
}
}
confirmPassword.addEventListener('input', checkPasswordMatch);
if (password) password.addEventListener('input', checkPasswordMatch);
}
// Prévisualisation d'avatar
const avatarInput = document.getElementById('avatar');
const avatarPreview = document.getElementById('avatar-preview');
if (avatarInput && avatarPreview) {
avatarInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
avatarPreview.innerHTML = `<img src="${e.target.result}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%;">`;
};
reader.readAsDataURL(file);
}
});
}
});
</script>
</body>
</html>
Vous maîtrisez maintenant les formulaires HTML sous toutes leurs formes. Explorez les autres aspects du HTML pour créer des sites web complets et interactifs.