Manipulation du DOM en JavaScript
Apprenez à manipuler le DOM en JavaScript : modifier le contenu avec textContent et innerHTML, gérer les attributs et classes, créer, insérer, cloner et supprimer des éléments, avec des exemples pratiques.
La manipulation du DOM (Document Object Model) en JavaScript est une compétence fondamentale pour les développeurs web. Le DOM est une représentation en arbre, dynamique, de la page que le navigateur construit à partir de votre HTML ; le manipuler consiste à utiliser JavaScript pour lire et modifier cet arbre à l'exécution, afin que la page se mette à jour sans rechargement.
Ce guide couvre les quatre opérations les plus courantes :
- Lire et modifier le contenu avec
textContentetinnerHTML. - Lire et modifier les attributs avec
getAttribute,setAttributeetclassList. - Créer et insérer des éléments avec
createElement,append,insertBeforeetinsertAdjacentHTML. - Supprimer et remplacer des éléments avec
remove()etreplaceWith().
Avant de pouvoir manipuler un élément, vous devez d'abord le trouver. Si vous n'êtes pas encore à l'aise avec getElementById, querySelector et leurs équivalents, lisez d'abord Sélectionner des éléments DOM et Recherche : getElement, querySelector — chaque exemple ci-dessous suppose que vous disposez déjà d'une référence au nœud.
Modifier le contenu et les attributs des éléments
Manipuler le contenu et les attributs des éléments DOM est un aspect crucial du développement web dynamique. En modifiant le contenu, nous pouvons mettre à jour le texte ou le HTML d'un élément. En modifiant les attributs, nous pouvons changer des propriétés comme class, id ou src. JavaScript fournit des méthodes puissantes pour accomplir ces tâches, nous permettant de créer des applications web réactives et interactives. Voyons comment utiliser efficacement innerHTML, textContent, setAttribute et getAttribute.
Modifier le contenu d'un élément
Nous pouvons modifier le contenu d'un élément en utilisant les propriétés innerHTML ou textContent.
innerHTML vs textContent
innerHTML nous permet de définir ou d'obtenir le contenu HTML d'un élément, y compris les balises HTML.
<!DOCTYPE html>
<html>
<head>
<title>innerHTML vs textContent</title>
<style>
.content-container {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.html-content {
color: red;
}
</style>
</head>
<body>
<div class="content-container">
<p id="content">Original paragraph content.</p>
<button id="change-innerHTML">Change using innerHTML</button>
<button id="change-textContent">Change using textContent</button>
</div>
<script>
const content = document.getElementById('content');
const innerHTMLButton = document.getElementById('change-innerHTML');
const textContentButton = document.getElementById('change-textContent');
innerHTMLButton.addEventListener('click', () => {
content.innerHTML = 'New content with <strong class="html-content">HTML</strong> tags.';
});
textContentButton.addEventListener('click', () => {
content.textContent = 'Updated paragraph content without HTML tags.';
});
</script>
</body>
</html>Explication :
innerHTMLnous permet de définir ou d'obtenir le contenu HTML d'un élément, y compris les balises HTML. Dans l'exemple, cliquer sur le bouton "Change using innerHTML" remplace le contenu du paragraphe par un nouveau contenu incluant des balises HTML, ce qui modifie l'apparence du texte.textContentdéfinit ou obtient le contenu texte d'un élément sans analyser les balises HTML. Cliquer sur le bouton "Change using textContent" remplace le contenu du paragraphe par du texte brut, en ignorant toute balise HTML.
Utilisez textContent lors de l'insertion de contenu généré par l'utilisateur pour éviter les risques de sécurité tels que les XSS (Cross-Site Scripting). Assigner une chaîne brute provenant d'un utilisateur à innerHTML permet à un attaquant d'injecter des balises <script> ou des attributs de gestionnaire d'événements qui s'exécutent sur votre page. textContent n'analyse jamais le HTML, donc la chaîne est affichée telle quelle et est toujours sûre.
Il existe également une troisième propriété, outerHTML, qui représente l'élément et sa propre balise. L'assigner remplace l'élément entièrement :
// Replace the whole <p> with an <h2>, keeping it in the same position
const p = document.querySelector('p');
p.outerHTML = '<h2>A heading instead</h2>';
// After this, `p` still points at the old, detached node — re-query if you need the new one.Modifier les attributs d'un élément
Nous pouvons modifier les attributs d'un élément à l'aide de la méthode setAttribute et les récupérer avec la méthode getAttribute. Ces méthodes sont utiles pour modifier dynamiquement des éléments en fonction des interactions utilisateur ou d'autres événements.
<!DOCTYPE html>
<html>
<head>
<title>setAttribute and getAttribute</title>
</head>
<body>
<div id="container" class="initial-class">Container content</div>
<button id="change-attribute">Change Attribute</button>
<button id="get-attribute">Get Attribute</button>
<script>
const container = document.getElementById('container');
const changeAttributeButton = document.getElementById('change-attribute');
const getAttributeButton = document.getElementById('get-attribute');
changeAttributeButton.addEventListener('click', () => {
container.setAttribute('class', 'new-class');
alert('Class attribute changed to "new-class"');
});
getAttributeButton.addEventListener('click', () => {
const className = container.getAttribute('class');
alert(`Current class attribute: ${className}`);
});
</script>
</body>
</html>Explication :
setAttribute('attributeName', 'value'): cette méthode nous permet de définir une nouvelle valeur pour un attribut spécifié d'un élément. Dans l'exemple, cliquer sur le bouton "Change Attribute" change l'attribut class du<div>de "initial-class" à "new-class".getAttribute('attributeName'): cette méthode récupère la valeur actuelle de l'attribut spécifié. Cliquer sur le bouton "Get Attribute" affiche une alerte avec la valeur actuelle de l'attribut class du<div>.
Utilisez toujours setAttribute et getAttribute pour les attributs personnalisés et les attributs générés dynamiquement. Pour la manipulation des classes, préférez l'API classList ci-dessous — elle modifie les classes individuellement au lieu d'écraser toute la chaîne class.
Attributs vs propriétés
Une source de confusion fréquente : un attribut HTML (ce qui est écrit dans le balisage) n'est pas toujours identique à la propriété DOM (la valeur dynamique sur l'objet JavaScript). getAttribute('value') lit la valeur d'origine dans le balisage, tandis que input.value lit ce que l'utilisateur a actuellement saisi. Pour les attributs boolean, la différence est encore plus marquée — checkbox.getAttribute('checked') reflète le balisage, tandis que checkbox.checked reflète l'état courant. En règle générale : utilisez les propriétés (el.id, el.value, el.checked) pour les valeurs standard qui changent fréquemment, et setAttribute/getAttribute pour les attributs data- personnalisés ou les attributs de balisage ponctuels.
Pour lire et écrire des attributs data-*, il existe une API dédiée et ergonomique — la propriété dataset :
// <div id="card" data-user-id="42" data-role="admin"></div>
const card = document.getElementById('card');
console.log(card.dataset.userId); // "42" (note: data-user-id → userId)
card.dataset.role = 'editor'; // writes data-role="editor"Gérer les classes avec classList
classList permet d'activer ou de désactiver des classes individuelles sans affecter les autres classes déjà présentes :
element.classList.add('new-class'); // add one (or several) classes
element.classList.remove('old-class'); // remove a class
element.classList.toggle('active'); // add if absent, remove if present
element.classList.replace('open', 'shut'); // swap one class for another
console.log(element.classList.contains('active')); // true / falsetoggle accepte un deuxième argument optionnel pour forcer un état, ce qui est pratique pour synchroniser une classe avec une condition : el.classList.toggle('valid', isValid). Pour un travail de style plus approfondi — notamment la lecture et l'écriture de la propriété style — consultez Travailler avec les styles dans le DOM.
Ajouter et supprimer des éléments
Nous pouvons ajouter de nouveaux éléments au DOM ou supprimer des éléments existants.
Ajouter des éléments
Pour ajouter de nouveaux éléments au DOM, nous les créons d'abord à l'aide de la méthode createElement, puis nous les ajoutons à un élément existant avec appendChild ou nous les insérons à une position spécifique avec insertBefore.
createElement(), appendChild(), insertBefore()
<!DOCTYPE html>
<html>
<head>
<title>Adding Elements</title>
</head>
<body>
<div id="task-list">
<h2>Task List</h2>
<ul id="tasks">
<li>Initial task</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
<button id="insert-before">Insert Before First Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
const insertBeforeButton = document.getElementById('insert-before');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
insertBeforeButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
const firstTask = taskList.firstElementChild;
taskList.insertBefore(newTask, firstTask);
}
});
</script>
</body>
</html>Explication :
createElement('tagName'): cette méthode crée un nouvel élément spécifié partagName. Par exemple,document.createElement('li')crée un nouvel élément<li>.appendChild(newElement): cette méthode ajoute un nouvel élément enfant à un élément parent spécifié. Dans l'exemple, cliquer sur le bouton "Add Task" crée un nouvel élément de liste (<li>) et l'ajoute à la liste des tâches (<ul>).insertBefore(newElement, referenceElement): cette méthode insère un nouvel élément avant un élément de référence spécifié au sein du même parent. Cliquer sur le bouton "Insert Before First Task" crée un nouvel élément de liste (<li>) et l'insère avant la première tâche de la liste.
Pour insérer des chaînes HTML directement, utilisez insertAdjacentHTML(). Pour remplacer un élément existant, element.replaceWith(newElement) est une alternative moderne à la combinaison de remove() et appendChild().
insertAdjacentHTML et les méthodes d'insertion modernes
Lorsque vous disposez déjà d'une chaîne HTML (plutôt que d'un nœud construit avec createElement), insertAdjacentHTML(position, html) l'analyse et l'insère en un seul appel. L'argument position est l'un des quatre mots-clés relatifs à l'élément :
// Given: <div id="box">content</div>
const box = document.getElementById('box');
box.insertAdjacentHTML('beforebegin', '<p>before the box</p>'); // before <div>
box.insertAdjacentHTML('afterbegin', '<p>first child</p>'); // inside, at the start
box.insertAdjacentHTML('beforeend', '<p>last child</p>'); // inside, at the end
box.insertAdjacentHTML('afterend', '<p>after the box</p>'); // after </div>Les navigateurs modernes fournissent également append(), prepend(), before() et after(), qui acceptent des nœuds ou des chaînes simples et peuvent prendre plusieurs arguments à la fois — ils sont généralement plus clairs que appendChild/insertBefore :
const list = document.getElementById('tasks');
const item = document.createElement('li');
item.textContent = 'New task';
list.append(item, 'some trailing text'); // append node + string together
list.prepend('Top of the list'); // insert at the startCloner des éléments
Pour dupliquer un nœud existant au lieu d'en construire un de zéro, utilisez cloneNode(deep). Passez true pour copier l'élément avec tous ses descendants ; passez false (ou rien) pour copier uniquement l'élément lui-même :
const template = document.getElementById('card');
const copy = template.cloneNode(true); // deep clone, including children
copy.id = 'card-2'; // ids must stay unique
document.body.append(copy);Pour des balises complexes répétées, l'élément <template> est l'outil conçu à cet effet — son contenu est inerte jusqu'à ce que vous le cloniez.
Supprimer des éléments
Pour supprimer un élément, nous pouvons utiliser la méthode moderne element.remove().
<!DOCTYPE html>
<html>
<head>
<title>Removing Elements</title>
</head>
<body>
<div id="container">
<p id="paragraph">This is a paragraph.</p>
<button id="remove-paragraph">Remove Paragraph</button>
</div>
<script>
const container = document.getElementById('container');
const paragraph = document.getElementById('paragraph');
const removeParagraphButton = document.getElementById('remove-paragraph');
removeParagraphButton.addEventListener('click', () => {
paragraph.remove();
});
</script>
</body>
</html>Explication :
element.remove(): cette méthode moderne supprime directement un élément spécifié du DOM. Dans l'exemple, cliquer sur le bouton "Remove Paragraph" supprime l'élément<p>de la page.
L'utilisation de element.remove() est l'approche recommandée pour les modifications de contenu dynamique, comme la suppression d'articles d'un panier d'achat, car elle offre une syntaxe plus propre que la méthode héritée parent.removeChild(child) (qui reste utile lorsque vous avez besoin d'une référence au nœud supprimé).
Insérer de nombreux éléments efficacement
Chaque fois que vous insérez un nœud dans le document actif, le navigateur peut devoir recalculer la mise en page et repeindre. Faire cela dans une boucle — une fois par élément — est inefficace. Un DocumentFragment vous permet d'assembler des nœuds hors écran et de les insérer tous en une seule opération :
const list = document.getElementById('tasks');
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 1000; i++) {
const li = document.createElement('li');
li.textContent = `Task ${i}`;
fragment.appendChild(li); // no layout work — fragment is not in the document
}
list.appendChild(fragment); // one insertion, one reflowDeux règles de base permettent de garder un code DOM-lourd performant : regroupez vos lectures et écritures (n'entremêlez pas la lecture de offsetHeight avec la définition de styles, sous peine de forcer des recalculs répétés), et préférez construire une chaîne et assigner innerHTML une seule fois plutôt que de nombreux petits appels appendChild lorsque vous n'attachez pas de gestionnaires d'événements à chaque nœud. Pour un traitement approfondi, consultez Optimisation des performances du DOM.
Exemple : liste de tâches dynamique
Créons une simple application de liste de tâches pour illustrer les concepts ci-dessus.
<!DOCTYPE html>
<html>
<head>
<title>To-Do List</title>
</head>
<body>
<div id="todo-list">
<h2>My To-Do List</h2>
<ul id="tasks">
<li>Learn JavaScript</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
taskList.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
event.target.remove();
}
});
</script>
</body>
</html>Explication :
createElementcrée un nouvel élément de liste pour la tâche, etappendChildl'ajoute à la liste.- Un seul écouteur de clic est attaché au
<ul>, et non à chaque<li>. Comme les clics remontent de l'élément cliqué vers ses ancêtres, l'écouteur inspecteevent.targetpour déterminer quelle tâche a été cliquée et la supprime. Ce modèle est appelé délégation d'événements, et c'est la raison pour laquelle le gestionnaire continue de fonctionner pour les tâches qui n'existaient pas au chargement de la page.
La délégation d'événements est la raison pour laquelle nous n'ajoutons pas un écouteur séparé à chaque fois qu'une tâche est créée — un seul écouteur sur le parent gère les enfants actuels et futurs. Pour approfondir la propagation, event.target vs event.currentTarget, et la délégation, lisez Gestion des événements dans le DOM.
Lorsqu'une interface comporte de nombreux éléments d'état indépendants et fréquemment mis à jour, maintenir manuellement la synchronisation du DOM devient source d'erreurs. Des frameworks tels que React, Vue ou Svelte vous permettent de décrire à quoi doit ressembler l'interface pour un état donné et gèrent les mises à jour du DOM pour vous. Les techniques manuelles décrites dans ce chapitre constituent la fondation sur laquelle ces frameworks sont construits.
Conclusion
Maîtriser la manipulation du DOM est essentiel pour créer des applications web dynamiques et interactives. Vous savez maintenant comment modifier le contenu (textContent, innerHTML, outerHTML), travailler avec les attributs et les classes (setAttribute, dataset, classList), créer et insérer des nœuds (createElement, append, insertAdjacentHTML, cloneNode), les supprimer et les remplacer (remove(), replaceWith()), et regrouper les insertions efficacement avec un DocumentFragment.
Pour continuer à construire sur ces bases :
- Sélectionner des éléments DOM — trouvez les nœuds que vous souhaitez modifier.
- Parcourir le DOM — naviguez entre parents, enfants et frères.
- Travailler avec les styles dans le DOM — lisez et écrivez du CSS depuis JavaScript.
- Gestion des événements dans le DOM — répondez aux interactions utilisateur.