From 6c01a550d05ff11721ab00607735c2dad6a4f528 Mon Sep 17 00:00:00 2001 From: matthieureynier Date: Mon, 22 Jun 2026 09:42:34 +0200 Subject: [PATCH] Solutions services (filet de securite, a consulter en dernier recours) pour le rattrapage JS Co-Authored-By: Claude Opus 4.8 (1M context) --- SOLUTIONS_services_2.md | 263 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 SOLUTIONS_services_2.md diff --git a/SOLUTIONS_services_2.md b/SOLUTIONS_services_2.md new file mode 100644 index 0000000..6b302f1 --- /dev/null +++ b/SOLUTIONS_services_2.md @@ -0,0 +1,263 @@ +# ✅ Solutions — rattrapage services (`PROCHAINE_ETAPE_services_2.md`) + +> 🛑 **À NE REGARDER QU'EN DERNIER RECOURS.** +> Cherche d'abord par toi-même (au moins 10–15 min, et regarde la doc MDN). Recopier une solution sans l'avoir cherchée, ça ne s'apprend pas. Quand tu lis une réponse ici : lis-la, **ferme le fichier**, et réécris-la **de mémoire**. Si tu y arrives, c'est gagné. + +Le code ci-dessous est **un** exemple correct. Le tien peut être écrit autrement et être tout aussi bon. + +--- + +## 🩹 Préambule — les 3 corrections de ton `devis.js` + +**1. L'attribut de la textarea** : c'est `rows` (pluriel), pas `row`. +```html + +``` + +**2. Bloquer AVANT d'afficher** : on teste la limite d'abord, on affiche ensuite. +```js +boutonPlus.addEventListener("click", function () { + if (nbVehicules < 100) { // on teste AVANT + nbVehicules = nbVehicules + 1; + } + mettreAJour(); // on affiche APRÈS (jamais 100 puis retour) +}); +``` + +**3. Le total se recalcule, il ne se stocke pas.** Supprime `totalargent` et `mettreAJourTOTAL`. Une seule fonction d'affichage suffit : +```js +function mettreAJour() { + affichageNb.textContent = nbVehicules; + affichageGeo.textContent = nbVehicules * PRIX_GPS; + affichageConso.textContent = nbVehicules * PRIX_CONSOMMATION; +} +``` +> 💬 *« Pourquoi pas une variable `totalargent` ? »* Parce qu'elle pourrait se désynchroniser de `nbVehicules`. Une valeur qui se déduit d'une autre, on la **calcule au moment de l'afficher** : impossible qu'elle soit fausse. + +--- + +## 🟢 Exo 1 — Singulier / pluriel + +Dans le HTML, mets le mot dans son propre span, à côté du nombre : +```html +1 véhicule +``` +Dans `mettreAJour()` : +```js +motVehicule.textContent = nbVehicules === 1 ? "véhicule" : "véhicules"; +``` +(avec `const motVehicule = document.getElementById("mot_vehicule");` en haut.) + +> 💬 *« `=`, `==` ou `===` ? »* `=` **range** une valeur. `===` **compare** (et vérifie aussi le type). Dans un `if`/un test, utilise toujours `===`. + +--- + +## 🟢 Exo 2 — Compteur de caractères + +HTML, sous la textarea : +```html +

Il vous reste 500 caractères.

+``` +JS : +```js +const zoneMsg = document.getElementById("msg"); +const restant = document.getElementById("restant"); +const MAX = 500; + +zoneMsg.addEventListener("input", function () { + const reste = MAX - zoneMsg.value.length; + restant.textContent = reste; + restant.style.color = reste < 50 ? "red" : "black"; // bonus +}); +``` +> 💬 *« Pourquoi `input` et pas `click` ? »* `click` ne se déclenche qu'au clic de souris ; `input` se déclenche à **chaque modification** du champ (clavier, collage…). C'est ce qu'on veut pour un compteur. + +--- + +## 🟢 Exo 3 — Bloquer l'envoi vide + +```js +const formulaire = document.querySelector("form"); + +formulaire.addEventListener("submit", function (evenement) { + if (zoneMsg.value.trim() === "") { + evenement.preventDefault(); + alert("Merci d'écrire une suggestion avant d'envoyer !"); + } +}); +``` +> 💬 *« À quoi sert `trim()` ? »* Il enlève les espaces au début/à la fin. Sans lui, « 3 espaces » compterait comme un message valide. +> 💬 *« `preventDefault`, c'est quoi ? »* Ça annule l'action par défaut du navigateur (ici : envoyer le formulaire et recharger la page). + +--- + +## 🟡 Exo 4 — Tableau d'objets + +Dans un fichier `services.js` (ou en haut de `devis.js`) : +```js +const services = [ + { nom: "Géolocalisation", description: "Suivi en temps réel", prix: "30€/véhicule/mois" }, + { nom: "Odomètre", description: "Kilométrage précis", prix: "200€" }, + { nom: "Consommation", description: "Carburant consommé", prix: "15€/véhicule/mois" }, + { nom: "Impact carbone", description: "CO₂ rejeté", prix: "30€/véhicule/mois" } +]; + +for (const service of services) { + console.log(service.nom + " — " + service.prix); +} +``` + +--- + +## 🟡 Exo 5 — Générer les lignes du tableau + +HTML : garde le ``, vide le corps et donne-lui un id : +```html + +``` +JS (version `innerHTML` + template literal) : +```js +const corps = document.getElementById("corps_services"); +let lignes = ""; +for (const s of services) { + lignes += `${s.nom}${s.description}${s.prix}`; +} +corps.innerHTML = lignes; +``` +> 💬 *« Pourquoi mes `${}` ne marchent pas ? »* Vérifie que tu utilises bien les accents **graves** `` ` `` (touche AltGr+7), pas des guillemets simples `'`. + +--- + +## 🟡 Exo 6 — Recherche + +Mets l'affichage dans une fonction réutilisable : +```js +function afficherServices(liste) { + let lignes = ""; + for (const s of liste) { + lignes += `${s.nom}${s.description}${s.prix}`; + } + corps.innerHTML = lignes; +} +afficherServices(services); + +const champ = document.getElementById("recherche"); // dans le HTML +champ.addEventListener("input", function () { + const mot = champ.value.toLowerCase(); + const resultats = services.filter(function (s) { + return s.nom.toLowerCase().includes(mot); + }); + afficherServices(resultats); +}); +``` +> 💬 *« `filter` modifie-t-il `services` ? »* Non. `filter` renvoie une **nouvelle** liste ; l'originale reste intacte (c'est pour ça qu'effacer la recherche fait tout revenir). + +--- + +## 🟡 Exo 7 — Se souvenir du nombre de véhicules + +```js +// à la fin de mettreAJour(), on sauvegarde +localStorage.setItem("nbVehicules", nbVehicules); + +// au chargement de la page, on relit +const sauvegarde = localStorage.getItem("nbVehicules"); +if (sauvegarde !== null) { + nbVehicules = parseInt(sauvegarde); // ⚠️ reconvertir le TEXTE en NOMBRE + mettreAJour(); +} +``` +> 💬 *« Pourquoi `parseInt` ? »* `localStorage` ne stocke que du **texte**. Sans conversion, `"12" + 1` donnerait `"121"`. `parseInt("12")` redonne le nombre `12`. + +--- + +## 🟠 Exo 8 — Mode sombre + +CSS : +```css +body.sombre { background-color: #1d2733; color: white; } +``` +JS (avec `` dans le HTML) : +```js +document.getElementById("theme").addEventListener("click", function () { + document.body.classList.toggle("sombre"); +}); +``` +> 💬 *« `toggle` fait quoi exactement ? »* Si la classe est présente il l'enlève, sinon il l'ajoute. Un seul appel gère l'aller **et** le retour. + +--- + +## 🟠 Exo 9 — Horloge + +```html + +``` +```js +const horloge = document.getElementById("horloge"); +function afficherHeure() { + horloge.textContent = new Date().toLocaleTimeString(); +} +afficherHeure(); // tout de suite +setInterval(afficherHeure, 1000); // puis chaque seconde +``` +> 💬 *« Pourquoi appeler `afficherHeure()` avant le `setInterval` ? »* Sinon la page reste vide pendant la 1ʳᵉ seconde, le temps que `setInterval` se déclenche la première fois. + +--- + +## 🔴 Exo 10 — `fetch` d'un fichier JSON + +`vehicules.json` (racine du projet) : +```json +[ + { "modele": "Renault Kangoo", "immatriculation": "AB-123-CD", "km": 45200 }, + { "modele": "Peugeot Partner", "immatriculation": "EF-456-GH", "km": 78900 } +] +``` +JS : +```js +async function chargerFlotte() { + const message = document.getElementById("message_flotte"); + const corps = document.getElementById("corps_flotte"); + try { + const reponse = await fetch("../vehicules.json"); + const vehicules = await reponse.json(); + for (const v of vehicules) { + const tr = document.createElement("tr"); + tr.textContent = `${v.modele} — ${v.immatriculation} — ${v.km} km`; + corps.append(tr); + } + } catch (erreur) { + message.textContent = "Impossible de charger la flotte 😕"; + console.error(erreur); + } +} +chargerFlotte(); +``` +> 💬 *« J'ai l'erreur "CORS" / "Failed to fetch" ! »* Tu as ouvert la page en double-cliquant (`file://`). Lance `python3 -m http.server 8000` dans le dossier du projet et ouvre `http://localhost:8000/pages/services.html`. +> 💬 *« Mon JSON ne charge pas. »* Vérifie : guillemets **doubles** partout, et **pas** de virgule après le dernier élément. + +--- + +## 🏁 Exo 11 — Navbar en composant + +```js +function creerNavbar(pageActive) { + return ` + `; +} +document.body.insertAdjacentHTML("afterbegin", creerNavbar("services")); +``` +N'oublie pas d'**enlever** la navbar écrite en dur dans ton HTML, sinon tu en auras deux. +> 💬 *« Pourquoi un paramètre `pageActive` ? »* Pour que la **même** fonction serve sur toutes les pages : sur services tu appelles `creerNavbar("services")`, sur contact `creerNavbar("contact")`, et c'est le bon bouton qui est mis en valeur. + +--- + +### 🆘 Encore bloqué après avoir lu la solution ? +Note précisément : *qu'est-ce que tu attendais, qu'est-ce qui se passe à la place, et qu'affiche la Console (F12) ?* Avec ces 3 infos, on débloque n'importe quoi en deux minutes.