Liste de tâches
Une liste de tâches : ajouter, cocher, supprimer. Le classique qui oblige à séparer les données de l'affichage.
- Garder les tâches dans un tableau d'objets
- Redessiner la liste à partir des données
- Écouter tous les clics avec un seul écouteur
Pourquoi une liste de tâches ? C'est le premier vrai petit logiciel : on ajoute, on coche, on supprime. Une grande partie du web n'est qu'une variation de ces trois gestes.
La règle d'or : les données d'abord. On garde un tableau de tâches, et on redessine la liste à partir de lui. Jamais l'inverse.
Pourquoi en JavaScript simple ? Un tableau, une fonction d'affichage, deux écouteurs. On voit exactement ce que React fera plus tard à votre place.
LE HTML
Un formulaire pour ajouter, une liste vide que le script remplit.
<form id="ajout"> <input id="texte" placeholder="Nouvelle tâche" required> <button>Ajouter</button> </form> <ul id="liste"></ul>
L'ÉTAT
Une seule source de vérité : le tableau. La page n'en est que le reflet.
let taches = [];
AFFICHER
On vide la liste et on la reconstruit depuis le tableau. textContent protège des balises tapées par l'utilisateur.
function afficher() { const ul = document.getElementById('liste'); ul.innerHTML = ''; taches.forEach(t => { const li = document.createElement('li'); li.dataset.id = t.id; li.innerHTML = '<label><input type="checkbox"><span></span></label><button>Supprimer</button>'; li.querySelector('span').textContent = t.texte; li.querySelector('input').checked = t.faite; ul.append(li); }); }AJOUTER
preventDefault empêche le formulaire de recharger la page.
document.getElementById('ajout').addEventListener('submit', e => { e.preventDefault(); const champ = document.getElementById('texte'); taches.push({ id: Date.now(), texte: champ.value.trim(), faite: false }); champ.value = ''; afficher(); });COCHER ET SUPPRIMER
Un seul écouteur sur la liste suffit : c'est la délégation d'événements.
document.getElementById('liste').addEventListener('click', e => { const li = e.target.closest('li'); if (!li) return; const id = Number(li.dataset.id); if (e.target.matches('input')) { taches = taches.map(t => t.id === id ? { ...t, faite: !t.faite } : t); afficher(); } else if (e.target.matches('button')) { taches = taches.filter(t => t.id !== id); afficher(); } });
PIÈGE 1 : Injecter le texte avec innerHTML
Mauvais : li.innerHTML = t.texte. Une tâche nommée <img src=x onerror=alert(1)> exécute du code dans la page.
Correct : textContent pour tout ce qui vient de l'utilisateur.
PIÈGE 2 : Un écouteur par tâche
Mauvais : un addEventListener sur chaque case, à chaque affichage. Ils s'empilent et la mémoire grossit.
Correct : un seul écouteur sur la liste, et e.target pour savoir où on a cliqué.
PIÈGE 3 : Modifier la page au lieu des données
Mauvais : barrer le texte directement dans le DOM. Au prochain affichage, la modification disparaît.
Correct : changer le tableau, puis appeler afficher().
- Un compteur « 3 tâches restantes » avec taches.filter(t => !t.faite).length.
- Trois filtres, Toutes, À faire et Faites, appliqués juste avant l'affichage.
- Un double clic sur une tâche qui la transforme en champ texte pour la renommer.
- Pour des centaines de tâches, ne recréez que la ligne qui change au lieu de toute la liste.
- Rangez le tableau dans localStorage à chaque modification : c'est la variante « Liste de tâches qui se souvient ».
taches devient un useState, et afficher() disparaît : React redessine tout seul.
Chaque <li> reçoit key={t.id}, jamais l'index du tableau.
On ne modifie jamais le tableau : on en crée un nouveau avec map et filter, exactement comme ici.
Chargement des commentaires…