Scoreboard
Several counters side by side for a match or a board game, with editable names and the leader highlighted. The right exercise to handle a list of objects rather than a single value.
- Describe the state with an array of objects
- Recompute the whole display from the data
- Find the leader and handle ties
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.
L'ÉTAT
Un tableau d'objets : chaque joueur porte son nom et son score.
const joueurs = [ { nom: 'Rouge', score: 0 }, { nom: 'Bleu', score: 0 }, ];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; }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); }); }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(); });AJOUTER UN JOUEUR
document.getElementById('ajout').addEventListener('click', () => { joueurs.push({ nom: 'Joueur ' + (joueurs.length + 1), score: 0 }); rendre(); });
PIÈGE 1 : Mettre le nom dans innerHTML
Mauvais : '<b>' + j.nom + '</b>'. Un nom comme <img onerror=...> exécute du code.
Correct : créer la structure, puis remplir avec textContent ou value.
PIÈGE 2 : Perdre le focus à chaque clic
Mauvais : tout redessiner sans y penser. Au clavier, le focus disparaît après chaque point.
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é
Mauvais : prendre le premier joueur qui a le score maximum. À 3 partout, Rouge passe en tête sans raison.
Correct : compter combien de joueurs ont le maximum, et n'afficher un meneur que s'il est seul.
- 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.
- 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.
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é.
Loading comments…