Files
stagiaire/SOLUTIONS_services_2.md
T

9.4 KiB
Raw Blame History

Solutions — rattrapage services (PROCHAINE_ETAPE_services_2.md)

🛑 À NE REGARDER QU'EN DERNIER RECOURS. Cherche d'abord par toi-même (au moins 1015 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 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 :

<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 un if/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 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

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);
});

💬 « 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

// à 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 :

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");
});

💬 « 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

<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 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) :

[
    { "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://). 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

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