W3docs

API JavaScript MutationObserver

Apprenez l'API JavaScript MutationObserver : observer les changements DOM, les méthodes observe/disconnect/takeRecords, les MutationRecords et des exemples pratiques.

L'API MutationObserver en JavaScript vous permet d'observer une partie du DOM et d'exécuter un callback dès que quelque chose y change — un nœud est ajouté ou supprimé, un attribut est modifié, ou le contenu textuel est mis à jour. Contrairement aux obsolètes mutation events qu'elle remplace, MutationObserver regroupe les changements et les livre de manière asynchrone, sans bloquer la page ni déclencher un événement distinct pour chaque petite modification.

Ce guide couvre ce que vous pouvez observer, les trois méthodes exposées par chaque observer, la structure d'un MutationRecord, des cas d'usage concrets, les pièges courants et un exemple interactif que vous pouvez exécuter.

Quand utiliser un MutationObserver ?

On fait appel à un MutationObserver lorsqu'un contenu que l'on ne contrôle pas change et qu'il faut y réagir :

  • Contenu tiers / injecté — un widget, une publicité ou un bloc rendu par un CMS apparaît dans le DOM et vous devez le styler ou l'enrichir.
  • Détecter quand un élément existe enfin — attendre qu'un nœud rendu plus tard par un framework soit disponible, plutôt que de sonder avec setInterval.
  • Synchroniser l'UI avec les changements d'attributs — réagir quand class, style, disabled ou un attribut data-* change sur un élément auquel vous ne pouvez pas ajouter d'écouteur.
  • Redimensionnement ou recalcul automatique — recalculer la mise en page lorsque des enfants sont insérés dans un conteneur.
  • Maintenir un modèle synchronisé avec le texte d'un contenteditable.

Si vous avez seulement besoin de savoir quand un élément entre ou sort du viewport, utilisez plutôt un IntersectionObserver — il est conçu à cet effet et moins coûteux pour ce travail.

Les trois méthodes

Chaque instance d'observer expose exactement trois méthodes :

MéthodeCe qu'elle fait
observe(target, options)Commence à surveiller target en utilisant un objet d'options. Appelez-la à nouveau avec une cible différente pour surveiller plusieurs nœuds avec le même observer.
disconnect()Arrête de surveiller toutes les cibles. Le callback ne se déclenchera plus. Appelez toujours cette méthode lorsque vous avez terminé pour éviter les fuites mémoire.
takeRecords()Retourne (et vide) de manière synchrone les MutationRecords en attente qui n'ont pas encore été transmis au callback. Utile juste avant disconnect() pour ne pas perdre le dernier lot.

L'objet options passé à observe() doit activer au moins l'une des options childList, attributes ou characterData, sinon l'appel lève une TypeError.

OptionSignification
childListSurveille l'ajout ou la suppression de nœuds enfants directs.
attributesSurveille les changements d'attributs.
characterDataSurveille les changements dans les données des nœuds texte.
subtreeÉtend la surveillance à tous les descendants, pas seulement à la cible.
attributeOldValueEnregistre la valeur précédente de l'attribut (implique attributes).
characterDataOldValueEnregistre la valeur textuelle précédente (implique characterData).
attributeFilterArray de noms d'attributs à surveiller — ignore les autres.

Exemple de Mutation Observer

Voici un exemple de base pour illustrer le fonctionnement d'un Mutation Observer en indiquant visuellement les changements dans le DOM.

<!DOCTYPE html>
<html>
<head>
  <title>Exploring DOM Changes: Live Examples with Mutation Observers</title>
</head>
<body>
  <div id="target" style="background-color: lightgray; padding: 10px;">
    Watch this space for changes!
  </div>
  <button style="margin-top: 10px;" onclick="addNewElement(); changeAttribute();">Add New Element and Change Color</button>
  <div id="log" style="margin-top: 20px;"></div>

  <script>
    // Get the element to observe
    const targetNode = document.getElementById('target');

    // Define configurations for the observer
    const config = { attributes: true, childList: true, subtree: true, attributeOldValue: true };

    // Callback function to execute when mutations are observed
    const callback = function(mutationsList, observer) {
      for (const mutation of mutationsList) {
        const message = document.createElement('p');
        if (mutation.type === 'childList') {
          message.textContent = 'A child node has been added or removed.';
          message.style.color = 'green';
        } else if (mutation.type === 'attributes') {
          message.textContent = 'The ' + mutation.attributeName + ' attribute was modified.';
          message.style.color = 'blue';
        }
        document.getElementById('log').appendChild(message);
      }
    };

    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callback);

    // Start observing the target node for configured mutations
    observer.observe(targetNode, config);

    // Function to add new elements
    function addNewElement() {
      const newElement = document.createElement('div');
      newElement.textContent = 'New element added!';
      targetNode.appendChild(newElement);
    }

    // Function to change attributes
    function changeAttribute() {
      const currentColor = targetNode.style.backgroundColor;
      targetNode.style.backgroundColor = currentColor === 'lightgray' ? 'lightblue' : 'lightgray';
    }
  </script>
</body>
</html>

Cet exemple illustre comment utiliser un Mutation Observer pour détecter et réagir aux changements dans le Document Object Model (DOM) d'une page web. Voici ce que fait chaque partie du JavaScript et ce que vous pouvez observer en interagissant avec l'exemple :

  1. Mise en place du Mutation Observer :
    • Nœud cible : c'est l'élément DOM que vous souhaitez surveiller. Ici, il s'agit du div avec l'ID target.
    • Configurations : elles précisent les types de changements à surveiller :
      • attributes : l'observer détectera les changements d'attributs (comme le style ou la classe).
      • childList : il vérifiera l'ajout ou la suppression d'éléments enfants (comme de nouveaux div ajoutés).
      • subtree : l'observer inspecte non seulement l'élément cible, mais aussi ses descendants. Notez que subtree n'a d'effet que si childList ou attributes est également activé.
      • attributeOldValue : enregistre la valeur précédente de tout attribut modifié (utile pour suivre les changements).
  2. Définir une fonction callback :
    • Cette fonction s'exécute chaque fois que l'observer détecte un changement selon les configurations définies.
    • Elle parcourt toutes les mutations détectées et crée un message de journal pour chacune :
      • Si un élément enfant est ajouté ou supprimé, elle consigne "A child node has been added or removed." en vert.
      • Si un attribut est modifié (comme la couleur de fond), elle consigne "The mutation.attributeName attribute was modified." en bleu.
  3. Instance de l'observer :
    • Le Mutation Observer est créé et lié à la fonction callback.
  4. Démarrer l'observation :
    • L'observer commence à surveiller le div target pour tout changement spécifié dans les configurations.
  5. Fonctions interactives :
    • Ajouter un nouvel élément : déclenchée par un clic sur le bouton, cette fonction ajoute un nouveau div avec le texte "New element added!" dans le div target.
    • Modifier un attribut : également déclenchée par le même clic, cette fonction alterne la couleur de fond du div target entre 'lightgray' et 'lightblue'. Remarque : bien que le onclick en ligne fonctionne pour cet exemple, il est recommandé d'utiliser addEventListener pour une meilleure séparation du code en production.

Résultats attendus :

  • Ajout d'un nouvel élément :
    • Chaque clic sur le bouton ajoute un nouveau div. Cela déclenche la vérification childList de l'observer et un message vert "A child node has been added or removed." apparaît.
  • Modification d'un attribut :
    • Le même clic change la couleur de fond du div target. Cela déclenche la vérification d'attribut de l'observer. Un message bleu indique quel attribut a été modifié ("The style attribute was modified.").

Cet exemple démontre efficacement comment les Mutation Observers peuvent être utilisés pour surveiller et journaliser les changements dans le DOM, en fournissant un retour en temps réel sur ce qui se passe dans la page web.

Lire un MutationRecord

Le callback reçoit un array d'objets MutationRecord — un par changement détecté. Les propriétés les plus utiles sont :

  • type"childList", "attributes" ou "characterData".
  • target — le nœud affecté par la mutation.
  • addedNodes / removedNodes — des NodeLists de nœuds insérés/supprimés (pour childList).
  • attributeName — l'attribut modifié (pour attributes).
  • oldValue — la valeur précédente, mais uniquement si attributeOldValue ou characterDataOldValue était activé.
const observer = new MutationObserver((records) => {
  for (const record of records) {
    if (record.type === "attributes") {
      console.log(`${record.attributeName} changed from "${record.oldValue}"`);
    } else if (record.type === "childList") {
      console.log(`+${record.addedNodes.length} / -${record.removedNodes.length} nodes`);
    }
  }
});

Un modèle pratique : attendre qu'un élément apparaisse

Un usage courant en situation réelle consiste à résoudre une Promise au moment précis où un nœud apparaît dans le DOM — bien plus efficace que le sondage. L'observer se déconnecte dès qu'il trouve l'élément :

function waitForElement(selector) {
  return new Promise((resolve) => {
    const existing = document.querySelector(selector);
    if (existing) return resolve(existing);

    const observer = new MutationObserver(() => {
      const el = document.querySelector(selector);
      if (el) {
        observer.disconnect(); // stop watching once found
        resolve(el);
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// Usage with async/await:
// const card = await waitForElement(".lazy-card");

Ce modèle s'associe naturellement avec async/await et les Promises.

Pièges et bonnes pratiques

  • Le callback est asynchrone et groupé. Les mutations sont mises en file d'attente en tant que microtasks et livrées après la fin du script en cours — voir microtasks et la boucle d'événements. Vous ne recevez pas un enregistrement par changement individuel ; ils vous parviennent regroupés.
  • Votre callback s'exécute après le changement. Vous êtes notifié de ce qui s'est déjà produit — vous ne pouvez pas annuler ni empêcher une mutation comme vous pourriez le faire avec preventDefault() sur un événement.
  • oldValue est opt-in. Si vous lisez record.oldValue sans avoir activé attributeOldValue/characterDataOldValue, la valeur est null.
  • Évitez de muter le sous-arbre observé dans le callback sauf intentionnellement — cela peut déclencher davantage d'enregistrements et créer une boucle de rétroaction.
  • Appelez toujours disconnect(). Un observer actif maintient sa cible accessible, et oublier de le déconnecter provoque des fuites mémoire. Déconnectez-le lorsque le composant se démonte ou que le travail est terminé.
  • Appelez takeRecords() avant de déconnecter si le dernier lot est important — disconnect() supprime les enregistrements non livrés.

Conclusion

Les Mutation Observers sont un élément essentiel de la boîte à outils JavaScript, offrant des solutions dynamiques pour gérer efficacement les changements du DOM. Ils permettent aux développeurs de créer des applications web réactives et interactives qui répondent harmonieusement aux interactions des utilisateurs et aux modifications programmatiques du DOM. Bien qu'ils soient puissants, il est essentiel d'utiliser les Mutation Observers avec discernement pour maintenir des performances optimales et une bonne expérience utilisateur. En sélectionnant soigneusement les mutations à observer, en minimisant la charge dans les callbacks de mutation et en appelant observer.disconnect() lorsque l'observer n'est plus nécessaire pour prévenir les fuites mémoire, les développeurs peuvent tirer parti des Mutation Observers pour améliorer les fonctionnalités du site sans compromettre l'efficacité. Comprendre et appliquer ces principes permet de créer des interfaces web avancées et conviviales qui se distinguent dans le paysage numérique moderne.

Entraînement

Pratique
Lesquelles des affirmations suivantes sont vraies concernant le JavaScript Mutation Observer ?
Lesquelles des affirmations suivantes sont vraies concernant le JavaScript Mutation Observer ?
Was this page helpful?