Afficher les tâches sans injecter de HTML risqué
Objectif : Créer et insérer une ligne de tâche à partir d’un objet, en affichant son titre comme du texte plutôt que comme du HTML interprétable.
Une tâche contient un titre que vous ne contrôlez pas forcément : il peut venir d’une saisie ou, plus tard, d’un chargement distant. Si vous le traitez comme du HTML, un titre contenant des balises peut modifier la page au lieu d’être simplement affiché. Vous allez construire une ligne de tâche avec les éléments prévus dans le modèle et insérer son titre comme du texte.
Dans la leçon précédente, vous avez sélectionné les éléments de l’interface et vérifié leur présence. Vous allez compléter ces références avec le modèle de tâche et les éléments nécessaires à son remplissage, puis créer une fonction de rendu qui reçoit un objet. Pour garder cette étape centrée sur le DOM, la démonstration appellera la fonction avec une tâche fixe : le raccordement aux données de l’application viendra ensuite.
Partir du modèle HTML
Le squelette contient déjà un élément template avec la structure d’une tâche : une case à cocher, un titre et un bouton de suppression. Son contenu n’est pas affiché directement dans la page. En le clonant, vous obtenez une nouvelle ligne qui respecte exactement cette structure, sans reconstruire manuellement chaque balise.
La fonction selectionnerElements() conserve les références essentielles déjà utilisées dans le projet. Vous la mettez à jour pour inclure le modèle, puis vous sélectionnez les éléments à remplir à l’intérieur de chaque copie. Les références internes se cherchent dans la ligne clonée, et non dans tout le document : chaque tâche reçoit ainsi son propre titre et sa propre case.
Construire et insérer une tâche
La fonction creerElementTache() reçoit un objet avec un identifiant, un titre et un état de réalisation. Elle clone le modèle, attribue le titre avec textContent, renseigne la case à cocher, puis renvoie la ligne créée. L’identifiant est placé sur l’élément de tâche prévu à cet effet : les leçons suivantes s’appuieront sur cette information pour traiter les actions.
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gestionnaire de tâches</title>
<link rel="stylesheet" href="style.css">
<script type="module" src="js/main.js"></script>
</head>
<body class="atelier-page">
<header class="atelier-entete">
<h1>Gestionnaire de tâches</h1>
<p>Organisez vos tâches et suivez votre progression.</p>
</header>
<main class="atelier-principal">
<section class="atelier-progression" aria-labelledby="titre-progression">
<h2 id="titre-progression">Progression</h2>
<p><span data-progression-texte>0 tâche terminée sur 0</span></p>
<progress class="atelier-progression__barre" data-progression max="100" value="0">0%</progress>
</section>
<form class="atelier-formulaire" data-formulaire>
<label for="nouvelle-tache">Nouvelle tâche</label>
<input class="atelier-champ" id="nouvelle-tache" name="titre" type="text" required>
<button class="atelier-bouton" type="submit">Ajouter</button>
</form>
<div class="atelier-actions">
<div class="atelier-filtres" role="group" aria-label="Filtrer les tâches">
<button class="atelier-bouton" type="button" data-filtre="toutes" aria-pressed="true">Toutes</button>
<button class="atelier-bouton" type="button" data-filtre="actives" aria-pressed="false">À faire</button>
<button class="atelier-bouton" type="button" data-filtre="terminees" aria-pressed="false">Terminées</button>
</div>
<button class="atelier-bouton" type="button" data-charger>Charger des tâches</button>
</div>
<p class="atelier-message" data-message role="status" aria-live="polite"></p>
<ul class="atelier-liste" data-liste aria-label="Liste des tâches"></ul>
<template data-modele-tache>
<li class="atelier-tache" data-id="">
<input type="checkbox" data-action="basculer">
<span data-titre></span>
<button class="atelier-bouton" type="button" data-action="supprimer">Supprimer</button>
</li>
</template>
</main>
</body>
</html>Dans afficherTache(), l’insertion se fait avec append() sur la liste. L’objet de démonstration contient un titre qui ressemble à une balise HTML : il doit apparaître littéralement dans la ligne, sans créer d’élément supplémentaire. Le code complet remplace le contenu précédent de js/main.js et garde le démarrage du module avec un contrôle des sélections.
export function selectionnerElements() {
const elements = {
formulaire: document.querySelector("[data-formulaire]"),
champ: document.querySelector("[name='titre']"),
liste: document.querySelector("[data-liste]"),
filtres: document.querySelectorAll("[data-filtre]"),
texteProgression: document.querySelector("[data-progression-texte]"),
barreProgression: document.querySelector("[data-progression]"),
modele: document.querySelector("[data-modele-tache]")
};
if (
!elements.formulaire ||
!elements.champ ||
!elements.liste ||
!elements.texteProgression ||
!elements.barreProgression ||
!elements.modele
) {
throw new Error("Un élément essentiel du gestionnaire est absent de la page.");
}
return elements;
}
export function creerElementTache(tache, modele) {
const fragment = modele.content.cloneNode(true);
const elementTache = fragment.querySelector(".atelier-tache");
const champTache = fragment.querySelector("[data-action='basculer']");
const titreTache = fragment.querySelector("[data-titre]");
if (!elementTache || !champTache || !titreTache) {
throw new Error("Le modèle de tâche ne contient pas les éléments attendus.");
}
elementTache.dataset.id = String(tache.id);
champTache.checked = Boolean(tache.terminee);
titreTache.textContent = String(tache.titre);
return fragment;
}
export function afficherTache(tache, elements) {
elements.liste.append(creerElementTache(tache, elements.modele));
}
export function afficherDemarrage() {
const elements = selectionnerElements();
console.info(`Module prêt : ${elements.filtres.length} filtres trouvés.`);
afficherTache(
{
id: "demo-1",
titre: "<img src=x onerror=alert('test')>",
terminee: false
},
elements
);
}
afficherDemarrage();Vérifier le résultat
Ouvrez la page dans le navigateur : la liste présente une tâche de démonstration et la console indique que les trois filtres ont été trouvés. Le titre affiché contient les caractères < et > ; ils restent visibles au lieu de produire une image. Vous avez ainsi une première fonction de construction réutilisable, que les prochaines leçons relieront aux interactions et aux données réelles du gestionnaire.
À retenir
- Clonez le modèle HTML pour conserver la structure prévue pour chaque tâche.
- Sélectionnez les éléments à remplir dans la copie de la tâche.
- Attribuez le titre avec textContent et insérez la ligne avec append().
- Gardez les données sous forme de texte : ne transformez pas un titre en HTML.