Intermédiaire120 min80 XP
Objectifs de cette leçon
- Construire un jeu de devinettes interactif
- Créer une galerie d'images dynamique
- Consommer une API météo
Projet final JavaScript 🏆
Ce projet final met en pratique toutes les competences acquises au cours du parcours JavaScript. Tu vas creer 3 mini-projets complets.
Mini-Projet 1 : Application de gestion de taches (Todo App) ✅
Cahier des charges
- Ajouter, supprimer, marquer comme termine
- Filtrer (tous, en cours, termines)
- Sauvegarde dans localStorage
- Compteur de taches restantes
ETAPE 1 : Le HTML
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <title>Todo App</title> <style> body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; } .termine { text-decoration: line-through; color: #999; } .filtres button { margin: 0 5px; } .filtres button.actif { font-weight: bold; } .tache { display: flex; align-items: center; gap: 10px; padding: 8px 0; } .tache .supprimer { color: red; cursor: pointer; } </style> </head> <body> <h1>Mes Taches</h1> <form id="formulaire"> <input type="text" id="input" placeholder="Ajouter une tache..." required> <button type="submit">Ajouter</button> </form> <div class="filtres"> <button class="actif" data-filtre="tous">Tous</button> <button data-filtre="en-cours">En cours</button> <button data-filtre="termines">Termines</button> </div> <ul id="liste"></ul> <p id="compteur"></p> <script src="app.js"></script> </body> </html>
ETAPE 2 : Le JavaScript — Modele de donnees
// Charger les taches depuis localStorage function chargerTaches() { const donnees = localStorage.getItem("taches"); return donnees ? JSON.parse(donnees) : []; } // Sauvegarder dans localStorage function sauvegarderTaches(taches) { localStorage.setItem("taches", JSON.stringify(taches)); } // Generer un ID unique function genererId() { return Date.now().toString(36) + Math.random().toString(36).substr(2); }
ETAPE 3 : Les operations CRUD
let taches = chargerTaches(); let filtreActuel = "tous"; function ajouterTache(texte) { const nouvelleTache = { id: genererId(), texte, terminee: false, dateCreation: new Date().toISOString() }; taches.push(nouvelleTache); sauvegarderTaches(taches); afficherTaches(); } function supprimerTache(id) { taches = taches.filter(t => t.id !== id); sauvegarderTaches(taches); afficherTaches(); } function basculerTache(id) { const tache = taches.find(t => t.id === id); if (tache) { tache.terminee = !tache.terminee; sauvegarderTaches(taches); afficherTaches(); } }
ETAPE 4 : Le rendu
function filtrerTaches() { switch (filtreActuel) { case "en-cours": return taches.filter(t => !t.terminee); case "termines": return taches.filter(t => t.terminee); default: return taches; } } function afficherTaches() { const liste = document.getElementById("liste"); const tachesFiltrees = filtrerTaches(); liste.innerHTML = tachesFiltrees.map(tache => ` <li class="tache ${tache.terminee ? "termine" : ""}"> <input type="checkbox" ${tache.terminee ? "checked" : ""} onchange="basculerTache('${tache.id}')" > <span>${tache.texte}</span> <span class="supprimer" onclick="supprimerTache('${tache.id}')">✕</span> </li> `).join(""); // Mettre a jour le compteur const restantes = taches.filter(t => !t.terminee).length; document.getElementById("compteur").textContent = `${restantes} tache${restantes > 1 ? "s" : ""} restante${restantes > 1 ? "s" : ""}`; }
ETAPE 5 : Les evenements
// Formulaire document.getElementById("formulaire").addEventListener("submit", (e) => { e.preventDefault(); const input = document.getElementById("input"); const texte = input.value.trim(); if (texte) { ajouterTache(texte); input.value = ""; } }); // Filtres — delegation d'evenements document.querySelector(".filtres").addEventListener("click", (e) => { if (e.target.tagName === "BUTTON") { filtreActuel = e.target.dataset.filtre; document.querySelectorAll(".filtres button").forEach(btn => { btn.classList.remove("actif"); }); e.target.classList.add("actif"); afficherTaches(); } }); // Afficher au chargement afficherTaches();
Mini-Projet 2 : Météo avec API 🌤️
Cahier des charges
- Rechercher une ville
- Afficher temperature, humidite, vent
- Historique des recherches
- Gestion d'erreurs
Le code complet
const API_KEY = "Ta cle API OpenWeatherMap"; class MeteoApp { constructor() { this.historique = JSON.parse(localStorage.getItem("historiqueMeteo")) || []; this.init(); } init() { document.getElementById("formulaire").addEventListener("submit", (e) => { e.preventDefault(); const ville = document.getElementById("ville").value.trim(); if (ville) this.rechercher(ville); }); this.afficherHistorique(); } async rechercher(ville) { try { this.afficherChargement(); const reponse = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${ville}&appid=${API_KEY}&units=metric&lang=fr` ); if (!reponse.ok) { throw new Error("Ville non trouvee"); } const donnees = await reponse.json(); this.afficherMeteo(donnees); this.ajouterHistorique(donnees); } catch (erreur) { this.afficherErreur(erreur.message); } } afficherMeteo(donnees) { const { name, main, weather, wind } = donnees; document.getElementById("resultat").innerHTML = ` <div class="meteo-card"> <h2>${name}</h2> <div class="temperature">${Math.round(main.temp)}°C</div> <div class="description">${weather[0].description}</div> <div class="details"> <span>Humidite : ${main.humidity}%</span> <span>Vent : ${wind.speed} km/h</span> <span>Ressenti : ${Math.round(main.feels_like)}°C</span> </div> </div> `; } afficherErreur(message) { document.getElementById("resultat").innerHTML = `<div class="erreur">❌ ${message}</div>`; } afficherChargement() { document.getElementById("resultat").innerHTML = `<div class="chargement">Chargement...</div>`; } ajouterHistorique(donnees) { this.historique.unshift({ ville: donnees.name, temperature: donnees.main.temp, date: new Date().toLocaleString("fr-FR") }); this.historique = this.historique.slice(0, 10); localStorage.setItem("historiqueMeteo", JSON.stringify(this.historique)); this.afficherHistorique(); } afficherHistorique() { const liste = document.getElementById("historique"); if (!liste) return; liste.innerHTML = this.historique.map(h => ` <li>${h.ville} : ${Math.round(h.temperature)}°C (${h.date})</li> `).join(""); } } // Lancer l'application new MeteoApp();
Mini-Projet 3 : Quiz interactif 🧠
Cahier des charges
- 10 questions a choix multiples
- Score en temps reel
- Barre de progression
- Classement local
Le code complet
const questions = [ { question: "Quel langage est le plus utilise pour le web frontend ?", options: ["Python", "JavaScript", "Java", "C++"], reponse: 1 }, { question: "Quelle est la methode pour selectionner un element par son ID ?", options: ["querySelector()", "getElement()", "getElementById()", "selectById()"], reponse: 2 }, { question: "Que retourne typeof null ?", options: ["null", "undefined", "object", "boolean"], reponse: 2 }, { question: "Quel mot-cle declare une constante ?", options: ["var", "let", "const", "fixed"], reponse: 2 }, { question: "Quelle methode transforme un tableau avec une fonction ?", options: ["forEach()", "map()", "filter()", "reduce()"], reponse: 1 }, { question: "Que fait le spread operator (...) ?", options: ["Copie profonde", "Etale les elements", "Filtre les doublons", "Trie le tableau"], reponse: 1 }, { question: "Quel est l'equivalent moderne de XMLHttpRequest ?", options: ["fetch()", "ajax()", "request()", "http()"], reponse: 0 }, { question: "Comment declare-t-on une fonction async ?", options: ["function async", "async function", "await function", "function await"], reponse: 1 }, { question: "Que fait optional chaining (?.) ?", options: ["Cree un lien", "Accede en securite", "Modifie une propriete", "Supprime une propriete"], reponse: 1 }, { question: "Quelle est la difference entre == et === ?", options: [ "Aucune", "=== verifie le type", "== est plus rapide", "=== est deprecie" ], reponse: 1 } ]; class Quiz { constructor() { this.questionActuelle = 0; this.score = 0; this.repondu = false; this.classement = JSON.parse(localStorage.getItem("classementQuiz")) || []; } demarrer() { this.afficherQuestion(); } afficherQuestion() { const q = questions[this.questionActuelle]; const progression = ((this.questionActuelle) / questions.length) * 100; document.getElementById("quiz").innerHTML = ` <div class="progression"> <div class="barre" style="width: ${progression}%"></div> </div> <div class="question-numero"> Question ${this.questionActuelle + 1}/${questions.length} </div> <h2>${q.question}</h2> <div class="options"> ${q.options.map((opt, i) => ` <button class="option" onclick="quiz.repondre(${i})"> ${opt} </button> `).join("")} </div> <div class="score-en-cours">Score : ${this.score}/${questions.length}</div> `; } repondre(index) { if (this.repondu) return; this.repondu = true; const q = questions[this.questionActuelle]; const options = document.querySelectorAll(".option"); options.forEach((opt, i) => { if (i === q.reponse) { opt.classList.add("correct"); } else if (i === index) { opt.classList.add("incorrect"); } }); if (index === q.reponse) { this.score++; } setTimeout(() => { this.repondu = false; this.questionActuelle++; if (this.questionActuelle < questions.length) { this.afficherQuestion(); } else { this.afficherResultat(); } }, 1500); } afficherResultat() { const pourcentage = Math.round((this.score / questions.length) * 100); this.classement.push({ score: this.score, total: questions.length, pourcentage, date: new Date().toLocaleString("fr-FR") }); this.classement.sort((a, b) => b.pourcentage - a.pourcentage); this.classement = this.classement.slice(0, 5); localStorage.setItem("classementQuiz", JSON.stringify(this.classement)); document.getElementById("quiz").innerHTML = ` <div class="resultat"> <h2>Quiz termine !</h2> <div class="score-final">${this.score}/${questions.length}</div> <div class="pourcentage">${pourcentage}%</div> <h3>Classement :</h3> <ul> ${this.classement.map((e, i) => ` <li>${i + 1}. ${e.score}/${e.total} (${e.pourcentage}%)</li> `).join("")} </ul> <button onclick="quiz = new Quiz(); quiz.demarrer();"> Recommencer </button> </div> `; } } let quiz = new Quiz(); quiz.demarrer();
Bilan du parcours JavaScript 🎓
| Competence | Niveau |
|---|---|
| Variables et types | ✅ |
| Fonctions et closures | ✅ |
| Tableaux et methodes | ✅ |
| Objets et destructuration | ✅ |
| DOM et evenements | ✅ |
| Promesses et async/await | ✅ |
| Modules ES | ✅ |
| ES6+ avance | ✅ |
| Projets concrets | ✅ |
🎉 Felicitations ! Tu as termine le parcours JavaScript complet. Tu maitrises maintenant les bases solides pour developper des applications web modernes. Continue a pratiquer, a creer des projets, et a explorer les frameworks comme React, Vue ou Angular !
⚡ Simulateur — Projet : Jeu de devinettes
Étape 1/10📝 Pseudo-code
1function jouer() {
2 const secret = Math.floor(Math.random() * 100) + 1;
3 let tentative = 0;
4 while (tentative !== secret) {
5 tentative++;
6 }
7 return tentative;
8}
⚙️ Exécution
📦 Mémoire
Aucune variable
🔀 Flux d'exécution
function jouer()
3
Définition du jeu
function jouer() définit la fonction du jeu de devinettes. Elle contient toute la logique : génération aléatoire, boucle et retour du résultat.