{
  "chapter": {
    "id": "javascript-dom",
    "level": "premiere",
    "theme": "IHM Web",
    "title": "JavaScript et DOM",
    "description": "Bases de JavaScript pour interagir avec une page web :\nintégration au HTML, syntaxe de base, manipulation du DOM\n(Document Object Model), sélection et modification\nd'éléments, exemples typiques.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Rôle de JavaScript",
      "statement": "À quoi sert principalement **JavaScript** dans\nune page web ?",
      "options": [
        {
          "text": "À mettre en forme les éléments",
          "correct": false,
          "feedback": "C'est le rôle de CSS.\n"
        },
        {
          "text": "À envoyer des e-mails",
          "correct": false,
          "feedback": "Erreur : pas son rôle (cela demande un\nserveur).\n"
        },
        {
          "text": "À rendre la page dynamique : interactions, animations, modification du contenu en réaction à l'utilisateur",
          "correct": true,
          "feedback": "Bonne réponse : HTML structure, CSS\nembellit, JavaScript fait vivre. Trois\npiliers complémentaires du Web.\n"
        },
        {
          "text": "À structurer le contenu de la page",
          "correct": false,
          "feedback": "C'est le rôle de HTML.\n"
        }
      ],
      "explanation": "Trois langages du Web côté navigateur : HTML\n(structure), CSS (style), JavaScript\n(comportement). JavaScript a aussi un\npendant côté serveur (Node.js)."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "integration"
      ],
      "title": "Intégrer du JavaScript",
      "statement": "Comment **inclure** un fichier `script.js` dans\nune page HTML ?",
      "options": [
        {
          "text": "```html\n<link href=\"script.js\">\n```\n",
          "correct": false,
          "feedback": "Erreur : `<link>` est pour les feuilles\nde style.\n"
        },
        {
          "text": "```html\n<script src=\"script.js\"></script>\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : balise `<script>` avec\n`src`. Préférable de placer en bas du\n`<body>` (ou avec `defer` dans `<head>`)\npour ne pas bloquer le rendu.\n"
        },
        {
          "text": "```html\n<import \"script.js\">\n```\n",
          "correct": false,
          "feedback": "Erreur : balise inexistante en HTML.\n"
        },
        {
          "text": "```html\n<js src=\"script.js\"></js>\n```\n",
          "correct": false,
          "feedback": "Erreur : balise inexistante.\n"
        }
      ],
      "explanation": "Variantes : script externe (`src`), script\ninline (`<script>...</script>`), module\n(`type=\"module\"`). Toujours préférer le\nscript externe pour la maintenance."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "variable"
      ],
      "title": "Déclaration de variable",
      "statement": "Quel mot-clé moderne déclare une variable\n**modifiable** en JavaScript ?",
      "options": [
        {
          "text": "`var`",
          "correct": false,
          "feedback": "Possible mais désuet (portée\ndéroutante). Préférer `let`.\n"
        },
        {
          "text": "`def`",
          "correct": false,
          "feedback": "Erreur : c'est Python.\n"
        },
        {
          "text": "`let`",
          "correct": true,
          "feedback": "Bonne réponse : `let x = 5;` déclare une\nvariable de portée bloc, modifiable.\nStandard depuis ES6 (2015).\n"
        },
        {
          "text": "`int`",
          "correct": false,
          "feedback": "Erreur : JS n'est pas typé statiquement.\n"
        }
      ],
      "explanation": "Trois mots-clés : `var` (ancien, à éviter),\n`let` (modifiable), `const` (constante,\nnon réassignable). Privilégier `const` par\ndéfaut, `let` quand on doit modifier."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "console-log"
      ],
      "title": "Afficher dans la console",
      "statement": "Comment **afficher** un message dans la console\ndu navigateur en JavaScript ?",
      "options": [
        {
          "text": "`console.log(\"Hello\")`",
          "correct": true,
          "feedback": "Bonne réponse : `console.log` est le\nstandard. Visible dans l'onglet\n« Console » des outils développeur (F12).\n"
        },
        {
          "text": "`echo \"Hello\"`",
          "correct": false,
          "feedback": "Erreur : c'est PHP/Bash.\n"
        },
        {
          "text": "`print(\"Hello\")`",
          "correct": false,
          "feedback": "Erreur : c'est Python.\n"
        },
        {
          "text": "`puts \"Hello\"`",
          "correct": false,
          "feedback": "Erreur : c'est Ruby.\n"
        }
      ],
      "explanation": "Variantes : `console.warn`, `console.error`,\n`console.table` (pour les objets/tableaux),\n`console.dir` (pour les éléments DOM)."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "dom-definition"
      ],
      "title": "DOM",
      "statement": "Que signifie **DOM** ?",
      "options": [
        {
          "text": "Dynamic Output Module",
          "correct": false,
          "feedback": "Aucune spécification ne\nporte ce nom. Le DOM est\nla représentation arborescente\nd'un document HTML, exposée\naux scripts pour permettre\nla modification dynamique\nde la page.\n"
        },
        {
          "text": "Display Order Manager",
          "correct": false,
          "feedback": "Cette dénomination est\ninventée. Le sigle DOM\nrenvoie au *Document\nObject Model*, normalisé\npar le W$3$C dans le cadre\nde la modélisation des\ndocuments HTML.\n"
        },
        {
          "text": "Document Object Model",
          "correct": true,
          "feedback": "Bonne réponse : représentation\narborescente de la page HTML, accessible\net modifiable depuis JavaScript via des\nobjets et des méthodes.\n"
        },
        {
          "text": "Domain Object Method",
          "correct": false,
          "feedback": "Cette appellation n'est\npas standard. Le sigle\nDOM, défini par le W$3$C,\nsignifie *Document Object\nModel* : il décrit la\nstructure d'un document\nHTML sous forme d'arbre\nd'objets.\n"
        }
      ],
      "explanation": "Le DOM est la « porte d'entrée » de\nJavaScript vers la page. Chaque élément HTML\ndevient un objet manipulable."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "getElementById"
      ],
      "title": "Sélection par id",
      "statement": "Comment récupérer en JavaScript l'élément ayant\n`id=\"titre\"` ?",
      "options": [
        {
          "text": "`getElementById(titre)`",
          "correct": false,
          "feedback": "Erreur : il manque `document.` et les\nguillemets autour de la chaîne.\n"
        },
        {
          "text": "`document.find(\"#titre\")`",
          "correct": false,
          "feedback": "Erreur : pas en JavaScript natif (oui en\njQuery).\n"
        },
        {
          "text": "`document.getId(\"titre\")`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante.\n"
        },
        {
          "text": "`document.getElementById(\"titre\")`",
          "correct": true,
          "feedback": "Bonne réponse : méthode classique. Renvoie\nl'objet DOM correspondant ou `null` si\nl'id n'existe pas.\n"
        }
      ],
      "explanation": "Alternative moderne et flexible :\n`document.querySelector(\"#titre\")` (accepte\ntout sélecteur CSS)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "textContent"
      ],
      "title": "Modifier le texte",
      "statement": "Comment changer le **texte** d'un élément `el`\nsélectionné par DOM ?",
      "options": [
        {
          "text": "`el.value = \"...\"`",
          "correct": false,
          "feedback": "Erreur : `value` concerne les champs de\nformulaire (`<input>`, `<select>`).\n"
        },
        {
          "text": "`el.text = \"...\"`",
          "correct": false,
          "feedback": "Erreur : propriété inexistante.\n"
        },
        {
          "text": "`el.textContent = \"...\"`",
          "correct": true,
          "feedback": "Bonne réponse : `textContent` modifie le\ncontenu textuel. Pour insérer du HTML\n(avec balises), utiliser `innerHTML`\n(mais attention aux failles XSS).\n"
        },
        {
          "text": "`el.changeText(\"...\")`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante.\n"
        }
      ],
      "explanation": "`textContent` (texte pur) vs `innerHTML`\n(HTML brut). Préférer `textContent` quand on\naffiche du contenu utilisateur (sécurité)."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "arbre-dom"
      ],
      "title": "DOM comme arbre",
      "statement": "Quelle structure de données est utilisée pour\nmodéliser le DOM ?",
      "options": [
        {
          "text": "Un graphe orienté avec cycles",
          "correct": false,
          "feedback": "Erreur : un arbre n'a pas de cycles.\n"
        },
        {
          "text": "Un arbre dont la racine est `<html>`",
          "correct": true,
          "feedback": "Bonne réponse : structure hiérarchique\nreflétant l'imbrication des balises HTML.\nChaque élément a un parent et peut avoir\ndes enfants.\n"
        },
        {
          "text": "Une liste plate",
          "correct": false,
          "feedback": "Erreur : trop limité.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : pas la structure adaptée.\n"
        }
      ],
      "explanation": "Vocabulaire : nœud parent, nœud enfant,\nfrère, racine, feuille. Chaque élément du\nDOM est un **nœud**, qui peut être de type\n« élément », « texte », « attribut », etc."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "event-click"
      ],
      "title": "Réagir à un clic",
      "statement": "Comment exécuter une fonction `f` quand on\nclique sur un bouton `btn` ?",
      "options": [
        {
          "text": "`btn.onClick = f`",
          "correct": false,
          "feedback": "Casse incorrecte. C'est `onclick` (tout\nen minuscules).\n"
        },
        {
          "text": "`btn.click = f`",
          "correct": false,
          "feedback": "Erreur : `.click` est une méthode (pour\ndéclencher le clic), pas un setter de\nhandler.\n"
        },
        {
          "text": "`btn.addEventListener(\"click\", f)`",
          "correct": true,
          "feedback": "Bonne réponse : méthode moderne et\nrecommandée. Permet d'ajouter plusieurs\nécouteurs sans s'écraser mutuellement.\n"
        },
        {
          "text": "`btn.listen(f)`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante en JS natif.\n"
        }
      ],
      "explanation": "`addEventListener` est la méthode standard.\nPremier paramètre = type d'événement\n(`\"click\"`, `\"input\"`, `\"submit\"`...),\nsecond = fonction à exécuter."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exemple-simple"
      ],
      "title": "Trace simple",
      "statement": "Que fait ce code ?\n```javascript\nlet x = 5;\nx = x + 3;\nconsole.log(x);\n```",
      "options": [
        {
          "text": "Erreur",
          "correct": false,
          "feedback": "Erreur : code valide.\n"
        },
        {
          "text": "Affiche `53` (concaténation)",
          "correct": false,
          "feedback": "Erreur : 3 est un nombre, pas une chaîne,\ndonc addition arithmétique.\n"
        },
        {
          "text": "Affiche `5`",
          "correct": false,
          "feedback": "Erreur : on additionne 3.\n"
        },
        {
          "text": "Affiche `8`",
          "correct": true,
          "feedback": "Bonne réponse : x devient 5 + 3 = 8, puis\non l'affiche.\n"
        }
      ],
      "explanation": "Attention au piège de l'addition vs\nconcaténation : `5 + \"3\"` = `\"53\"` en JS\n(coercition de type). C'est une source\nclassique de bugs."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "querySelector"
      ],
      "title": "querySelector",
      "statement": "Que renvoie\n`document.querySelector(\".bouton\")` ?",
      "options": [
        {
          "text": "Le bouton cliqué le plus récemment",
          "correct": false,
          "feedback": "La méthode `querySelector`\ninterroge la structure du\ndocument, pas son historique\nd'interaction. Le suivi de\nl'élément actif passe par\n`document.activeElement` ou\ndes gestionnaires d'évènements.\n"
        },
        {
          "text": "Une chaîne contenant le HTML du bouton",
          "correct": false,
          "feedback": "Erreur : renvoie l'objet DOM, pas une\nchaîne.\n"
        },
        {
          "text": "Le premier élément ayant la classe « bouton » (ou `null` si aucun)",
          "correct": true,
          "feedback": "Bonne réponse : `querySelector` accepte\nun sélecteur CSS (classe, id,\ncombinaisons...) et renvoie le premier\nélément correspondant.\n"
        },
        {
          "text": "Tous les éléments ayant la classe « bouton »",
          "correct": false,
          "feedback": "Erreur : c'est `querySelectorAll` qui\nrenvoie tous les éléments.\n"
        }
      ],
      "explanation": "`querySelector(sel)` = premier élément ;\n`querySelectorAll(sel)` = NodeList. Plus\nflexibles que `getElementById` /\n`getElementsByClassName`."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "innerHTML"
      ],
      "title": "textContent vs innerHTML",
      "statement": "Quelle est la différence entre `textContent` et\n`innerHTML` ?",
      "options": [
        {
          "text": "Aucune",
          "correct": false,
          "feedback": "Erreur : différence importante.\n"
        },
        {
          "text": "`textContent` traite la valeur comme du texte brut (échappant les balises) ; `innerHTML` interprète le HTML",
          "correct": true,
          "feedback": "Bonne réponse : `el.textContent =\n\"<b>x</b>\"` affiche littéralement\n`<b>x</b>` ; `el.innerHTML = \"<b>x</b>\"`\naffiche `x` en gras.\n"
        },
        {
          "text": "`textContent` n'existe pas",
          "correct": false,
          "feedback": "Erreur : méthode standard.\n"
        },
        {
          "text": "`innerHTML` est plus rapide",
          "correct": false,
          "feedback": "Erreur : tendance inverse, et la sécurité\nprime.\n"
        }
      ],
      "explanation": "Sécurité : si le contenu vient de\nl'utilisateur, **toujours** utiliser\n`textContent` pour éviter les attaques XSS\n(injection de scripts)."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "conditions"
      ],
      "title": "Conditions",
      "statement": "Quelle syntaxe est **valide** en JavaScript pour\nune condition ?",
      "options": [
        {
          "text": "```javascript\nif (x > 0) {\n    console.log(\"positif\");\n}\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : parenthèses autour de la\ncondition, accolades pour le bloc, points-\nvirgules en fin d'instruction.\n"
        },
        {
          "text": "```javascript\nif x > 0 then\n    console.log(\"positif\")\nend\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est la syntaxe Lua/Ruby.\n"
        },
        {
          "text": "```javascript\nif x > 0:\n    console.log(\"positif\")\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est la syntaxe Python.\n"
        },
        {
          "text": "```javascript\nif x > 0:\n    console.log \"positif\"\n```\n",
          "correct": false,
          "feedback": "Erreur : syntaxe inventée.\n"
        }
      ],
      "explanation": "Syntaxe similaire à C/Java. Astuce : les\npoints-virgules sont techniquement\nfacultatifs (insertion automatique), mais\nrecommandés pour éviter les pièges."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "boucle"
      ],
      "title": "Boucle for",
      "statement": "Quelle syntaxe `for` JavaScript itère **5 fois** ?",
      "options": [
        {
          "text": "```javascript\nfor i in range(5)\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est Python.\n"
        },
        {
          "text": "```javascript\nfor i = 0 to 5\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est BASIC/Pascal.\n"
        },
        {
          "text": "```javascript\nloop 5 times\n```\n",
          "correct": false,
          "feedback": "Erreur : syntaxe inventée.\n"
        },
        {
          "text": "```javascript\nfor (let i = 0; i < 5; i++) { ... }\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : trois parties dans le\n`for` : initialisation, condition,\nincrément (séparées par `;`).\n"
        }
      ],
      "explanation": "Variantes modernes : `for...of` (itérer sur\nles valeurs d'un tableau), `for...in`\n(itérer sur les clés d'un objet),\n`forEach` (méthode des tableaux)."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "fonction"
      ],
      "title": "Définir une fonction",
      "statement": "Quelle est la syntaxe valide d'une fonction JS ?",
      "options": [
        {
          "text": "```javascript\ndef carre(x): return x * x\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est Python.\n"
        },
        {
          "text": "```javascript\nmethod carre(x) { ... }\n```\n",
          "correct": false,
          "feedback": "Erreur : pas de mot-clé `method` global\nen JS.\n"
        },
        {
          "text": "```javascript\ncarre(x) -> x * x\n```\n",
          "correct": false,
          "feedback": "Erreur : syntaxe inventée.\n"
        },
        {
          "text": "```javascript\nfunction carre(x) { return x * x; }\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : mot-clé `function`,\naccolades pour le bloc, `return` pour la\nvaleur.\n"
        }
      ],
      "explanation": "Syntaxe moderne (« arrow function ») :\n`const carre = x => x * x;`. Plus concise\nmais subtilités sur `this`."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "modifier-attribut"
      ],
      "title": "Modifier un attribut",
      "statement": "Comment changer l'**attribut `src`** d'une image ?",
      "options": [
        {
          "text": "`img.attribute(\"src\", \"...\")`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante.\n"
        },
        {
          "text": "`img.changeSrc(\"...\")`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante.\n"
        },
        {
          "text": "`img.src = \"nouvelle.jpg\"`",
          "correct": true,
          "feedback": "Bonne réponse : on peut accéder aux\nattributs comme à des propriétés JS.\nForme alternative :\n`img.setAttribute(\"src\", \"nouvelle.jpg\")`.\n"
        },
        {
          "text": "`img.attr.src = \"...\"`",
          "correct": false,
          "feedback": "Erreur : `.attr` n'existe pas.\n"
        }
      ],
      "explanation": "Pour les attributs standards (`src`, `href`,\n`alt`, `id`...), on accède directement par\nla propriété. Pour les attributs\npersonnalisés ou spéciaux : `setAttribute`/\n`getAttribute`."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "creation-element"
      ],
      "title": "Créer un élément",
      "statement": "Comment créer dynamiquement un nouveau\nparagraphe `<p>` et l'**ajouter** au `<body>` ?",
      "options": [
        {
          "text": "```javascript\nadd(\"p\", \"Hello\");\n```\n",
          "correct": false,
          "feedback": "Erreur : fonction inexistante.\n"
        },
        {
          "text": "```javascript\nnew HTMLElement(\"p\");\n```\n",
          "correct": false,
          "feedback": "Erreur : pas la bonne API.\n"
        },
        {
          "text": "```javascript\ndocument.body.innerHTML = \"<p>Hello</p>\";\n```\n",
          "correct": false,
          "feedback": "Cela écrase **tout** le contenu de\n`<body>`. Pas équivalent (et peu sûr).\n"
        },
        {
          "text": "```javascript\nconst p = document.createElement(\"p\");\np.textContent = \"Hello\";\ndocument.body.appendChild(p);\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : trois étapes, à savoir\ncréer, remplir, ajouter au DOM.\n"
        }
      ],
      "explanation": "Variantes pour ajouter : `appendChild` (à la\nfin), `prepend` (au début), `insertBefore`\n(à un endroit précis)."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "comparaison-egalite"
      ],
      "title": "== vs ===",
      "statement": "Quelle est la différence entre `==` et `===` en\nJavaScript ?",
      "options": [
        {
          "text": "Aucune",
          "correct": false,
          "feedback": "Erreur : différence importante !\n"
        },
        {
          "text": "`==` compare avec conversion de type (`5 == \"5\"` est `true`) ; `===` compare strictement (sans conversion : `5 === \"5\"` est `false`)",
          "correct": true,
          "feedback": "Bonne réponse : préférer `===` pour\néviter les surprises de coercition.\nSource de bugs subtils en JS.\n"
        },
        {
          "text": "`==` n'existe pas en JS moderne",
          "correct": false,
          "feedback": "Erreur : il existe, mais à éviter.\n"
        },
        {
          "text": "`===` est obsolète",
          "correct": false,
          "feedback": "Erreur : c'est la version recommandée.\n"
        }
      ],
      "explanation": "Règle d'or : **toujours utiliser `===`** sauf\ncas très précis. Idem pour l'inégalité :\n`!==` plutôt que `!=`."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "tableau"
      ],
      "title": "Tableau JavaScript",
      "statement": "Comment déclarer un **tableau** de trois\néléments en JS ?",
      "options": [
        {
          "text": "`let t = [1, 2, 3];`",
          "correct": true,
          "feedback": "Bonne réponse : crochets, virgules.\nAccès : `t[0]` (= 1). Méthodes :\n`t.push`, `t.pop`, `t.length`...\n"
        },
        {
          "text": "`Array(1, 2, 3)`",
          "correct": false,
          "feedback": "Forme valide mais on préfère la\nnotation littérale `[1, 2, 3]`.\n"
        },
        {
          "text": "`let t = {1, 2, 3};`",
          "correct": false,
          "feedback": "Erreur : `{}` désigne un objet en JS,\navec syntaxe `{cle: valeur}`.\n"
        },
        {
          "text": "`let t = (1, 2, 3);`",
          "correct": false,
          "feedback": "Erreur : ce serait un tuple Python.\n"
        }
      ],
      "explanation": "Tableaux JS : indexés à partir de 0,\nhétérogènes (peuvent contenir n'importe quel\ntype). Très polyvalents."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "event-handler-this"
      ],
      "title": "Événement et données",
      "statement": "Comment savoir **sur quel élément** un\névénement a eu lieu dans le gestionnaire ?",
      "options": [
        {
          "text": "En relançant le code",
          "correct": false,
          "feedback": "Relancer le script ne\nfournit aucune information\nsupplémentaire. C'est l'objet\n`event`, automatiquement\ntransmis au gestionnaire,\nqui contient toutes les\ndonnées utiles.\n"
        },
        {
          "text": "Impossible",
          "correct": false,
          "feedback": "Erreur : il existe une variable\nd'événement.\n"
        },
        {
          "text": "Avec une variable globale",
          "correct": false,
          "feedback": "Pratique fragile, à éviter.\n"
        },
        {
          "text": "Via `event.target` (passé au callback)",
          "correct": true,
          "feedback": "Bonne réponse :\n`btn.addEventListener(\"click\", (e) =>\nconsole.log(e.target));` affiche\nl'élément qui a déclenché l'événement.\n"
        }
      ],
      "explanation": "L'objet `event` est passé automatiquement\nau callback. Propriétés utiles :\n`event.target` (qui a déclenché),\n`event.type` (type d'événement),\n`event.preventDefault()` (annuler le\ncomportement par défaut)."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "debug-console"
      ],
      "title": "Débogage",
      "statement": "Pour déboguer un script JavaScript dans le\nnavigateur, quel outil utiliser ?",
      "options": [
        {
          "text": "Visual Studio",
          "correct": false,
          "feedback": "IDE pour développer, pas pour déboguer\ndans le navigateur.\n"
        },
        {
          "text": "Notepad",
          "correct": false,
          "feedback": "Pas un débogueur.\n"
        },
        {
          "text": "Un script Python",
          "correct": false,
          "feedback": "Python est un langage\nautonome, exécuté côté\nserveur ou en local. Le\ndébogage d'un script\nJavaScript se fait dans\nle navigateur, là où le\ncode s'exécute réellement.\n"
        },
        {
          "text": "Les outils développeur du navigateur (touche F12) : console, débogueur, inspecteur, onglet Network",
          "correct": true,
          "feedback": "Bonne réponse : Chrome DevTools, Firefox\nDevTools, Safari Web Inspector. Tous\ngratuits et puissants. Apprendre à les\nutiliser est essentiel.\n"
        }
      ],
      "explanation": "Astuces : `debugger;` pour mettre un point\nd'arrêt en code ; `console.table(arr)` pour\nvisualiser un tableau ; `console.dir(elem)`\npour explorer un nœud DOM."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "synchronisation-dom"
      ],
      "title": "DOM pas encore prêt",
      "statement": "Mon script JS fait `document.getElementById`\nmais renvoie `null`. Pourquoi ?",
      "options": [
        {
          "text": "L'élément n'existe pas dans la page",
          "correct": false,
          "feedback": "Possible aussi, mais la cause la plus\nfréquente est le timing.\n"
        },
        {
          "text": "JavaScript ne peut pas lire le DOM",
          "correct": false,
          "feedback": "Erreur : c'est exactement son rôle.\n"
        },
        {
          "text": "Le script s'exécute avant que le DOM ait été parsé. L'élément n'existe pas encore.",
          "correct": true,
          "feedback": "Bonne réponse : si `<script>` est dans\n`<head>` sans `defer`, il s'exécute avant\nle `<body>`. Solutions : placer le\nscript en bas du `<body>`, utiliser\n`defer`, ou encapsuler dans un\nécouteur `DOMContentLoaded`.\n"
        },
        {
          "text": "Le navigateur est buggé",
          "correct": false,
          "feedback": "Très improbable.\n"
        }
      ],
      "explanation": "Trois solutions : (1) placer le `<script>` à\nla fin de `<body>`, (2) `<script defer>`\ndans `<head>`, (3) `document.addEventListener\n(\"DOMContentLoaded\", ...)`."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "coercition"
      ],
      "title": "Coercition de type",
      "statement": "Que renvoie `\"5\" + 3` en JavaScript ?",
      "options": [
        {
          "text": "\"53\" (chaîne, par concaténation)",
          "correct": true,
          "feedback": "Bonne réponse : avec `+`, si l'un des\nopérandes est une chaîne, l'autre est\nconverti en chaîne et on concatène.\nComportement spécifique à JS, source\nfréquente de bugs.\n"
        },
        {
          "text": "Erreur de type",
          "correct": false,
          "feedback": "Erreur : JS coercite silencieusement.\n"
        },
        {
          "text": "5",
          "correct": false,
          "feedback": "Erreur : 5 ignorerait le 3.\n"
        },
        {
          "text": "8",
          "correct": false,
          "feedback": "Erreur : pas d'addition arithmétique car\nun opérande est une chaîne.\n"
        }
      ],
      "explanation": "Comparaison : `\"5\" - 3` = `2` (le `-` force\nla conversion en nombre). Imprévisible :\ntoujours convertir explicitement avec\n`Number(s)` ou `parseInt(s)`."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "securite"
      ],
      "title": "Sécurité innerHTML",
      "statement": "Pourquoi `el.innerHTML = userInput;` est-il\n**dangereux** ?",
      "options": [
        {
          "text": "Parce que c'est lent",
          "correct": false,
          "feedback": "Erreur : pas d'impact significatif sur la\nperformance.\n"
        },
        {
          "text": "Parce qu'un utilisateur malveillant peut injecter du code HTML/JS (attaque XSS : Cross-Site Scripting)",
          "correct": true,
          "feedback": "Bonne réponse : si `userInput` contient\n`<script>...</script>`, le code malveillant\ns'exécute dans le navigateur. Solution :\nutiliser `textContent` (qui échappe) ou\nassainir l'entrée.\n"
        },
        {
          "text": "Parce que `innerHTML` est obsolète",
          "correct": false,
          "feedback": "Erreur : pas obsolète.\n"
        },
        {
          "text": "Aucun risque réel",
          "correct": false,
          "feedback": "Erreur : XSS est l'une des\nvulnérabilités les plus exploitées.\n"
        }
      ],
      "explanation": "Règle d'or : ne jamais insérer de contenu\nutilisateur via `innerHTML`. Préférer\n`textContent`, ou un nettoyage avec une\nbibliothèque (DOMPurify)."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur JavaScript\net le DOM, laquelle est **fausse** ?",
      "options": [
        {
          "text": "`addEventListener` permet d'ajouter plusieurs gestionnaires d'événements à un élément",
          "correct": false,
          "feedback": "Vrai : avantage majeur sur `onclick =`.\n"
        },
        {
          "text": "`document.getElementById(id)` renvoie le premier élément ou `null`",
          "correct": false,
          "feedback": "Vrai : comportement standard.\n"
        },
        {
          "text": "`==` et `===` sont synonymes en JavaScript",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : `==` fait\nune comparaison **avec coercition** (donc\nparfois surprenante : `0 == false`,\n`\"\" == 0`...) ; `===` est une\ncomparaison **stricte**, sans conversion\nde type.\n"
        },
        {
          "text": "Le DOM est une représentation arborescente de la page HTML",
          "correct": false,
          "feedback": "Vrai : structure clé du Web.\n"
        }
      ],
      "explanation": "Mnémonique : « toujours triple égal » en\nJS, sauf cas très exceptionnels documentés."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "let",
        "const",
        "var"
      ],
      "title": "let, const et var",
      "statement": "Lequel de ces mots-clés permet, en JavaScript\nmoderne, de déclarer une variable dont la valeur ne\npourra **plus changer** après l'affectation initiale ?",
      "options": [
        {
          "text": "`var`",
          "correct": false,
          "feedback": "Erreur : `var` autorise au contraire la\nréaffectation. C'est l'ancien mot-clé de\ndéclaration, à éviter aujourd'hui car il a\négalement une portée moins prévisible.\n"
        },
        {
          "text": "`function`",
          "correct": false,
          "feedback": "Erreur : `function` sert à déclarer une\nfonction, pas une variable. Il n'a aucun\nrapport avec la modification d'une valeur.\n"
        },
        {
          "text": "`const`",
          "correct": true,
          "feedback": "Bonne réponse : `const` interdit la\nréaffectation après l'initialisation. Cela\nn'empêche cependant pas de **muter** un objet\nou un tableau (modifier ses propriétés ou ses\néléments).\n"
        },
        {
          "text": "`let`",
          "correct": false,
          "feedback": "Erreur : `let` déclare une variable dont la\nvaleur peut être réaffectée. Pour interdire la\nréaffectation, on utilise plutôt `const`.\n"
        }
      ],
      "explanation": "Recommandation moderne : préférer `const` par\ndéfaut, et n'utiliser `let` que si on a vraiment\nbesoin de réaffecter la variable. Le mot-clé `var`\nest conservé pour la rétrocompatibilité mais ne\ndoit plus apparaître dans du code récent."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "classlist",
        "attributs"
      ],
      "title": "Modifier les classes d'un élément",
      "statement": "Pour ajouter la classe `actif` à un élément `el`\nsélectionné via `querySelector`, quelle instruction\nutiliser en JavaScript moderne ?",
      "options": [
        {
          "text": "`el.style.actif = true`",
          "correct": false,
          "feedback": "Erreur : la propriété `style` permet de\nmodifier les styles **inline**, pas la liste\ndes classes. Il n'existe pas de style\n`actif`.\n"
        },
        {
          "text": "`el.addClass(\"actif\")`",
          "correct": false,
          "feedback": "Erreur : `addClass` n'est pas une méthode\nnative du DOM. C'est une fonction de jQuery,\nlibrairie qui n'est pas disponible par défaut\ndans le navigateur.\n"
        },
        {
          "text": "`el.classList.add(\"actif\")`",
          "correct": true,
          "feedback": "Bonne réponse : la propriété `classList`\nfournit une API dédiée à la gestion des\nclasses : `add`, `remove`, `toggle`,\n`contains`. C'est la méthode recommandée\naujourd'hui.\n"
        },
        {
          "text": "`el.class = \"actif\"",
          "correct": false,
          "feedback": "Erreur : la propriété DOM s'appelle\n`className`, pas `class` (qui est un mot\nréservé en JavaScript). Et l'assigner écrase\ntoutes les classes existantes au lieu d'en\najouter une.\n"
        }
      ],
      "explanation": "Avantages de `classList` : on n'écrase pas les\nautres classes, le code est lisible, et\n`toggle(\"actif\")` est un raccourci pratique pour\najouter ou retirer la classe selon son état\ncourant. C'est l'API à privilégier pour basculer\nentre des états visuels."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "getelementbyid",
        "queryselector"
      ],
      "title": "getElementById vs querySelector",
      "statement": "Quelle est la différence entre `document.getElementById(\"titre\")`\net `document.querySelector(\"#titre\")` ?",
      "options": [
        {
          "text": "La première renvoie une liste, la seconde un seul élément",
          "correct": false,
          "feedback": "Erreur : `getElementById` renvoie un seul\nélément (ou `null`). C'est `getElementsByClassName`\nou `querySelectorAll` qui renvoient une\ncollection.\n"
        },
        {
          "text": "`querySelector` ne fonctionne que sur les pages chargées par AJAX",
          "correct": false,
          "feedback": "Erreur : `querySelector` fonctionne sur tout\nélément du DOM, quel que soit son mode de\nchargement.\n"
        },
        {
          "text": "Aucune dans le résultat ; les deux retournent l'élément dont l'`id` vaut `titre` (s'il existe)",
          "correct": true,
          "feedback": "Bonne réponse : pour un identifiant donné, les\ndeux méthodes renvoient le même élément.\n`getElementById` est plus rapide et plus\nancienne ; `querySelector` accepte tout\nsélecteur CSS, ce qui la rend plus polyvalente\nmais légèrement plus lente.\n"
        },
        {
          "text": "La première fonctionne sur les classes, la seconde sur les identifiants",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. `getElementById`\ncible un identifiant ; `querySelector` accepte\ntout sélecteur CSS, dont les classes\n(`.maClasse`).\n"
        }
      ],
      "explanation": "Bonne pratique : utiliser `getElementById` quand\non cible précisément un identifiant ;\n`querySelector` lorsqu'on a besoin d'un sélecteur\ncomplexe (par exemple `nav > a.actif:first-child`).\nPour récupérer plusieurs éléments,\n`querySelectorAll` renvoie une `NodeList`\nitérable."
    }
  ]
}