{
  "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.",
    "parties": [
      {
        "id": "javascript",
        "title": "Le langage JavaScript"
      },
      {
        "id": "dom",
        "title": "Le DOM : sélection et modification"
      },
      {
        "id": "evenements",
        "title": "Événements et mise au point"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Rôle de JavaScript",
      "partie": "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)."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "integration"
      ],
      "title": "Intégrer du JavaScript",
      "partie": "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\nl'attribut `src`, qui indique le fichier\nà charger.\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": "Deux variantes : script externe (`src`) ou\nscript écrit directement entre\n`<script>` et `</script>`. Le script externe\nfacilite la maintenance, car le même\nfichier sert à plusieurs pages."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "variable"
      ],
      "title": "Déclaration de variable",
      "partie": "javascript",
      "statement": "Quel mot-clé moderne déclare une variable\n**modifiable** en JavaScript ?",
      "options": [
        {
          "text": "`var`",
          "correct": false,
          "feedback": "Erreur : `var` déclare aussi une\nvariable, mais c'est l'ancien mot-clé ;\nla forme moderne est `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 dont on peut ensuite modifier\nla valeur.\n"
        },
        {
          "text": "`int`",
          "correct": false,
          "feedback": "Erreur : JS n'est pas typé statiquement.\n"
        }
      ],
      "explanation": "Deux mots-clés modernes : `let` pour une\nvariable modifiable, `const` pour une\nvaleur qu'on ne réaffecte pas."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "console-log"
      ],
      "title": "Afficher dans la console",
      "partie": "evenements",
      "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",
      "partie": "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",
      "partie": "dom",
      "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 : `document` ne possède pas de\nméthode `find`.\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",
      "partie": "dom",
      "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), on utilise `innerHTML`.\n"
        },
        {
          "text": "`el.changeText(\"...\")`",
          "correct": false,
          "feedback": "Erreur : méthode inexistante.\n"
        }
      ],
      "explanation": "`textContent` affiche un texte tel quel,\nbalises comprises ; `innerHTML` interprète\nles balises contenues dans la chaîne."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "arbre-dom"
      ],
      "title": "DOM comme arbre",
      "partie": "dom",
      "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",
      "partie": "evenements",
      "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",
      "partie": "javascript",
      "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.",
      "variants": [
        {
          "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."
        },
        {
          "statement": "Que fait ce code ?\n```javascript\nlet x = 7;\nx = x + 4;\nconsole.log(x);\n```",
          "options": [
            {
              "text": "Erreur",
              "correct": false,
              "feedback": "Erreur : code valide.\n"
            },
            {
              "text": "Affiche `74` (concaténation)",
              "correct": false,
              "feedback": "Erreur : 4 est un nombre, pas une chaîne,\ndonc addition arithmétique.\n"
            },
            {
              "text": "Affiche `7`",
              "correct": false,
              "feedback": "Erreur : on additionne 4.\n"
            },
            {
              "text": "Affiche `11`",
              "correct": true,
              "feedback": "Bonne réponse : x devient 7 + 4 = 11, puis\non l'affiche.\n"
            }
          ],
          "explanation": "Attention au piège de l'addition vs\nconcaténation : `7 + \"4\"` = `\"74\"` en JS\n(coercition de type). C'est une source\nclassique de bugs."
        },
        {
          "statement": "Que fait ce code ?\n```javascript\nlet x = 10;\nx = x + 5;\nconsole.log(x);\n```",
          "options": [
            {
              "text": "Erreur",
              "correct": false,
              "feedback": "Erreur : code valide.\n"
            },
            {
              "text": "Affiche `105` (concaténation)",
              "correct": false,
              "feedback": "Erreur : 5 est un nombre, pas une chaîne,\ndonc addition arithmétique.\n"
            },
            {
              "text": "Affiche `10`",
              "correct": false,
              "feedback": "Erreur : on additionne 5.\n"
            },
            {
              "text": "Affiche `15`",
              "correct": true,
              "feedback": "Bonne réponse : x devient 10 + 5 = 15, puis\non l'affiche.\n"
            }
          ],
          "explanation": "Attention au piège de l'addition vs\nconcaténation : `10 + \"5\"` = `\"105\"` en JS\n(coercition de type). C'est une source\nclassique de bugs."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "querySelector"
      ],
      "title": "querySelector",
      "partie": "dom",
      "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 : elle ne sait\npas quel bouton a été cliqué.\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)` renvoie le premier\nélément correspondant au sélecteur ;\n`querySelectorAll(sel)` les renvoie tous."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "innerHTML"
      ],
      "title": "textContent vs innerHTML",
      "partie": "dom",
      "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 : l'une affiche les balises telles\nquelles, l'autre les interprète.\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 : la vitesse n'est pas ce qui\ndistingue les deux propriétés ;\n`innerHTML` interprète les balises,\n`textContent` non.\n"
        }
      ],
      "explanation": "Si le contenu vient de l'utilisateur, on\npréfère `textContent` : une balise tapée\ndans un champ s'affiche alors telle quelle\nau lieu d'être interprétée par le\nnavigateur."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "conditions"
      ],
      "title": "Conditions",
      "partie": "javascript",
      "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",
      "partie": "javascript",
      "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": "La même boucle s'écrit en Python\n`for i in range(5):`. Les deux langages\nrépètent cinq fois le corps, mais JavaScript\nécrit explicitement l'initialisation, la\ncondition et l'incrément."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "fonction"
      ],
      "title": "Définir une fonction",
      "partie": "javascript",
      "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\ndef carre(x) { return x * x; }\n```\n",
          "correct": false,
          "feedback": "Erreur : `def` est le mot-clé de Python.\nEn JavaScript, une fonction se déclare\navec `function` ; ici, 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": "Comparaison avec Python : `def carre(x):`\nsuivi du bloc indenté `return x * x`. Les\ndeux langages utilisent `return`, mais\nJavaScript délimite le bloc par des\naccolades et Python par l'indentation."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "modifier-attribut"
      ],
      "title": "Modifier un attribut",
      "partie": "dom",
      "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",
      "partie": "dom",
      "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": [
        "evenements"
      ],
      "title": "Réagir à chaque caractère tapé",
      "partie": "evenements",
      "statement": "Une page contient le champ `<input id=\"pseudo\">`. On veut que la\nfonction `verifier` s'exécute à chaque caractère tapé dans ce\nchamp, sans attendre aucun clic :\n\n```javascript\ndocument.getElementById(\"pseudo\").addEventListener(???, verifier);\n```\n\nQuel nom d'événement faut-il écrire à la place de `???` ?",
      "options": [
        {
          "text": "`\"click\"`",
          "correct": false,
          "feedback": "Erreur : `click` ne se déclenche que\nlorsqu'on clique sur le champ. Taper des\ncaractères ensuite ne rappelle pas la\nfonction.\n"
        },
        {
          "text": "`\"input\"`",
          "correct": true,
          "feedback": "Bonne réponse : l'événement `input` se\ndéclenche à chaque modification du\ncontenu du champ, donc à chaque caractère\ntapé ou effacé.\n"
        },
        {
          "text": "`\"mouseover\"`",
          "correct": false,
          "feedback": "Erreur : `mouseover` se déclenche quand\nle pointeur de la souris passe au-dessus\ndu champ, que l'utilisateur tape ou non.\n"
        },
        {
          "text": "`\"submit\"`",
          "correct": false,
          "feedback": "Erreur : `submit` concerne un formulaire\nentier, au moment de son envoi. Il ne se\nproduit pas à chaque caractère tapé.\n"
        }
      ],
      "explanation": "Un même composant peut réagir à plusieurs\névénements : `click` pour un clic,\n`mouseover` pour un survol, `input` pour une\nmodification du contenu d'un champ, `submit`\npour l'envoi d'un formulaire. Choisir\nl'événement, c'est choisir le moment où la\nfonction s'exécute."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "tableau"
      ],
      "title": "Tableau JavaScript",
      "partie": "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",
      "partie": "evenements",
      "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": "Erreur : une variable globale ne dit pas\nd'elle-même quel élément a déclenché\nl'événement ; il faudrait la mettre à jour\nà la main, ce qui est fragile.\n"
        },
        {
          "text": "Avec la propriété `event.target`",
          "correct": true,
          "feedback": "Bonne réponse : avec\n`function afficher(e) { console.log(e.target); }`\npuis `btn.addEventListener(\"click\", afficher);`,\nla console affiche l'élément qui a déclenché\nl'événement.\n"
        }
      ],
      "explanation": "L'objet `event` est passé automatiquement\nau gestionnaire. Deux propriétés utiles :\n`event.target` (l'élément qui a déclenché\nl'événement) et `event.type` (le type\nd'événement, par exemple `\"click\"`)."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "debug-console"
      ],
      "title": "Débogage",
      "partie": "evenements",
      "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": "Astuce : `console.log(...)` affiche la valeur\nd'une variable dans la console des outils\ndéveloppeur, ce qui permet de suivre\nl'exécution du script pas à pas."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "synchronisation-dom"
      ],
      "title": "DOM pas encore prêt",
      "partie": "dom",
      "statement": "Mon script, placé dans `<head>`,\nfait `document.getElementById(\"menu\")` mais\nobtient `null`. L'élément `id=\"menu\"` existe\nbien plus bas dans le `<body>`, sans faute de\nfrappe. Pourquoi ?",
      "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 la lecture du `<body>`",
          "correct": true,
          "feedback": "Bonne réponse : le navigateur lit la page de\nhaut en bas et exécute le script dès qu'il le\nrencontre dans `<head>`. À ce moment,\nl'élément `menu`, placé plus bas, n'existe pas\nencore. Il suffit de placer le `<script>` à la\nfin du `<body>`.\n"
        },
        {
          "text": "Le navigateur est buggé",
          "correct": false,
          "feedback": "Erreur : tous les navigateurs se comportent\nainsi ; la cause est l'ordre de lecture de la\npage.\n"
        }
      ],
      "explanation": "Le navigateur construit la page au fur et à\nmesure de sa lecture : un script ne peut\natteindre que les éléments déjà lus. Placer\nle `<script>` juste avant `</body>` garantit\nque tous les éléments existent."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "trace",
        "evenement-clic"
      ],
      "title": "Un score remis à zéro",
      "partie": "javascript",
      "statement": "Une page associe au bouton `<button id=\"jouer\">` la fonction\nsuivante, qui écrit dans l'élément `score` :\n```javascript\nlet n = 0;\nfunction clic() {\n  n = n + 2;\n  if (n > 4) {\n    n = 0;\n  }\n  document.getElementById(\"score\").textContent = n;\n}\ndocument.getElementById(\"jouer\").addEventListener(\"click\", clic);\n```\nQu'affiche l'élément `score` après **cinq** clics sur le bouton ?",
      "options": [
        {
          "text": "`10`",
          "correct": false,
          "feedback": "Erreur : ce total ignore le `if`. Au\ntroisième clic, `n` passe à 6, dépasse 4\net revient à 0 ; l'affichage ne dépasse\ndonc jamais 4.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : la remise à zéro n'est pas\ndéfinitive. Après le troisième clic, `n`\nvaut 0, puis les clics suivants l'augmentent à nouveau de 2.\n"
        },
        {
          "text": "`2`",
          "correct": false,
          "feedback": "Erreur : un clic manque dans le décompte.\nLes valeurs affichées sont successivement\n2, 4, 0, 2 puis 4.\n"
        },
        {
          "text": "`4`",
          "correct": true,
          "feedback": "Bonne réponse : les cinq clics affichent\nsuccessivement 2, 4, 0, 2, 4 (la remise à zéro survient dès que\n`n` dépasse 4). La variable `n`, déclarée hors de\nla fonction, garde sa valeur d'un clic à\nl'autre.\n"
        }
      ],
      "explanation": "Pour analyser une fonction exécutée lors\nd'un clic, on déroule chaque appel en notant\nla valeur des variables. Une variable\ndéclarée en dehors de la fonction garde sa\nvaleur entre deux clics, ce qui permet de\nconstruire un compteur.",
      "variants": [
        {
          "statement": "Une page associe au bouton `<button id=\"jouer\">` la fonction\nsuivante, qui écrit dans l'élément `score` :\n```javascript\nlet n = 0;\nfunction clic() {\n  n = n + 2;\n  if (n > 4) {\n    n = 0;\n  }\n  document.getElementById(\"score\").textContent = n;\n}\ndocument.getElementById(\"jouer\").addEventListener(\"click\", clic);\n```\nQu'affiche l'élément `score` après **cinq** clics sur le bouton ?",
          "options": [
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : ce total ignore le `if`. Au\ntroisième clic, `n` passe à 6, dépasse 4\net revient à 0 ; l'affichage ne dépasse\ndonc jamais 4.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : la remise à zéro n'est pas\ndéfinitive. Après le troisième clic, `n`\nvaut 0, puis les clics suivants l'augmentent à nouveau de 2.\n"
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : un clic manque dans le décompte.\nLes valeurs affichées sont successivement\n2, 4, 0, 2 puis 4.\n"
            },
            {
              "text": "`4`",
              "correct": true,
              "feedback": "Bonne réponse : les cinq clics affichent\nsuccessivement 2, 4, 0, 2, 4 (la remise à zéro survient dès que\n`n` dépasse 4). La variable `n`, déclarée hors de\nla fonction, garde sa valeur d'un clic à\nl'autre.\n"
            }
          ],
          "explanation": "Pour analyser une fonction exécutée lors\nd'un clic, on déroule chaque appel en notant\nla valeur des variables. Une variable\ndéclarée en dehors de la fonction garde sa\nvaleur entre deux clics, ce qui permet de\nconstruire un compteur."
        },
        {
          "statement": "Une page associe au bouton `<button id=\"jouer\">` la fonction\nsuivante, qui écrit dans l'élément `score` :\n```javascript\nlet n = 0;\nfunction clic() {\n  n = n + 3;\n  if (n > 7) {\n    n = 0;\n  }\n  document.getElementById(\"score\").textContent = n;\n}\ndocument.getElementById(\"jouer\").addEventListener(\"click\", clic);\n```\nQu'affiche l'élément `score` après **cinq** clics sur le bouton ?",
          "options": [
            {
              "text": "`15`",
              "correct": false,
              "feedback": "Erreur : ce total ignore le `if`. Au\ntroisième clic, `n` passe à 9, dépasse 7\net revient à 0 ; l'affichage ne dépasse\ndonc jamais 7.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : la remise à zéro n'est pas\ndéfinitive. Après le troisième clic, `n`\nvaut 0, puis les clics suivants l'augmentent à nouveau de 3.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : un clic manque dans le décompte.\nLes valeurs affichées sont successivement\n3, 6, 0, 3 puis 6.\n"
            },
            {
              "text": "`6`",
              "correct": true,
              "feedback": "Bonne réponse : les cinq clics affichent\nsuccessivement 3, 6, 0, 3, 6 (la remise à zéro survient dès que\n`n` dépasse 7). La variable `n`, déclarée hors de\nla fonction, garde sa valeur d'un clic à\nl'autre.\n"
            }
          ],
          "explanation": "Pour analyser une fonction exécutée lors\nd'un clic, on déroule chaque appel en notant\nla valeur des variables. Une variable\ndéclarée en dehors de la fonction garde sa\nvaleur entre deux clics, ce qui permet de\nconstruire un compteur."
        },
        {
          "statement": "Une page associe au bouton `<button id=\"jouer\">` la fonction\nsuivante, qui écrit dans l'élément `score` :\n```javascript\nlet n = 0;\nfunction clic() {\n  n = n + 4;\n  if (n > 10) {\n    n = 0;\n  }\n  document.getElementById(\"score\").textContent = n;\n}\ndocument.getElementById(\"jouer\").addEventListener(\"click\", clic);\n```\nQu'affiche l'élément `score` après **cinq** clics sur le bouton ?",
          "options": [
            {
              "text": "`20`",
              "correct": false,
              "feedback": "Erreur : ce total ignore le `if`. Au\ntroisième clic, `n` passe à 12, dépasse 10\net revient à 0 ; l'affichage ne dépasse\ndonc jamais 10.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : la remise à zéro n'est pas\ndéfinitive. Après le troisième clic, `n`\nvaut 0, puis les clics suivants l'augmentent à nouveau de 4.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : un clic manque dans le décompte.\nLes valeurs affichées sont successivement\n4, 8, 0, 4 puis 8.\n"
            },
            {
              "text": "`8`",
              "correct": true,
              "feedback": "Bonne réponse : les cinq clics affichent\nsuccessivement 4, 8, 0, 4, 8 (la remise à zéro survient dès que\n`n` dépasse 10). La variable `n`, déclarée hors de\nla fonction, garde sa valeur d'un clic à\nl'autre.\n"
            }
          ],
          "explanation": "Pour analyser une fonction exécutée lors\nd'un clic, on déroule chaque appel en notant\nla valeur des variables. Une variable\ndéclarée en dehors de la fonction garde sa\nvaleur entre deux clics, ce qui permet de\nconstruire un compteur."
        }
      ]
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "client-serveur",
        "verification"
      ],
      "title": "Une vérification refaite par le serveur",
      "partie": "javascript",
      "statement": "Sur un site d'inscription, une fonction JavaScript vérifie que\nle mot de passe choisi compte au moins 8 caractères et bloque\nl'envoi du formulaire sinon. Le développeur fait pourtant refaire\nla même vérification par le programme du serveur. Pourquoi ?",
      "options": [
        {
          "text": "Parce que JavaScript ne sait pas compter les caractères d'une chaîne",
          "correct": false,
          "feedback": "Erreur : JavaScript compte sans difficulté\nles caractères d'une chaîne, avec\n`length`. La raison tient à l'endroit où\nle code s'exécute.\n"
        },
        {
          "text": "Parce que le script s'exécute chez le client, qui peut le contourner",
          "correct": true,
          "feedback": "Bonne réponse : le script est envoyé au\nnavigateur et s'exécute sur la machine de\nl'utilisateur, qui peut le modifier ou\nenvoyer une requête sans passer par la\npage. Seul le serveur maîtrise son propre\nprogramme.\n"
        },
        {
          "text": "Parce que le serveur exécute le script de la page avant de l'envoyer",
          "correct": false,
          "feedback": "Erreur : le serveur envoie le script au\nnavigateur sans l'exécuter ; c'est le\nclient qui l'exécute. La vérification du\nserveur est un programme distinct.\n"
        },
        {
          "text": "Parce que le formulaire est envoyé deux fois, une fois par vérification",
          "correct": false,
          "feedback": "Erreur : le formulaire n'est envoyé\nqu'une fois. Le navigateur vérifie avant\nl'envoi, puis le serveur vérifie à la\nréception.\n"
        }
      ],
      "explanation": "Une vérification en JavaScript rend service\nà l'utilisateur, qui voit son erreur tout de\nsuite, sans attendre le serveur. Mais seule\nune vérification exécutée sur le serveur\ngarantit les données qu'il enregistre."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "evenements",
      "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": "`textContent` interprète les balises contenues dans la chaîne affectée",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : `textContent`\naffiche la chaîne telle quelle, balises\ncomprises. C'est `innerHTML` qui interprète\nles balises.\n"
        },
        {
          "text": "Le DOM est une représentation arborescente de la page HTML",
          "correct": false,
          "feedback": "Vrai : structure clé du Web.\n"
        }
      ],
      "explanation": "Pour afficher un simple texte, on utilise\n`textContent` ; `innerHTML` sert quand la\nchaîne contient des balises à interpréter."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "html-javascript",
        "evenements"
      ],
      "title": "Ce qui décide de la réaction d'un bouton",
      "partie": "javascript",
      "statement": "Une page contient :\n\n```html\n<button id=\"valider\">Envoyer</button>\n<p id=\"message\"></p>\n<script src=\"page.js\"></script>\n```\n\net le fichier `page.js` contient :\n\n```javascript\nfunction valider() {\n  document.getElementById(\"message\").textContent = \"Merci !\";\n}\ndocument.getElementById(\"valider\").addEventListener(\"click\", valider);\n```\n\nQu'est-ce qui décide de ce qui se passe au clic sur le bouton ?",
      "options": [
        {
          "text": "Le texte `Envoyer` écrit entre les balises du bouton",
          "correct": false,
          "feedback": "Erreur : ce texte décrit seulement ce\nqu'affiche le bouton. Le remplacer par\n`OK` ne changerait rien à la réaction au\nclic.\n"
        },
        {
          "text": "La fonction du fichier `page.js` associée à l'événement `click`",
          "correct": true,
          "feedback": "Bonne réponse : le HTML décrit le\ncomposant, un bouton d'identifiant\n`valider` ; le JavaScript programme son\ncomportement en associant la fonction\n`valider` à l'événement `click`.\n"
        },
        {
          "text": "L'attribut `id=\"valider\"`, qui lance la fonction du même nom",
          "correct": false,
          "feedback": "Erreur : l'identifiant sert seulement à\nretrouver le bouton depuis le script. La\nfonction pourrait porter un autre nom :\nc'est `addEventListener` qui la relie au\nclic.\n"
        },
        {
          "text": "La balise `<button>`, qui envoie la page au serveur à chaque clic",
          "correct": false,
          "feedback": "Erreur : hors d'un formulaire, un bouton\nn'envoie rien au serveur. Sans le script,\nun clic sur ce bouton ne produirait aucun\neffet.\n"
        }
      ],
      "explanation": "Il faut distinguer la description d'un\ncomposant graphique, écrite en HTML, de son\ncomportement, programmé en JavaScript.\nModifier ce qui se passe au clic revient à\nmodifier la fonction associée à l'événement,\nsans toucher au HTML."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "classlist",
        "attributs"
      ],
      "title": "Modifier les classes d'un élément",
      "partie": "dom",
      "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\ndes éléments de la page ; cette ligne lève\nune erreur.\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",
      "partie": "dom",
      "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` attend le nom `titre` sans dièse",
          "correct": false,
          "feedback": "Erreur : `querySelector` attend un\nsélecteur CSS, et le dièse y désigne\njustement un identifiant. Sans dièse,\n`titre` viserait une balise `<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."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "python-javascript",
        "traits-communs",
        "boucle"
      ],
      "title": "Le même programme en Python et en JavaScript",
      "partie": "javascript",
      "statement": "Voici deux programmes.\n\n```python\ntotal = 0\nfor i in range(1, 4):\n    total = total + i\nprint(total)\n```\n\n```javascript\nlet total = 0;\nfor (let i = 1; i < 4; i++) {\n    total = total + i;\n}\nconsole.log(total);\n```\n\nQue peut-on dire ?",
      "options": [
        {
          "text": "Ils affichent tous deux `6` : mêmes constructions, seule la syntaxe diffère",
          "correct": true,
          "feedback": "Les deux boucles font prendre à `i` les valeurs\n$1$, $2$ et $3$.\n"
        },
        {
          "text": "Le programme JavaScript affiche `10`, car la condition `i < 4` fait entrer la valeur $4$ dans la boucle",
          "correct": false,
          "feedback": "`i < 4` est strict : la boucle s'arrête avant\n$4$, exactement comme `range(1, 4)`.\n"
        },
        {
          "text": "Le programme Python affiche `10`, car `range(1, 4)` parcourt les entiers de $1$ à $4$ inclus",
          "correct": false,
          "feedback": "`range(1, 4)` s'arrête avant $4$ ; la somme est\n$1 + 2 + 3 = 6$.\n"
        },
        {
          "text": "Seul le programme Python contient une vraie boucle ; JavaScript répète simplement l'addition trois fois",
          "correct": false,
          "feedback": "`for (let i = 1; i < 4; i++)` est bien une\nboucle ; elle s'écrit avec une initialisation,\nune condition et un pas.\n"
        }
      ],
      "explanation": "Affectation, boucle bornée, accumulateur, affichage :\nles constructions élémentaires sont les mêmes d'un\nlangage à l'autre. Ce qui change est la syntaxe :\naccolades et point-virgule en JavaScript,\nindentation et `range` en Python."
    },
    {
      "id": "q30",
      "difficulty": 1,
      "skills": [
        "python-javascript",
        "traits-particuliers",
        "syntaxe"
      ],
      "title": "Un trait particulier de JavaScript",
      "partie": "javascript",
      "statement": "Parmi ces affirmations, laquelle décrit un trait de\nJavaScript qui le distingue de Python ?",
      "options": [
        {
          "text": "Les blocs d'instructions sont délimités par des accolades, et non par l'indentation",
          "correct": true,
          "feedback": "En JavaScript, l'indentation est libre ; ce sont\n`{` et `}` qui marquent le début et la fin d'un\nbloc.\n"
        },
        {
          "text": "Les variables doivent être déclarées avec leur type, par exemple `int x`",
          "correct": false,
          "feedback": "JavaScript déclare avec `let` ou `const`, sans\ntype ; c'est en C ou en Java qu'on écrit `int x`.\n"
        },
        {
          "text": "Une fonction JavaScript ne peut pas renvoyer de valeur au programme qui l'appelle",
          "correct": false,
          "feedback": "`return` existe et joue le même rôle qu'en\nPython.\n"
        },
        {
          "text": "JavaScript ne possède pas de boucle `while`, seulement des boucles `for`",
          "correct": false,
          "feedback": "`while` existe en JavaScript avec la même logique\nqu'en Python.\n"
        }
      ],
      "explanation": "Les deux langages partagent variables, conditions,\nboucles et fonctions. JavaScript se distingue par sa\nsyntaxe (accolades, points-virgules, `let`,\n`function`) et par son terrain d'emploi, le\nnavigateur."
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "python-javascript",
        "division-entiere",
        "piege"
      ],
      "title": "Traduire une division entière",
      "partie": "javascript",
      "statement": "En Python, `n // 2` donne le quotient entier de `n`\npar $2$. Un élève traduit en JavaScript :\n\n```javascript\nlet n = 7;\nlet q = n // 2;\nconsole.log(q);\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`7`",
          "correct": true,
          "feedback": "En JavaScript, `//` commence un commentaire : la\nligne se lit `let q = n;` et `q` vaut $7$.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Ce serait le résultat voulu, obtenu avec\n`Math.floor(n / 2)` ; `//` n'est pas un opérateur\nen JavaScript.\n"
        },
        {
          "text": "`3.5`",
          "correct": false,
          "feedback": "`n / 2` donnerait $3{,}5$, mais ici la division\nn'est jamais exécutée : tout ce qui suit `//` est\nignoré.\n"
        },
        {
          "text": "Une erreur de syntaxe, car `//` n'existe pas en JavaScript",
          "correct": false,
          "feedback": "`//` existe, mais comme marque de commentaire ; le\nprogramme est valide et s'exécute.\n"
        }
      ],
      "explanation": "Un même symbole peut changer de sens d'un langage à\nl'autre : `//` est la division entière en Python et\nun commentaire en JavaScript. La traduction correcte\nest `let q = Math.floor(n / 2);`, qui affiche `3`."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "addEventListener",
        "fonction",
        "syntaxe"
      ],
      "title": "La fonction passée au gestionnaire",
      "partie": "evenements",
      "statement": "Quelle écriture déclenche la fonction `saluer` à chaque clic sur le\nbouton `btn` ?",
      "options": [
        {
          "text": "`btn.addEventListener(\"click\", saluer);`",
          "correct": true,
          "feedback": "Bonne réponse : on transmet la fonction elle-même, sans\nparenthèses. Le navigateur l'appellera à chaque clic.\n"
        },
        {
          "text": "`btn.addEventListener(\"click\", saluer());`",
          "correct": false,
          "feedback": "Erreur : avec les parenthèses, `saluer` est appelée une seule\nfois, tout de suite, et c'est son résultat qui est transmis.\n"
        },
        {
          "text": "`btn.addEventListener(saluer, \"click\");`",
          "correct": false,
          "feedback": "Erreur : l'ordre des arguments est inversé ; le nom de\nl'événement vient en premier.\n"
        },
        {
          "text": "`btn.click = saluer();`",
          "correct": false,
          "feedback": "Erreur : cette affectation appelle `saluer` immédiatement et ne\ncrée aucun gestionnaire.\n"
        }
      ],
      "explanation": "Un gestionnaire est une fonction donnée en argument, pas appelée :\nc'est l'événement qui l'appellera plus tard."
    },
    {
      "id": "q33",
      "difficulty": 3,
      "skills": [
        "addEventListener",
        "plusieurs-gestionnaires"
      ],
      "title": "Deux gestionnaires pour un même clic",
      "partie": "evenements",
      "statement": "Un bouton reçoit `addEventListener(\"click\", a)` puis\n`addEventListener(\"click\", b)`. Que se passe-t-il lors d'un clic ?",
      "options": [
        {
          "text": "`a` puis `b` sont appelées, dans l'ordre d'enregistrement",
          "correct": true,
          "feedback": "Bonne réponse : `addEventListener` ajoute un gestionnaire sans\nremplacer les précédents. Tous sont appelés, dans l'ordre où ils\nont été attachés.\n"
        },
        {
          "text": "Seule `b` est appelée, le second enregistrement remplaçant le premier",
          "correct": false,
          "feedback": "Erreur : le remplacement serait le comportement d'une affectation\n`onclick`, pas d'`addEventListener`.\n"
        },
        {
          "text": "Seule `a` est appelée, le second enregistrement étant refusé",
          "correct": false,
          "feedback": "Erreur : rien n'est refusé ; plusieurs gestionnaires peuvent\ncoexister.\n"
        },
        {
          "text": "Une erreur, un événement n'acceptant qu'un seul gestionnaire",
          "correct": false,
          "feedback": "Erreur : c'est précisément l'intérêt d'`addEventListener` que\nd'en accepter plusieurs.\n"
        }
      ],
      "explanation": "`addEventListener` empile les gestionnaires ; pour en retirer un, on\nutilise `removeEventListener`."
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "onclick",
        "addEventListener",
        "html"
      ],
      "title": "Gestionnaire dans le HTML ou dans le script",
      "partie": "evenements",
      "statement": "`<button onclick=\"valider()\">` et\n`btn.addEventListener(\"click\", valider)` déclenchent tous deux\n`valider` au clic. Pourquoi préfère-t-on la seconde écriture ?",
      "options": [
        {
          "text": "Elle sépare le script du HTML et accepte plusieurs gestionnaires",
          "correct": true,
          "feedback": "Bonne réponse : le HTML reste purement descriptif, le script\nregroupe toute la logique, et plusieurs fonctions peuvent écouter\nle même événement.\n"
        },
        {
          "text": "Elle est la seule qui fonctionne dans les navigateurs récents",
          "correct": false,
          "feedback": "Erreur : l'attribut `onclick` fonctionne toujours ; la question\nest d'organisation.\n"
        },
        {
          "text": "Elle rend le bouton plus rapide à réagir au clic",
          "correct": false,
          "feedback": "Erreur : les deux écritures réagissent aussi vite.\n"
        },
        {
          "text": "Elle évite d'avoir à définir la fonction `valider`",
          "correct": false,
          "feedback": "Erreur : dans les deux cas, la fonction doit exister.\n"
        }
      ],
      "explanation": "HTML pour le contenu, CSS pour la forme, JavaScript pour le\ncomportement : attacher les gestionnaires dans le script respecte\ncette séparation."
    },
    {
      "id": "q35",
      "difficulty": 1,
      "skills": [
        "dom",
        "style",
        "modification"
      ],
      "title": "Changer une couleur depuis JavaScript",
      "partie": "dom",
      "statement": "Que fait l'instruction\n`document.getElementById(\"titre\").style.color = \"red\";` ?",
      "options": [
        {
          "text": "Elle colore en rouge le texte de l'élément d'`id` `titre`",
          "correct": true,
          "feedback": "Bonne réponse : la propriété `style` d'un élément donne accès à\nses propriétés CSS, modifiables depuis le script.\n"
        },
        {
          "text": "Elle colore en rouge tous les titres de la page",
          "correct": false,
          "feedback": "Erreur : `getElementById` ne renvoie que l'élément portant cet\n`id`.\n"
        },
        {
          "text": "Elle ajoute la classe `red` à l'élément",
          "correct": false,
          "feedback": "Erreur : aucune classe n'est touchée ; la couleur est fixée\ndirectement.\n"
        },
        {
          "text": "Elle provoque une erreur, le style étant réservé au CSS",
          "correct": false,
          "feedback": "Erreur : JavaScript peut lire et écrire les styles d'un élément.\n"
        }
      ],
      "explanation": "Modifier `element.style` revient à ajouter un style en ligne ;\nchanger une classe est souvent préférable pour garder le CSS\ncentralisé."
    },
    {
      "id": "q36",
      "difficulty": 3,
      "skills": [
        "dom",
        "querySelectorAll",
        "boucle"
      ],
      "title": "Modifier tous les éléments d'une liste",
      "partie": "dom",
      "statement": "Sami veut mettre en rouge tous les `li` de la page. Pourquoi\n`document.querySelector(\"li\").style.color = \"red\";` ne suffit-il\npas ?",
      "options": [
        {
          "text": "Parce que `querySelector` ne renvoie que le premier `li`",
          "correct": true,
          "feedback": "Bonne réponse : `querySelectorAll(\"li\")` renvoie tous les\néléments correspondants ; une boucle applique ensuite le style à\nchacun.\n"
        },
        {
          "text": "Parce que les `li` ne peuvent pas être colorés depuis JavaScript",
          "correct": false,
          "feedback": "Erreur : tout élément peut recevoir un style depuis le script.\n"
        },
        {
          "text": "Parce que `style.color` n'accepte pas la valeur `red`",
          "correct": false,
          "feedback": "Erreur : `red` est une valeur de couleur valide.\n"
        },
        {
          "text": "Parce que `querySelector` ne fonctionne qu'avec des `id`",
          "correct": false,
          "feedback": "Erreur : `querySelector` accepte tout sélecteur CSS, ici le nom\nd'une balise.\n"
        }
      ],
      "explanation": "`querySelector` donne un élément, `querySelectorAll` une collection\nà parcourir : la différence se voit dès qu'on veut agir sur\nplusieurs éléments."
    }
  ]
}