Prérequis
- Niveau : bases JavaScript (cf. les bases JS) et HTML.
- Outils : VS Code + Live Server, navigateur moderne avec DevTools.
- Temps estimé : 1 h pour parcourir, 2 h pour pratiquer.
Pourquoi le DOM ?
Le DOM (Document Object Model) est le pont entre le code JavaScript et la page web. C’est lui qui permet à votre script de réagir aux clics, modifier le contenu, créer des éléments et donner vie à votre interface. Maîtriser le DOM, c’est passer de pages statiques à des applications interactives.
Le DOM : votre page HTML vue par JavaScript
Le DOM (Document Object Model) est la représentation de votre page HTML comme un arbre d’objets que JavaScript peut lire et modifier. Chaque balise HTML est un « nœud » que vous pouvez cibler, modifier, créer ou supprimer.
Sélectionner des éléments
// Par ID (le plus rapide, retourne 1 élément)
const titre = document.getElementById('titre-principal');
// Par classe (retourne une collection)
const cartes = document.getElementsByClassName('carte');
// Par sélecteur CSS (le plus flexible)
const premiereCarte = document.querySelector('.carte'); // 1er élément
const toutesCartes = document.querySelectorAll('.carte'); // Tous les éléments
const lien = document.querySelector('nav a.actif'); // Sélecteur complexe
const input = document.querySelector('input[type="email"]'); // Par attribut
// Parcourir les résultats
toutesCartes.forEach(carte => {
console.log(carte.textContent);
});
Modifier le contenu
const élément = document.getElementById('message');
// Modifier le texte (sécurisé, pas de HTML interprété)
élément.textContent = 'Nouveau message texte';
// Modifier le HTML (attention aux injections XSS !)
élément.innerHTML = '<strong>Message</strong> en <em>gras</em>';
// Lire le contenu
console.log(élément.textContent); // Texte brut
console.log(élément.innerHTML); // Code HTML
Modifier les styles
const boite = document.querySelector('.boite');
// Modifier un style directement
boite.style.backgroundColor = '#667eea';
boite.style.padding = '20px';
boite.style.borderRadius = '12px';
boite.style.color = 'white';
// Ajouter / retirer des classes (méthode recommandée)
boite.classList.add('visible');
boite.classList.remove('cache');
boite.classList.toggle('actif'); // Ajoute si absent, retire si présent
boite.classList.contains('actif'); // true ou false
// Exemple pratique : afficher/cacher un modal
function toggleModal() {
document.getElementById('modal').classList.toggle('visible');
}
Modifier les attributs
const image = document.querySelector('img');
const lien = document.querySelector('a');
// Lire un attribut
console.log(image.getAttribute('src'));
console.log(image.alt); // Raccourci pour les attributs standard
// Modifier un attribut
image.setAttribute('src', 'nouvelle-image.jpg');
image.alt = 'Description mise à jour';
lien.href = 'https://itskillscenter.io';
// Attributs data- (données personnalisées)
const carte = document.querySelector('.carte');
carte.dataset.id = '42'; // Crée data-id="42"
carte.dataset.categorie = 'web'; // Crée data-categorie="web"
console.log(carte.dataset.id); // "42"
Créer et ajouter des éléments
// Créer un élément
const nouvelArticle = document.createElement('div');
nouvelArticle.className = 'carte';
nouvelArticle.innerHTML = `
<h3>Nouvel article</h3>
<p>Description de l'article</p>
<button class="btn-lire">Lire la suite</button>
`;
// Ajouter à la page
const conteneur = document.getElementById('articles');
conteneur.appendChild(nouvelArticle); // À la fin
conteneur.prepend(nouvelArticle); // Au début
conteneur.insertBefore(nouvelArticle, ref); // Avant un élément
// Méthode moderne : insertAdjacentHTML
conteneur.insertAdjacentHTML('beforeend', `
<div class="carte">
<h3>Article ajouté</h3>
</div>
`);
// Positions : 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
Supprimer des éléments
// Supprimer un élément
const élément = document.getElementById('a-supprimer');
élément.remove();
// Supprimer un enfant
const parent = document.getElementById('liste');
const enfant = parent.querySelector('.item');
parent.removeChild(enfant);
// Vider tout le contenu d'un élément
parent.innerHTML = '';
Naviguer dans l’arbre DOM
const élément = document.querySelector('.carte');
// Parent
élément.parentElement;
// Enfants
élément.children; // Collection d'enfants
élément.firstElementChild; // Premier enfant
élément.lastElementChild; // Dernier enfant
// Frères (siblings)
élément.nextElementSibling; // Élément suivant
élément.previousElementSibling; // Élément précédent
// Ancêtre le plus proche qui correspond au sélecteur
élément.closest('.conteneur'); // Remonte dans l'arbre
Exemple complet : Todo List
// HTML : <input id="tache-input"> <button id="ajouter">+</button> <ul id="liste"></ul>
const input = document.getElementById('tache-input');
const btnAjouter = document.getElementById('ajouter');
const liste = document.getElementById('liste');
// Ajouter une tâche
btnAjouter.addEventListener('click', () => {
const texte = input.value.trim();
if (!texte) return;
const li = document.createElement('li');
li.innerHTML = `
<span>${texte}</span>
<button class="supprimer">✕</button>
`;
// Clic sur la tâche = barré
li.querySelector('span').addEventListener('click', () => {
li.classList.toggle('fait');
});
// Supprimer
li.querySelector('.supprimer').addEventListener('click', () => {
li.remove();
});
liste.appendChild(li);
input.value = '';
input.focus();
});
Résumé des méthodes DOM
| Action | Méthode |
|---|---|
| Sélectionner | querySelector(), querySelectorAll(), getElementById() |
| Contenu | textContent, innerHTML |
| Classes | classList.add/remove/toggle/contains |
| Styles | élément.style.propriété |
| Attributs | getAttribute(), setAttribute(), dataset |
| Créer | createElement(), insertAdjacentHTML() |
| Supprimer | remove(), removeChild() |
Erreurs fréquentes
document.querySelector('...') retourne null
Cause : le script s’exécute avant que le DOM soit chargé.
Solution : ajoutez defer au <script>, ou enveloppez dans document.addEventListener('DOMContentLoaded', () => { ... }).
Faille XSS via innerHTML
Cause : insérer du contenu utilisateur via innerHTML permet l’injection de scripts.
Solution : préférez textContent pour du texte. Si vous devez insérer du HTML, échappez ou utilisez DOMPurify.
Boucle qui modifie une HTMLCollection en cours
Cause : getElementsByClassName renvoie une collection vivante qui se met à jour pendant la boucle.
Solution : utilisez querySelectorAll (renvoie un NodeList statique) ou copiez avec Array.from(...).
Listeners qui s’accumulent
Cause : ré-ajouter addEventListener sans le retirer (souvent dans un render qui s’exécute plusieurs fois).
Solution : utilisez removeEventListener avec la même référence de fonction, ou { once: true } dans les options.
Exercice pratique
🎯 Défi : Panier d’achat interactif
- Affichez une liste de 5 produits avec nom, prix et bouton « Ajouter »
- Au clic sur « Ajouter », créez un élément dans le panier (DOM)
- Chaque article du panier a un bouton « Supprimer »
- Calculez et affichez le total en temps réel
- Ajoutez un compteur d’articles sur l’icône panier
API DOM modernes en 2026 : querySelector, dataset, classList
Le DOM moderne offre trois APIs essentielles à connaître. querySelector / querySelectorAll qui acceptent les sélecteurs CSS complets : document.querySelector('.user-card[data-active="true"] .email'). Plus puissant et lisible que les anciens getElementById, getElementsByTagName, getElementsByClassName. dataset permet d’accéder aux attributs data-* en JS : un data-user-id="42" devient el.dataset.userId automatiquement camelCase.
classList remplace la manipulation manuelle de className. el.classList.add('active'), el.classList.remove('hidden'), el.classList.toggle('open'), el.classList.contains('selected'). Plus sûr et lisible que la concaténation/regex sur className. Ces trois APIs sont supportées par tous les navigateurs modernes depuis 2014 — utilisez-les sans hésiter.
Manipulation efficace : DocumentFragment et insertAdjacentHTML
Ajouter 100 éléments un par un au DOM via appendChild déclenche 100 reflows et tue les performances. Deux patterns optimisés. Premier pattern : DocumentFragment, conteneur invisible où on construit l’arbre DOM hors-document, puis on l’insère en un seul appel. Le navigateur ne déclenche qu’un seul reflow final.
const frag = document.createDocumentFragment();
for (const item of items) {
const li = document.createElement('li');
li.textContent = item.name;
frag.appendChild(li);
}
document.querySelector('#liste').appendChild(frag);
Deuxième pattern : insertAdjacentHTML qui parse une chaîne HTML et l’insère à un endroit précis (beforebegin, afterbegin, beforeend, afterend). Plus rapide que innerHTML car ne reparse pas tout le contenu existant. À éviter quand le HTML vient d’un input utilisateur (risque XSS) — préférer alors la création programmatique.
Performance DOM : éviter les reflows et repaints
Chaque modification de style ou structure peut déclencher un reflow (recalcul de layout) ou un repaint (redessin pixel). Quatre règles d’optimisation. Première : batcher les modifications. Plutôt que modifier 5 styles à la suite, regrouper en un seul cssText ou ajouter une classe qui combine les styles. Deuxième : éviter les lectures suivies d’écritures en alternance — déclenche le layout thrashing ruineux.
Troisième : animer via transform et opacity seulement (gérés sur le compositor GPU). Animer top ou width déclenche un reflow à chaque frame. Quatrième : requestAnimationFrame pour synchroniser les modifications visuelles avec le cycle de rendu navigateur. Pour profiler les performances DOM réelles, ouvrir Chrome DevTools Performance Tab et enregistrer une interaction — les reflows excessifs apparaissent immédiatement.
Frameworks vs DOM brut : quand basculer
Manipuler le DOM brut reste pertinent pour les sites simples (sites vitrines, landing pages, blogs) avec quelques interactions ponctuelles. Au-delà de 5-10 composants interactifs ou d’une UI avec état complexe (formulaires multi-étapes, listes filtrables, dashboards), basculer sur React, Vue ou Svelte économise des dizaines d’heures de développement et de maintenance. Le tipping point typique : quand vous commencez à manipuler 3+ états en parallèle, le framework apporte une vraie valeur.
Pour les frameworks ultra-légers, Alpine.js (15 KB) ajoute de l’interactivité déclarative directement dans le HTML sans build step — idéal pour enrichir un site WordPress ou un thème statique. Petite Vue et HTMX sont d’autres alternatives minimalistes à connaître.
Sécurité DOM : XSS et innerHTML
L’usage le plus dangereux du DOM en 2026 reste innerHTML = userInput. Si userInput contient <script>alert(1)</script>, le code s’exécute dans le contexte du site avec accès aux cookies et localStorage. C’est la faille XSS classique qui compromet 50 % des applications web mal codées.
Trois règles obligatoires. Première règle : ne jamais utiliser innerHTML avec du contenu utilisateur. Préférer textContent qui ignore le HTML. Deuxième règle : si vous devez insérer du HTML rich (markdown rendu, par exemple), passer par un sanitizer comme DOMPurify qui retire les scripts et attributs dangereux. Troisième règle : configurer un Content Security Policy (CSP) côté serveur qui bloque l’exécution de scripts inline non autorisés — barrière de défense en profondeur. Pour les patterns de validation, voir aussi le tutoriel validation JavaScript.
Patterns avancés : MutationObserver et Shadow DOM
MutationObserver permet de réagir aux changements du DOM (ajout d’éléments, modifications d’attributs) sans poller en boucle. Utile pour intercepter du contenu inséré dynamiquement par un script tiers ou un framework. Plus efficace que setInterval qui scrute en permanence. Shadow DOM encapsule un sous-arbre DOM isolé du reste de la page — base des Web Components. Les styles CSS ne fuient pas vers l’extérieur ni vice versa. Pour les composants partagés entre projets, c’est l’isolation la plus robuste sans framework lourd.
Adaptation au contexte ouest-africain
Pour un développeur basé à Dakar, Abidjan, Bamako ou Cotonou qui livre des sites WordPress ou des applications custom, manipuler le DOM efficacement impacte directement les performances perçues sur Android d’entrée de gamme. Sur un Galaxy A03 avec 2 Go de RAM et une 4G dégradée, chaque reflow inutile fait baisser le framerate de 60 à 30 fps, ce qui se voit immédiatement comme un effet de lag. Trois conseils pratiques. Premièrement, profiler avec Chrome DevTools Performance en mode mobile throttle pour identifier les hotspots. Deuxièmement, préférer les frameworks réactifs (React, Vue) qui optimisent automatiquement les updates DOM. Troisièmement, mesurer avec Lighthouse — un score Performance > 80 sur mobile reste atteignable même avec du DOM brut bien codé.
Outils de débogage DOM dans les DevTools
Chrome et Firefox DevTools offrent plusieurs outils dédiés à l’inspection DOM. Premier outil : panneau Elements qui affiche l’arbre DOM en temps réel avec ses styles calculés et ses event listeners. Deuxième outil : break on subtree modifications permet de mettre en pause l’exécution dès qu’un script modifie un élément précis — utile pour traquer la source d’une modification mystérieuse. Troisième outil : copy as JS path donne le sélecteur unique d’un élément à coller dans la console. Pour les sites complexes avec dizaines de scripts tiers, ces outils économisent des heures de débogage manuel.
Animation et transitions performantes
Animer le DOM via JavaScript reste possible mais coûteux. Préférer les animations CSS (transition, @keyframes) déclenchées via classes ajoutées en JS. Pour les animations complexes pilotées par scroll ou interaction, utiliser la Web Animations API (element.animate()) qui combine la performance CSS et la flexibilité JS. Bibliothèques comme GSAP ou Anime.js restent pertinentes pour les animations très complexes (timelines synchronisées, easings custom) mais ajoutent du poids — les éviter sur les sites simples.
Sélecteurs : performance et lisibilité
Tous les sélecteurs CSS ne sont pas équivalents en performance. document.getElementById('monid') reste le plus rapide (lookup direct dans une table de hash). querySelector('.classe') est légèrement plus lent mais plus expressif. querySelector('div > ul li:nth-child(3)') est nettement plus coûteux car il parcourt l’arbre. Pour les boucles serrées (10 000+ itérations), privilégier les sélecteurs simples ou cacher la référence dans une variable. Pour les codes typiques avec quelques manipulations par seconde, la lisibilité prime — utilisez les sélecteurs CSS les plus expressifs possible.
Pour étoffer le tableau sur la performance frontend, lire les Core Web Vitals de Google qui donnent le standard 2026 pour évaluer la qualité technique perçue d’un site.
Tester sur de vrais appareils mobiles ouest-africains reste le seul juge fiable de la performance perçue.