skilling.maSkill. Scale. Soar.
Partie 1 — Architecture & Rôle de JS Compilé vs Interprété JavaScript
⏱ 16 min

Langage compilé vs langage de script

Avant d'écrire une seule ligne de JavaScript, il faut comprendre comment le code que vous écrivez devient des instructions exécutables par l'ordinateur. Il existe deux grandes familles de langages : les langages compilés qui transforment le code source en code machine avant exécution, et les langages interprétés (ou de script) comme JavaScript, qui sont exécutés ligne par ligne au moment du lancement. Cette différence fondamentale explique pourquoi JavaScript peut s'exécuter directement dans un navigateur, sans installation ni compilation préalable.

✓ Ce que vous allez apprendre
  • La différence fondamentale entre compilation et interprétation
  • Pourquoi JavaScript est un langage de script côté client
  • Les avantages de JavaScript pour le développement web
  • Comment intégrer JavaScript dans une page HTML
  • Les outils de débogage du navigateur (Console DevTools)

1
Compilation vs Interprétation
Compilateur · Interpréteur · Code machine · Bytecode

Imaginez une recette de cuisine écrite en français. Pour qu'un robot cuisinier l'exécute, deux approches existent : soit on traduit toute la recette en langage robot une bonne fois pour toutes avant de commencer (compilation), soit le traducteur lit et traduit chaque étape au fur et à mesure de la réalisation (interprétation). Les deux aboutissent au même résultat — un plat préparé — mais avec des processus très différents.

Le langage compilé

Un compilateur prend l'intégralité du code source, l'analyse, le vérifie, et le transforme en code machine (des instructions binaires directement compréhensibles par le processeur). Cette transformation se fait avant l'exécution du programme. Le code machine produit est rapide à exécuter car le travail de traduction est déjà fait. C'est le fonctionnement de C, C++, Rust, Go.

Le compilateur vérifie également les types à la compilation : si vous affectez une chaîne de caractères à une variable entière, l'erreur est détectée avant même de lancer le programme. Ce typage statique préserve de nombreux bugs.

Le langage interprété (de script)

Un interpréteur lit et exécute le code source ligne par ligne, au moment de l'exécution. Il n'y a pas d'étape de compilation préalable. JavaScript est interprété par le moteur JavaScript du navigateur (V8 pour Chrome, SpiderMonkey pour Firefox). Quand le navigateur charge une page web, il lit le code JavaScript et l'exécute immédiatement.

Les avantages pour le web sont évidents : le développeur écrit son script, le sauvegarde, recharge la page — les modifications sont immédiatement visibles. Pas de compilation à attendre. Pas d'environnement à configurer pour l'utilisateur final. Le navigateur fait tout.

CritèreLangage compilé (C, Java)Langage de script (JavaScript)
TransformationAvant l'exécution — une compilationPendant l'exécution — ligne par ligne
Vérification des typesÀ la compilation — erreurs détectées tôtÀ l'exécution — typage dynamique
Vitesse d'exécutionPlus rapide — code machine prêtUn peu plus lent (moteurs JS modernes compensent)
PortabilitéCode machine lié à une plateformeS'exécute partout où il y a un interpréteur
EnvironnementCompilation + exécutable nécessairesNavigateur suffit — aucune installation
Cycle de développementÉcrire → Compiler → ExécuterÉcrire → Recharger la page
ExemplesC, C++, Java (bytecode), Go, RustJavaScript, Python, PHP, Ruby

2
JavaScript — définition et intégration dans HTML
Balise script · Fichier externe · Console · Débogage

JavaScript est un langage de programmation de scripts côté client, principalement utilisé pour rendre les pages web interactives. Il est interprété directement par le navigateur, sans installation côté utilisateur. C'est l'un des trois piliers du web : HTML structure, CSS met en forme, JavaScript anime et interagit.

JavaScript a été créé en 1995 par Brendan Eich chez Netscape en seulement 10 jours. Malgré son nom, il n'a aucun lien avec Java — le choix de ce nom était purement marketing. Depuis, il est standardisé par ECMA International sous le nom ECMAScript. La version ES6 (2015) a modernisé radicalement le langage avec les arrow functions, let, const, les Promises, les classes et les modules.

html
/* ── 3 façons d'intégrer JavaScript dans HTML ──────────────── */

/* 1. Inline — directement dans la balise (déconseillé) */
<button onclick="alert('Bienvenue sur DariGo !')">Commander</button>

/* 2. Balise <script> dans le HTML ──────────────────────────── */
<!DOCTYPE html>
<html lang="fr">
<head>
  <title>DariGo — Livraison de repas</title>
</head>
<body>
  <h1>Menu DariGo</h1>

  /* Toujours placer le script AVANT </body> */
  /* Le HTML est chargé avant que JS s'exécute */
  <script>
    console.log("Page DariGo chargée !");
    alert("Bienvenue sur DariGo, livraison rapide à Casablanca !");
  </script>
</body>
</html>

/* 3. Fichier externe — la bonne pratique ─────────────────── */
<!-- index.html -->
<body>
  <h1>DariGo</h1>
  <script src="darigo.js"></script>  /* lien vers le fichier JS */
</body>

/* darigo.js */
console.log("Script DariGo chargé");
// Avantages du fichier externe :
// - Séparation des préoccupations (HTML / JS)
// - Réutilisable sur plusieurs pages
// - Mis en cache par le navigateur — chargement plus rapide

/* ── La console — votre meilleur outil de débogage ─────────── */
console.log("Message normal");       // affiche en noir
console.warn("Attention !");         // affiche en jaune
console.error("Erreur critique !");  // affiche en rouge
console.table([{nom:"Tajine", prix:75}, {nom:"Harira", prix:25}]);

// Ouvrir DevTools : F12 (Chrome/Firefox) → onglet Console
// Taper du code directement dans la console pour tester

✓
Vérification des acquis
5 questions — corrigé intégré
Quelle est la différence principale entre Java et JavaScript ?
Dans une page HTML, où doit-on idéalement placer la balise <script> ?
Quel avantage a l'utilisation d'un fichier JavaScript externe (src="darigo.js") par rapport au code inline ?
Qu'affiche console.warn("Stock épuisé") dans les DevTools ?
Un interpréteur JavaScript exécute le code :
Partie 1 — Architecture & Rôle de JS Client/Serveur HTTP · Navigateur · Requête
⏱ 16 min

Architecture client/serveur & Web dynamique

Comprendre l'architecture client/serveur est indispensable pour savoir à quel moment JavaScript intervient dans le cycle de vie d'une page web. JavaScript s'exécute côté client — dans le navigateur de l'utilisateur — après que le serveur a envoyé la page. Il peut modifier la page sans retourner au serveur, gérer les interactions utilisateur en temps réel, et communiquer avec le serveur en arrière-plan (AJAX) sans rechargement.


1
Le modèle client/serveur
Navigateur · Serveur web · HTTP · Requête/Réponse

Dans une architecture client/serveur, deux types de machines se distinguent par leurs rôles :

Le client est la machine de l'utilisateur final — son navigateur (Chrome, Firefox, Safari). Il envoie des requêtes et affiche les réponses. Le navigateur interprète HTML, CSS et JavaScript pour afficher la page.

Le serveur est une machine puissante, toujours connectée, qui reçoit les requêtes des clients et renvoie les ressources demandées. Un serveur web comme Apache ou Nginx gère les fichiers HTML/CSS/JS statiques. Un serveur d'application comme Node.js ou PHP génère du HTML dynamiquement en lisant une base de données.

Une machine personnelle peut jouer le rôle de serveur — il suffit d'installer le logiciel serveur (XAMPP, WAMP, Node.js). C'est ce qu'on fait en développement local.

Le protocole HTTP

La communication client/serveur sur le web suit le protocole HTTP (HyperText Transfer Protocol). Chaque échange est une paire requête/réponse : le client envoie une requête HTTP (GET, POST, PUT, DELETE), le serveur répond avec un code de statut (200 OK, 404 Not Found, 500 Server Error) et les données demandées.

text
── Cycle d'une visite sur darigo.ma ──────────────────────────

1. L'utilisateur tape https://darigo.ma dans son navigateur
   │
   ▼
2. DNS → résout darigo.ma en adresse IP (ex: 185.23.45.67)
   │
   ▼
3. Navigateur → envoie requête HTTP : GET / HTTP/1.1
   │
   ▼
4. Serveur web → retourne index.html (200 OK)
   │
   ▼
5. Navigateur → parse HTML, découvre <link href="style.css">
   → envoie requête : GET /style.css
   → envoie requête : GET /darigo.js
   │
   ▼
6. Navigateur → construit le DOM, applique CSS, exécute JS
   │
   ▼
7. Page affichée — JavaScript prend le contrôle côté client :
   - Gère les clics sur "Ajouter au panier"
   - Valide les formulaires sans recharger
   - Envoie des requêtes AJAX au serveur en arrière-plan
   - Met à jour l'interface sans rechargement complet
AspectCôté serveur (PHP, Node.js)Côté client (JavaScript)
Où s'exécute le code ?Sur la machine du serveurDans le navigateur de l'utilisateur
Accès à la base de donnéesOui — directementNon — via requêtes API/AJAX
Résultat envoyé au clientHTML généré dynamiquementModification du DOM déjà reçu
Rechargement de pageNécessaire pour toute mise à jourNon — manipulation directe du DOM
Visibilité du codeCode invisible (exécuté sur serveur)Code visible dans DevTools
Cas d'usage DariGoRécupérer la liste des plats depuis MySQLAfficher/cacher le panier, valider la commande

2
JavaScript dans l'architecture web DariGo
Front-end · Back-end · Full Stack · Rôle de JS

Dans l'application DariGo — plateforme de livraison de repas marocains — JavaScript joue plusieurs rôles distincts selon l'endroit où il s'exécute :

text
── Architecture DariGo — rôle de JavaScript ──────────────────

NAVIGATEUR (JavaScript côté client)
├── Affichage dynamique du menu (DOM)
├── Ajout/suppression plats du panier (DOM + localStorage)
├── Validation formulaire de commande (Events)
├── Mise à jour du badge panier sans rechargement (DOM)
└── Communication asynchrone avec le serveur (AJAX / Fetch)
         │
         │ HTTP / AJAX (JSON)
         ▼
SERVEUR (PHP côté serveur)
├── API REST : GET /api/plats → retourne JSON des plats
├── API REST : POST /api/commandes → enregistre la commande
├── Connexion MySQL : lit/écrit les données
└── Génère les réponses JSON que JavaScript consomme
         │
         ▼
BASE DE DONNÉES MySQL
└── Tables : plats, restaurants, commandes, livreurs, clients

── Ce que JavaScript PEUT faire côté client ──────────────────
✓ Modifier le DOM sans rechargement
✓ Réagir aux clics, saisies, scroll, touch
✓ Valider les formulaires avant envoi au serveur
✓ Stocker des données en localStorage / sessionStorage
✓ Communiquer avec le serveur via AJAX (JSON)
✓ Animer l'interface (CSS transitions via classList)

── Ce que JavaScript ne PEUT PAS faire côté client ──────────
✗ Accéder directement à la base de données MySQL
✗ Lire les fichiers sur le serveur
✗ Exécuter des commandes système
✗ Cacher son propre code (tout est visible dans DevTools)

✓
Vérification des acquis
4 questions — corrigé intégré
Sur DariGo, un utilisateur clique sur "Ajouter au panier". La quantité dans le badge passe de 0 à 1 instantanément, sans rechargement. Quel langage gère cela ?
Peut-on accéder directement à une base de données MySQL depuis JavaScript côté client ?
Quels sont les types de serveurs cités dans le cours (point de vue logiciel) ?
Quel code de statut HTTP le serveur renvoie-t-il quand une ressource est introuvable ?
Partie 1 — Architecture & Rôle de JS VS Code jQuery · React · Vue · Angular
⏱ 16 min

Écosystème JavaScript & librairies

JavaScript ne s'utilise jamais seul dans un projet professionnel. Il est accompagné d'un environnement de développement, d'outils de build, et souvent de bibliothèques ou frameworks qui enrichissent ses capacités. Comprendre l'écosystème JS — de l'IDE jusqu'aux frameworks — permet de choisir les bons outils et de comprendre pourquoi des bibliothèques comme jQuery existent et quand les utiliser.


1
L'environnement de développement — VS Code
IDE · Extensions · Live Server · Débogage

Visual Studio Code (VS Code) est l'éditeur de code recommandé pour JavaScript. Créé par Microsoft, gratuit et open source, il est devenu l'IDE le plus utilisé au monde pour le développement web selon les sondages Stack Overflow depuis 2018. Sa légèreté, son système d'extensions et son intégration Git en font le choix parfait pour JavaScript.

bash
# ── Extensions VS Code indispensables pour JavaScript ────────

# 1. Live Server (Ritwick Dey)
# Lance un serveur local avec rechargement automatique
# Clic droit sur index.html → "Open with Live Server"
# → http://localhost:5500  — se recharge à chaque sauvegarde

# 2. Prettier — Code Formatter
# Formate automatiquement le code JS/HTML/CSS
# Raccourci : Shift+Alt+F (Windows) / Shift+Option+F (Mac)

# 3. ESLint
# Détecte les erreurs et mauvaises pratiques JS en temps réel
# Souligne les problèmes dans le code avant même d'exécuter

# 4. JavaScript (ES6) code snippets
# Raccourcis pour écrire du code plus vite
# Exemple : taper "cl" puis Tab → console.log()
# "fn" → function nom() {}
# "arfn" → const nom = () => {}

# 5. Path Intellisense
# Autocomplétion des chemins de fichiers (src="./js/darigo.js")

# ── Raccourcis VS Code essentiels ─────────────────────────────
# Ctrl+S          → Sauvegarder
# Ctrl+/          → Commenter/décommenter une ligne
# Alt+↑↓          → Déplacer une ligne
# Ctrl+D          → Sélectionner le prochain occurrence
# Ctrl+Shift+P    → Palette de commandes
# F12             → Aller à la définition d'une fonction

2
Bibliothèques et frameworks JavaScript
jQuery · React · Vue.js · Angular · Node.js

Le JavaScript pur (vanilla JS) permet de tout faire, mais certaines tâches répétitives — sélectionner des éléments, gérer des animations, faire des requêtes AJAX — deviennent fastidieuses à écrire à la main. Les bibliothèques et frameworks ont été créés pour simplifier ces tâches.

La différence entre une bibliothèque et un framework est fondamentale : une bibliothèque (comme jQuery) est un ensemble de fonctions que vous appelez quand vous voulez. Un framework (comme Angular) est une structure qui appelle votre code — il impose une architecture et contrôle le flux de l'application. On dit qu'avec une bibliothèque vous gardez le contrôle ; avec un framework, c'est lui qui dirige.

OutilTypeCréé parUsage principalPertinence DariGo
jQueryBibliothèqueJohn Resig, 2006Simplifier DOM, AJAX, animationsAppels AJAX vers l'API PHP, effets UI
ReactBibliothèque UIFacebook/Meta, 2013Interfaces composants réutilisablesInterface DariGo complète (voir cours React)
Vue.jsFramework progressifEvan You, 2014UI réactive, progressiveAlternative légère à React pour DariGo
AngularFramework completGoogle, 2016Applications d'entreprise, TypeScriptPour des projets plus larges que DariGo
Node.jsRuntime JSRyan Dahl, 2009JavaScript côté serveurBack-end DariGo (voir cours Cloud Native)
javascript
// ── Comparaison jQuery vs Vanilla JS pour DariGo ─────────────

// Sélectionner et modifier un élément
// Vanilla JS :
document.getElementById('prix-tajine').textContent = '75 MAD';
// jQuery :
$('#prix-tajine').text('75 MAD');

// Faire une requête AJAX
// Vanilla JS (Fetch API) :
fetch('api/plats.php')
  .then(response => response.json())
  .then(plats => afficherPlats(plats));
// jQuery :
$.get('api/plats.php', function(plats) {
  afficherPlats(plats);
});

// Gérer un clic
// Vanilla JS :
document.querySelector('#btn-commander').addEventListener('click', passerCommande);
// jQuery :
$('#btn-commander').on('click', passerCommande);

// ── Quand utiliser jQuery ? ───────────────────────────────────
// jQuery est pertinent pour :
// - Projets existants qui l'utilisent déjà
// - Prototypage rapide
// - Compatibilité avec de nombreux plugins (carousels, datepickers)
// - Intégration simple avec Bootstrap 4

// Vanilla JS suffit pour les nouveaux projets (ES6+ est puissant)

✓
Vérification des acquis
4 questions — corrigé intégré
Quel est le meilleur format d'échange de données entre une application front-end JavaScript et un back-end PHP ?
Quelle est la différence entre une bibliothèque (jQuery) et un framework (Angular) ?
L'extension Live Server dans VS Code sert à :
Peut-on écrire un serveur web avec JavaScript ?
Partie 2 — Fondamentaux JavaScript var · let · const Types · Opérateurs · I/O
⏱ 25 min

Variables, types primitifs & opérateurs

Les variables sont les conteneurs de données de tout programme JavaScript. Contrairement à Java ou C, JavaScript est un langage à typage dynamique : vous n'avez pas besoin de déclarer le type d'une variable — il est déterminé automatiquement à l'exécution selon la valeur assignée. Une même variable peut contenir un nombre, puis une chaîne, puis un objet au cours de son existence. Cette flexibilité est puissante mais exige de la rigueur pour éviter les bugs silencieux.

✓ Ce que vous allez apprendre
  • Déclarer des variables avec var, let et const — différences et bonnes pratiques
  • Les 7 types primitifs JavaScript et le typage dynamique
  • Tous les opérateurs : arithmétiques, comparaison, logiques, affectation
  • Lire des données : prompt(), document.getElementById()
  • Écrire des données : alert(), innerHTML, console.log()
  • Conversions de types : parseInt(), parseFloat(), String()

1
Déclarer des variables — var, let, const
Portée · Hoisting · Immutabilité · Bonnes pratiques ES6
javascript
// ── var — ancienne syntaxe (ES5, déconseillée) ────────────────
var nomRestaurant = "La Sqala";
var prixTajine = 75;
// Problèmes de var :
// 1. Portée fonctionnelle (pas de bloc)
// 2. Hoisting : déclaration remontée en haut de la fonction
// 3. Peut être re-déclarée : var x = 1; var x = 2; → OK (bug silencieux)

// ── let — variable modifiable (ES6) ──────────────────────────
let totalPanier = 0;
let villeLivraison = "Casablanca";
let quantite = 2;

totalPanier = 145;          // re-assignation autorisée
villeLivraison = "Rabat";   // OK
// let quantite = 5;        // ERREUR — ne peut pas être re-déclarée dans le même scope

// ── const — constante (ES6) ───────────────────────────────────
const FRAIS_LIVRAISON = 15;  // convention MAJUSCULES pour les constantes
const API_URL = "https://api.darigo.ma";
const plat = { nom: "Tajine Kefta", prix: 75 };

// FRAIS_LIVRAISON = 20;   // TypeError — impossible de re-assigner
// MAIS : on peut modifier les propriétés d'un objet const
plat.prix = 80;             // OK — on modifie le contenu, pas la référence
plat.disponible = true;     // OK — ajout de propriété

// ── Règle d'or (ES6 moderne) ──────────────────────────────────
// 1. Utiliser const par défaut
// 2. Utiliser let si la valeur doit changer
// 3. Ne jamais utiliser var dans un nouveau projet

// ── Exemples DariGo ───────────────────────────────────────────
const MAX_ARTICLES_PANIER = 10;  // ne change jamais
const NOM_APP = "DariGo";

let nbArticles = 0;          // va changer quand l'utilisateur ajoute
let statutCommande = "en_attente"; // changera au fil du temps

// Calcul de la portée (scope)
function afficherTotal() {
  let remise = 0.1;           // existe SEULEMENT dans cette fonction
  let total = 275 * (1 - remise);
  console.log(total);         // 247.5
}
// console.log(remise);       // ReferenceError : remise n'existe pas ici

if (totalPanier > 200) {
  let livraisonGratuite = true;   // existe SEULEMENT dans ce bloc
}
// livraisonGratuite ici → ReferenceError (contrairement à var)

2
Types de données et typage dynamique
number · string · boolean · null · undefined · typeof · NaN
javascript
// ── Les 7 types primitifs JavaScript ─────────────────────────

// 1. number — entiers et décimaux (un seul type)
let prix = 75;
let note = 4.8;
let temperature = -5;
console.log(typeof prix);          // "number"
console.log(typeof note);          // "number"

// Valeurs spéciales number :
let infini = Infinity;             // 1/0
let pasUnNombre = NaN;             // Not a Number : résultat d'opération invalide
console.log(10 % "abc");          // NaN
console.log(isNaN(10 % "abc"));   // true — tester si NaN
console.log("1s"++);              // NaN

// 2. string — chaînes de caractères
let nomClient = "Fatima-Zahra Alaoui";
let adresse = 'Hay Riad, Avenue Mohammed VI';
let message = `Marhba ${nomClient} ! Votre commande est prête.`; // template literal

console.log(nomClient.length);     // 20
console.log(nomClient.toUpperCase()); // "FATIMA-ZAHRA ALAOUI"
console.log(nomClient.includes("Alaoui")); // true
console.log(nomClient.split(" ")); // ["Fatima-Zahra", "Alaoui"]

// 3. boolean — vrai ou faux
let estConnecte = true;
let panierVide = false;
let estDisponible = (prix < 100 && note > 4);  // true
console.log(typeof estConnecte);  // "boolean"

// 4. null — absence intentionnelle de valeur
let livreurAssigne = null;   // commande sans livreur pour l'instant
console.log(typeof null);    // "object" — BUG historique de JS !

// 5. undefined — variable déclarée mais non assignée
let codePromo;
console.log(codePromo);      // undefined
console.log(typeof codePromo); // "undefined"

// 6. Symbol (ES6) — identifiant unique (usage avancé)
// 7. BigInt (ES2020) — entiers très grands

// ── Typage dynamique — une variable change de type ────────────
let valeur = 42;
console.log(typeof valeur);   // "number"
valeur = "Casablanca";
console.log(typeof valeur);   // "string"  ← même variable, type différent !
valeur = true;
console.log(typeof valeur);   // "boolean"

// ── Conversions de types ──────────────────────────────────────
// String → Number
let saisie = "75";            // vient d'un champ input HTML — toujours string !
let prix2 = parseInt(saisie, 10);     // 75 (base 10)
let note2 = parseFloat("4.8");        // 4.8
let val = Number("42");               // 42
console.log(typeof prix2);   // "number"

// Number → String
let montant = 145;
console.log(String(montant));    // "145"
console.log(montant.toString()); // "145"
console.log(`${montant} MAD`);   // "145 MAD" — template literal

// Conversions implicites (à connaître pour éviter les bugs)
console.log("5" + 3);    // "53" — concaténation (string gagne)
console.log("5" - 3);    // 2   — soustraction convertit en number
console.log("5" * "2");  // 10  — multiplication force number
console.log(!!0);        // false — conversion en boolean
console.log(!!undefined); // false
console.log(!!"DariGo"); // true — toute string non vide est truthy

3
Opérateurs & entrées/sorties
Arithmétiques · Comparaison · Logiques · prompt · innerHTML
javascript
// ── Opérateurs arithmétiques ──────────────────────────────────
let a = 275, b = 15;
console.log(a + b);   // 290 — addition
console.log(a - b);   // 260 — soustraction
console.log(a * b);   // 4125 — multiplication
console.log(a / b);   // 18.333... — division
console.log(a % b);   // 5 — modulo (reste de division)
console.log(2 ** 10); // 1024 — puissance (ES7)

let stock = 5;
stock++;              // 6 — incrémentation
stock--;              // 5 — décrémentation
stock += 10;          // 15 — équivalent à stock = stock + 10
stock *= 2;           // 30

// ── Opérateurs de comparaison ─────────────────────────────────
console.log(5 == "5");    // true  — égalité lâche (convertit les types)
console.log(5 === "5");   // false — égalité stricte (type ET valeur)
console.log(5 !== "5");   // true  — inégalité stricte
console.log(10 > 5);      // true
console.log(10 >= 10);    // true

// TOUJOURS utiliser === et !== pour éviter les surprises
console.log(undefined == null);  // true (égalité lâche !)
console.log(undefined === null); // false (stricte — types différents)

// ── Opérateurs logiques ───────────────────────────────────────
let estClient = true, aCommandeRecente = false;
console.log(estClient && aCommandeRecente); // false — ET logique
console.log(estClient || aCommandeRecente); // true  — OU logique
console.log(!estClient);                    // false — NON logique

// Cas DariGo : vérification avant commande
let montantPanier = 275, estConnecte2 = true;
let peutCommander = montantPanier > 0 && estConnecte2;
console.log(peutCommander); // true

// ── Entrées/Sorties ───────────────────────────────────────────
// ENTRÉE 1 : prompt() — boîte de dialogue
let nomSaisi = prompt("Entrez votre nom :");
// Attention : prompt() retourne TOUJOURS une string ou null

let prixSaisi = parseFloat(prompt("Prix du plat :"));
// TOUJOURS convertir si on attend un nombre

// ENTRÉE 2 : champ HTML (pratique professionnelle)
// <input id="nom-client" type="text">
let nomClient = document.getElementById("nom-client").value;
// .value retourne toujours une string

// SORTIE 1 : alert() — boîte modale (peu utilisée en prod)
alert("Commande confirmée !");

// SORTIE 2 : innerHTML — écrire dans le HTML (méthode standard)
document.getElementById("message").innerHTML = "<strong>Merci " + nomClient + "</strong>";
document.getElementById("total").textContent = "145 MAD"; // sans HTML

// SORTIE 3 : console — pour les développeurs
console.log("Debug : total =", montantPanier);

// ── Application TP : calculatrice DariGo ─────────────────────
function calculerTotal() {
  let n1 = parseFloat(document.getElementById("prix1").value);
  let n2 = parseFloat(document.getElementById("prix2").value);
  let total = n1 + n2 + FRAIS_LIVRAISON;
  document.getElementById("resultat").textContent = total + " MAD";
}

✓
Vérification des acquis
5 questions — corrigé intégré
Quel est le résultat de typeof null en JavaScript ?
L'utilisateur saisit "10" dans un champ input. On lit avec document.getElementById("qte").value. Quel est le résultat de valeur + 5 ?
Quel est le résultat de 5 == "5" et 5 === "5" ?
Pourquoi faut-il utiliser parseInt(prompt("Saisir quantité :"), 10) plutôt que juste prompt("Saisir quantité :") ?
On déclare const plat = { nom: "Tajine", prix: 75 }. Que se passe-t-il si on fait plat.prix = 80 ?
Partie 2 — Fondamentaux JavaScript if · else · switch Ternaire · Nullish
⏱ 20 min

Structures alternatives — if, switch & ternaire

Les structures alternatives permettent à un programme de prendre des décisions et d'exécuter différents blocs de code selon des conditions. Dans DariGo, elles sont omniprésentes : afficher un prix différent selon le type de client, valider une note entre 0 et 20, déterminer le statut d'une commande selon son montant, gérer les cas d'erreur. Maîtriser if/else, switch et l'opérateur ternaire est fondamental pour tout programme logique.


1
if / else if / else — la structure fondamentale
Condition · Bloc · Valeurs falsy · Imbrication
javascript
// ── if / else — structure de base ─────────────────────────────
let totalPanier = 275;

if (totalPanier > 200) {
  console.log("Livraison gratuite !");
} else {
  console.log("Frais de livraison : 15 MAD");
}

// ── if / else if / else — plusieurs cas ───────────────────────
let note = 3.2;

if (note >= 4.5) {
  console.log("Excellent restaurant !");
} else if (note >= 4.0) {
  console.log("Très bon restaurant");
} else if (note >= 3.5) {
  console.log("Bon restaurant");
} else if (note >= 3.0) {
  console.log("Restaurant correct");
} else {
  console.log("Avis mitigés");
}
// Affiche : "Restaurant correct"

// ── Valeurs falsy — ce que JavaScript considère comme "faux" ──
// Les 6 valeurs falsy : false, 0, "", null, undefined, NaN
// TOUT le reste est truthy (y compris "0", [], {}, -1)

let codePromo = "";   // string vide = falsy
if (codePromo) {
  console.log("Code promo appliqué :", codePromo);
} else {
  console.log("Pas de code promo");  // affiché
}

let livreur = null;   // null = falsy
if (livreur) {
  console.log("Livreur assigné :", livreur.nom);
} else {
  console.log("En attente d'un livreur"); // affiché
}

// ── Imbrication de conditions ─────────────────────────────────
function verifierCommande(estConnecte, totalPanier, stockDisponible) {
  if (estConnecte) {
    if (totalPanier > 0) {
      if (stockDisponible) {
        console.log("Commande valide — traitement en cours");
        return true;
      } else {
        console.log("Certains plats ne sont plus disponibles");
      }
    } else {
      console.log("Votre panier est vide");
    }
  } else {
    console.log("Veuillez vous connecter pour commander");
  }
  return false;
}

// Meilleure écriture avec early return (guard clauses)
function verifierCommandeClean(estConnecte, totalPanier, stockDisponible) {
  if (!estConnecte)       { return "Connexion requise"; }
  if (totalPanier === 0)  { return "Panier vide"; }
  if (!stockDisponible)   { return "Stock épuisé"; }
  return "OK";
}

2
switch — choix parmi plusieurs valeurs fixes
case · break · default · Fall-through
javascript
// ── switch — idéal pour les valeurs discrètes ─────────────────
let statutCommande = "en_preparation";

switch (statutCommande) {
  case "en_attente":
    console.log("Commande en attente de confirmation");
    break;   // OBLIGATOIRE — sans break, on passe au case suivant
  case "confirmee":
    console.log("Commande confirmée par le restaurant");
    break;
  case "en_preparation":
    console.log("Votre plat est en cours de préparation");
    break;
  case "en_livraison":
    console.log("Votre livreur est en route !");
    break;
  case "livree":
    console.log("Commande livrée. Bon appétit !");
    break;
  case "annulee":
    console.log("Commande annulée. Remboursement en cours.");
    break;
  default:
    console.log("Statut inconnu :", statutCommande);
}
// Affiche : "Votre plat est en cours de préparation"

// ── Fall-through — plusieurs cas avec la même action ─────────
let categorieMenu = "plat";

switch (categorieMenu) {
  case "plat":
  case "tajine":
  case "couscous":
    console.log("Plat principal — portion copieuse");
    break;
  case "entree":
  case "soupe":
    console.log("Entrée — portion réduite");
    break;
  case "dessert":
  case "patisserie":
    console.log("Dessert");
    break;
  default:
    console.log("Catégorie non reconnue");
}

// ── Opérateur ternaire — if/else en une ligne ─────────────────
// Syntaxe : condition ? valeurSiVrai : valeurSiFaux
let nbArticles = 3;
let message = nbArticles > 0 ? "Panier non vide" : "Panier vide";
console.log(message); // "Panier non vide"

let fraisLivraison = totalPanier > 200 ? 0 : 15;
let labelLivraison = fraisLivraison === 0 ? "Gratuite" : fraisLivraison + " MAD";

// Dans le DOM : affichage conditionnel
document.getElementById("badge-panier").textContent =
  nbArticles > 0 ? nbArticles : "";

// Opérateur nullish coalescing ?? (ES2020)
// Retourne le côté droit seulement si la valeur est null ou undefined
let nomUtilisateur = null;
let affichage = nomUtilisateur ?? "Invité";
console.log(affichage); // "Invité"

let remise = 0;
console.log(remise ?? "Pas de remise"); // 0  (0 n'est pas null/undefined)
console.log(remise || "Pas de remise"); // "Pas de remise" (0 est falsy avec ||)

✓
Vérification des acquis
5 questions — corrigé intégré
Dans un switch, que se passe-t-il si on oublie le mot-clé break après un case ?
Quelles sont les 6 valeurs "falsy" en JavaScript ?
Quelle est la différence entre || et ?? pour une valeur 0 ?
On veut calculer la valeur absolue d'un nombre avec if/else. Pour x = -5, que retourne la fonction ?
Réécrire let label = "";
if (stock > 0) { label = "Disponible"; } else { label = "Rupture"; }
en une ligne avec l'opérateur ternaire.
Partie 2 — Fondamentaux JavaScript for · while · do...while forEach · break · continue
⏱ 20 min

Structures itératives — for, while & boucles modernes

Les boucles permettent de répéter des instructions un nombre déterminé ou indéterminé de fois. Dans DariGo, elles sont utilisées pour afficher la liste des plats, calculer le total du panier, parcourir les commandes d'un client, ou valider plusieurs champs de formulaire. Chaque type de boucle a son cas d'usage privilégié — choisir la bonne boucle rend le code plus lisible et prévisible.


1
for — boucle à compteur
Initialisation · Condition · Incrément · for...of · for...in
javascript
// ── for classique — quand on connaît le nombre d'itérations ───
// Afficher les nombres de 20 à 0 avec pas de -2
for (let i = 20; i >= 0; i -= 2) {
  if (i !== 0)
    document.write(i + " - ");
  else
    document.write(i);
}
// Affiche : 20 - 18 - 16 - 14 - 12 - 10 - 8 - 6 - 4 - 2 - 0

// Table de multiplication
for (let i = 1; i <= 10; i++) {
  console.log(`7 × ${i} = ${7 * i}`);
}

// Triangle d'étoiles (TP classique)
for (let i = 0; i < 5; i++) {
  let ligne = "";
  for (let j = 0; j <= i; j++) {  // boucle imbriquée
    ligne += "*";
  }
  document.write(ligne + "<br>");
}
// *
// **
// ***
// ****
// *****

// ── for...of — parcourir un tableau (ES6) ─────────────────────
const plats = ["Tajine Kefta", "Harira Ramadaniya", "Pastilla", "Couscous Royale"];

for (const plat of plats) {
  console.log(plat);   // affiche chaque nom
}

// Avec index : utiliser entries()
for (const [index, plat] of plats.entries()) {
  console.log(`${index + 1}. ${plat}`);
}
// 1. Tajine Kefta
// 2. Harira Ramadaniya
// ...

// ── for...in — parcourir les propriétés d'un objet ────────────
const commande = {
  id: 1042,
  client: "Fatima-Zahra Alaoui",
  ville: "Rabat",
  total: 145,
  statut: "en_preparation"
};

for (const cle in commande) {
  console.log(`${cle} : ${commande[cle]}`);
}
// id : 1042
// client : Fatima-Zahra Alaoui
// ville : Rabat
// total : 145
// statut : en_preparation

2
while & do...while — boucles conditionnelles
Condition avant/après · break · continue · forEach
javascript
// ── while — quand le nombre d'itérations est inconnu ─────────
// Saisir une note valide (TP M105 - exercice classique)
let note;
do {
  note = parseFloat(prompt("Saisir une note entre 0 et 20 :"));
} while (note > 20 || note < 0 || isNaN(note));
// La boucle recommence tant que la note est invalide
console.log("Note saisie :", note);

// while standard — saisir trois nombres valides
let count = 0;
let somme = 0;
while (count < 3) {
  let val;
  do {
    val = parseFloat(prompt(`Entrez le nombre ${count + 1} :`));
  } while (isNaN(val));
  somme += val;
  count++;
}
console.log("Somme :", somme);

// ── do...while — s'exécute AU MOINS une fois ──────────────────
// Utile pour les menus et saisies obligatoires
let choix;
do {
  choix = prompt("Choisir : 1-Menu  2-Commande  3-Suivi  0-Quitter");
  switch (choix) {
    case "1": console.log("Affichage du menu DariGo"); break;
    case "2": console.log("Nouvelle commande"); break;
    case "3": console.log("Suivi de livraison"); break;
    case "0": console.log("Au revoir !"); break;
    default:  console.log("Choix invalide");
  }
} while (choix !== "0");

// ── break et continue ──────────────────────────────────────────
const prix = [75, 45, 0, 120, 30, -5, 90];

for (const p of prix) {
  if (p < 0) break;      // arrêt total de la boucle dès un négatif
  if (p === 0) continue; // sauter ce tour, passer au suivant
  console.log(p + " MAD");
}
// 75 MAD, 45 MAD, 120 MAD, 30 MAD (0 sauté, -5 arrête)

// ── forEach — parcourir un tableau avec une fonction ──────────
const restaurants = [
  { nom: "La Sqala", ville: "Casablanca", note: 4.8 },
  { nom: "Dar Zitoun", ville: "Marrakech", note: 4.6 },
  { nom: "Al Fassia", ville: "Rabat", note: 4.7 },
];

restaurants.forEach((resto, index) => {
  console.log(`${index + 1}. ${resto.nom} (${resto.ville}) — ${resto.note}/5`);
});

// Calculer la note moyenne
let totalNotes = 0;
restaurants.forEach(resto => totalNotes += resto.note);
let moyenne = totalNotes / restaurants.length;
console.log("Note moyenne :", moyenne.toFixed(1));

✓
Vérification des acquis
4 questions — corrigé intégré
Quelle est la différence entre while et do...while ?
On parcourt un tableau de prix et on veut ignorer les prix à 0 mais continuer la boucle. Quelle instruction utiliser ?
Quelle boucle est la plus appropriée pour afficher la liste des plats d'un tableau JavaScript ?
Avec do { ... } while (note < 0 || note > 20), que se passe-t-il si l'utilisateur entre 15 au premier essai ?
Partie 2 — Fondamentaux JavaScript Fonctions · Arrow · Scope Callback · Promise · try/catch
⏱ 28 min

Fonctions, arrow functions, callbacks & Promises

Les fonctions sont les briques fondamentales de tout programme JavaScript. Elles permettent d'encapsuler de la logique, de la réutiliser et de la tester indépendamment. ES6 a introduit les arrow functions (expressions lambdas) qui simplifient la syntaxe et résolvent les problèmes de contexte this. Les callbacks et les Promises permettent de gérer les opérations asynchrones — indispensables pour les appels AJAX à l'API DariGo. La gestion des exceptions avec try/catch rend le code robuste face aux erreurs.


1
Fonctions — déclaration, paramètres & portée
function · return · Paramètres par défaut · Scope · Hoisting
javascript
// ── Déclaration de fonction ───────────────────────────────────
function calculerSurface(largeur, hauteur) {
  return largeur * hauteur;
}
console.log(calculerSurface(5, 3));  // 15

// ── Paramètres par défaut (ES6) ───────────────────────────────
function calculerTotal(sousTotal, fraisLivraison = 15, remise = 0) {
  return sousTotal + fraisLivraison - (sousTotal * remise / 100);
}
console.log(calculerTotal(275));          // 290 (frais: 15, remise: 0)
console.log(calculerTotal(275, 0));       // 275 (livraison gratuite)
console.log(calculerTotal(275, 15, 10)); // 277.5 (10% de remise)

// ── Expression de fonction ────────────────────────────────────
const multiplie = function(x, valeurDefaut = 8) {
  return 3 * x;
};
console.log(multiplie(5));    // 15
console.log(multiplie());     // 24 (utilise la valeur par défaut 8)

// ── Portée (Scope) ────────────────────────────────────────────
let prixGlobal = 100;  // variable globale — accessible partout

function appliquerRemise() {
  let remiseLocale = 0.1;   // variable locale — accessible SEULEMENT ici
  return prixGlobal * (1 - remiseLocale);  // peut accéder à prixGlobal
}
console.log(appliquerRemise());  // 90
// console.log(remiseLocale);   // ReferenceError !

// Hoisting — les déclarations de fonctions sont remontées
bonjour();  // fonctionne même avant la déclaration !
function bonjour() { console.log("Marhba !"); }

// Les expressions de fonctions NE sont PAS hoistées
// saluer(); // TypeError: saluer is not a function
const saluer = function() { console.log("Salam !"); };

// ── Fonctions qui retournent plusieurs valeurs via objet ───────
function analyserPrix(prix) {
  return {
    original: prix,
    avecRemise: prix * 0.9,
    avecLivraison: prix + 15,
    ttc: prix * 1.2
  };
}
const { original, avecRemise, ttc } = analyserPrix(75);
console.log(original, avecRemise, ttc);  // 75  67.5  90

2
Arrow functions (expressions lambdas) — ES6
Syntaxe concise · this lexical · Retour implicite
javascript
// ── Arrow function — syntaxe progressive ─────────────────────
// Fonction classique
function calculer(a, b) { return a + b; }

// Arrow function équivalente
const calculer2 = (a, b) => { return a + b; };

// Retour implicite (sans accolades — une seule expression)
const calculer3 = (a, b) => a + b;

// Un seul paramètre — parenthèses optionnelles
const doubler = n => n * 2;
const formaterPrix = prix => `${prix} MAD`;

// Zéro paramètre — parenthèses obligatoires
const getTimestamp = () => Date.now();

// ── Cas d'usage DariGo ────────────────────────────────────────
const plats = [
  { nom: "Tajine Kefta", prix: 75, categorie: "plat" },
  { nom: "Harira", prix: 25, categorie: "soupe" },
  { nom: "Pastilla", prix: 65, categorie: "entree" },
  { nom: "Couscous", prix: 90, categorie: "plat" },
];

// map() — transformer chaque élément
const noms = plats.map(p => p.nom);
// ["Tajine Kefta", "Harira", "Pastilla", "Couscous"]

const prixAvecLivraison = plats.map(p => ({ ...p, prixFinal: p.prix + 15 }));

// filter() — filtrer selon une condition
const platsAbordables = plats.filter(p => p.prix < 70);
const platsPrincipaux = plats.filter(p => p.categorie === "plat");

// reduce() — calculer une valeur unique
const totalMenu = plats.reduce((acc, p) => acc + p.prix, 0);
console.log(totalMenu);  // 255

// sort() — trier
const platsTries = [...plats].sort((a, b) => a.prix - b.prix);

// Tout enchaîné :
const totalPlatsPrincipaux = plats
  .filter(p => p.categorie === "plat")
  .map(p => p.prix)
  .reduce((acc, prix) => acc + prix, 0);
console.log(totalPlatsPrincipaux);  // 165 (75+90)

3
Callbacks, Promises & try/catch
Asynchrone · Callback hell · Promise · then/catch · try/catch
javascript
// ── Callback — fonction passée en argument ────────────────────
function chargerPlats(callback) {
  // Simule un délai réseau
  setTimeout(() => {
    const plats = [{ nom: "Tajine", prix: 75 }, { nom: "Harira", prix: 25 }];
    callback(plats);
  }, 1000);
}

chargerPlats(function(plats) {
  plats.forEach(p => console.log(p.nom));
});
// Avec arrow function :
chargerPlats(plats => plats.forEach(p => console.log(p.nom)));

// ── Exemple TP M105 : Promise avec présence ───────────────────
// (Exercice 4 du TP — "présence au bout de 10 secondes")
setInterval(() => {
  const date = new Date();
  document.querySelector("#montre").innerHTML = date.toLocaleTimeString("fr-FR");

  if (date.getSeconds() % 10 === 0) {
    const maPromesse = new Promise((resolve, reject) => {
      document.getElementById("marquer").style.display = "block";
      setTimeout(() => reject("Absent"), 5000);  // timeout 5 secondes
    });

    maPromesse
      .then(() => console.log("Présent !"))
      .catch(() => console.log("Absent !"))
      .finally(() => {
        document.getElementById("marquer").style.display = "none";
      });
  }
}, 1000);

// ── try/catch — gestion des exceptions ───────────────────────
function calculerSurface(dim1, dim2) {
  if (isNaN(dim1) || isNaN(dim2)) {
    throw new Error("Paramètres invalides — nombres attendus");
  }
  return dim1 * dim2;
}

try {
  const surface = calculerSurface(5, "abc");  // va lever une erreur
  console.log("Surface :", surface);
} catch (erreur) {
  console.error("Erreur :", erreur.message);  // "Paramètres invalides..."
} finally {
  console.log("Calcul terminé (succès ou erreur)");
}

// Validation de note avec try/catch (TP M105)
function verifierNote() {
  const note = parseInt(document.querySelector("#note").value);
  if (note < 0 || note > 20) {
    throw new Error("Note hors intervalle [0-20]");
  }
  return note;
}

try {
  const note = verifierNote();
  document.getElementById("resultat").textContent = "Note valide : " + note;
} catch (e) {
  document.getElementById("resultat").textContent = e.message;
}

✓
Vérification des acquis
5 questions — corrigé intégré
Quelle est la version arrow function la plus concise de function doubler(n) { return n * 2; } ?
Quel est le résultat de [75, 45, 120].filter(p => p > 50).reduce((acc, p) => acc + p, 0) ?
Qu'est-ce qu'un callback en JavaScript ?
Le bloc finally dans try/catch s'exécute :
Dans l'exercice TP de la surface avec try/catch, que se passe-t-il si on appelle calculerSurface(3, "A") ?
Partie 2 — Fondamentaux JavaScript Objets · Array · Date · Math RegEx · JSON · Classes
⏱ 28 min

Objets, objets natifs & JSON

En JavaScript, presque tout est un objet. Les objets permettent de regrouper des données liées et les comportements associés. Les objets natifs (Array, Date, Math, RegEx) sont fournis par le langage et offrent des méthodes puissantes prêtes à l'emploi. JSON (JavaScript Object Notation) est le format standard d'échange de données entre le front-end JavaScript et le back-end PHP de DariGo.


1
Créer et manipuler des objets
Littéral · Constructeur · Classes ES6 · this · Prototype
javascript
// ── Objet littéral — la façon la plus simple ──────────────────
const plat = {
  nom: "Tajine Kefta",
  prix: 75,
  categorie: "plat",
  disponible: true,
  restaurant: "La Sqala, Casablanca",

  // Méthode dans l'objet
  afficher: function() {
    return `${this.nom} — ${this.prix} MAD`;
  },
  // Arrow function dans objet (this ne pointe plus vers l'objet !)
  // afficherArrow: () => `${this.nom}` // undefined !
};

console.log(plat.nom);          // "Tajine Kefta"
console.log(plat["prix"]);      // 75 — accès par bracket notation
console.log(plat.afficher());   // "Tajine Kefta — 75 MAD"

// Ajouter/modifier/supprimer des propriétés
plat.note = 4.8;               // ajout
plat.prix = 80;                // modification
delete plat.disponible;        // suppression

// ── Constructeur de fonction (ES5) ────────────────────────────
// Exercice TP M105 : Classe Personne
var Personne = function(nom, age) {
  this.nom = nom;
  this.age = age;
  this.decrire = function() {
    return this.nom + ", " + this.age + " ans";
  };
};

var obj1 = new Personne("Ali", 25);
var obj2 = new Personne("Nadia", 24);
console.log(obj1.decrire());    // "Ali, 25 ans"
console.log(obj2.decrire());    // "Nadia, 24 ans"

// ── Classes ES6 — syntaxe moderne ─────────────────────────────
class Livreur {
  constructor(nom, ville, vehicule) {
    this.nom = nom;
    this.ville = ville;
    this.vehicule = vehicule;
    this.disponible = true;
    this.nbLivraisons = 0;
  }

  accepterMission() {
    if (!this.disponible) return `${this.nom} est déjà en mission`;
    this.disponible = false;
    this.nbLivraisons++;
    return `${this.nom} accepte la mission (${this.nbLivraisons}ème livraison)`;
  }

  terminerLivraison() {
    this.disponible = true;
    return `${this.nom} est disponible`;
  }

  toString() {
    return `${this.nom} — ${this.ville} — ${this.vehicule}`;
  }
}

const khalid = new Livreur("Khalid El Amrani", "Casablanca", "moto");
console.log(khalid.accepterMission()); // "Khalid El Amrani accepte la mission (1ème livraison)"
console.log(khalid.disponible);        // false
khalid.terminerLivraison();
console.log(khalid.disponible);        // true

// ── Déstructuration d'objet ───────────────────────────────────
const { nom, ville, vehicule } = khalid;
console.log(nom, ville);  // "Khalid El Amrani" "Casablanca"

2
Objets natifs — Array, Date, Math & RegEx
Méthodes tableau · Date · Math · RegEx · test · match
javascript
// ── Array — méthodes essentielles ────────────────────────────
const panier = ["Tajine Kefta", "Harira", "Pastilla"];

// Ajout / Suppression
panier.push("Chebakia");         // ajout en fin → ["Tajine", "Harira", "Pastilla", "Chebakia"]
panier.pop();                    // suppression en fin → retourne "Chebakia"
panier.unshift("Couscous");      // ajout en début
panier.shift();                  // suppression en début

panier.splice(1, 1);             // supprimer 1 élément à l'index 1
panier.splice(1, 0, "Mechoui");  // insérer sans supprimer

// Recherche
console.log(panier.indexOf("Harira"));   // 0 ou -1 si absent
console.log(panier.includes("Tajine")); // true/false
console.log(panier.find(p => p.startsWith("H"))); // "Harira"

// Transformation
const majuscules = panier.map(p => p.toUpperCase());
const abordables = panier.filter(p => p.length < 10);
const total = [75, 25, 65].reduce((acc, p) => acc + p, 0); // 165
const tries = [...panier].sort(); // copie triée alphabétiquement

// ── Objet Date ────────────────────────────────────────────────
const maintenant = new Date();
console.log(maintenant.getFullYear());   // 2025
console.log(maintenant.getMonth() + 1);  // mois (0-11) → +1 pour afficher correctement
console.log(maintenant.getDate());       // jour du mois
console.log(maintenant.getHours());      // heure
console.log(maintenant.getMinutes());    // minutes
console.log(maintenant.getSeconds());    // secondes
console.log(maintenant.toLocaleDateString("fr-MA")); // "06/01/2025"
console.log(maintenant.toLocaleTimeString("fr-FR")); // "14:32:05"

// Date de livraison estimée (30 minutes)
const livraison = new Date();
livraison.setMinutes(livraison.getMinutes() + 30);
console.log("Livraison estimée :", livraison.toLocaleTimeString("fr-FR"));

// ── Objet Math ────────────────────────────────────────────────
console.log(Math.abs(-75));      // 75 — valeur absolue
console.log(Math.round(4.6));    // 5 — arrondir
console.log(Math.floor(4.9));    // 4 — arrondir vers le bas
console.log(Math.ceil(4.1));     // 5 — arrondir vers le haut
console.log(Math.max(75, 25, 120, 45)); // 120
console.log(Math.min(75, 25, 120, 45)); // 25
console.log(Math.sqrt(144));     // 12 — racine carrée
console.log(Math.PI);            // 3.14159...

// Nombre aléatoire entre min et max
function nombreAleatoire(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(nombreAleatoire(1, 6)); // dé à 6 faces

// Surface du cercle (TP M105 — exercice 1)
function surfaceCercle() {
  const rayon = parseFloat(prompt("Entrez le rayon du cercle :"));
  return Math.PI * rayon * rayon;
}

// ── RegEx — expressions régulières ───────────────────────────
// Valider un email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRegex.test("khalid@darigo.ma")); // true
console.log(emailRegex.test("khalid@"));          // false

// Valider un numéro marocain
const telRegex = /^(\+212|0)[5-7]\d{8}$/;
console.log(telRegex.test("0612345678"));  // true
console.log(telRegex.test("0112345678"));  // false

// Extraire des données
const texte = "Commande #1042 — Total : 275 MAD";
const match = texte.match(/\d+/g);
console.log(match); // ["1042", "275"]

// Remplacer
const clean = "  Tajine  Kefta  ".replace(/\s+/g, " ").trim();
console.log(clean); // "Tajine Kefta"

3
JSON — échange de données avec le back-end
JSON.stringify · JSON.parse · API · fetch
javascript
// ── JSON — JavaScript Object Notation ────────────────────────
// Format texte léger pour l'échange de données
// Ressemble à un objet JS mais avec des règles strictes :
// - Clés entre guillemets doubles
// - Pas de commentaires
// - Pas de fonctions

// Objet JavaScript → JSON string (pour envoyer au serveur)
const commande = {
  idClient: 42,
  plats: [
    { idPlat: 1, nom: "Tajine Kefta", prix: 75, quantite: 2 },
    { idPlat: 5, nom: "Harira", prix: 25, quantite: 1 }
  ],
  adresse: "Hay Riad, Rabat",
  villeLivraison: "Rabat",
  total: 175,
  createdAt: new Date().toISOString()
};

const jsonString = JSON.stringify(commande);
console.log(jsonString);
// {"idClient":42,"plats":[{"idPlat":1,"nom":"Tajine Kefta",...}],...}

// Format indenté pour l'affichage
const jsonPretty = JSON.stringify(commande, null, 2);

// JSON string → Objet JavaScript (pour traiter la réponse du serveur)
const reponseServeur = '{"success":true,"commandeId":1042,"statut":"en_attente","eta":25}';
const reponse = JSON.parse(reponseServeur);
console.log(reponse.commandeId);  // 1042
console.log(reponse.eta);         // 25

// ── Envoi vers l'API PHP DariGo ───────────────────────────────
fetch("api/commandes.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(commande)  // convertir en string avant envoi
})
.then(response => response.json())  // response.json() parse automatiquement
.then(data => {
  console.log("Commande créée :", data.commandeId);
  document.getElementById("statut").textContent = "Commande confirmée !";
})
.catch(err => {
  console.error("Erreur :", err);
});

// ── Stockage local avec JSON ──────────────────────────────────
// Sauvegarder le panier dans localStorage
const panierData = [{ id: 1, nom: "Tajine", prix: 75 }];
localStorage.setItem("panier-darigo", JSON.stringify(panierData));

// Récupérer le panier au chargement de la page
const panierSauvegarde = localStorage.getItem("panier-darigo");
const panier2 = panierSauvegarde ? JSON.parse(panierSauvegarde) : [];
console.log(panier2);

✓
Vérification des acquis
5 questions — corrigé intégré
On appelle new Date().getMonth() en janvier. Quel est le résultat ?
Quelle différence entre JSON.stringify() et JSON.parse() ?
On a un tableau [75, 45, 120, 30]. Quelle méthode retourne le plus grand prix ?
Le regex /^(\+212|0)[5-7]\d{8}$/ teste "0712345678". Quel est le résultat ?
Dans la classe ES6, pourquoi ne peut-on pas utiliser une arrow function comme méthode pour accéder à this.nom ?
Partie 3 — DOM & Manipulation DOM · Document parentNode · childNodes · Navigation
⏱ 22 min

Arbre DOM & objet Document

Le DOM (Document Object Model) est la représentation en mémoire d'une page HTML sous forme d'arbre d'objets JavaScript. Quand le navigateur charge une page, il parse le HTML et construit cet arbre — chaque balise HTML devient un nœud, chaque texte devient un nœud texte. JavaScript peut parcourir cet arbre, lire ses nœuds, les modifier, en ajouter ou en supprimer — c'est cela, la manipulation dynamique d'une page web. Comprendre la structure de cet arbre est le prérequis à toute manipulation DOM.


1
Structure de l'arbre DOM
Nœuds · Parents · Enfants · Fratrie · Objet document

Chaque page HTML génère un arbre DOM unique. La racine de cet arbre est toujours document — l'objet global représentant le document entier. Sous document se trouve html, puis head et body, et ainsi de suite jusqu'aux feuilles de l'arbre (les nœuds texte).

Chaque nœud a des relations avec ses voisins : son parent (parentNode), ses enfants (childNodes, children), son premier et dernier enfant (firstChild, lastChild), et ses frères/sœurs (nextSibling, previousSibling).

html
<!-- Page DariGo simplifiée ──────────────────────────────────── -->
<!DOCTYPE html>
<html>                        <!-- document.documentElement -->
  <head>                      <!-- document.head -->
    <title>DariGo</title>
  </head>
  <body>                      <!-- document.body -->
    <h1 id="titre">Menu DariGo</h1>
    <ul id="liste-plats">
      <li class="plat">Tajine Kefta — 75 MAD</li>
      <li class="plat">Harira — 25 MAD</li>
      <li class="plat">Pastilla — 65 MAD</li>
    </ul>
  </body>
</html>

/*
Arbre DOM correspondant :
document
└── html
    ├── head
    │   └── title → [texte: "DariGo"]
    └── body
        ├── h1#titre → [texte: "Menu DariGo"]
        └── ul#liste-plats
            ├── li.plat → [texte: "Tajine Kefta — 75 MAD"]
            ├── li.plat → [texte: "Harira — 25 MAD"]
            └── li.plat → [texte: "Pastilla — 65 MAD"]
*/

2
Navigation dans le DOM
parentNode · children · firstChild · nextSibling · nodeType
javascript
// ── Propriétés de navigation ──────────────────────────────────
const liste = document.getElementById("liste-plats");

// Parent
console.log(liste.parentNode);          // <body>
console.log(liste.parentElement);       // <body> (même chose pour les éléments)

// Enfants
console.log(liste.childNodes);          // NodeList incluant texte et commentaires
console.log(liste.children);            // HTMLCollection — UNIQUEMENT les éléments HTML
console.log(liste.children.length);     // 3
console.log(liste.firstElementChild);   // premier <li>
console.log(liste.lastElementChild);    // dernier <li>

// Fratrie (siblings)
const premierLi = liste.firstElementChild;
console.log(premierLi.nextElementSibling);     // <li>Harira</li>
console.log(premierLi.previousElementSibling); // null (il est le premier)

// Type de nœud
// 1 = ELEMENT_NODE, 3 = TEXT_NODE, 8 = COMMENT_NODE
console.log(liste.nodeType);      // 1 (élément)
console.log(liste.nodeName);      // "UL"
console.log(premierLi.textContent); // "Tajine Kefta — 75 MAD"

// ── Exercice TP M105 — liste de villes ───────────────────────
// Ajouter, sélectionner et supprimer des villes

function ajouterVille() {
  const input = document.querySelector("#ville");
  const liste = document.querySelector("#liste");

  const li = document.createElement("li");
  li.className = "unselected";
  li.textContent = input.value;

  // Clic pour sélectionner/désélectionner
  li.addEventListener("click", function() {
    this.className = this.className === "unselected" ? "selected" : "unselected";
  });

  liste.appendChild(li);
  input.value = "";
}

function supprimerVille() {
  const liste = document.querySelector("#liste");
  const selected = liste.querySelectorAll(".selected");
  selected.forEach(el => liste.removeChild(el));
}

✓
Vérification des acquis
4 questions — corrigé intégré
Quelle est la différence entre childNodes et children ?
Quel objet JavaScript représente la racine du DOM et est accessible partout dans le navigateur ?
On a une liste <ul> avec 3 <li>. Comment accéder au deuxième <li> via la navigation DOM ?
Quel est le nodeType d'un élément HTML comme <div> ?
Partie 3 — DOM & Manipulation getElementById · querySelector getElementsByClassName · NodeList
⏱ 18 min

Sélecteurs & modes d'accès aux éléments

Pour manipuler un élément HTML avec JavaScript, il faut d'abord le cibler — le sélectionner dans l'arbre DOM. JavaScript offre plusieurs méthodes selon le critère de sélection : par identifiant, par classe, par balise, ou par sélecteur CSS. En 2025, querySelector et querySelectorAll sont les méthodes standard — elles acceptent n'importe quel sélecteur CSS et unifient l'API de sélection.


1
Toutes les méthodes de sélection
getElementById · querySelector · querySelectorAll · HTMLCollection vs NodeList
javascript
// ── getElementById — sélection par id (le plus rapide) ────────
const titre = document.getElementById("titre-menu");
const champRecherche = document.getElementById("recherche");
// Retourne : l'élément ou null si non trouvé

// ── getElementsByClassName — par classe ───────────────────────
const cartes = document.getElementsByClassName("carte-plat");
// Retourne : HTMLCollection (live — se met à jour automatiquement)
console.log(cartes.length);   // nombre de .carte-plat
// Parcourir :
for (let i = 0; i < cartes.length; i++) {
  cartes[i].style.border = "1px solid #e74c3c";
}

// ── getElementsByTagName — par balise ─────────────────────────
const tousLesLi = document.getElementsByTagName("li");
const tousLesInput = document.getElementsByTagName("input");

// ── querySelector — premier élément correspondant ─────────────
// Accepte tout sélecteur CSS — méthode moderne standard
const premierPlat = document.querySelector(".carte-plat");
const titreMenu   = document.querySelector("h1");
const inputPrix   = document.querySelector("#formulaire input[type='number']");
const actif       = document.querySelector(".nav-item.active");
const premierLi   = document.querySelector("ul#liste-plats li:first-child");

// ── querySelectorAll — TOUS les éléments correspondants ────────
// Retourne une NodeList (statique — snapshot au moment de l'appel)
const toutesCartes = document.querySelectorAll(".carte-plat");
const inputsRequis = document.querySelectorAll("input[required]");
const lieuxDispos  = document.querySelectorAll(".ville:not(.fermee)");

// Parcourir une NodeList
toutesCartes.forEach(carte => {
  carte.addEventListener("click", () => console.log(carte.dataset.id));
});
// OU avec for...of
for (const input of inputsRequis) {
  if (!input.value) input.classList.add("erreur");
}

// ── Tableau comparatif des sélecteurs ─────────────────────────
/*
Méthode                  | Retour              | Sélecteur CSS | Live ?
-------------------------|---------------------|---------------|-------
getElementById("id")     | Element ou null     | Non           | —
getElementsByClassName   | HTMLCollection      | Non           | Oui
getElementsByTagName     | HTMLCollection      | Non           | Oui
querySelector(css)       | Element ou null     | Oui           | Non
querySelectorAll(css)    | NodeList (statique) | Oui           | Non
*/

// ── Exemples concrets DariGo ──────────────────────────────────
// Récupérer la valeur d'un champ
const adresse = document.querySelector("#adresse-livraison").value;

// Récupérer tous les plats sélectionnés
const platsChoisis = document.querySelectorAll(".plat-checkbox:checked");
let total = 0;
platsChoisis.forEach(cb => {
  total += parseFloat(cb.dataset.prix);
});

// Trouver le bouton Commander dans un formulaire
const btnCommander = document.querySelector("form#commande button[type='submit']");
btnCommander.disabled = total === 0;

✓
Vérification des acquis
4 questions — corrigé intégré
Quelle méthode utiliser pour sélectionner tous les éléments .carte-plat d'une page ?
Que retourne document.getElementById("inexistant") si l'élément n'existe pas dans le DOM ?
Quelle est la différence entre une HTMLCollection (getElementsByClassName) et une NodeList (querySelectorAll) ?
Quel sélecteur CSS utiliser pour cibler tous les input de type number dans un formulaire d'id commande ?
Partie 3 — DOM & Manipulation createElement · appendChild innerHTML · removeChild · insertBefore
⏱ 22 min

Manipulation des éléments HTML — CRUD DOM

Créer, lire, modifier et supprimer des éléments dans le DOM — c'est la compétence centrale du développement JavaScript front-end. Afficher dynamiquement une liste de plats reçus de l'API, ajouter une ligne au tableau du panier, mettre à jour le prix total, supprimer un article — tout cela se fait via la manipulation du DOM. Cette section couvre toutes les opérations CRUD sur les nœuds DOM, depuis la création d'éléments jusqu'à leur suppression.


1
Créer, insérer et supprimer des éléments
createElement · appendChild · insertBefore · removeChild · innerHTML
javascript
// ── CRÉER un élément ──────────────────────────────────────────
const nouvelleCarte = document.createElement("div");
nouvelleCarte.className = "carte-plat";
nouvelleCarte.id = "plat-42";

// Ajouter du contenu
const nomElement = document.createElement("h3");
nomElement.textContent = "Tajine Kefta";   // textContent : texte pur (sûr)

const prixElement = document.createElement("p");
prixElement.innerHTML = "75 MAD"; // innerHTML : avec HTML

const bouton = document.createElement("button");
bouton.textContent = "Ajouter au panier";
bouton.className = "btn-ajouter";
bouton.dataset.platId = "42";        // attribut data-plat-id
bouton.dataset.prix = "75";

// Assembler l'arbre
nouvelleCarte.appendChild(nomElement);
nouvelleCarte.appendChild(prixElement);
nouvelleCarte.appendChild(bouton);

// ── INSÉRER dans le DOM ───────────────────────────────────────
const grille = document.getElementById("grille-plats");

grille.appendChild(nouvelleCarte);           // à la fin
grille.prepend(nouvelleCarte);               // au début (ES6)
grille.insertBefore(nouvelleCarte, grille.firstElementChild); // avant le premier

// insertAdjacentHTML — insertion rapide par position
grille.insertAdjacentHTML("beforeend", `
  <div class="carte-plat" data-id="43">
    <h3>Harira Ramadaniya</h3>
    <p><strong>25 MAD</strong></p>
    <button class="btn-ajouter" data-prix="25">Ajouter</button>
  </div>
`);
// Positions : beforebegin, afterbegin, beforeend, afterend

// ── MODIFIER un élément ───────────────────────────────────────
const titre = document.getElementById("titre-menu");
titre.textContent = "Menu DariGo — Casablanca"; // modifier le texte
titre.innerHTML = "Menu DariGo — Casablanca"; // avec HTML

// Modifier un attribut
const img = document.querySelector(".photo-plat");
img.src = "images/tajine-kefta.jpg";
img.alt = "Tajine Kefta servi chaud";
img.setAttribute("loading", "lazy");
console.log(img.getAttribute("src")); // lire un attribut
img.removeAttribute("loading");        // supprimer un attribut

// ── SUPPRIMER un élément ──────────────────────────────────────
const carteSupprimee = document.getElementById("plat-42");
carteSupprimee.parentNode.removeChild(carteSupprimee); // méthode classique
// OU plus moderne :
carteSupprimee.remove(); // ES5+ — plus simple

// ── Exercice TP M105 : Tableau CRUD complet ───────────────────
function ajouterLigne(nom, score) {
  const tbody = document.querySelector("#tableau-notes tbody");
  const tr = document.createElement("tr");
  tr.innerHTML = `
    <td>${nom}</td>
    <td>${score}</td>
    <td><button onclick="supprimerLigne(this)">Supprimer</button></td>
  `;
  tbody.appendChild(tr);
  calculerMoyenne();
}

function supprimerLigne(btn) {
  btn.closest("tr").remove();
  calculerMoyenne();
}

function calculerMoyenne() {
  const scores = document.querySelectorAll("#tableau-notes td:nth-child(2)");
  let somme = 0;
  scores.forEach(td => somme += parseFloat(td.textContent));
  document.getElementById("moyenne").textContent =
    scores.length > 0 ? (somme / scores.length).toFixed(2) : "—";
}

✓
Vérification des acquis
4 questions — corrigé intégré
Quelle différence entre textContent et innerHTML pour modifier le contenu d'un élément ?
On crée un élément avec createElement("li") mais on ne l'insère pas. Est-il visible dans la page ?
Comment supprimer un élément de façon moderne en ES5+ ?
Quel est l'avantage de insertAdjacentHTML par rapport à innerHTML += pour ajouter du contenu ?
Partie 3 — DOM & Manipulation style · classList setAttribute · dataset · DOMMenuObject
⏱ 20 min

Styles, attributs & classes CSS

Modifier l'apparence des éléments via JavaScript est essentiel pour créer des interfaces dynamiques. On peut changer les styles directement via la propriété style, mais la bonne pratique est de manipuler les classes CSS — ajouter, retirer, basculer — et laisser le CSS faire le travail visuel. classList est l'API centrale pour cela. Les attributs data-* permettent de stocker des données liées aux éléments HTML sans polluer le balisage.


1
style, classList et attributs data-*
style inline · addClass · removeClass · toggle · dataset
javascript
// ── style inline — modification directe ──────────────────────
const carte = document.querySelector(".carte-plat");
carte.style.backgroundColor = "#fff3cd"; // camelCase (pas background-color)
carte.style.border = "2px solid #f0ad4e";
carte.style.padding = "16px";
carte.style.display = "none";   // masquer
carte.style.display = "";       // restaurer (vider réinitialise)

// Lire un style calculé (inclut les styles CSS)
const styleCal = window.getComputedStyle(carte);
console.log(styleCal.backgroundColor); // "rgb(255, 243, 205)"

// ── classList — la méthode recommandée ───────────────────────
const badge = document.getElementById("badge-panier");

badge.classList.add("visible");         // ajouter une classe
badge.classList.add("badge-rouge", "pulse"); // plusieurs à la fois
badge.classList.remove("masque");       // retirer
badge.classList.toggle("active");       // ajouter si absent, retirer si présent
badge.classList.replace("old", "new");  // remplacer
console.log(badge.classList.contains("visible")); // true/false

// Exercice TP M105 — basculer des classes (2 classes alternées)
const para = document.querySelector(".c1");
para.addEventListener("click", function() {
  if (this.classList.contains("c1")) {
    this.classList.remove("c1");
    this.classList.add("c2");
  } else {
    this.classList.remove("c2");
    this.classList.add("c1");
  }
  // Version courte avec toggle :
  // this.classList.toggle("c1");
  // this.classList.toggle("c2");
});

// ── Attributs HTML ────────────────────────────────────────────
const input = document.querySelector("#quantite");
input.setAttribute("min", "1");
input.setAttribute("max", "10");
input.setAttribute("placeholder", "Quantité");
console.log(input.getAttribute("type")); // "number"
input.removeAttribute("disabled");

// Attribut boolean
const btn = document.querySelector("#btn-commander");
btn.disabled = true;            // propriété JS directe
btn.disabled = false;
btn.setAttribute("disabled", ""); // équivalent
btn.removeAttribute("disabled");

// ── data-* — stocker des données dans le HTML ────────────────
// HTML : <div class="carte-plat" data-id="42" data-prix="75" data-categorie="plat">
const platEl = document.querySelector(".carte-plat[data-id='42']");
console.log(platEl.dataset.id);         // "42" — toujours string
console.log(platEl.dataset.prix);       // "75"
console.log(platEl.dataset.categorie);  // "plat"

// Écrire
platEl.dataset.stock = "5";             // ajoute data-stock="5"
platEl.dataset.id = "99";              // modifie data-id

// Utilisation dans les event listeners
document.querySelectorAll(".btn-ajouter").forEach(btn => {
  btn.addEventListener("click", function() {
    const id    = this.dataset.platId;
    const prix  = parseFloat(this.dataset.prix);
    ajouterAuPanier(id, prix);
  });
});

// ── DOMMenuObject — menu contextuel dynamique ─────────────────
// Exercice TP M105 : clic droit → menu contextuel
window.addEventListener("load", () => {
  document.getElementById("myDiv").addEventListener("contextmenu", (event) => {
    event.preventDefault();
    const menu = document.getElementById("menu");
    menu.style.display = "inline-block";
    menu.style.left = event.pageX + "px";
    menu.style.top  = event.pageY + "px";
  });
  // Fermer le menu au clic ailleurs
  document.addEventListener("click", () => {
    document.getElementById("menu").style.display = "none";
  });
});

✓
Vérification des acquis
4 questions — corrigé intégré
Pourquoi préfère-t-on manipuler les classes CSS plutôt que le style inline en JavaScript ?
Comment masquer un élément avec classList plutôt que style.display ?
Un bouton a l'attribut HTML data-prix="75". On lit btn.dataset.prix. De quel type est la valeur retournée ?
classList.toggle("active") sur un élément qui possède déjà la classe "active". Que se passe-t-il ?
Partie 4 — Événements & Formulaires addEventListener MouseEvents · Clavier · event · preventDefault
⏱ 22 min

Événements — addEventListener, MouseEvents & clavier

La programmation événementielle est le paradigme central du JavaScript côté client. Plutôt que d'exécuter du code séquentiellement, on définit des réponses à des événements déclenchés par l'utilisateur ou le navigateur — un clic, une frappe au clavier, un survol de souris, le chargement de la page. addEventListener est la méthode standard pour attacher ces réponses à des éléments DOM. L'objet event transmis au handler contient toutes les informations sur ce qui s'est passé.


1
addEventListener — la méthode standard
event object · removeEventListener · DOMContentLoaded · Propagation
javascript
// ── addEventListener — syntaxe de base ───────────────────────
// element.addEventListener(type, handler, [options])
const btn = document.getElementById("btn-commander");

// Handler nommé — recommandé pour pouvoir le retirer
function passerCommande(event) {
  console.log("Clic !", event.type, event.target);
  event.preventDefault(); // empêcher le comportement par défaut
}
btn.addEventListener("click", passerCommande);

// Handler anonyme (arrow function) — simple mais non-supprimable
btn.addEventListener("click", (e) => {
  console.log("Position du clic :", e.clientX, e.clientY);
});

// Retirer un événement
btn.removeEventListener("click", passerCommande);

// ── L'objet event ─────────────────────────────────────────────
document.querySelector(".carte-plat").addEventListener("click", function(event) {
  console.log(event.type);         // "click"
  console.log(event.target);       // l'élément cliqué (peut être un enfant)
  console.log(event.currentTarget);// l'élément qui porte le listener
  console.log(event.clientX, event.clientY); // position dans la fenêtre
  console.log(event.pageX, event.pageY);     // position dans la page
  event.stopPropagation();         // empêcher la propagation au parent
});

// ── DOMContentLoaded — quand le DOM est prêt ─────────────────
document.addEventListener("DOMContentLoaded", () => {
  // Tout le code qui dépend du DOM va ici
  console.log("DOM chargé — initialisation DariGo");
  initialiserMenus();
  chargerPlatsDuJour();
});

// ── Exercice TP M105 : Histoires (événement click sur bouton) ─
document.addEventListener("DOMContentLoaded", function() {
  document.getElementById("raconter").addEventListener("click", raconterHistoire);
});

function raconterHistoire() {
  const personne = document.getElementById("pers").value;
  const adjectif = document.getElementById("adj").value;
  const nom      = document.getElementById("nom").value;
  document.getElementById("histoire").innerHTML =
    `${personne} mange ${adjectif} ${nom} à Casablanca.`;
}

// ── Délégation d'événements — pattern efficace ────────────────
// Plutôt que d'attacher un listener à chaque .carte-plat,
// attacher un seul listener au conteneur parent
const grille = document.getElementById("grille-plats");
grille.addEventListener("click", function(event) {
  const btn = event.target.closest(".btn-ajouter");
  if (!btn) return; // clic ailleurs dans la grille
  const id   = btn.dataset.platId;
  const prix = parseFloat(btn.dataset.prix);
  ajouterAuPanier(id, prix);
});

2
MouseEvents & interactions clavier
click · mouseover · contextmenu · keydown · keyCode · touch
javascript
// ── Événements souris ─────────────────────────────────────────
const carte = document.querySelector(".carte-plat");

carte.addEventListener("click",      e => console.log("Clic simple"));
carte.addEventListener("dblclick",   e => console.log("Double clic"));
carte.addEventListener("mouseenter", e => carte.classList.add("survol"));
carte.addEventListener("mouseleave", e => carte.classList.remove("survol"));
carte.addEventListener("mousemove",  e => {
  document.getElementById("coords").textContent = `${e.clientX}, ${e.clientY}`;
});

// Prévenir le comportement par défaut d'un lien
document.querySelector("a#lien-externe").addEventListener("click", function(event) {
  event.preventDefault(); // empêche la navigation
  console.log("Redirection interdite");
});

// ── Menu contextuel (clic droit) ─────────────────────────────
// Exercice TP M105
window.addEventListener("load", () => {
  document.getElementById("myDiv").addEventListener("contextmenu", (event) => {
    event.preventDefault();
    const menu = document.getElementById("menu");
    menu.style.display = "inline-block";
    menu.style.left = event.pageX + "px";
    menu.style.top  = event.pageY + "px";
  });
  document.addEventListener("click", () => {
    document.getElementById("menu").style.display = "none";
  });
});

// ── Événements clavier ────────────────────────────────────────
// Exercice TP M105 — combinaison de touches
const keys = [];
document.addEventListener("keydown", function(event) {
  keys[event.keyCode] = true;
  const msg = document.getElementById("affichage");
  if (keys[65] && keys[66]) {  // A=65, B=66
    const texte = document.createTextNode("Vous avez cliqué sur A et B");
    msg.appendChild(texte);
  }
});
document.addEventListener("keyup", function(event) {
  keys[event.keyCode] = false;
});

// Touches usuelles
// Enter = 13, Échap = 27, Espace = 32
// Flèches : Gauche=37, Haut=38, Droite=39, Bas=40
// Ctrl=17, Shift=16, Alt=18

// Recherche avec Enter
document.getElementById("recherche").addEventListener("keydown", (e) => {
  if (e.key === "Enter") { lancerRecherche(); }
  if (e.key === "Escape") { e.target.value = ""; }
});

// ── Événements fenêtre ────────────────────────────────────────
window.addEventListener("scroll", () => {
  const progress = (window.scrollY / (document.body.offsetHeight - window.innerHeight)) * 100;
  document.getElementById("barre-progression").style.width = progress + "%";
});

window.addEventListener("resize", () => {
  console.log("Fenêtre :", window.innerWidth, "×", window.innerHeight);
});

// ── Événements tactiles ───────────────────────────────────────
document.addEventListener("touchstart", function(e) {
  if (e.sourceCapabilities && e.sourceCapabilities.fireTouchEvents) {
    console.log("Écran tactile détecté");
  }
});

✓
Vérification des acquis
5 questions — corrigé intégré
Quelle est la différence entre event.target et event.currentTarget ?
On attache un listener click à une liste et les clics sur les éléments enfants remontent jusqu'au listener. Quel mécanisme JavaScript décrit cela ?
Pourquoi event.stopPropagation() est-il utile dans la délégation d'événements ?
Quel événement utiliser pour exécuter du code dès que le DOM est prêt (sans attendre les images) ?
Qu'est-ce que la délégation d'événements et quel est son principal avantage ?
Partie 4 — Événements & Formulaires submit · preventDefault Validation · focus · blur · change
⏱ 20 min

Formulaires — soumission, interruption & validation

Les formulaires sont le principal moyen par lequel les utilisateurs interagissent avec DariGo — inscription, connexion, adresse de livraison, avis sur un plat. JavaScript permet de valider les données côté client avant envoi au serveur, d'afficher des messages d'erreur contextuels en temps réel, et d'intercepter la soumission pour envoyer les données via AJAX sans rechargement de page. La validation côté client améliore l'expérience utilisateur mais ne remplace pas la validation côté serveur.


1
Événements de formulaire et validation complète
submit · preventDefault · change · focus · blur · Validation DariGo
javascript
// ── Lire les valeurs d'un formulaire ─────────────────────────
// Les éléments de formulaire s'accèdent par name, id ou index
const form = document.forms["formCommande"];  // par name
const nom   = form["nom"].value;
const email = form.elements["email"].value;
const ville = document.getElementById("ville-livraison").value;

// Types de champs
const montant   = parseFloat(document.getElementById("montant").value);
const accepte   = document.getElementById("cgv").checked;    // checkbox
const type      = document.querySelector('input[name="paiement"]:checked').value; // radio

// ── Événements formulaire ────────────────────────────────────
const formulaire = document.getElementById("form-commande");

// submit — déclenché à la soumission
formulaire.addEventListener("submit", function(event) {
  event.preventDefault(); // TOUJOURS pour les SPA — empêche le rechargement
  if (validerFormulaire()) {
    envoyerCommande();
  }
});

// change — valeur modifiée et champ quitté
document.getElementById("ville").addEventListener("change", function() {
  mettreAJourFraisLivraison(this.value);
});

// input — valeur modifiée en temps réel (à chaque frappe)
document.getElementById("recherche").addEventListener("input", function() {
  filtrerPlats(this.value);
});

// focus / blur — entrée / sortie du champ
document.getElementById("email").addEventListener("blur", function() {
  validerEmail(this); // valider quand l'utilisateur quitte le champ
});

// ── Exercice TP M105 : Formulaire d'inscription complet ───────
function validation() {
  const nom    = document.forms["formInscp"]["nom"].value;
  const email  = document.forms["formInscp"]["email"].value;
  const tel    = document.forms["formInscp"]["telephone"].value;
  const pass   = document.forms["formInscp"]["password"].value;

  if (nom.trim() === "") {
    alert("Merci de saisir votre nom");
    return false; // bloque la soumission
  }
  if (!email.includes("@") || !email.includes(".")) {
    alert("Adresse email invalide");
    return false;
  }
  if (!/^(\+212|0)[5-7]\d{8}$/.test(tel.replace(/\s/g, ""))) {
    alert("Numéro de téléphone marocain invalide");
    return false;
  }
  if (pass.length < 6) {
    alert("Le mot de passe doit avoir au moins 6 caractères");
    return false;
  }
  return true; // autorise la soumission
}
// HTML : <form onsubmit="return validation()">

// ── Validation avancée DariGo — avec affichage contextuel ────
function validerFormulaireDariGo() {
  let estValide = true;
  const erreurs = {};

  const nom  = document.getElementById("nom-client").value.trim();
  const tel  = document.getElementById("telephone").value.trim();
  const adr  = document.getElementById("adresse").value.trim();

  if (nom.length < 2) {
    erreurs.nom = "Nom trop court (min. 2 caractères)";
    estValide = false;
  }
  if (!/^(\+212|0)[5-7]\d{8}$/.test(tel)) {
    erreurs.tel = "Format : 06xxxxxxxx ou 07xxxxxxxx";
    estValide = false;
  }
  if (adr.length < 10) {
    erreurs.adr = "Adresse trop courte (min. 10 caractères)";
    estValide = false;
  }

  // Afficher les erreurs
  Object.keys(erreurs).forEach(champ => {
    const msgEl = document.getElementById(`err-${champ}`);
    if (msgEl) msgEl.textContent = erreurs[champ];
    document.getElementById(champ).classList.add("champ-erreur");
  });

  // Effacer les erreurs des champs valides
  if (!erreurs.nom) {
    document.getElementById("err-nom").textContent = "";
    document.getElementById("nom-client").classList.remove("champ-erreur");
  }

  return estValide;
}

// ── Validation en temps réel (blur) ───────────────────────────
document.getElementById("telephone").addEventListener("blur", function() {
  const regex = /^(\+212|0)[5-7]\d{8}$/;
  const errEl = document.getElementById("err-tel");
  if (!regex.test(this.value.trim())) {
    errEl.textContent = "Format marocain invalide";
    this.classList.add("champ-erreur");
  } else {
    errEl.textContent = "";
    this.classList.remove("champ-erreur");
    this.classList.add("champ-valide");
  }
});

✓
Vérification des acquis
5 questions — corrigé intégré
Pourquoi appelle-t-on event.preventDefault() dans le handler submit d'un formulaire React/AJAX ?
Quelle différence entre les événements input et change sur un champ texte ?
La validation JavaScript côté client est-elle suffisante pour sécuriser une application ?
Comment lire la valeur d'une case à cocher (checkbox) avec JavaScript ?
Quel événement se déclenche quand l'utilisateur quitte un champ de formulaire ?
Partie 5 — jQuery & AJAX jQuery 3 $() · .css() · .val() · Chaînage
⏱ 20 min

jQuery — sélecteurs, fonctions essentielles & chaînage

jQuery est une bibliothèque JavaScript créée en 2006 qui simplifie la manipulation du DOM, les événements, les animations et les appels AJAX. Sa philosophie : Write less, do more. Ce qui nécessite 5 lignes en JavaScript pur s'écrit souvent en 1 ligne avec jQuery. Elle est encore présente dans une grande partie du web — notamment tous les sites utilisant Bootstrap 4 et inférieur — et reste un outil à connaître pour maintenir des projets existants et pour la rapidité de prototypage.

ℹ Intégrer jQuery dans une page
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

Placer avant la balise </body> ou dans le <head> avec l'attribut defer.


1
Sélecteurs, manipulation DOM et chaînage
$() · .html() · .text() · .val() · .css() · .addClass() · Chaînage
javascript
// ── $(document).ready() — équivalent de DOMContentLoaded ─────
$(document).ready(function() {
  // Tout le code jQuery va ici
  console.log("jQuery prêt — DariGo initialisé");
});
// Forme courte :
$(function() { /* ... */ });

// ── Sélecteurs jQuery — comme les sélecteurs CSS ──────────────
$("#titre-menu")              // par id
$(".carte-plat")              // par classe
$("li")                       // par balise
$("ul#liste > li.active")     // sélecteur CSS complexe
$("input[type='text']")       // attribut
$(".carte-plat:first")        // pseudo-sélecteur jQuery
$("tr:odd")                   // lignes impaires

// ── Manipulation du contenu ───────────────────────────────────
$("#affichage").html("<strong>Bienvenue sur DariGo !</strong>"); // avec HTML
$("#titre").text("Menu DariGo");           // texte brut uniquement
$("#prix").text(75 + " MAD");

// Lire une valeur
const valeur = $("#affichage").text();
const inputVal = $("#nom-client").val();  // valeur d'un champ input
$("#nom-client").val("Khalid");           // définir la valeur

// ── Manipulation du CSS ───────────────────────────────────────
$("#zone").css("background-color", "yellow");
$("#zone").css({ color: "red", fontWeight: "bold", padding: "10px" });

const couleur = $("#zone").css("background-color"); // lire

// ── Classes CSS ───────────────────────────────────────────────
$(".carte").addClass("active");
$(".carte").removeClass("inactive");
$(".carte").toggleClass("selected");    // bascule
$(".carte").hasClass("active");         // true/false

// Exercice TP M105 — basculer entre deux classes
$(".c1").click(function() {
  $(this).removeClass("c1").addClass("c2");
});
$(".c2").click(function() {
  $(this).removeClass("c2").addClass("c1");
});

// ── Attributs et data ─────────────────────────────────────────
$("img").attr("src", "images/tajine.jpg");
$("img").attr("alt", "Tajine Kefta");
const src = $("img").attr("src");

// ── show / hide / toggle ──────────────────────────────────────
$("#message").show();           // afficher
$("#message").hide();           // masquer
$("#message").toggle();         // basculer
// Avec animation :
$("#message").show(300);        // afficher en 300ms
$("#message").hide("slow");     // masquer lentement
$("#message").slideToggle(200); // slide animé
$("#message").fadeToggle(400);  // fade animé

// ── Chaînage — jQuery retourne toujours l'élément ────────────
// Exercice TP M105 — plusieurs opérations en chaîne
$("#zone")
  .addClass("actif")
  .css("background-color", "yellow")
  .text("Plat ajouté au panier !")
  .fadeIn(200);

// Sans chaînage (JavaScript pur)
const el = document.getElementById("zone");
el.classList.add("actif");
el.style.backgroundColor = "yellow";
el.textContent = "Plat ajouté au panier !";
// pas d'équivalent fadeIn natif sans bibliothèque

✓
Vérification des acquis
4 questions — corrigé intégré
Que fait $(document).ready(function() { ... }) ?
Quelle est la différence entre .html() et .text() en jQuery ?
Pourquoi le chaînage est-il possible en jQuery ?
Traduction en jQuery de document.getElementById("prix").textContent = "75 MAD" :
Partie 5 — jQuery & AJAX .on() · .click() · .blur() Plugins · Datepicker · DataTable
⏱ 18 min

jQuery — événements, plugins & déclenchement

jQuery simplifie considérablement la gestion des événements par rapport au JavaScript pur. La méthode .on() centralise l'attachement d'événements et supporte la délégation en un seul appel. Les méthodes raccourcis (.click(), .blur(), .focus()) rendent le code plus lisible. L'écosystème de plugins jQuery est immense — des dizaines de milliers de plugins gratuits pour le datepicker, les tableaux dynamiques, les carousels, la validation, et bien plus.


1
Événements jQuery et plugins courants
.on() · .click() · .blur() · .trigger() · Plugins jQuery UI
javascript
$(document).ready(function() {

  // ── .on() — méthode standard ─────────────────────────────────
  $("#btn-commander").on("click", function() {
    alert("Commande passée !");
  });
  // Plusieurs événements à la fois
  $("#recherche").on("focus blur", function(e) {
    $(this).toggleClass("actif", e.type === "focus");
  });

  // Délégation avec .on()
  $("#grille-plats").on("click", ".btn-ajouter", function() {
    const id   = $(this).data("plat-id");  // data-plat-id
    const prix = $(this).data("prix");
    ajouterAuPanier(id, prix);
  });

  // ── Raccourcis événements ─────────────────────────────────────
  // Exercice TP M105 — bienvenue au clic
  $("#boutton").click(function() {
    $("#affichage").html("Bienvenue au cours de jQuery");
  });

  // Fond jaune au focus
  $(".t_input").click(function() {
    $(this).css("background-color", "yellow");
  });

  // Erreur si valeur > 10 au blur
  $("#t1").blur(function() {
    const val = parseInt($(this).val());
    if (val > 10) {
      $(this).addClass("erreur");
    } else {
      $(this).removeClass("erreur");
    }
  });

  // ── Calculatrice jQuery (TP M105 Exercice 3) ─────────────────
  // Activer les boutons seulement si les deux champs sont remplis
  function validerInputs() {
    const v1 = $("#t1").val();
    const v2 = $("#t2").val();
    $("#btnAdd, #btnSous, #btnMult, #btnDiv").prop("disabled", !(v1 && v2));
  }
  $("#t1, #t2").on("input", validerInputs);

  $("#btnAdd").click(function() {
    $("#t3").val(parseFloat($("#t1").val()) + parseFloat($("#t2").val()));
  });
  $("#btnSous").click(function() {
    $("#t3").val(parseFloat($("#t1").val()) - parseFloat($("#t2").val()));
  });
  $("#btnMult").click(function() {
    $("#t3").val(parseFloat($("#t1").val()) * parseFloat($("#t2").val()));
  });
  $("#btnDiv").click(function() {
    const d = parseFloat($("#t2").val());
    $("#t3").val(d !== 0 ? parseFloat($("#t1").val()) / d : "Division par zéro");
  });

  // ── .trigger() — déclencher un événement programmatiquement ──
  $("#btn-recherche").trigger("click");   // simuler un clic
  $("#formulaire").trigger("submit");      // simuler une soumission
  $("#email").trigger("focus");           // forcer le focus

  // ── Plugins jQuery — exemples courants ───────────────────────
  // jQuery UI Datepicker (sélecteur de date)
  // CDN : jquery-ui.min.js + jquery-ui.min.css
  $("#date-livraison").datepicker({
    dateFormat: "dd/mm/yy",
    minDate: 0,          // pas de date passée
    maxDate: "+7d",      // max 7 jours dans le futur
    firstDay: 1,         // semaine commence lundi
  });

  // DataTables (tableaux dynamiques avec tri, recherche, pagination)
  // CDN : datatables.min.js + datatables.min.css
  $("#tableau-commandes").DataTable({
    language: { url: "//cdn.datatables.net/plug-ins/1.13/i18n/fr-FR.json" },
    pageLength: 10,
    order: [[0, "desc"]],  // trier par première colonne décroissant
  });

  // Select2 (select enrichi avec recherche)
  $(".select-ville").select2({
    placeholder: "Choisir une ville",
    data: ["Casablanca", "Rabat", "Fès", "Marrakech", "Agadir", "Tanger"]
  });
});

// ── Formulaire de notes (TP M105 Exercice 4 complet) ─────────
$(document).ready(function() {
  let somme = 0;
  const r1 = Number($("#table tr:first-child td:last-child").text());
  const r2 = Number($("#table tr:nth-child(2) td:last-child").text());
  somme = r1 + r2;
  $("#moyenne").text((somme / 2).toFixed(2));

  $("#Aj").click(function(e) {
    const nom   = $("#nom").val();
    const score = parseFloat($("#score").val());

    if (isNaN(score) || score < 0 || score > 20 || nom.trim() === "") {
      alert("Données invalides");
      e.preventDefault();
      return;
    }

    const tr = $("<tr>").append(
      $("<td>").text(nom),
      $("<td>").text(score)
    );
    $("#table table").append(tr);
    somme += score;

    $("#table table tr:odd").css("background", "#CCCCFF");
    $("#table table tr:even").css("background", "#9966ff");

    const len = $("#table tr").length;
    $("#moyenne").text((somme / len).toFixed(2));
  });
});

✓
Vérification des acquis
4 questions — corrigé intégré
Comment attacher un événement click aux .btn-ajouter qui seront créés dynamiquement APRÈS le chargement de la page, avec jQuery ?
Comment lire la valeur de l'attribut data-prix en jQuery ?
Qu'est-ce qu'un plugin jQuery et comment l'intégrer ?
Que fait $("#formulaire").trigger("submit") ?
Partie 5 — jQuery & AJAX AJAX Asynchrone · XMLHttpRequest · JSON · Fetch
⏱ 20 min

AJAX — introduction & fonctionnement

AJAX (Asynchronous JavaScript and XML) est la technologie qui transforme une page web statique en application interactive. Elle permet d'envoyer des requêtes HTTP au serveur et de recevoir des réponses sans rechargement de la page — en arrière-plan, de façon asynchrone. La liste des plats DariGo se charge sans recharger la page, la commande est envoyée sans quitter la page menu, le statut de livraison se met à jour automatiquement toutes les 10 secondes — tout cela est AJAX.


1
Fonctionnement d'AJAX
XMLHttpRequest · Asynchrone · Callback · JSON · Fetch API

Avant AJAX (années 1990-2000), toute interaction avec le serveur nécessitait un rechargement complet de la page. AJAX, popularisé par Google Maps et Gmail en 2005, a révolutionné le web en permettant aux pages de communiquer avec le serveur en arrière-plan.

Le terme "XML" est historique — aujourd'hui, JSON est le format standard. "Asynchrone" signifie que la requête ne bloque pas l'exécution du JavaScript pendant l'attente de la réponse — le code continue et le résultat est traité via un callback ou une Promise quand il arrive.

text
── Cycle d'une requête AJAX sur DariGo ───────────────────────

1. Utilisateur change de ville → JavaScript déclenche AJAX
   │
   ▼
2. JavaScript crée une requête HTTP (XMLHttpRequest ou fetch)
   GET https://darigo.ma/api/plats.php?ville=Rabat
   │
   ▼
3. Requête envoyée au serveur — JS CONTINUE de s'exécuter
   (interface reste réactive, l'utilisateur peut faire autre chose)
   │
   ▼
4. Serveur PHP reçoit la requête :
   - Lit la ville depuis $_GET["ville"]
   - Interroge MySQL : SELECT * FROM plats WHERE ville = 'Rabat'
   - Encode les résultats en JSON
   - Retourne : Content-Type: application/json
   │
   ▼
5. JavaScript reçoit la réponse JSON :
   [{"id":1,"nom":"Tajine Kefta","prix":75},...]
   │
   ▼
6. JavaScript parse le JSON et met à jour le DOM :
   - Vide la liste actuelle
   - Crée les cartes de plats pour Rabat
   - Affiche sans rechargement

── Sans AJAX ─────────────────────────────────────────────────
Clic → nouvelle requête HTTP → serveur génère HTML complet
→ navigateur recharge tout → page affichée depuis zéro
→ 500ms - 2s de chargement · perte du contexte utilisateur
javascript
// ── XMLHttpRequest — la méthode historique ────────────────────
// Encore présente dans le code legacy
const xhr = new XMLHttpRequest();
xhr.open("GET", "api/plats.php?ville=Casablanca", true); // true = asynchrone
xhr.setRequestHeader("Content-Type", "application/json");

xhr.onload = function() {
  if (xhr.status === 200) {
    const plats = JSON.parse(xhr.responseText);
    afficherPlats(plats);
  } else {
    console.error("Erreur :", xhr.status);
  }
};
xhr.onerror = function() {
  console.error("Erreur réseau");
};
xhr.send();

// ── Fetch API — la méthode moderne (ES6) ─────────────────────
// Retourne une Promise — plus lisible que XHR
fetch("api/plats.php?ville=Casablanca")
  .then(response => {
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json(); // parse automatiquement le JSON
  })
  .then(plats => {
    console.log(`${plats.length} plats chargés`);
    afficherPlats(plats);
  })
  .catch(erreur => {
    console.error("Erreur :", erreur.message);
  });

// POST avec fetch
fetch("api/commandes.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    clientId: 42,
    plats: panier,
    adresse: "Hay Riad, Rabat"
  })
})
.then(r => r.json())
.then(data => {
  document.getElementById("num-commande").textContent = data.commandeId;
  document.getElementById("statut").textContent = "Commande confirmée !";
});

✓
Vérification des acquis
4 questions — corrigé intégré
Que signifie le "A" de AJAX — "Asynchronous" ? Quel impact sur l'exécution du code ?
Quel format de données est standard pour les échanges AJAX modernes entre JavaScript et PHP ?
Quelle est la différence principale entre XMLHttpRequest et Fetch API ?
Pourquoi doit-on ajouter l'en-tête Content-Type: application/json dans une requête Fetch POST ?
Partie 5 — jQuery & AJAX $.ajax() · $.get() · $.post() JSON · success · error · done/fail
⏱ 20 min

Implémentation AJAX via jQuery

jQuery simplifie encore plus les requêtes AJAX via $.ajax(), $.get() et $.post(). Ces méthodes gèrent automatiquement le parsing JSON, les en-têtes, et offrent une interface cohérente avec les méthodes .done(), .fail() et .always(). Dans ce cours, vous consommerez l'API PHP DariGo — construite en PHP dans le module PHP — depuis le front-end JavaScript/jQuery.


1
$.ajax(), $.get() et $.post()
type: GET/POST · dataType: json · success · error · done/fail
javascript
// ── TP M105 Exercice 1 — GET depuis JSONPlaceholder ───────────
$("#b1").click(function() {
  const id = 5;
  $.ajax({
    type: "GET",
    url: "https://jsonplaceholder.typicode.com/todos/" + id,
    success: function(response) {
      $("#userid").text("User : " + response.userId);
      $("#id").text("ID : " + response.id);
      $("#title").text("Titre : " + response.title);
      $("#completed").text("Terminé : " + response.completed);
    },
    error: function(xhr, status, err) {
      console.error("Erreur :", status, err);
    }
  });
});

// ── $.get() — raccourci pour les requêtes GET ─────────────────
$.get("api/plats.php", { ville: "Casablanca" }, function(plats) {
  plats.forEach(plat => {
    $("#liste-plats").append(`
      <li>${plat.nom} — ${plat.prix} MAD</li>
    `);
  });
}, "json");  // 4ème argument = type de données attendu

// Avec .done() et .fail() (interface Promise-like)
$.get("api/plats.php")
  .done(function(plats) {
    console.log(plats.length + " plats chargés");
    afficherPlats(plats);
  })
  .fail(function(xhr) {
    console.error("Erreur :", xhr.status);
  })
  .always(function() {
    $("#spinner").hide();
  });

// ── $.post() — raccourci pour les requêtes POST ───────────────
const commande = {
  clientId: 42,
  plats: [{ id: 1, quantite: 2 }, { id: 3, quantite: 1 }],
  adresse: "Hay Riad, Avenue Mohammed VI, Rabat",
  villeLivraison: "Rabat"
};

$.post("api/commandes.php", JSON.stringify(commande), function(response) {
  if (response.success) {
    $("#num-commande").text("#" + response.commandeId);
    $("#statut-commande").text("Commande confirmée !");
    $("#page-confirmation").show();
  }
}, "json");

// ── $.ajax() — configuration complète ────────────────────────
$.ajax({
  url: "api/commandes.php",
  type: "POST",
  contentType: "application/json",
  dataType: "json",
  data: JSON.stringify(commande),
  beforeSend: function() {
    $("#btn-commander").prop("disabled", true).text("Envoi en cours...");
    $("#spinner").show();
  },
  success: function(response) {
    window.location.href = "suivi.html?id=" + response.commandeId;
  },
  error: function(xhr, status, error) {
    const msg = xhr.responseJSON?.message || "Erreur de connexion";
    $("#erreur-commande").text(msg).show();
  },
  complete: function() {
    $("#btn-commander").prop("disabled", false).text("Commander");
    $("#spinner").hide();
  }
});

// ── $.getJSON() — raccourci pour GET JSON ────────────────────
$.getJSON("api/restaurants.php", { ville: "Marrakech" })
  .done(function(restaurants) {
    restaurants.forEach(r => {
      $("#liste-restaurants").append(
        `<div class="carte-restaurant">
          <h3>${r.nom}</h3>
          <p>${r.adresse}</p>
        </div>`
      );
    });
  });

// ── Polling AJAX — mise à jour périodique du statut ───────────
function verifierStatutLivraison(commandeId) {
  $.get("api/livraison.php", { commandeId: commandeId }, function(data) {
    $("#statut-livraison").text(data.statut);
    $("#position-livreur").text(data.position);

    if (data.statut !== "livree") {
      // Re-vérifier dans 10 secondes si pas encore livré
      setTimeout(() => verifierStatutLivraison(commandeId), 10000);
    } else {
      $("#message-livraison").text("Livrée ! Bon appétit !").show();
    }
  }, "json");
}

// Démarrer le suivi
verifierStatutLivraison(1042);

✓
Vérification des acquis
5 questions — corrigé intégré
Dans $.ajax(), quelle est la différence entre error et complete ?
Pourquoi appelle-t-on $("#btn").prop("disabled", true) dans beforeSend ?
Quelle méthode jQuery utiliser pour une simple requête GET qui retourne du JSON ?
Le polling AJAX vérifie le statut de livraison toutes les 10 secondes. Comment arrêter proprement ce polling quand la livraison est confirmée ?
En PHP (back-end DariGo), comment retourner une réponse JSON correctement pour une requête AJAX ?
Projet Final — DariGo Interactif Architecture · PHP · JS · jQuery · AJAX
⏱ 15 min

DariGo Interactif — Présentation & Architecture

Le projet final consiste à construire la page de commande interactive de DariGo en utilisant toutes les compétences du cours : manipulation du DOM, gestion des événements, validation de formulaire, jQuery et AJAX vers une API PHP. Ce projet se connecte directement à l'API PHP DariGo construite dans le module PHP — les mêmes endpoints, les mêmes structures JSON.

✓ Ce que vous allez construire
  • Page d'accueil DariGo avec liste des plats chargée en AJAX depuis l'API PHP
  • Panier dynamique géré entièrement avec le DOM et localStorage
  • Formulaire de commande avec validation JavaScript complète
  • Envoi de la commande via AJAX jQuery vers api/commandes.php
  • Suivi de livraison avec polling AJAX toutes les 10 secondes

1
Architecture du projet
Structure fichiers · API PHP · Endpoints · localStorage
text
darigo-js/
├── index.html          ← page principale (menu + panier)
├── commande.html       ← formulaire de commande
├── suivi.html          ← suivi de livraison (polling)
├── css/
│   └── darigo.css
├── js/
│   ├── menu.js         ← DOM + AJAX chargement plats
│   ├── panier.js       ← gestion panier localStorage
│   ├── commande.js     ← validation + envoi AJAX
│   └── suivi.js        ← polling statut livraison
└── api/                ← back-end PHP (module PHP)
    ├── plats.php       → GET  /api/plats.php?ville=Casablanca
    ├── commandes.php   → POST /api/commandes.php
    └── livraisons.php  → GET  /api/livraisons.php?id=1042

── Flux complet ─────────────────────────────────────────────
1. index.html charge → menu.js → $.getJSON(api/plats.php)
2. Plats affichés via DOM → clic "Ajouter" → panier.js
3. Panier sauvegardé en localStorage
4. Clic "Commander" → commande.html → commande.js
5. Validation JS → $.post(api/commandes.php) → réponse JSON
6. Redirect suivi.html?id=1042 → polling $.get(api/livraisons.php)
Projet Final — Étape 1 DOM · localStorage · Panier dynamique
⏱ 25 min

Étape 1 — DOM dynamique : menu & panier

Afficher le menu DariGo en créant les éléments DOM dynamiquement depuis les données JSON, et construire le panier interactif persistant avec localStorage.


1
Structure HTML + affichage DOM des plats
createElement · appendChild · dataset · Filtres catégorie
html
<!-- index.html — structure de base ──────────────────────────── -->
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>DariGo — Livraison de repas au Maroc</title>
  <link rel="stylesheet" href="css/darigo.css">
</head>
<body>

<header class="header">
  <a href="/" class="logo">DariGo</a>
  <div class="header-actions">
    <button id="btn-panier" class="btn-panier">
      Panier <span id="badge-nb">0</span>
    </button>
  </div>
</header>

<main>
  <div class="filtres" id="filtres"></div>   <!-- généré par JS -->
  <div class="grille-plats" id="grille-plats"></div> <!-- généré par JS -->
</main>

<aside class="panier" id="panier" hidden>
  <h2>Votre panier</h2>
  <div id="lignes-panier"></div>
  <div class="panier-footer">
    <strong>Total : <span id="total-panier">0</span> MAD</strong>
    <a href="commande.html" class="btn btn-primary" id="btn-commander">Commander</a>
  </div>
</aside>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="js/panier.js"></script>
<script src="js/menu.js"></script>
</body>
</html>
javascript
// ── js/menu.js — affichage DOM des plats ─────────────────────
$(document).ready(function() {

  // Données de test si l'API n'est pas disponible
  const platsMock = [
    { id: 1, nom: "Tajine Kefta", prix: 75, categorie: "plat",    disponible: true,  note: 4.8, image: "tajine.jpg" },
    { id: 2, nom: "Harira",       prix: 25, categorie: "soupe",   disponible: true,  note: 4.6, image: "harira.jpg" },
    { id: 3, nom: "Pastilla",     prix: 65, categorie: "entree",  disponible: true,  note: 4.7, image: "pastilla.jpg" },
    { id: 4, nom: "Couscous",     prix: 90, categorie: "plat",    disponible: false, note: 4.9, image: "couscous.jpg" },
    { id: 5, nom: "Chebakia",     prix: 20, categorie: "dessert", disponible: true,  note: 4.5, image: "chebakia.jpg" },
  ];

  let tousLesPlats = [];
  let categorieActive = "tous";

  // Construire les filtres par catégorie
  function construireFiltres(plats) {
    const categories = ["tous", ...new Set(plats.map(p => p.categorie))];
    const filtresEl = document.getElementById("filtres");
    filtresEl.innerHTML = "";

    categories.forEach(cat => {
      const btn = document.createElement("button");
      btn.className = "btn-filtre" + (cat === "tous" ? " actif" : "");
      btn.textContent = cat.charAt(0).toUpperCase() + cat.slice(1);
      btn.dataset.cat = cat;
      btn.addEventListener("click", function() {
        document.querySelectorAll(".btn-filtre").forEach(b => b.classList.remove("actif"));
        this.classList.add("actif");
        categorieActive = this.dataset.cat;
        afficherPlats(tousLesPlats);
      });
      filtresEl.appendChild(btn);
    });
  }

  // Créer une carte plat avec createElement
  function creerCartePlat(plat) {
    const div = document.createElement("div");
    div.className = "carte-plat" + (!plat.disponible ? " carte-plat--grise" : "");
    div.dataset.id = plat.id;

    div.innerHTML = `
      <div class="carte-plat__corps">
        <h3 class="carte-plat__nom">${plat.nom}</h3>
        <span class="carte-plat__note">★ ${plat.note}</span>
        <div class="carte-plat__footer">
          <strong class="carte-plat__prix">${plat.prix} MAD</strong>
          <button
            class="btn btn-ajouter"
            data-id="${plat.id}"
            data-nom="${plat.nom}"
            data-prix="${plat.prix}"
            ${!plat.disponible ? "disabled" : ""}
          >
            ${plat.disponible ? "Ajouter" : "Indisponible"}
          </button>
        </div>
      </div>
    `;
    return div;
  }

  // Afficher les plats filtrés
  function afficherPlats(plats) {
    const grille = document.getElementById("grille-plats");
    grille.innerHTML = "";

    const filtres = categorieActive === "tous"
      ? plats
      : plats.filter(p => p.categorie === categorieActive);

    if (filtres.length === 0) {
      grille.innerHTML = "<p class='vide'>Aucun plat dans cette catégorie.</p>";
      return;
    }

    filtres.forEach(plat => grille.appendChild(creerCartePlat(plat)));
  }

  // Délégation : écouter les clics sur les boutons "Ajouter"
  document.getElementById("grille-plats").addEventListener("click", function(e) {
    const btn = e.target.closest(".btn-ajouter");
    if (!btn || btn.disabled) return;
    ajouterAuPanier({
      id:   parseInt(btn.dataset.id),
      nom:  btn.dataset.nom,
      prix: parseFloat(btn.dataset.prix)
    });
    // Feedback visuel
    btn.textContent = "Ajouté !";
    btn.classList.add("btn-ajouter--confirme");
    setTimeout(() => {
      btn.textContent = "Ajouter";
      btn.classList.remove("btn-ajouter--confirme");
    }, 1200);
  });

  // Charger les plats (mock ou API)
  tousLesPlats = platsMock;
  construireFiltres(tousLesPlats);
  afficherPlats(tousLesPlats);
});

// ── js/panier.js — panier avec localStorage ───────────────────
let panier = JSON.parse(localStorage.getItem("darigo-panier") || "[]");

function sauvegarderPanier() {
  localStorage.setItem("darigo-panier", JSON.stringify(panier));
}

function ajouterAuPanier(plat) {
  const existant = panier.find(p => p.id === plat.id);
  if (existant) {
    existant.quantite++;
  } else {
    panier.push({ ...plat, quantite: 1 });
  }
  sauvegarderPanier();
  afficherPanier();
}

function retirerDuPanier(id) {
  panier = panier.filter(p => p.id !== id);
  sauvegarderPanier();
  afficherPanier();
}

function afficherPanier() {
  const lignesEl = document.getElementById("lignes-panier");
  const badgeEl  = document.getElementById("badge-nb");
  const totalEl  = document.getElementById("total-panier");

  lignesEl.innerHTML = "";
  let total = 0;
  let nbArticles = 0;

  panier.forEach(item => {
    total += item.prix * item.quantite;
    nbArticles += item.quantite;

    const div = document.createElement("div");
    div.className = "ligne-panier";
    div.innerHTML = `
      <span>${item.nom} × ${item.quantite}</span>
      <span>${(item.prix * item.quantite).toFixed(2)} MAD</span>
      <button onclick="retirerDuPanier(${item.id})">✕</button>
    `;
    lignesEl.appendChild(div);
  });

  badgeEl.textContent = nbArticles;
  totalEl.textContent = total.toFixed(2);
  document.getElementById("btn-commander").style.display = panier.length ? "inline-block" : "none";
}

// Ouvrir/fermer le panier
document.getElementById("btn-panier").addEventListener("click", function() {
  const panierEl = document.getElementById("panier");
  panierEl.hidden = !panierEl.hidden;
});

// Initialiser à l'affichage
afficherPanier();
Projet Final — Étape 2 Événements · Validation · Formulaire commande
⏱ 20 min

Étape 2 — Événements & formulaire de commande

Construire la page de confirmation de commande avec validation JavaScript complète et affichage contextuel des erreurs, avant d'envoyer les données via AJAX à l'étape suivante.


1
Page commande — HTML & validation JS
submit · blur · preventDefault · Erreurs contextuelles
html
<!-- commande.html ───────────────────────────────────────────── -->
<main class="page-commande">
  <h1>Confirmer votre commande</h1>

  <!-- Récapitulatif panier lu depuis localStorage -->
  <div id="recap-panier"></div>

  <form id="form-commande" novalidate>
    <div class="champ">
      <label for="nom">Nom complet</label>
      <input id="nom" type="text" placeholder="Khalid El Amrani">
      <span class="msg-erreur" id="err-nom"></span>
    </div>

    <div class="champ">
      <label for="tel">Téléphone</label>
      <input id="tel" type="tel" placeholder="06 12 34 56 78">
      <span class="msg-erreur" id="err-tel"></span>
    </div>

    <div class="champ">
      <label for="adresse">Adresse de livraison</label>
      <input id="adresse" type="text" placeholder="123 Rue Mohammed V, Maarif">
      <span class="msg-erreur" id="err-adresse"></span>
    </div>

    <div class="champ">
      <label for="ville">Ville</label>
      <select id="ville">
        <option value="">Choisir une ville</option>
        <option>Casablanca</option><option>Rabat</option>
        <option>Fès</option><option>Marrakech</option>
        <option>Agadir</option><option>Tanger</option>
      </select>
      <span class="msg-erreur" id="err-ville"></span>
    </div>

    <div id="erreur-global" class="alerte alerte-erreur" hidden></div>

    <button type="submit" id="btn-submit" class="btn btn-primary btn-full">
      Commander
    </button>
  </form>
</main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="js/commande.js"></script>
javascript
// ── js/commande.js ────────────────────────────────────────────
$(document).ready(function() {

  // ── Afficher le récapitulatif du panier ───────────────────
  const panier = JSON.parse(localStorage.getItem("darigo-panier") || "[]");

  if (panier.length === 0) {
    window.location.href = "index.html"; // panier vide → retour menu
    return;
  }

  let total = 0;
  let html = "<ul class='recap-liste'>";
  panier.forEach(item => {
    const sousTotal = item.prix * item.quantite;
    total += sousTotal;
    html += `<li>${item.nom} × ${item.quantite} — ${sousTotal.toFixed(2)} MAD</li>`;
  });
  html += `</ul><p class='recap-total'><strong>Total : ${(total + 15).toFixed(2)} MAD</strong> (dont 15 MAD livraison)</p>`;
  document.getElementById("recap-panier").innerHTML = html;

  // ── Validation en temps réel (blur) ──────────────────────
  const regexTel = /^(\+212|0)[5-7]\d{8}$/;

  function afficherErreur(idErreur, message) {
    const el = document.getElementById(idErreur);
    el.textContent = message;
    el.previousElementSibling.classList.toggle("champ-erreur", !!message);
    el.previousElementSibling.classList.toggle("champ-valide", !message);
  }

  document.getElementById("nom").addEventListener("blur", function() {
    afficherErreur("err-nom",
      this.value.trim().length < 2 ? "Nom trop court (min. 2 caractères)" : ""
    );
  });

  document.getElementById("tel").addEventListener("blur", function() {
    const val = this.value.replace(/\s/g, "");
    afficherErreur("err-tel",
      !regexTel.test(val) ? "Format invalide — ex: 0612345678" : ""
    );
  });

  document.getElementById("adresse").addEventListener("blur", function() {
    afficherErreur("err-adresse",
      this.value.trim().length < 10 ? "Adresse trop courte (min. 10 caractères)" : ""
    );
  });

  document.getElementById("ville").addEventListener("change", function() {
    afficherErreur("err-ville", !this.value ? "Veuillez choisir une ville" : "");
  });

  // ── Validation complète à la soumission ───────────────────
  function validerFormulaire() {
    const nom     = document.getElementById("nom").value.trim();
    const tel     = document.getElementById("tel").value.replace(/\s/g, "");
    const adresse = document.getElementById("adresse").value.trim();
    const ville   = document.getElementById("ville").value;
    let valide    = true;

    afficherErreur("err-nom",     nom.length < 2 ? "Nom requis (min. 2 caractères)" : "");
    afficherErreur("err-tel",     !regexTel.test(tel) ? "Numéro marocain invalide" : "");
    afficherErreur("err-adresse", adresse.length < 10 ? "Adresse trop courte" : "");
    afficherErreur("err-ville",   !ville ? "Ville requise" : "");

    if (nom.length < 2 || !regexTel.test(tel) || adresse.length < 10 || !ville) {
      valide = false;
    }
    return valide;
  }

  // ── Soumission du formulaire ──────────────────────────────
  document.getElementById("form-commande").addEventListener("submit", function(e) {
    e.preventDefault();
    if (!validerFormulaire()) return;

    const donnees = {
      client: {
        nom:     document.getElementById("nom").value.trim(),
        tel:     document.getElementById("tel").value.trim(),
        adresse: document.getElementById("adresse").value.trim(),
        ville:   document.getElementById("ville").value
      },
      articles: panier.map(p => ({ id: p.id, nom: p.nom, prix: p.prix, quantite: p.quantite })),
      total: panier.reduce((acc, p) => acc + p.prix * p.quantite, 0) + 15
    };

    // Envoi AJAX → étape suivante
    envoyerCommande(donnees);
  });
});
Projet Final — Étape 3 AJAX jQuery · API PHP · Polling · Suivi
⏱ 20 min

Étape 3 — AJAX jQuery : consommer l'API PHP DariGo

Envoyer la commande via $.ajax() vers api/commandes.php, afficher la confirmation, puis suivre la livraison en temps réel avec le polling AJAX.


1
Envoi commande + chargement plats depuis l'API PHP
$.ajax POST · $.getJSON · api/plats.php · api/commandes.php
javascript
// ── Envoi de la commande via $.ajax() ────────────────────────
function envoyerCommande(donnees) {
  const btn = document.getElementById("btn-submit");
  btn.disabled = true;
  btn.textContent = "Envoi en cours...";

  $.ajax({
    url:         "api/commandes.php",
    type:        "POST",
    contentType: "application/json",
    dataType:    "json",
    data:        JSON.stringify(donnees),

    success: function(reponse) {
      if (reponse.success) {
        // Vider le panier après commande confirmée
        localStorage.removeItem("darigo-panier");
        // Naviguer vers la page de suivi
        window.location.href = "suivi.html?id=" + reponse.commandeId;
      } else {
        afficherErreurGlobal(reponse.message || "Erreur lors de la commande");
      }
    },

    error: function(xhr) {
      const msg = xhr.responseJSON?.message || "Erreur de connexion au serveur";
      afficherErreurGlobal(msg);
    },

    complete: function() {
      btn.disabled = false;
      btn.textContent = "Commander";
    }
  });
}

function afficherErreurGlobal(message) {
  const el = document.getElementById("erreur-global");
  el.textContent = message;
  el.hidden = false;
  el.scrollIntoView({ behavior: "smooth" });
}

// ── Charger les plats depuis l'API PHP (menu.js) ─────────────
// Remplace platsMock — à utiliser quand l'API PHP est disponible
function chargerPlatsDeLAPI(ville) {
  $("#grille-plats").html("<p>Chargement...</p>");

  $.getJSON("api/plats.php", { ville: ville })
    .done(function(plats) {
      tousLesPlats = plats;
      construireFiltres(plats);
      afficherPlats(plats);
    })
    .fail(function(xhr) {
      console.warn("API indisponible, données de test utilisées");
      tousLesPlats = platsMock;  // fallback sur les données mock
      construireFiltres(platsMock);
      afficherPlats(platsMock);
    });
}

// ── api/plats.php — code PHP correspondant ───────────────────
/*
<?php
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: *");

$ville = $_GET["ville"] ?? "Casablanca";

// Connexion PDO (voir module PHP)
$pdo = new PDO("mysql:host=localhost;dbname=darigo;charset=utf8", "root", "");
$stmt = $pdo->prepare("SELECT * FROM plats WHERE ville = ? AND actif = 1");
$stmt->execute([$ville]);
$plats = $stmt->fetchAll(PDO::FETCH_ASSOC);

echo json_encode($plats);
*/

// ── api/commandes.php — code PHP correspondant ───────────────
/*
<?php
header("Content-Type: application/json");

$data = json_decode(file_get_contents("php://input"), true);

if (!$data || empty($data["articles"])) {
  http_response_code(400);
  echo json_encode(["success" => false, "message" => "Données invalides"]);
  exit;
}

$pdo = new PDO("mysql:host=localhost;dbname=darigo;charset=utf8", "root", "");

// Insérer la commande
$stmt = $pdo->prepare("
  INSERT INTO commandes (nom_client, telephone, adresse, ville, total, statut)
  VALUES (?, ?, ?, ?, ?, 'en_attente')
");
$stmt->execute([
  $data["client"]["nom"],
  $data["client"]["tel"],
  $data["client"]["adresse"],
  $data["client"]["ville"],
  $data["total"]
]);
$commandeId = $pdo->lastInsertId();

echo json_encode(["success" => true, "commandeId" => $commandeId]);
*/

2
Suivi de livraison — polling AJAX
setTimeout · $.get · Statut · Timeline DOM · api/livraisons.php
javascript
// ── js/suivi.js — polling du statut de livraison ─────────────
$(document).ready(function() {

  // Récupérer l'id de commande depuis l'URL
  const params     = new URLSearchParams(window.location.search);
  const commandeId = params.get("id");

  if (!commandeId) {
    window.location.href = "index.html";
    return;
  }

  document.getElementById("num-commande").textContent = "#" + commandeId;

  const ETAPES = [
    { statut: "en_attente",    label: "Reçue"          },
    { statut: "confirmee",     label: "Confirmée"       },
    { statut: "en_preparation",label: "Préparation"     },
    { statut: "en_livraison",  label: "En livraison"    },
    { statut: "livree",        label: "Livrée"          },
  ];
  const STATUTS_ACTIFS = ["en_attente","confirmee","en_preparation","en_livraison"];

  // Mettre à jour la timeline DOM
  function mettreAJourTimeline(statutActuel) {
    const timeline = document.getElementById("timeline");
    timeline.innerHTML = "";
    const indexActuel = ETAPES.findIndex(e => e.statut === statutActuel);

    ETAPES.forEach((etape, i) => {
      const div = document.createElement("div");
      div.className = "etape-timeline " + (
        i < indexActuel ? "passee" : i === indexActuel ? "active" : "future"
      );
      div.innerHTML = `
        <div class="etape-point"></div>
        <span class="etape-label">${etape.label}</span>
      `;
      timeline.appendChild(div);
    });

    document.getElementById("statut-principal").textContent =
      ETAPES.find(e => e.statut === statutActuel)?.label || statutActuel;
  }

  // Polling — vérifier toutes les 10 secondes
  function verifierStatut() {
    $.get("api/livraisons.php", { commandeId: commandeId }, function(data) {
      mettreAJourTimeline(data.statut);

      if (data.livreur) {
        document.getElementById("info-livreur").textContent =
          "Livreur : " + data.livreur.nom + " — " + data.livreur.vehicule;
      }

      if (STATUTS_ACTIFS.includes(data.statut)) {
        // Pas encore livré → re-vérifier dans 10 secondes
        setTimeout(verifierStatut, 10000);
      } else {
        // Livré ou annulé → arrêter le polling
        document.getElementById("message-final").textContent =
          data.statut === "livree"
            ? "Votre commande est arrivée. Bon appétit !"
            : "Commande annulée. Remboursement en cours.";
        document.getElementById("message-final").hidden = false;
      }
    })
    .fail(function() {
      // En cas d'erreur réseau, réessayer quand même
      setTimeout(verifierStatut, 10000);
    });
  }

  // Démarrer le suivi immédiatement
  verifierStatut();
});

// ── api/livraisons.php — code PHP correspondant ───────────────
/*
<?php
header("Content-Type: application/json");

$commandeId = intval($_GET["commandeId"] ?? 0);
if (!$commandeId) {
  http_response_code(400);
  echo json_encode(["erreur" => "ID manquant"]);
  exit;
}

$pdo = new PDO("mysql:host=localhost;dbname=darigo;charset=utf8", "root", "");

$stmt = $pdo->prepare("
  SELECT c.statut, l.nom AS livreur_nom, l.vehicule
  FROM commandes c
  LEFT JOIN livreurs l ON c.livreur_id = l.id
  WHERE c.id = ?
");
$stmt->execute([$commandeId]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$row) {
  http_response_code(404);
  echo json_encode(["erreur" => "Commande introuvable"]);
  exit;
}

$reponse = ["statut" => $row["statut"]];
if ($row["livreur_nom"]) {
  $reponse["livreur"] = [
    "nom"      => $row["livreur_nom"],
    "vehicule" => $row["vehicule"]
  ];
}
echo json_encode($reponse);
*/

✓
Checklist finale du projet DariGo Interactif
Récapitulatif complet
✓ Checklist — DariGo Interactif JS
  • Structure HTML sémantique (index.html, commande.html, suivi.html)
  • Chargement des plats via $.getJSON(api/plats.php) avec fallback mock
  • Affichage DOM dynamique des plats avec createElement
  • Filtres par catégorie fonctionnels (classList.toggle & filter)
  • Délégation d'événements sur la grille des plats
  • Panier persistant avec localStorage
  • Ajout / retrait de plats avec mise à jour DOM en temps réel
  • Récapitulatif panier affiché sur la page commande
  • Validation JS complète : nom, téléphone (regex), adresse, ville
  • Affichage contextuel des erreurs (blur + submit)
  • Envoi commande $.ajax POST vers api/commandes.php
  • Vidage panier localStorage après succès
  • Suivi livraison avec polling $.get toutes les 10 secondes
  • Timeline DOM mise à jour dynamiquement selon le statut
  • Arrêt automatique du polling quand statut = livree ou annulee
FichierTechnologieCompétences mobilisées
menu.jsJS + jQueryDOM · createElement · délégation · $.getJSON
panier.jsJS purlocalStorage · DOM · événements · fonctions
commande.jsJS + jQueryFormulaire · validation · RegEx · $.ajax POST
suivi.jsjQueryURLSearchParams · polling · $.get · DOM
api/plats.phpPHPPDO · json_encode · header Content-Type
api/commandes.phpPHPphp://input · json_decode · INSERT PDO
api/livraisons.phpPHPSELECT JOIN · json_encode · GET params