Question 1Juste après const li = document.createElement("li");, où se trouve li sur la page ?
Créer, modifier et supprimer des éléments — createElement / classList / remove
createElement et append pour ajouter des éléments, remove pour les retirer, classList et dataset pour modifier classes et attributs, et textContent pour afficher une saisie en texte brut.
Si tu affectes une valeur à innerHTML à chaque modification (ajouter un élément à une liste, en marquer un comme terminé, en supprimer un), tu reconstruis aussi les li que tu voulais garder. Et si tu affectes une saisie de l'utilisateur de la même façon, les balises qui s'y cachent, comme <a>, deviennent elles aussi des éléments.
Cet article montre comment créer et retirer des éléments, comment modifier classes et attributs avec classList et dataset, et comment insérer une saisie en texte brut.
Créer et ajouter des éléments — createElement et append
Tu veux ajouter « Envoyer la facture » à la fin d'une liste de tâches. Si tu concatènes une chaîne à innerHTML et que tu réaffectes le résultat, tous les li déjà présents sont eux aussi reconstruits en nouveaux éléments. Un li que tu avais rangé plus tôt dans une variable avec querySelector pointe toujours vers l'ancien élément, qui n'est plus sur la page : modifier son texte via cette variable ne change rien à l'affichage.
createElement (une méthode qui prend un nom de balise et crée un nouvel élément, placé nulle part dans l'arbre DOM) s'appelle comme document.createElement("li"). L'élément créé fait partie de la page dès que tu l'ajoutes à un élément parent avec append (une méthode qui ajoute l'élément passé en argument à la fin des enfants de l'élément sur lequel elle est appelée).
document.body.innerHTML = `<ul id="tasks"><li>Préparer le devis</li><li>Partager le compte rendu</li></ul>`;
const taskList = document.querySelector("#tasks");
// Créer un élément li et y mettre du texte (il n'est pas encore sur la page)
const newTask = document.createElement("li");
newTask.textContent = "Envoyer la facture";
console.log(newTask.outerHTML); // <li>Envoyer la facture</li>
console.log(document.querySelectorAll("li").length); // 2
// L'ajouter à la fin des enfants du ul le fait entrer dans la page
taskList.append(newTask);
console.log(document.querySelectorAll("li").length); // 3
console.log(taskList.outerHTML);
// <ul id="tasks"><li>Préparer le devis</li><li>Partager le compte rendu</li><li>Envoyer la facture</li></ul>
La variable newTask pointe vers le même élément li avant et après append. Si tu affectes une valeur à newTask.textContent après l'appel à append, c'est donc le texte du troisième li de la page qui change.
Marquer une tâche comme terminée — classList et dataset
Tu veux maintenant ajouter une classe done à une tâche terminée pour que le CSS l'affiche barrée. L'attribut class du li contient une seule chaîne, task urgent : si tu la remplaces par done, tu perds aussi la classe task, qui sert au CSS pour mettre en forme la ligne.
classList (un objet qui manipule les classes d'un élément mot par mot) propose add pour ajouter un mot, remove pour en retirer un, et toggle pour l'ajouter s'il manque ou le retirer s'il est là. Les attributs autres que class s'écrivent avec setAttribute(nom, valeur), et ceux qui commencent par data- passent par dataset (un objet qui lit et écrit les attributs data par leur nom).
document.body.innerHTML = `<ul><li class="task urgent" data-id="T-1">Préparer le devis</li><li class="task urgent" data-id="T-2">Envoyer la facture</li></ul>`;
const quote = document.querySelector('[data-id="T-1"]');
const invoice = document.querySelector('[data-id="T-2"]');
// classList ajoute et retire un mot à la fois (les autres classes restent)
quote.classList.add("done");
console.log(quote.outerHTML); // <li class="task urgent done" data-id="T-1">Préparer le devis</li>
quote.classList.toggle("urgent"); // C'est terminé : on enlève urgent (présent, donc toggle le retire)
console.log(quote.outerHTML); // <li class="task done" data-id="T-1">Préparer le devis</li>
// setAttribute remplace toute la valeur (utilisé sur class, il efface aussi task)
invoice.setAttribute("class", "done");
console.log(invoice.outerHTML); // <li class="done" data-id="T-2">Envoyer la facture</li>
// Les attributs qui commencent par data- s'écrivent via dataset (doneAt devient data-done-at)
invoice.dataset.doneAt = "10/09";
console.log(invoice.outerHTML);
// <li class="done" data-id="T-2" data-done-at="10/09">Envoyer la facture</li>
Le nom à utiliser avec dataset est le nom de l'attribut sans data-, avec une majuscule sur la lettre qui suit chaque tiret restant. data-id se lit et s'écrit avec dataset.id, data-done-at avec dataset.doneAt, et une affectation écrit l'attribut dans le HTML sous son nom d'origine.
dataset n'accepte pas les noms avec tiret
Si tu lis en gardant le tiret, comme dataset["done-at"], tu obtiens undefined au lieu d'une erreur, et la faute passe facilement inaperçue. Une affectation écrite ainsi lève une erreur sur cette ligne. L'erreur s'appelle SyntaxError, mais contrairement à une vraie faute de syntaxe dans ton code, elle se produit à l'exécution : les lignes précédentes s'exécutent donc quand même.
Retirer des éléments — remove et les références qui restent dans les variables
Cette fois, tu veux sortir une tâche terminée de la liste. Si tu réécris la liste avec innerHTML, tu dois aussi recopier dans la chaîne HTML les classes et attributs data ajoutés dans la section précédente, et s'il en manque un seul, une tâche que tu gardes perd son marquage « terminée ».
remove (une méthode qui détache l'élément sur lequel elle est appelée de son parent et le retire de l'arbre DOM) s'appelle sans argument sur l'élément à supprimer, comme doneItem.remove(). Le classList.remove("done") de la section précédente ne retire qu'un mot de l'attribut class : c'est une méthode différente de celle-ci, qui retire l'élément lui-même.
document.body.innerHTML = `<ul id="tasks"><li data-id="T-1">Préparer le devis</li><li data-id="T-2" class="done">Envoyer la facture</li><li data-id="T-3">Partager le compte rendu</li></ul>`;
const tasks = document.querySelectorAll("li");
// Trouver le li terminé et le retirer de l'arbre DOM
const doneItem = document.querySelector(".done");
doneItem.remove();
console.log(document.querySelectorAll("li").length); // 2
// L'élément retiré et la NodeList obtenue plus tôt restent tous deux dans leurs variables
console.log(doneItem.textContent); // Envoyer la facture
console.log(tasks.length); // 3 (les éléments trouvés à ce moment-là)
// Il est toujours dans la variable : append peut le remettre à la fin de la liste
document.querySelector("#tasks").append(doneItem);
console.log(document.querySelectorAll("li").length); // 3
querySelectorAll se contente de renvoyer les éléments trouvés au moment de l'appel ; il ne suit pas les modifications ultérieures du DOM. Le doneItem retiré reste lui aussi dans sa variable : tu peux toujours lire son texte ou le remettre dans la liste avec append.
Afficher une saisie comme du texte — textContent et innerHTML
Prends une section d'avis produit qui affiche les commentaires publiés par les utilisateurs. Les commentaires sont enregistrés, et la même chaîne s'affiche aussi sur la page des autres visiteurs. Si un commentaire contient une balise a (la balise qui crée un lien, avec la destination dans son attribut href), l'affecter à innerHTML transforme le faux lien glissé par son auteur en vrai lien sur la page.
Une chaîne affectée à innerHTML est analysée comme du HTML, et ses balises deviennent des éléments. Une chaîne affectée à textContent n'est pas analysée : même < y entre comme un simple caractère. Mettre une saisie dans innerHTML ne permet pas seulement de glisser de faux liens ; cela peut aussi ouvrir la porte au XSS (une faille où du code glissé par un utilisateur dans sa saisie s'exécute sur la page des autres visiteurs).
document.body.innerHTML = `<p class="review"></p>`;
const review = document.querySelector(".review");
// Une chaîne saisie par un utilisateur dans le champ d'avis (avec un faux lien dedans)
const reviewText = '<a href="https://example.com/login">Clique ici</a> pour continuer';
// Dans innerHTML, la balise a devient un vrai élément a
review.innerHTML = reviewText;
console.log(document.querySelectorAll("a").length); // 1
// Dans textContent, les balises entrent en texte brut
review.textContent = reviewText;
console.log(document.querySelectorAll("a").length); // 0
console.log(review.outerHTML);
// <p class="review"><a href="https://example.com/login">Clique ici</a> pour continuer</p>
- Son texte : « Clique ici »
- Un clic mène vers un autre site
- Suit l'élément a
<a href="…">Clique ici</a> pour continuer- 0 élément a
Les < et > d'outerHTML sont la façon dont le HTML écrit < et > en tant que texte. Relus avec textContent, ils redonnent les caractères d'origine. Le tableau ci-dessous classe les façons de modifier des éléments vues dans cet article selon ce qu'elles modifient.
| Ce que tu modifies | Écriture | Point d'attention |
|---|---|---|
| L'élément lui-même | createElement / append / remove | Absent de la page avant append |
| class | classList.add / remove / toggle | setAttribute remplace tout |
| Attributs data- | dataset.doneAt = "10/09" | Sans data-, majuscule après tiret |
| Autres attributs | setAttribute("href", "/tasks/T-2") | Ajoute, ou remplace la valeur |
| Texte du contenu | textContent | Pour les saisies utilisateur |
| HTML du contenu | innerHTML | Seulement ton propre HTML |
Le code onerror d'une balise img s'exécute
Un <script> inséré via innerHTML ne s'exécute pas. En revanche, avec une balise img (qui affiche une image) écrite <img src="x" onerror="…">, le code de onerror (un attribut qui contient du code exécuté quand le chargement échoue) s'exécute bel et bien. Que <script> ne s'exécute pas ne veut donc pas dire qu'on peut mettre une saisie dans innerHTML sans risque.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Si l'attribut class d'un li vaut task urgent, que vaut-il après setAttribute("class", "done") ?
Question 3Après p.textContent = "<b>Nouveau</b>";, combien d'éléments b y a-t-il dans p ?