Gli eventi del DOM ci consentono di scrivere codice che reagisce all'interazione con le pagine HTML. Abbiamo visto come alcuni eventi non sono generati direttamente dall'utente ma sono una conseguenza di altri eventi. Un tipico esempio è il caricamento di una nuova pagina che segnala il fatto che il browser ha completato la generazione della struttura del DOM.
Nello sviluppo di un'applicazione può essere interessante effettuare elaborazioni o personalizzazioni in base alle variazioni della struttura del DOM. Potremmo ad esempio voler sapere se è stato aggiunto o eliminato un elemento o un suo attributo o se ne è stato modificato il contenuto.
Mutation Observer
La soluzione proposta dal W3C è quella dei Mutation observer.
A differenza dei Mutation event
(di cui parliamo tra breve), gli observer non sono eventi, ma degli oggetti che monitorano elementi del DOM ed eseguono una funzione di callback in corrispondenza di una o più modifiche della loro struttura o dei contenuti. Con questo approccio la gestione delle variazioni del DOM non è sincrona e le variazioni vengono riportate in blocco al gestore JavaScript e non man mano che si verificano, evitando quindi un ingolfamento della coda degli eventi ed una paralisi del browser.
Esaminiamo in concreto come utilizzare i Mutation observer. Nelle sue linee essenziali, per gestire le variazioni degli elementi del DOM dobbiamo innanzitutto creare un Mutation observer ed assegnargli la funzione di callback da eseguire:
// mutationManager è una funzione di callback
// che utilizzeremo per sfruttare i dati acquisiti
// dall'observer
var mutationManager = function(mutationRecords) {
// ...
};
var observer = new MutationObserver(mutationManager);
Nella signature della funzione di callback dovrà necessariamente esserci come argomento un array di oggetti MutationRecords che servirà a contenere le informazioni sulle variazioni avvenute all'elemento in osservazione. Prima di analizzare in dettaglio come interpretare queste informazioni, vediamo come indicare al Mutation observer l'elemento da monitorare (target) e a quali variazioni siamo interessati.
var div = document.getElementById("myDiv");
observer.observe(div, { childList: true, attributes: true });
Il tutto viene effettuato tramite il metodo observe() che prevede due parametri:
- il primo è il target: l'elemento da tenere sotto osservazione;
- il secondo è un oggetto che descrive le variazioni a cui siamo interessati.
Nell'esempio abbiamo indicato di voler osservare le variazioni degli attributi del div myDiv
e di eventuali modifiche dei suoi figli. Le possibili variazioni del DOM che possiamo osservare sono riassunte dalle seguenti proprietà:
Proprietà | Descrizione |
---|---|
attributes |
Valore booleano per l'osservazionedi variazioni degli attributi dell'elemento |
attributeOldValue |
Valore booleano che consente diinserire nei Mutation record il vecchio valore di unattributo (solo se attributes è true ) |
characterData |
Valore booleano per l'osservazionedi variazioni del contenuto dell'elemento |
characterDataOldValue |
Valore booleano che consente diinserire nei Mutation record il vecchio valore delcontenuto dell'elemento (solo se characterData ètrue ) |
childList |
Valore booleano che consentel'osservazione di variazioni dei figli dell'elemento |
subtree |
Valore booleano che prevedel'osservazione di variazioni di tutti i discendenti dell'elemento |
attributeFilter |
Array di nomi di attributi da teneresotto osservazione (es.: ["class", "src"]) |
Una volta impostato il Mutation observer, la nostra funzione di callback verrà invocata quando verranno rilevate le variazioni a cui siamo interessati. Come accennato prima, la funzione riceverà un array di Mutation record con i dettagli sulle variazioni avvenute.
NOTA: È importante comprendere che la funzione di callback non sarà invocata dall'observer nel momento in cui si verifica la variazione di un elemento, ma al termine dell'esecuzione della funzione o comunque del blocco di codice che modifica il DOM. Questo fa in modo che possano verificarsi più variazioni al DOM, ciascuna delle quali viene rappresentata da un singolo Mutation record
.
Il mutation record
Un Mutation record è un oggetto che prevede le seguenti proprietà:
Proprietà | Descrizione |
---|---|
type |
Il tipo di variazione rilevata. Ipossibili valori sono attribute , characterData ochildList |
target |
L'elemento su cui è statarilevata la variazione |
addedNodes |
Un array di nodi aggiunti al DOM |
removedNodes |
Un array di nodi rimossi dal DOM |
nextSibling |
L'eventuale elemento successivoall'elemento che è stato aggiunto o rimosso |
previousSibling |
L'eventuale elemento precedenteall'elemento che è stato aggiunto o rimosso |
attributeName |
Il nome dell'attributo modificato |
oldValue |
Il valore dell'attributo o delcontenuto precedente alla variazione |
Sfruttando le informazioni fornite dai Mutation record
possiamo definire il comportamento della funzione di callback. Ad esempio, il seguente codice visualizza un messaggio sulla console che indica il numero di elementi aggiunti come figli dell'elemento osservato:
var mutationManager = function(mutationRecords) {
var i;
for(i=0; i < mutationRecords.length; i++) {
if (mutationRecords[i].type == "childList") {
console.log("Sono stati aggiunti " + mutationRecords[i].addedNodes.length + " elementi.");
}
}
};
Se decidiamo di non osservare più le variazioni legate ad un elemento del DOM possiamo utilizzare il metodo disconnect()
dell'observer:
observer.disconnect()
Un'ultima importante considerazione riguarda ciò che un Mutation observer è in grado di osservare. Come da definizione, esso può tenere sotto controllo le variazioni della struttura di un sottolabero, la modifica del contenuto o degli attributi di un elemento. Esso non è però in grado di rilevare lo stato interno di un elemento, come ad esempio l'attributo value
o checked
di un elemento <input>
. Quindi in sostanza non possiamo utilizzare un Mutation observer per monitorare le variazioni degli elementi di una form determinati dall'input dell'utente.
Mutation Event, l'alternativa deprecata
Esiste un altro approccio per intercettare le variazioni di struttura e contenuto del DOM e consiste nello sfruttare i Mutation event, una serie di eventi generati dal browser proprio al verificarsi di questo tipo di variazioni.
Purtroppo questo approccio presenta alcuni problemi che possono pregiudicare sensibilmente le prestazioni del browser, tanto da portare il W3C a deprecarli. Ne parliamo in questa guida solo per dare maggior completezza.
Possiamo riassumere i principali difetti dei Mutation event nei seguenti punti:
- sono eventi sincroni, quindi in presenza di numerose variazioni del DOM la coda degli eventi può riempirsi ad un punto tale da rendere il browser non responsivo
- nella gestione di un
Mutation event
può essere necessario modificare il DOM generando quindi altriMutation event
in una reazione a catena che può sfociare in un vero e proprio blocco del browser