{
  "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 et interactive",
          "correct": true,
          "feedback": "Bonne réponse : HTML structure, CSS\nembellit, JavaScript fait vivre. Trois\npiliers complémentaires du Web.\n"
        },
        {
          "text": "À structurer tout 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": "`Let`",
          "correct": false,
          "feedback": "Erreur de casse : les mots-clés\nJavaScript s'écrivent tout en\nminuscules. `Let x = 5;` provoque une\nSyntaxError.\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": "`Console.log(\"Hello\")`",
          "correct": false,
          "feedback": "Erreur de casse : JavaScript distingue\nmajuscules et minuscules. L'objet\ns'appelle `console`, tout en minuscules ;\n`Console` n'est pas défini\n(ReferenceError).\n"
        },
        {
          "text": "`console.log \"Hello\"`",
          "correct": false,
          "feedback": "Parenthèses oubliées : en JavaScript,\nl'appel d'une fonction exige des\nparenthèses autour des arguments. Sans\nelles, SyntaxError.\n"
        },
        {
          "text": "`log(\"Hello\")`",
          "correct": false,
          "feedback": "Préfixe oublié : `log` seul n'est pas\ndéfini (ReferenceError). C'est une\nméthode de l'objet `console`, il faut\ndonc écrire `console.log`.\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 plusieurs 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.addEventListener(\"onclick\", f)`",
          "correct": false,
          "feedback": "Erreur : le nom de l'événement est\n`\"click\"`, sans le préfixe `on`. Aucun\névénement nommé `\"onclick\"` n'existe : la\nfonction n'est jamais appelée, et aucun\nmessage d'erreur ne le signale.\n"
        },
        {
          "text": "`btn.addEventListener(\"click\", f())`",
          "correct": false,
          "feedback": "Erreur : `f()` **appelle** la fonction tout\nde suite et transmet son résultat (souvent\n`undefined`) à `addEventListener`. Il faut\npasser la fonction elle-même, `f`, sans\nparenthèses.\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.onclick(\"click\", f)`",
          "correct": false,
          "feedback": "Erreur : `onclick` est une propriété à\nlaquelle on **affecte** une fonction\n(`btn.onclick = f`), pas une méthode à\nappeler avec le nom de l'événement.\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 de classe « bouton »",
          "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, les deux propriétés sont interchangeables",
          "correct": false,
          "feedback": "Erreur : la différence est importante,\nnotamment pour la sécurité.\n"
        },
        {
          "text": "`textContent` = texte brut, `innerHTML` = 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": "La propriété `textContent` n'existe pas en JavaScript",
          "correct": false,
          "feedback": "Erreur : c'est une propriété standard du\nDOM, disponible sur tout élément.\n"
        },
        {
          "text": "`innerHTML` est systématiquement 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 {\n    console.log(\"positif\");\n}\n```\n",
          "correct": false,
          "feedback": "Parenthèses du `if` oubliées : en\nJavaScript, la condition doit être\nentre parenthèses, `if (x > 0)`. Sans\nelles, SyntaxError.\n"
        },
        {
          "text": "```javascript\nif x > 0:\n    console.log(\"positif\")\n```\n",
          "correct": false,
          "feedback": "Réflexe Python : deux-points et bloc\nindenté. En JavaScript, la condition va\nentre parenthèses et le bloc entre\naccolades.\n"
        },
        {
          "text": "```javascript\nif (x > 0):\n    console.log(\"positif\");\n```\n",
          "correct": false,
          "feedback": "Deux-points en trop : malgré les\nparenthèses correctes, le `:` après la\ncondition n'existe pas en JavaScript\n(SyntaxError). Le bloc s'écrit entre\naccolades.\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 (let i = 0; i <= 5; i++) { ... }\n```\n",
          "correct": false,
          "feedback": "Erreur de borne : avec `<=`, la variable\ni prend les valeurs 0, 1, 2, 3, 4 et 5,\nsoit **six** itérations. Piège classique\ndu « un de trop ».\n"
        },
        {
          "text": "```javascript\nfor (let i = 0, i < 5, i++) { ... }\n```\n",
          "correct": false,
          "feedback": "Erreur : les trois parties du `for` sont\nséparées par des points-virgules, pas\npar des virgules. Ici, SyntaxError.\n"
        },
        {
          "text": "```javascript\nfor (let i = 0; i > 5; i++) { ... }\n```\n",
          "correct": false,
          "feedback": "Erreur : la condition `i > 5` est fausse\ndès le départ (i vaut 0), donc le corps\nde la boucle ne s'exécute jamais (zéro\nitération, pas cinq).\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\ncarre(x) { return x * x; }\n```\n",
          "correct": false,
          "feedback": "Mot-clé `function` oublié : sans lui, ce\ncode est une SyntaxError (le `return` se\nretrouve hors de toute fonction).\n"
        },
        {
          "text": "```javascript\nFunction carre(x) { return x * x; }\n```\n",
          "correct": false,
          "feedback": "Erreur de casse : le mot-clé s'écrit\n`function`, tout en minuscules.\n`Function` avec majuscule désigne un\nconstructeur, pas le mot-clé, d'où\nSyntaxError ici.\n"
        },
        {
          "text": "```javascript\nconst carre = x -> x * x;\n```\n",
          "correct": false,
          "feedback": "Mauvaise flèche : les fonctions fléchées\ns'écrivent avec `=>` et non `->`. Avec\n`->`, SyntaxError.\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\nconst p = document.createElement(\"p\");\np.textContent = \"Hello\";\n```\n",
          "correct": false,
          "feedback": "Erreur : l'élément est créé mais jamais\ninséré dans le document. Sans\n`document.body.appendChild(p)`, rien ne\ns'affiche.\n"
        },
        {
          "text": "```javascript\nconst p = document.createElement(\"p\");\np.textContent = \"Hello\";\ndocument.body.appendChild(\"p\");\n```\n",
          "correct": false,
          "feedback": "Erreur : `appendChild` attend un **nœud**,\npas une chaîne : `\"p\"` provoque une\n`TypeError`. Il faut lui passer l'élément\ncréé, `p`.\n"
        },
        {
          "text": "```javascript\nconst p = \"<p>Hello</p>\";\ndocument.body.innerHTML = p;\n```\n",
          "correct": false,
          "feedback": "Erreur : affecter `innerHTML` **remplace**\ntout le contenu de `<body>` par ce fragment.\nOn n'ajoute pas, on écrase (et injecter du\nHTML brut est risqué).\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": "`==` convertit le type, `===` non",
          "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 totalement 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": "Quelle est la notation **littérale** recommandée\npour déclarer un tableau de trois é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": "Ce code est valide mais ne crée pas de\ntableau : les parenthèses appliquent\nl'**opérateur virgule**, qui évalue chaque\nexpression et ne garde que la dernière.\nIci `t` vaut le nombre `3`.\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 simple variable globale partagée",
          "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": "Un environnement de développement comme Visual Studio",
          "correct": false,
          "feedback": "IDE pour développer, pas pour déboguer\ndans le navigateur, là où le script\ns'exécute.\n"
        },
        {
          "text": "Un éditeur de texte comme Notepad",
          "correct": false,
          "feedback": "Un éditeur permet d'écrire le code, pas\nde l'exécuter pas à pas ni d'inspecter\nla page.\n"
        },
        {
          "text": "Un simple script Python d'analyse",
          "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",
          "correct": true,
          "feedback": "Bonne réponse : accessibles avec F12.\nChrome DevTools, Firefox DevTools, Safari\nWeb Inspector. Tous gratuits et puissants.\nApprendre à les utiliser 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, placé dans `<head>` sans `defer`,\nfait `document.getElementById(\"menu\")` mais\nrenvoie `null`. L'élément `id=\"menu\"` existe\nbien dans le HTML, sans faute de frappe.\nPourquoi ?",
      "options": [
        {
          "text": "L'élément n'existe pas dans la page",
          "correct": false,
          "feedback": "Erreur : l'énoncé précise que l'élément\nexiste et que l'`id` est correct. La\ncause est le moment d'exécution.\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 le parsing du DOM",
          "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\"",
          "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 l'affectation ralentit l'affichage de la page",
          "correct": false,
          "feedback": "Erreur : pas d'impact significatif sur la\nperformance ; le danger est ailleurs.\n"
        },
        {
          "text": "Un utilisateur peut injecter du code (faille XSS)",
          "correct": true,
          "feedback": "Bonne réponse : si `userInput` contient\npar exemple `<img src=x onerror=\"...\">`,\nle code du gestionnaire s'exécute dans le\nnavigateur (les `<script>` insérés via\n`innerHTML` ne s'exécutent pas, mais les\ngestionnaires d'événements, si). Solution :\nutiliser `textContent` (qui échappe) ou\nassainir l'entrée.\n"
        },
        {
          "text": "Parce que `innerHTML` serait totalement obsolète",
          "correct": false,
          "feedback": "Erreur : pas obsolète.\n"
        },
        {
          "text": "Aucun risque réel, le navigateur filtre le contenu",
          "correct": false,
          "feedback": "Erreur : le navigateur n'assainit pas\n`innerHTML` ; 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": "const et mutation",
      "statement": "Le code suivant est-il permis en JavaScript ?\n\n```javascript\nconst t = [1];\nt.push(2);\n```",
      "options": [
        {
          "text": "Oui : `const` interdit la réaffectation de `t`, pas la modification du tableau",
          "correct": true,
          "feedback": "Bonne réponse : `const` fige le **lien** entre\nle nom `t` et le tableau, pas le tableau\nlui-même. Après le `push`, `t` vaut `[1, 2]`.\nEn revanche, `t = [9];` lèverait une\n`TypeError` (réaffectation interdite).\n"
        },
        {
          "text": "Non : une `TypeError` est levée, car `t` est déclaré constant",
          "correct": false,
          "feedback": "Erreur classique : croire que `const` rend la\nvaleur immuable. La `TypeError` ne survient\nque pour une **réaffectation** (`t = ...`),\npas pour une mutation comme `push`.\n"
        },
        {
          "text": "Non : il faudrait déclarer `t` avec `let` pour pouvoir le modifier",
          "correct": false,
          "feedback": "Erreur : `let` n'est nécessaire que pour\n**réaffecter** la variable. Muter le tableau\n(ajouter, retirer, modifier des éléments) est\npermis même avec `const`.\n"
        },
        {
          "text": "Oui, mais `t` vaut toujours `[1]` : le `push` est ignoré en silence",
          "correct": false,
          "feedback": "Erreur : rien n'est ignoré, `const` ne protège\npas le contenu du tableau. Le `push` s'exécute\nnormalement et `t` vaut `[1, 2]`. Pour geler\nle contenu, il faudrait `Object.freeze(t)`.\n"
        }
      ],
      "explanation": "`const` garantit que le nom pointe toujours vers\nle même objet ; il ne garantit pas que cet objet\nreste inchangé. Recommandation moderne : `const`\npar défaut, `let` seulement quand la variable doit\nêtre réaffectée, jamais `var`."
    },
    {
      "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": "Pour un même identifiant présent dans la page, que\nrenvoient les deux appels `document.getElementById(\"titre\")`\net `document.querySelector(\"#titre\")` ?",
      "options": [
        {
          "text": "Une liste d'éléments pour le premier, un seul élément pour le second",
          "correct": false,
          "feedback": "Erreur : `getElementById` renvoie un seul\nélément (ou `null`). C'est `getElementsByClassName`\nou `querySelectorAll` qui renvoient une\ncollection.\n"
        },
        {
          "text": "`null` pour le second, car `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, et accepte le sélecteur `#titre`.\n"
        },
        {
          "text": "Le même élément, dans les deux cas",
          "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": "Un élément pour le second seulement, le premier ne ciblant que les classes",
          "correct": false,
          "feedback": "Erreur : `getElementById` cible bien un\nidentifiant (pas une classe), et renvoie donc\nici l'élément. `querySelector`, lui, 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."
    }
  ]
}