L’invasion du html mutant

Slides en ligne
ffoodd.fr/devfest.2024/
Le jeu
ffoodd.fr/devfest.2024/jeu/
Les accès wi-fi
codelab12ans

Le décor

Préparez-vous à découvrir un script qui vous fera baliser !

  1. Lancez le jeu dans votre navigateur favori.
  2. Démarrez votre inspecteur, avec F12 ou Maj+Ctrl+i.

Vous aurez besoin d’ouvrir et refermer votre inspecteur régulièrement.

Le héros

Parce que c’est vous, le héros. Ça change, un peu.

Chaque niveau aura la même structure :

Des personnages mal intentionnés apparaissent inopinément…
À vous de vous en débarrasser avant qu’il ne soit trop tard !

L’échauffement

Prenons quelques repères.

Nous allons nous servir d’un MutationObserver. Quelques règles :

  1. Il faut l’instancier.
  2. Il faut lui donner un cible.
  3. Il faut le débrancher, à un moment.

Ces éléments seront déjà en place dans la suite du jeu
— mais il est important de savoir qu’ils existent.

Le décollage

Bouclez vos ceintures…

Vous avez remarqué l’objet vide, à la fin ? C’est lui qui va nous intéresser le plus !

Le premier palier du jeu concerne les options de base d’une observation.

Le zombie

Entrons dans le vif du sujet !

Les changements d’attribut

Les attributs, ça va, ça vient…

attributes: true

Cas d’usage

Songez à tous ces attributs qui peuvent être importants : hidden, aria-label, aria-invalid

Le vilain

Et oui, il n’y a pas que des gentils, dans la vie.

Les changements textuels

Quoi de pire qu’un changement au sein d’un nœud texte, franchement ?

characterData: true

Cas d’usage

Imaginez un peu : un bouton dont l’intitulé change, mais pas d’état ni de classe auxquels s’accrocher ? Ou mieux, vous faites vos slides en HTML, avec une vue présentateur que vous voulez synchroniser sur votre pagination.

L’alien

Ils sont parmi nous !

Les changements de descendance directe

Les enfants, ça ne tient pas en place !

childList: true

Cas d’usage

Un contenu injecté inopinément — au hasard, lors d’un scroll infini — c’est irritant ! Ou encore : une iframe dont vous devriez ajuster la hauteur en fonction de son contenu…

Ceci n’est pas inspiré de faits réels, évidemment !

Le fantôme

Vous n’avez pas entendu un truc ?

Quand il y a trop d’attributs…

Il faut choisir !

attributeFilter: ['id']

Cas d’usage

Imaginez un framework qui indique l’état invalide d’un champ de formulaire avec une classe, mais sans aria-invalid ? Vous savez comment patcher ça, maintenant.

Toute ressemblance avec des frameworks serait purement fortuite.

Le sorcier

Il faut se méfier des types encapuchonnés
avec un grand bâton.

Les changements de descendance profonde

Il n’y a pas que les parents pour surveiller les enfants : tous leurs aïeux le peuvent.

childList: true, subtree: true

Cas d’usage

Le saviez-vous ? Un WebComponent ne peut pas émettre un événement en disparaissant… Donc si vous voulez déclencher quelque chose, vous saurez comment faire !

Le vampire

Il faut aussi se méfier des types avec un sourire qui déborde.

Les valeurs disparues

Se souvenir du passé, ça peut aider.

attributeOldValue: true
mutation.oldValue.includes('mutant')

On peut aussi affiner :

mutation.attributeName === 'class'

Cas d’usage

Vous avez connu l’époque des classes .open et .show pour afficher ou masquer un élément — et aucun autre changement dans le DOM ? Ou un script tiers qui fait des trucs sans émettre d’événements ?

Bien entendu, ce sujet est une pure fiction.

Le vieillissement

Vous ne l’aviez pas vu venir, hein ?

Les caractères disparus

C’est presque un cercle, les caractères disparus.

characterDataOldValue: true
mutation.oldValue === '💀'

Cas d’usage

Un composant de création de mot de passe qui enlève la description des contraintes franchies… Vous n’avez jamais vu ça ?

Le boss final

C’est le moment de sauver des vies.

Les solutions

Le bazooka

document.querySelectorAll('mu-tant:not([type=""])')
	.forEach(mutant => mutant.remove());

Le précautionneux

if (mutation.target.nodeName === 'MU-TANT' && mutation.target.type !== '') {
	mutation.target.remove();
} else if (mutation.target.closest('mu-tant') !== undefined) {
	mutation.target.closest('mu-tant').remove();
}

Malgré toutes les options, on peut avoir du mal à trier les mutations. Fort à propos, le type MutationRecord dispose de tout un tas de propriétés bien utiles.

Mais encore ?

Les MutationObserver ne sont pas seuls !

Merci

Et à bientôt

Crédits

Laissez votre feedback

Openfeedback