Remplir une zone de la page avec plusieurs éléments tient parfois en une ligne. C'est la promesse d'innerHTML, et la raison de sa popularité.
C'est aussi la propriété qui provoque le plus de dégâts silencieux, du champ vidé sans raison à la faille de sécurité ouverte sans le savoir.
Définition
innerHTML représente le contenu HTML d'un élément sous forme de chaîne. En lecture, elle rend le balisage des enfants. En écriture, le navigateur analyse la chaîne fournie, en construit des nœuds, et remplace tout le contenu existant.
const liste = document.querySelector("#panier");
liste.innerHTML = "<li>Clavier</li><li>Souris</li>";
console.log(liste.children.length); // 2
console.log(liste.innerHTML);La différence avec textContent est essentielle : ce dernier traite la chaîne comme du texte brut, les balises apparaissant alors telles quelles à l'écran.
Ce que l'écriture détruit
L'affectation ne modifie pas les nœuds existants, elle les jette et en fabrique de nouveaux. Tout ce qui était attaché aux anciens disparaît avec eux.
- Les écouteurs posés par addEventListener() sur les enfants sont perdus.
- Les champs de formulaire reviennent à leur valeur d'origine, y compris le texte déjà saisi.
- Le focus et la position de défilement sont réinitialisés.
L'écriture element.innerHTML += "..." aggrave le cas : elle relit tout le contenu, le concatène, puis reconstruit l'ensemble. Sur une boucle de cent éléments, le travail est refait cent fois.
Le risque d'injection
Insérer une chaîne venue d'un visiteur revient à laisser cette personne écrire du HTML dans la page. C'est la faille dite d'injection de script, et elle permet de lire un jeton de session ou d'envoyer des données ailleurs.
const message = '<img src="x" onerror="alert(1)">';
zone.innerHTML = message; // le code s'exécute
zone.textContent = message; // le texte s'affiche, sans dangerUne balise <script> insérée par innerHTML ne s'exécute pas, c'est exact. Ce n'est pas une protection : les attributs d'événement comme onerror ou onload, eux, fonctionnent parfaitement.
Les alternatives
| Écriture | Quand l'employer |
|---|---|
textContent | Dès qu'il s'agit de texte, donc la plupart du temps |
insertAdjacentHTML | Ajouter du balisage sans reconstruire l'existant |
createElement | Construire des éléments dont on garde la référence |
Questions fréquentes
Est-ce plus rapide que de créer les éléments un à un ?
Pour une grosse insertion en une seule fois, l'analyse d'une chaîne est souvent compétitive. L'avantage s'inverse dès que la propriété est réécrite en boucle, et createElement() garde une référence sur chaque élément, ce qui évite de le rechercher ensuite.
Comment ajouter sans tout reconstruire ?
insertAdjacentHTML reçoit une position et une chaîne, et insère sans toucher aux nœuds voisins. Les quatre positions acceptées sont beforebegin, afterbegin, beforeend et afterend, la dernière plaçant le contenu juste après l'élément.
Comment insérer du HTML de façon sûre ?
La règle tient en une phrase : n'insérez que du balisage que vous avez écrit, jamais une chaîne venue d'un formulaire ou d'une interface. Les bibliothèques d'interface règlent la question autrement, React échappant le texte par défaut et réclamant une propriété au nom volontairement dissuasif pour insérer du HTML brut, un mécanisme détaillé dans la formation React.