Les Formulaires HTML

Maîtrisez la création de formulaires interactifs et accessibles : inputs, validation, design responsive et expérience utilisateur optimale.

📝
Inputs & Champs
Validation HTML5
Accessibilité
📱
UX/UI Design

Syntaxe de Base des Formulaires

Les formulaires HTML permettent de collecter et envoyer des données utilisateur avec une structure claire et des contrôles appropriés.

Structure de base d'un formulaire

Éléments essentiels :

<form> ← Conteneur principal
<label> ← Étiquette
<input> ← Champ de saisie
<button> ← Bouton d'action
</form>

Exemple minimal :

<form action="/submit" method="POST">
<label for="name">Nom :</label>
<input type="text" id="name" name="name">
<button type="submit">Envoyer</button>
</form>

Rendu interactif :

Points clés :

  • Relation label[for]input[id]
  • Attribut name pour l'envoi des données
  • Type approprié pour chaque input
  • Button type="submit" pour la soumission

Éléments de Formulaire

Découvrez tous les types d'inputs et éléments disponibles pour créer des formulaires riches et interactifs.

📝 Inputs Texte

<input type="text">
<input type="email">
<input type="password">
<textarea rows="3"></textarea>
<input type="search">
Validation automatique pour email
Password masque la saisie
Search avec bouton d'effacement

🔢 Inputs Numériques

<input type="number" min="0" max="100">
<input type="range" min="0" max="100">
<input type="tel">
<input type="url">
Number avec boutons +/-
Range avec curseur visuel
Tel optimisé mobile

📅 Date & Temps

<input type="date">
<input type="time">
<input type="datetime-local">
<input type="month">
<input type="week">
Sélecteur natif du navigateur
Format standardisé automatique
Validation intégrée

☑️ Sélections

<input type="checkbox" id="accept">
<label for="accept">J'accepte</label>
<input type="radio" name="gender" value="M">
<input type="radio" name="gender" value="F">
<select name="country">
<option value="fr">France</option>
</select>
Checkbox : sélection multiple
Radio : choix unique par groupe
Select : liste déroulante

📁 Fichiers & Médias

<input type="file">
<input type="file" accept="image/*">
<input type="file" multiple>
<input type="color">
Accept filtre les types
Multiple pour plusieurs fichiers
Color avec sélecteur visuel

🎯 Boutons & Actions

<button type="submit">Envoyer</button>
<button type="reset">Effacer</button>
<button type="button">Action</button>
<input type="submit" value="Valider">
Submit envoie le formulaire
Reset vide tous les champs
Button pour actions JavaScript

Validation HTML5

Utilisez les attributs de validation natifs pour améliorer l'expérience utilisateur et la qualité des données.

Attributs de Validation

required

Rend un champ obligatoire

<input type="email" required>

pattern

Expression régulière pour valider le format

<input pattern="[0-9]{5}" title="Code postal">

min/max & minlength/maxlength

Contraintes de valeur et de longueur

<input type="number" min="18" max="65">
<input minlength="8" maxlength="20">

Messages Personnalisés

title

Message d'aide pour l'utilisateur

title="Entrez votre code postal (5 chiffres)"

JavaScript personnalisé

Validation avec setCustomValidity()

input.setCustomValidity("Message d'erreur");

Pseudo-classes CSS

Styles pour les états de validation

:valid { border-color: green; }
:invalid { border-color: red; }

Exemple : Formulaire avec validation complète

Code HTML avec validation :

<form novalidate>
<div>
<label for="email">Email *</label>
<input
type="email"
id="email"
required
placeholder="nom@exemple.com"
>
</div>
<div>
<label for="password">Mot de passe *</label>
<input
type="password"
id="password"
required
minlength="8"
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}"
title="Au moins 8 caractères avec majuscule, minuscule et chiffre"
>
</div>
<div>
<label for="age">Âge</label>
<input
type="number"
id="age"
min="13"
max="120"
>
</div>
<button type="submit">S'inscrire</button>
</form>

Formulaire fonctionnel :

Min. 8 caractères, 1 majuscule, 1 minuscule, 1 chiffre

Bonnes Pratiques

Créez des formulaires accessibles, ergonomiques et sécurisés qui offrent une excellente expérience utilisateur.

♿ Accessibilité

Labels explicites

✅ Toujours associer : label[for] ↔ input[id]
❌ Éviter : Placeholder comme seul label

Groupes et structure

<fieldset>
<legend>Informations personnelles</legend>
</fieldset>

Messages d'erreur

  • • Messages clairs et constructifs
  • • Associés avec aria-describedby
  • • Focus sur le premier champ en erreur
  • • Couleurs + texte (pas que couleur)

🎨 UX/UI Design

Progression logique

  • • Ordre de tabulation naturel
  • • Groupement par thématique
  • • Informations essentielles en premier
  • • Bouton principal bien visible

Feedback utilisateur

.loading { cursor: wait; }
.success { border-color: green; }
.error { border-color: red; }

Mobile-friendly

  • • Types d'inputs appropriés (tel, email)
  • • Taille tactile suffisante (44px min)
  • • Espacement généreux
  • • Zoom activé sur les inputs

🔒 Sécurité

Validation côté serveur

⚠️ La validation HTML5 peut être contournée ! Toujours valider côté serveur.

<form action="/secure-endpoint" method="POST">

Protection CSRF

<input type="hidden" name="_token" value="csrf_token">

Données sensibles

  • • HTTPS obligatoire
  • • Autocomplete="off" pour mots de passe
  • • Ne pas stocker en plaintext
  • • Limiter les tentatives

⚡ Performance

Optimisation du DOM

  • • Éviter les formulaires trop complexes
  • • Pagination pour longs formulaires
  • • Lazy loading pour les selects
  • • Debounce pour validation en temps réel

Upload de fichiers

<form enctype="multipart/form-data">
<input type="file" accept=".jpg,.png" max-size="5MB">

Autocomplete intelligent

<input autocomplete="email">
<input autocomplete="current-password">

Travaux Pratiques

Créez des formulaires professionnels et interactifs avec ces exercices progressifs couvrant tous les aspects.

Exercice 1 : Formulaire de contact professionnel

Objectif :

Créez un formulaire de contact complet avec validation, accessibilité et design responsive.

Spécifications :

  • Informations personnelles (nom, email, téléphone)
  • Sujet avec liste déroulante pré-définie
  • Message avec textarea redimensionnable
  • Validation HTML5 complète
  • Accessibilité avec fieldset et labels

Aperçu attendu :

Informations personnelles

Exercice 2 : Formulaire d'inscription avancé

Mission :

Créez un formulaire d'inscription complexe avec tous les types d'inputs et une validation poussée.

Défis techniques :

  • Validation en temps réel avec JavaScript
  • Upload d'avatar avec prévisualisation
  • Confirmation de mot de passe dynamique
  • Sélecteurs date/temps pour naissance
  • Préférences avec checkboxes multiples

Fonctionnalités attendues :

📷
Force: ●○○○

Code de démarrage

<!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>

Collectez des Données avec Style ! 📝

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.