Aller au contenu
← 1001 Widgets
N° 046 · ●●○

Quiz chronométré

Cinq questions tirées d'un fichier JSON, quinze secondes pour chacune, et des points en plus pour les plus rapides. Le bon exercice pour séparer les données de l'interface et gérer un compte à rebours précis.

JS
Ce qu'on apprend
  • Charger des questions depuis un JSON
  • Mesurer le temps restant avec performance.now
  • Mélanger un tableau avec l'algorithme de Fisher-Yates
Pourquoi ce langage

Pourquoi un quiz chronométré ? Le quiz de base laisse tout le temps de réfléchir. Avec un chrono, il devient un jeu : on répond vite, et la vitesse rapporte des points.

Les questions ne sont plus écrites dans le code. Elles vivent dans un fichier JSON : on peut en ajouter, les traduire ou les faire écrire par quelqu'un d'autre sans toucher au JavaScript. À chaque partie, on en tire cinq dans un ordre différent.

Le temps restant se calcule à partir de l'heure de départ, et une barre se vide en continu. Au bout de quinze secondes, la question est comptée comme ratée et la bonne réponse s'affiche avec une explication.

Pourquoi en JavaScript simple ? Un fetch, un mélange, une boucle d'animation. Le clavier marche aussi : A, B, C, D ou 1 à 4.

Comment ça marche
  1. LES QUESTIONS DANS UN FICHIER

    questions.json : [{ "q": "...", "r": ["...", "..."], "b": 1, "e": "explication" }]

    const questions = await fetch('/questions.json').then(r => r.json());
  2. MÉLANGER SANS BIAIS

    Fisher-Yates : chaque ordre a exactement la même chance.

    function melanger(tab) {
      const a = [...tab];
      for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
      }
      return a;
    }
    const partie = melanger(questions).slice(0, 5);
  3. LE CHRONO

    On note le départ, et on calcule le reste à chaque image.

    const DUREE = 15;
    let debut, raf;
    const reste = () => Math.max(0, DUREE - (performance.now() - debut) / 1000);
    function tic() {
      const r = reste();
      barre.style.transform = `scaleX(${r / DUREE})`;
      chiffre.textContent = Math.ceil(r);
      if (r <= 0) return repondre(-1);         // temps écoulé
      raf = requestAnimationFrame(tic);
    }
  4. RÉPONDRE

    Bonne réponse : 100 points, plus 10 par seconde restante.

    function repondre(choix) {
      cancelAnimationFrame(raf);
      const q = partie[index], ok = choix === q.b;
      score += ok ? 100 + Math.round(reste() * 10) : 0;
      montrerCorrection(q, choix);
    }
  5. LANCER UNE QUESTION

    function poser() {
      afficherQuestion(partie[index]);
      debut = performance.now();
      tic();
    }
Pièges

PIÈGE 1 : Un mélange biaisé

Piège

Mauvais : tab.sort(() => Math.random() - 0.5). Certains ordres sortent bien plus souvent que d'autres.

Fix

Correct : l'algorithme de Fisher-Yates, qui échange chaque case avec une case tirée avant elle.

PIÈGE 2 : Oublier d'arrêter le chrono

Piège

Mauvais : répondre sans annuler l'animation. Le chrono atteint zéro plus tard et compte une deuxième réponse.

Fix

Correct : cancelAnimationFrame dès la réponse, et désactiver les boutons.

PIÈGE 3 : Mettre la question dans innerHTML

Piège

Mauvais : innerHTML = q.q. Une question sur la balise <title> casse l'affichage, et un fichier piégé peut injecter du code.

Fix

Correct : textContent pour tout ce qui vient du fichier.

PERSONNALISER
  • Des catégories, choisies au début de la partie.
  • Un mode « mort subite » : la première erreur termine la partie.
  • Les meilleurs scores, enregistrés comme dans le Snake avec classement.
OPTIMISATION
  • Validez le fichier JSON au chargement : une question sans bonne réponse doit être ignorée, pas faire planter le jeu.
  • Chargez les questions une fois et gardez-les en mémoire pour les parties suivantes.
VERS REACT

Les questions se chargent dans un useEffect ou un composant serveur avec Next.js.

L'index, le score et les réponses vivent dans un useReducer : chaque action, répondre ou passer, est un cas clair.

Le chrono reste dans un useRef pour ne pas provoquer soixante rendus par seconde.

Commentaires
0/1000

Chargement des commentaires…