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

Tableau de score

Plusieurs compteurs côte à côte pour un match ou un jeu de société, avec des noms modifiables et le meneur mis en avant. Le bon exercice pour gérer une liste d'objets plutôt qu'une seule valeur.

HTMLCSSJS
Ce qu'on apprend
  • Décrire l'état avec un tableau d'objets
  • Recalculer tout l'affichage à partir des données
  • Trouver le meneur et gérer l'égalité
Pourquoi ce langage

Pourquoi un tableau de score ? Au ping-pong, aux cartes ou au Mölkky, on finit toujours par compter sur un bout de papier. Ici, chaque joueur a son nom, son score, ses boutons.

Le vrai changement, c'est l'état : ce n'est plus un nombre, c'est un tableau d'objets. Ajouter un joueur, c'est ajouter un objet. Après chaque action, on redessine tout à partir de ce tableau, et le joueur en tête change de couleur.

Pourquoi en JavaScript simple ? Un tableau, une boucle qui crée les cartes, un calcul de maximum. C'est le premier pas vers ce que font React ou Vue.

Comment ça marche
  1. L'ÉTAT

    Un tableau d'objets : chaque joueur porte son nom et son score.

    const joueurs = [
      { nom: 'Rouge', score: 0 },
      { nom: 'Bleu', score: 0 },
    ];
  2. LE MENEUR

    S'il y a plusieurs joueurs au même score maximum, personne ne mène.

    function meneur() {
      const max = Math.max(...joueurs.map(j => j.score));
      const enTete = joueurs.filter(j => j.score === max);
      return enTete.length === 1 && max > 0 ? enTete[0] : null;
    }
  3. DESSINER

    On reconstruit la grille à partir des données, à chaque changement.

    function rendre() {
      const tete = meneur();
      const grille = document.getElementById('grille');
      grille.innerHTML = '';
      joueurs.forEach((j, i) => {
        const carte = document.createElement('div');
        carte.className = 'joueur' + (j === tete ? ' tete' : '');
        carte.innerHTML = '<input aria-label="Nom"><b></b><button data-d="-1">−</button><button data-d="1">+</button>';
        carte.querySelector('input').value = j.nom;      // value et textContent : pas d'injection de HTML
        carte.querySelector('b').textContent = j.score;
        carte.dataset.i = i;
        grille.appendChild(carte);
      });
    }
  4. UN SEUL ÉCOUTEUR

    document.getElementById('grille').addEventListener('click', e => {
      const b = e.target.closest('[data-d]');
      if (!b) return;
      const j = joueurs[b.closest('.joueur').dataset.i];
      j.score = Math.max(0, j.score + Number(b.dataset.d));
      rendre();
    });
  5. AJOUTER UN JOUEUR

    document.getElementById('ajout').addEventListener('click', () => {
      joueurs.push({ nom: 'Joueur ' + (joueurs.length + 1), score: 0 });
      rendre();
    });
Pièges

PIÈGE 1 : Mettre le nom dans innerHTML

Piège

Mauvais : '<b>' + j.nom + '</b>'. Un nom comme <img onerror=...> exécute du code.

Fix

Correct : créer la structure, puis remplir avec textContent ou value.

PIÈGE 2 : Perdre le focus à chaque clic

Piège

Mauvais : tout redessiner sans y penser. Au clavier, le focus disparaît après chaque point.

Fix

Correct : remettre le focus sur le bouton équivalent après le rendu, ou ne mettre à jour que la carte concernée.

PIÈGE 3 : Un meneur en cas d'égalité

Piège

Mauvais : prendre le premier joueur qui a le score maximum. À 3 partout, Rouge passe en tête sans raison.

Fix

Correct : compter combien de joueurs ont le maximum, et n'afficher un meneur que s'il est seul.

PERSONNALISER
  • Un score cible : la partie s'arrête quand un joueur atteint 21 ou 50.
  • Un historique des manches, avec le total de chacun.
  • Des couleurs par joueur, choisies dans la palette du site.
OPTIMISATION
  • Gardez la partie dans localStorage pour la reprendre après une coupure, comme le compteur qui se souvient.
  • Avec beaucoup de joueurs, ne redessinez que la carte modifiée au lieu de toute la grille.
VERS REACT

Le tableau vit dans un useState, et chaque modification crée un nouveau tableau avec map.

Chaque joueur devient un composant Carte, avec une key stable comme un id, pas l'index.

Le meneur se calcule pendant le rendu, sans état séparé.

Commentaires
0/1000

Chargement des commentaires…