diff --git a/PROCHAINE_ETAPE_contact_2.md b/PROCHAINE_ETAPE_contact_2.md new file mode 100644 index 0000000..ba52640 --- /dev/null +++ b/PROCHAINE_ETAPE_contact_2.md @@ -0,0 +1,456 @@ +# 🚀 Niveau 3 — JavaScript : tableaux, boucles & pages vivantes + +Salut MĂ©lissa ! 👋 + +Tu as fini tout le Niveau 2 (conditions, singulier/pluriel, envoi bloquĂ© si vide, bouton « Effacer », badge horaires, navbar en composant). On enchaĂźne. + +> 🔎 DĂ©tail au passage : ton `restant <= 1 ? "caractĂšre" : "caractĂšres"` est correct — en français, **0 prend le singulier** (« 0 caractĂšre »). + +Ce fichier est une sĂ©rie d'exercices **qui s'enchaĂźnent**, chacun introduisant **une notion de plus**. Fil rouge : rendre la page **vivante et pilotĂ©e par des donnĂ©es**. Il y en a beaucoup, de quoi tenir l'aprĂšs-midi — tu n'es pas obligĂ©e de tout finir. + +> 🧭 **Mode d'emploi** (toujours le mĂȘme) : +> - Avance **dans l'ordre**, un exo Ă  la fois. +> - **Teste** (✅) avant de passer au suivant. +> - Garde la **Console (F12)** ouverte : c'est ton tableau de bord. +> - BloquĂ©e ? C'est **normal** : note ta question et on en parle. + +--- + +## đŸ—ș Le programme de l'aprĂšs-midi + +| Module | Tu vas apprendre
 | Exos | +|---|---|---| +| **A. Tableaux & boucles** | stocker une liste, la parcourir, gĂ©nĂ©rer du HTML automatiquement | 1 → 4 | +| **B. Manipuler les listes** | rechercher, trier, filtrer | 5 → 6 | +| **C. La mĂ©moire** | sauvegarder des infos qui survivent au rechargement | 7 → 8 | +| **D. Le style dynamique** | changer l'apparence au clic, faire un accordĂ©on | 9 → 10 | +| **E. Pour finir en beautĂ©** | factoriser, paramĂ©trer, dĂ©fis libres | 11 → 12 | + +--- + +# đŸ§± Module A — Les tableaux et les boucles + +Jusqu'ici tes variables contenaient **une seule** valeur (`const MAX = 500`). Mais comment ranger **une liste** ? Le nom de tes 2 dĂ©veloppeurs, par exemple ? → avec un **tableau** (`array`). + +## đŸ§© La notion : un tableau, c'est une liste + +```js +const fruits = ["pomme", "banane", "cerise"]; + +console.log(fruits[0]); // "pomme" → on compte Ă  partir de 0 ! +console.log(fruits.length); // 3 → combien d'Ă©lĂ©ments +``` + +> 🔎 **Point qui surprend tout le monde au dĂ©but :** on compte **Ă  partir de 0**. Le 1ᔉ Ă©lĂ©ment est `fruits[0]`, le 2ᔉ est `fruits[1]`
 Le dernier est donc `fruits[fruits.length - 1]`. + +## đŸ§© La notion : une boucle, pour rĂ©pĂ©ter sans copier-coller + +Pour faire quelque chose **avec chaque** Ă©lĂ©ment d'une liste, on utilise une **boucle** `for...of` : + +```js +for (const fruit of fruits) { + console.log("J'aime la " + fruit); +} +// affiche 3 lignes, une par fruit +``` + +> 💡 **À retenir :** `for...of` se lit « **pour chaque** *fruit* **parmi** *fruits*, fais
 ». La boucle s'occupe de passer d'un Ă©lĂ©ment au suivant toute seule. + +--- + +## đŸ§Ș Exo 1 — Ta premiĂšre liste, dans la console + +CrĂ©e un fichier `equipe.js` (on garde ton `contact.js` pour le formulaire). Branche-le dans ta page, **avant** `contact.js`, juste avant `` : + +```html + + +``` + +Dans `equipe.js` : + +```js +const equipe = ["MĂ©lissa", "Guillaume"]; + +for (const personne of equipe) { + console.log("Membre de l'Ă©quipe : " + personne); +} +``` + +**✅ Teste :** ouvre la Console (F12). Tu dois voir 2 lignes. Ajoute un 3ᔉ prĂ©nom au tableau, recharge → 3 lignes, **sans toucher Ă  la boucle**. + +--- + +## đŸ§Ș Exo 2 — Des donnĂ©es plus riches : les **objets** + +Un prĂ©nom tout seul, c'est peu. Pour dĂ©crire une personne (nom **+** job **+** partie créée), on utilise un **objet** `{ 
 }` : + +```js +const membre = { + nom: "MĂ©lissa", + job: "Stagiaire", + partie: "Page contact" +}; + +console.log(membre.nom); // "MĂ©lissa" +console.log(membre.partie); // "Page contact" +``` + +Et une **liste d'objets** = un tableau d'objets. Remplace le contenu de `equipe.js` : + +```js +const equipe = [ + { nom: "MĂ©lissa", job: "Stagiaire", partie: "Page contact" }, + { nom: "Guillaume", job: "Stagiaire", partie: "Page services" } +]; + +for (const membre of equipe) { + console.log(membre.nom + " — " + membre.job + " — " + membre.partie); +} +``` + +**✅ Teste :** la Console affiche une ligne par membre, avec les 3 infos. + +> đŸ§© **À retenir :** un **tableau** `[ ]` = une liste ordonnĂ©e. Un **objet** `{ }` = une fiche avec des **Ă©tiquettes** (`nom`, `job`
). Les deux combinĂ©s (`[ {
}, {
} ]`), c'est **la** façon de stocker des donnĂ©es dans presque toutes les applis. + +--- + +## đŸ§Ș Exo 3 — GĂ©nĂ©rer ton tableau HTML **automatiquement** + +C'est l'exercice central du module. Aujourd'hui, ton `` d'Ă©quipe est **Ă©crit Ă  la main** dans le HTML. ProblĂšme : pour ajouter un membre, il faut retaper tout un ``. On va le faire **gĂ©nĂ©rer par le JavaScript** Ă  partir du tableau `equipe`. + +### Étape 1 — PrĂ©parer le HTML + +Dans `contact.html`, garde l'en-tĂȘte du tableau (``) mais **vide le corps** et donne-lui un `id` : + +```html +
+ + + + + + + + + + +
NomJobParties créées
+``` + +### Étape 2 — Construire les lignes en JavaScript + +Dans `equipe.js` : + +```js +const corps = document.getElementById("corps_equipe"); +let lignes = ""; // on va empiler le HTML dans ce texte + +for (const membre of equipe) { + lignes = lignes + ` + + ${membre.nom} + ${membre.job} + ${membre.partie} + `; +} + +corps.innerHTML = lignes; // on injecte tout d'un coup +``` + +**✅ Teste :** ton tableau s'affiche comme avant, mais cette fois gĂ©nĂ©rĂ© par le JS. La preuve : ajoute un 3ᔉ membre dans le tableau `equipe` (ex. `{ nom: "Matthieu", job: "Tuteur", partie: "Relecture" }`), recharge → la ligne apparaĂźt toute seule. + +> 🔎 Deux nouveautĂ©s ici : +> - **Le `` `texte ${variable}` ``** (avec les accents graves `` ` ``) : ça s'appelle un *template literal*. Le `${
}` insĂšre la valeur d'une variable **dans** le texte. (Tu l'utilises dĂ©jĂ  dans `creerNavbar` 😉 !) +> - **`.innerHTML = 
`** remplace **tout le contenu HTML** d'un Ă©lĂ©ment. Pratique, mais Ă  rĂ©server Ă  **tes propres** donnĂ©es (jamais avec du texte tapĂ© par un inconnu — question de sĂ©curitĂ©, on en reparlera). + +> 🔎 **Le principe Ă  retenir :** on sĂ©pare les **donnĂ©es** (le tableau `equipe`) de l'**affichage** (la boucle qui fabrique le HTML). Change les donnĂ©es → l'affichage suit. C'est le mode de fonctionnement de React, Vue, etc. + +--- + +## đŸ§Ș Exo 4 — Compter et afficher un total + +Au-dessus du tableau, ajoute dans le HTML : + +```html +

đŸ‘„ L'Ă©quipe compte personne(s).

+``` + +Et en JS, aprĂšs ta boucle : + +```js +document.getElementById("nb_membres").textContent = equipe.length; +``` + +**✅ Teste :** le nombre correspond Ă  la taille de ton tableau. Ajoute/enlĂšve un membre → le compte suit tout seul. + +> 💡 `equipe.length`, tu l'avais dĂ©jĂ  croisĂ© avec `zoneMessage.value.length` : `.length`, c'est « **combien** ». Sur un texte = nombre de caractĂšres ; sur un tableau = nombre d'Ă©lĂ©ments. + +--- + +# 🔎 Module B — Rechercher et trier dans une liste + +## đŸ§Ș Exo 5 — Une **barre de recherche** qui filtre l'Ă©quipe en direct + +> 🆕 Nouvelles notions : `.filter()`, `.toLowerCase()`, `.includes()`, et **rĂ©-afficher** quand les donnĂ©es changent. + +D'abord, range ton affichage du tableau dans une **fonction** (pour pouvoir le rejouer) : + +```js +function afficherEquipe(liste) { + let lignes = ""; + for (const membre of liste) { + lignes += ` + + ${membre.nom} + ${membre.job} + ${membre.partie} + `; + } + corps.innerHTML = lignes; +} + +afficherEquipe(equipe); // affichage initial : toute l'Ă©quipe +``` + +> 🔎 `lignes += "
"` est un raccourci pour `lignes = lignes + "
"`. Pratique pour empiler. + +Ajoute un champ de recherche dans le HTML, au-dessus du tableau : + +```html + +``` + +Puis en JS : + +```js +const champRecherche = document.getElementById("recherche"); + +champRecherche.addEventListener("input", function () { + const motCherche = champRecherche.value.toLowerCase(); + + // on garde seulement les membres dont le nom contient le texte tapĂ© + const resultats = equipe.filter(function (membre) { + return membre.nom.toLowerCase().includes(motCherche); + }); + + afficherEquipe(resultats); // on rĂ©-affiche, mais filtrĂ© +}); +``` + +**✅ Teste :** tape « mĂ© » → seule MĂ©lissa reste. Efface → toute l'Ă©quipe revient. + +> đŸ§© **À retenir :** +> - `.filter(...)` crĂ©e un **nouveau** tableau avec seulement les Ă©lĂ©ments qui passent le test. +> - `.toLowerCase()` met en minuscules → la recherche ne tient pas compte des majuscules. +> - `.includes("
")` rĂ©pond vrai/faux : « ce texte contient-il 
 ? ». + +--- + +## đŸ§Ș Exo 6 — Un bouton « Trier par nom » + +> 🆕 Nouvelle notion : `.sort()`. + +```html + +``` + +```js +document.getElementById("trier").addEventListener("click", function () { + const triee = [...equipe].sort(function (a, b) { + return a.nom.localeCompare(b.nom); + }); + afficherEquipe(triee); +}); +``` + +**✅ Teste :** clique → l'Ă©quipe s'affiche dans l'ordre alphabĂ©tique. + +> 🔎 `[...equipe]` fait une **copie** du tableau (pour ne pas modifier l'original). `localeCompare` compare deux mots dans l'ordre alphabĂ©tique (et gĂšre les accents français correctement). C'est un peu abstrait : retiens juste la **recette**, tu la comprendras Ă  force de l'utiliser. + +--- + +# đŸ’Ÿ Module C — Donner une mĂ©moire Ă  ta page + +Recharge ta page : tout ce que l'utilisateur avait tapĂ© **disparaĂźt**. On va y remĂ©dier avec le **`localStorage`** : un petit coffre-fort dans le navigateur qui **survit au rechargement**. + +## đŸ§Ș Exo 7 — Sauvegarder le brouillon du message + +> 🆕 Nouvelle notion : `localStorage.setItem(...)` / `localStorage.getItem(...)`. + +Dans `contact.js`, ajoute : Ă  **chaque frappe**, on sauvegarde le message. + +```js +zoneMessage.addEventListener("input", function () { + localStorage.setItem("brouillon", zoneMessage.value); +}); +``` + +> ⚠ Tu as **dĂ©jĂ ** un `addEventListener("input", rafraichirCompteur)` sur la mĂȘme zone. Pas de souci : on peut en mettre plusieurs, ils s'exĂ©cutent tous les deux. (Encore mieux : ajoute la ligne `localStorage.setItem(...)` **Ă  l'intĂ©rieur** de `rafraichirCompteur`.) + +**✅ Teste :** tape un message, ouvre la Console et tape `localStorage.getItem("brouillon")` → tu vois ton texte. + +## đŸ§Ș Exo 8 — Recharger le brouillon au dĂ©marrage + +Au tout dĂ©but (au chargement de la page), on regarde si un brouillon existe : + +```js +const brouillon = localStorage.getItem("brouillon"); +if (brouillon) { // s'il y a quelque chose de sauvegardĂ© + zoneMessage.value = brouillon; + rafraichirCompteur(); // pour que le compteur soit juste dĂšs le dĂ©part +} +``` + +Et quand le message est envoyĂ© avec succĂšs, on vide le coffre (sinon le vieux brouillon revient) : + +```js +// Ă  ajouter dans ton addEventListener("submit", 
), quand le message N'EST PAS vide +localStorage.removeItem("brouillon"); +``` + +**✅ Teste :** Ă©cris un message, **recharge la page** (F5) → ton texte est toujours lĂ , et le compteur est correct. + +> 💡 **À retenir :** `localStorage` garde des infos **entre les visites** (jusqu'Ă  ce qu'on les efface). `setItem(clĂ©, valeur)` pour ranger, `getItem(clĂ©)` pour relire, `removeItem(clĂ©)` pour effacer. + +--- + +# 🎹 Module D — Changer l'apparence en JavaScript + +## đŸ§Ș Exo 9 — Un bouton « Mode sombre 🌙 » + +> 🆕 Nouvelle notion : `classList.toggle(...)` — ajouter/retirer une classe CSS au clic. + +D'abord, prĂ©pare la classe dans ton CSS (`contact.css`) : + +```css +body.sombre { + background-color: #1d2733; + color: white; +} +``` + +Un bouton dans le HTML : + +```html + +``` + +Et le JS : + +```js +document.getElementById("theme").addEventListener("click", function () { + document.body.classList.toggle("sombre"); +}); +``` + +**✅ Teste :** clique → la page bascule en sombre, re-clique → elle revient. Un seul `toggle` fait les deux ! + +> đŸ§© **À retenir :** `classList.toggle("sombre")` = « si la classe est lĂ , enlĂšve-la ; sinon, ajoute-la ». C'est **le** pont entre ton JavaScript et ton CSS : le JS gĂšre le *quand*, le CSS gĂšre le *Ă  quoi ça ressemble*. + +--- + +## đŸ§Ș Exo 10 — Une FAQ en accordĂ©on + +> 🆕 Nouvelles notions : `querySelectorAll(...)` (sĂ©lectionner **plusieurs** Ă©lĂ©ments) + une boucle `forEach`. + +Beaucoup de pages contact ont une FAQ. Ajoute dans le HTML : + +```html +
+

Questions fréquentes

+
+ +

Grùce à notre service de géolocalisation, accessible 24h/24.

+
+
+ +

Oui, sans frais, depuis votre espace client.

+
+
+``` + +Dans le CSS, on cache les rĂ©ponses au dĂ©part : + +```css +.q-reponse { display: none; } +.q-reponse.ouverte { display: block; } +``` + +En JavaScript, on veut que **chaque** bouton ouvre/ferme **sa** rĂ©ponse : + +```js +const titres = document.querySelectorAll(".q-titre"); // TOUS les boutons FAQ + +titres.forEach(function (titre) { + titre.addEventListener("click", function () { + // la rĂ©ponse est le paragraphe juste aprĂšs le bouton + const reponse = titre.nextElementSibling; + reponse.classList.toggle("ouverte"); + }); +}); +``` + +**✅ Teste :** clique sur une question → sa rĂ©ponse apparaĂźt ; re-clique → elle se cache. Chaque question est indĂ©pendante. + +> đŸ§© **À retenir :** +> - `getElementById` attrape **un** Ă©lĂ©ment ; `querySelectorAll(".classe")` en attrape **plusieurs** (tous ceux qui ont cette classe). +> - `.forEach(...)` est une boucle qui fait quelque chose **pour chaque** Ă©lĂ©ment trouvĂ© — ici : « ajoute un Ă©couteur de clic Ă  chacun ». +> - `nextElementSibling` = « l'Ă©lĂ©ment juste aprĂšs moi ». Pratique pour relier un bouton Ă  sa rĂ©ponse. + +--- + +# 🏁 Module E — Pour finir en beautĂ© + +## đŸ§Ș Exo 11 — Une navbar qui sait quelle page est active + +Ta `creerNavbar()` est dĂ©jĂ  un beau composant. Rendons-la **paramĂ©trable** : on lui dit quelle page est active, et elle met le bon bouton en surbrillance. + +> 🆕 Nouvelle notion : une fonction qui prend un **paramĂštre**. + +```js +function creerNavbar(pageActive) { // ← pageActive est le "rĂ©glage" qu'on lui donne + return ` + `; +} + +document.body.insertAdjacentHTML("afterbegin", creerNavbar("contact")); +``` + +**✅ Teste :** sur la page contact, le bouton « contact » apparaĂźt enfoncĂ© (`instyled`). Le jour oĂč Guillaume rĂ©utilise cette fonction sur sa page, il Ă©crira juste `creerNavbar("services")` → c'est SON bouton qui sera actif. + +> 💡 **À retenir :** un **paramĂštre** (`pageActive`), c'est un rĂ©glage qu'on passe Ă  une fonction pour qu'elle s'adapte. Une seule fonction, plusieurs rĂ©sultats selon ce qu'on lui donne. C'est ÇA, un vrai composant rĂ©utilisable. ♻ + +--- + +## 🎯 Exo 12 — DĂ©fis libres (si tu as encore de l'Ă©nergie) + +Choisis ceux qui t'attirent, dans l'ordre que tu veux : + +1. **Recap avant envoi** : Ă  la soumission, affiche « Merci {prĂ©nom}, ton message de {N} caractĂšres a bien Ă©tĂ© pris en compte ! » en lisant la valeur du champ prĂ©nom. +2. **Validation e-mail maison** : si l'e-mail ne contient pas de `@`, bloque l'envoi avec un message (en plus de la vĂ©rification du navigateur). +3. **Compteur de mots** (en plus des caractĂšres) : `zoneMessage.value.trim().split(" ").length`. +4. **Ajouter un membre via un formulaire** : un petit champ + bouton qui fait `equipe.push({...})` puis `afficherEquipe(equipe)`. +5. **MĂ©moriser le mode sombre** dans `localStorage` pour qu'il reste actif au rechargement. + +--- + +## đŸ—ș RĂ©cap — tout ce que tu sais faire maintenant + +- **DonnĂ©es** : variables, **tableaux** `[ ]`, **objets** `{ }`, listes d'objets +- **RĂ©pĂ©ter** : boucles `for...of` et `.forEach()` +- **Transformer des listes** : `.filter()`, `.sort()` +- **GĂ©nĂ©rer du HTML** depuis des donnĂ©es (`innerHTML` + *template literals*) +- **MĂ©moire** : `localStorage` (setItem / getItem / removeItem) +- **Style dynamique** : `classList.toggle()` +- **SĂ©lection multiple** : `querySelectorAll` + `forEach` +- **Fonctions paramĂ©trĂ©es** : un composant qui s'adapte + +Prochaine grande Ă©tape (pour une autre fois) : **les requĂȘtes vers un serveur** avec `fetch`, pour aller chercher de vraies donnĂ©es en ligne.