Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.4 KiB
✅ 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.
<textarea id="msg" name="user_message" rows="6" cols="40" maxlength="500"></textarea>
2. Bloquer AVANT d'afficher : on teste la limite d'abord, on affiche ensuite.
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 :
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 denbVehicules. 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 :
<span id="nb_vehicules">1</span> <span id="mot_vehicule">véhicule</span>
Dans mettreAJour() :
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 unif/un test, utilise toujours===.
🟢 Exo 2 — Compteur de caractères
HTML, sous la textarea :
<p>Il vous reste <span id="restant">500</span> caractères.</p>
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
inputet pasclick? »clickne se déclenche qu'au clic de souris ;inputse déclenche à chaque modification du champ (clavier, collage…). C'est ce qu'on veut pour un compteur.
🟢 Exo 3 — Bloquer l'envoi vide
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) :
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 <thead>, vide le corps et donne-lui un id :
<tbody id="corps_services"></tbody>
JS (version innerHTML + template literal) :
const corps = document.getElementById("corps_services");
let lignes = "";
for (const s of services) {
lignes += `<tr><td>${s.nom}</td><td>${s.description}</td><td>${s.prix}</td></tr>`;
}
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 :
function afficherServices(liste) {
let lignes = "";
for (const s of liste) {
lignes += `<tr><td>${s.nom}</td><td>${s.description}</td><td>${s.prix}</td></tr>`;
}
corps.innerHTML = lignes;
}
afficherServices(services);
const champ = document.getElementById("recherche"); // <input id="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);
});
💬 «
filtermodifie-t-ilservices? » Non.filterrenvoie 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
// à 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? »localStoragene stocke que du texte. Sans conversion,"12" + 1donnerait"121".parseInt("12")redonne le nombre12.
🟠 Exo 8 — Mode sombre
CSS :
body.sombre { background-color: #1d2733; color: white; }
JS (avec <button id="theme">Mode sombre</button> dans le HTML) :
document.getElementById("theme").addEventListener("click", function () {
document.body.classList.toggle("sombre");
});
💬 «
togglefait 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
<span id="horloge"></span>
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 lesetInterval? » Sinon la page reste vide pendant la 1ʳᵉ seconde, le temps quesetIntervalse déclenche la première fois.
🔴 Exo 10 — fetch d'un fichier JSON
vehicules.json (racine du projet) :
[
{ "modele": "Renault Kangoo", "immatriculation": "AB-123-CD", "km": 45200 },
{ "modele": "Peugeot Partner", "immatriculation": "EF-456-GH", "km": 78900 }
]
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://). Lancepython3 -m http.server 8000dans le dossier du projet et ouvrehttp://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
function creerNavbar(pageActive) {
return `
<div id="nav_barre" class="shadow">
<h1>FleetZen</h1>
<div id="buttons">
<a href="contact.html"><button class="${pageActive === "contact" ? "x_to_x" : "styled"}">contact</button></a>
<a href="services.html"><button class="${pageActive === "services" ? "x_to_x" : "styled"}">services</button></a>
<a href="../index.html"><button class="retour btn_icone">retour</button></a>
</div>
</div>`;
}
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 appellescreerNavbar("services"), sur contactcreerNavbar("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.