MutationObserver
Observe les changements dans l’arbre du DOM : éléments ajoutés et supprimés, changements d’attributs et de texte, et les signale dans une seule fonction.
- Renvoie
- Un nouvel objet
MutationObserver.
Exemple
#const chat = document.querySelector('#chat')
const unread = document.querySelector('#unread')
const observer = new MutationObserver((mutations) => {
console.log(`Changements dans ce lot : ${mutations.length}`)
for (const mutation of mutations) {
mutation.addedNodes.forEach((node) => console.log('Nouveau message :', node.textContent))
}
unread.textContent = chat.children.length - 1
})
observer.observe(chat, { childList: true })
function addMessage(text) {
const item = document.createElement('li')
item.textContent = text
chat.append(item)
}
addMessage('Lucas : Hé !')
addMessage('Emma : Qui fait le quiz ?')
console.log('Code synchrone terminé')Les deux messages ont été ajoutés l’un après l’autre, donc l’observateur les a reçus en un seul appel, seulement après la fin du code synchrone.
Définition et utilisation
#MutationObserver permet de réagir aux changements dans la structure de la page, quel que soit le fragment de code qui les a provoqués. C’est utile quand un autre script, une bibliothèque ou une extension modifie le contenu et que vous n’avez pas accès à l’endroit où cela se passe, par exemple pour compter les nouveaux messages d’un chat ou surveiller les attributs d’un widget.
La méthode observe(element, options) exige d’indiquer quoi observer : childList (ajout et suppression d’enfants), attributes (changements d’attributs) ou characterData (changements de texte dans les nœuds de texte). L’option subtree étend l’observation à tous les descendants, attributeFilter la restreint à certains attributs, et attributeOldValue mémorise leurs valeurs précédentes.
La fonction de rappel reçoit un tableau d’objets MutationRecord avec des champs comme type, target, addedNodes, removedNodes et attributeName. Les changements sont regroupés et transmis ensemble comme une microtâche, c’est-à-dire après la fin du code en cours, mais avant les timers. Plusieurs changements effectués l’un après l’autre arrivent donc dans un seul appel.
La méthode disconnect() met fin à l’observation, et takeRecords() renvoie les changements déjà collectés mais pas encore transmis. Évitez d’observer tout le document avec subtree: true si ce n’est pas nécessaire, car lors de grands changements la fonction de rappel serait appelée très souvent.
Syntaxe
#new MutationObserver(callback)
observer.observe(target, options)
observer.disconnect()
observer.takeRecords()Paramètres
#callback
function (mutations, observer)
La fonction appelée après des changements. Elle reçoit un tableau d’objetsMutationRecord.target
nœud
L’élément (ou un autre nœud) à partir duquel commence l’observation.options
objet
Au moins une des optionschildList,attributesoucharacterDataréglée surtrue, plus éventuellementsubtree,attributeFilter,attributeOldValueetcharacterDataOldValue.
Plus d’exemples
#const lesson = document.querySelector('#lesson')
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
console.log(`${mutation.attributeName}, valeur précédente : ${mutation.oldValue}`)
}
console.log('Statut actuel :', lesson.dataset.status)
})
observer.observe(lesson, { attributeFilter: ['data-status'], attributeOldValue: true })
lesson.dataset.status = 'in-progress'
lesson.dataset.status = 'done'
lesson.title = 'Terminée'Le changement de title n’atteint pas l’observateur, car attributeFilter limite l’observation à data-status. Chaque changement a sa propre entrée avec la valeur d’avant le changement.
const editor = document.querySelector('#editor')
const note = document.querySelector('#note')
const observer = new MutationObserver((mutations) => {
console.log(mutations.map((mutation) => mutation.type))
})
observer.observe(editor, { childList: true, characterData: true, subtree: true })
note.firstChild.data = 'Note sur les boucles'
editor.append(document.createElement('hr'))
setTimeout(() => {
observer.disconnect()
note.firstChild.data = 'Personne ne verra ce changement'
console.log('Observation terminée')
}, 100)Un changement de texte dans un nœud de texte est characterData, et l’ajout de <hr> est childList. Le texte se trouve plus profondément que #editor, c’est pourquoi l’option subtree est nécessaire. Après disconnect(), les changements suivants ne sont plus signalés.
Compatibilité des navigateurs
#Largement disponible
Fonctionne dans tous les navigateurs modernes, y compris sur mobile. Vous pouvez l’utiliser sans crainte.
- Chrome
- Edge
- Firefox
- Safari
Bonnes pratiques
#- Indiquez toujours dans les options ce qu’il faut observer.
observe(element)seul, sanschildList,attributesnicharacterData, lève une erreurTypeError. - Ne modifiez pas le fragment observé dans la fonction de rappel sans condition. Chaque changement de ce type redéclenche l’observateur, et une boucle infinie arrive vite.
- Si vous modifiez vous-même le DOM dans votre code, il est plus simple d’appeler la fonction correspondante juste après le changement. Un observateur est utile quand les changements viennent d’un code que vous ne contrôlez pas.
Entrées associées
#- IntersectionObserverObserve si un élément est visible dans la fenêtre ou dans un conteneur défilant, et appelle une fonction quand cela change.
- ResizeObserverAppelle une fonction chaque fois que la taille d’un élément observé change, quelle qu’en soit la cause.
- Element.append()Ajoute des éléments ou du texte à la fin du contenu d’un élément.
- Element.setAttribute()Définit la valeur d’un attribut HTML d’un élément, ou en ajoute un nouveau.
- HTMLElement.datasetUn objet avec les valeurs des attributs data-* d’un élément, pour lire et écrire vos propres données.
Vous voyez une erreur ou il manque un exemple ? Écrivez-nous.