Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 71 di 112
  • livello avanzato
Indice lezioni

Il flusso di propagazione degli eventi nel DOM

Come funziona e come si gestisce la propagazione degli eventi nel browser: intercettare, bloccare e modificare il comportamento standard del browser.
Come funziona e come si gestisce la propagazione degli eventi nel browser: intercettare, bloccare e modificare il comportamento standard del browser.
Link copiato negli appunti

Abbiamo più volte citato il flusso di propagazione di un evento e di come in certi casi questo possa influenzare l'oggetto identificato dalla parola chiave this. Introduciamo questo importante concetto partendo con un esempio. Supponiamo di avere due paragrafi contenuti in un <div> come mostrato dal seguente codice HTML:

<div id="mainDiv">
	<p id="paragrafo1">Clicca su questo paragrafo</p>
	<p id="paragrafo2">Altro paragrafo</p>
</div>

Immaginiamo ora di voler gestire il clic sul primo paragrafo e sul <div>. Dal momento che si tratta di elementi contenuti l'uno all'interno dell'altro, vengono spontanee alcune domande: quale dei due elementi catturerà l'evento click? Se lo catturano entrambi, chi lo catturerà per primo?

La risposta a queste domande è data dal meccanismo di propagazione di un evento tra gli elementi del DOM. Secondo le specifiche del W3C, la propagazione di un evento avviene in tre fasi:

Fase Descrizione
Capture phase In questa fase l'evento si propaga dalla radice del DOM verso l'elemento destinatario effettivo; facendo riferimento al nostro esempio, l'evento viaggia dal <body> della pagina verso il <div> mainDiv.
Target phase In questa fase l'evento raggiunge l'elemento destinatario; nel nostro caso rappresenta il momento in cui l'evento raggiunge il primo paragrafo.
Bubble phase Questa è la fase in cui l'evento risale l'albero del DOM partendo dall'elemento target fino a raggiungere la radice, passando quindi dagli stessi nodi attraversati nella fase di cattura.

La seguente immagine tratta dalle specifiche del W3C illustra graficamente il propagarsi dell'evento nelle tre fasi:

Flusso propagazione eventi nel DOM

Nel corso delle tre fasi, l'oggetto event associato all'evento viene passato agli eventuali gestori incontrati durante il cammino. Normalmente, nella fase di capturing i gestori ignorano l'evento e lo fanno fluire verso l'elemento target. Una volta raggiunto l'elemento target viene eseguito il codice del gestore associato all'evento. Nella successiva fase di bubbling vengono eseguiti i gestori dell'evento che si incontrano man mano che si va verso la radice del DOM.

Ritornando al nostro esempio, quindi, possiamo desumere che verrà eseguito per primo il gestore del clic sul primo paragrafo e dopo il gestore del clic sul <div>. Possiamo rendercene conto associando il seguente gestore ad entrambi gli elementi:

var myDiv = document.getElementById("mainDiv");
var myParagrafo = document.getElementById("paragrafo1");
var handler = function() { console.log(this.id)};
myDiv.addEventListener("click", handler);
myParagrafo.addEventListener("click", handler);

Otterremo sulla console del browser prima l'id del primo paragrafo e poi quello del <div>.

Tra l'altro, da questo esempio dovrebbe risultare chiaro il perchè è opportuno ricorrere alla proprietà target dell'oggetto event invece che a this per individuare l'elemento su cui si è verificato l'evento. La proprietà target fa riferimento sempre all'oggetto destinatario dell'evento, mentre this rappresenta l'oggetto che in quel momento sta gestendo l'evento. L'oggetto event mette a disposizione anche la proprietà currentTarget che corrisponde praticamente a this.

E se volessimo invertire l'ordine di gestione degli eventi del nostro esempio? Cioè, se volessimo fare in modo che prima venga gestito il clic sul <div> e poi quello sul paragrafo? Possiamo farlo sfruttando un terzo parametro opzionale del metodo addEventListener(). Questo parametro abilita la gestione dell'evento nella fase di capturing, consentendo quindi la sua intercettazione prima che arrivi al destinatario effettivo:

myDiv.addEventListener("click", handler, true);

Immaginiamo ora di voler fare in modo che quando si clicca sul primo paragrafo venga eseguito soltanto il gestore del clic del paragrafo e non quello del <div>, mentre quando si clicca all'esterno del primo paragrafo, ad esempio sul secondo paragrafo, venga eseguito il gestore del <div>.

Possiamo ottenere questo effetto interrompendo il flusso di propagazione dell'evento tramite il metodo stopPropagation() dell'oggetto event. Definiremo quindi un gestore dell'evento clic per il primo paragrafo analogo al seguente:

var handler = function(e) {
	console.log(this.id);
	e.stopPropagation();
};
myParagrafo.addEventListener("click", handler);

In questo modo quando viene eseguito il gestore del clic sul paragrafo, nella fase di targeting, viene bloccato il flusso dell'evento, cioè viene annullata la fase di bubbling.

Un'altra interessante possibilità è l'inibizione del comportamento predefinito in corrispondenza di certi eventi. Supponiamo ad esempio l'evento clic su un link:

<a id="link" href="http://www.html.it">HTML.it</a>

Il comportamento predefinito del browser al clic sul link è il caricamento della pagina corrispondente all'URL. Possiamo associare un gestore all'evento clic sul link come nel seguente esempio:

var link = document.getElementyById("link");
link.addEventListener("click", function() {alert("Stai lasciando questa pagina...");});

Al clic sul link verrà visualizzata la finestra di dialogo con il messaggio specificato, ma in ogni caso il comportamento predefinito del browser verrà mantenuto. Se vogliamo impedire l'esecuzione del comportamento predefinito possiamo utilizzare il metodo preventDefault() dell'oggetto event.

Consideriamo il seguente codice:

link.addEventListener("click", function(e) {
	if (!confirm("Stai lasciando questa pagina. Vuoi procedere?")) {
      	e.preventDefault();
	}
});

Al clic sul link, in questo caso, verrà richiesto se si vuole veramente lasciare la pagina corrente. In caso di mancata conferma verrà inibito il comportamento predefinito del browser.

È importante sottolineare che l'inibizione del comportamento predefinito del browser è cosa diversa dall'interruzione della propagazione dell'evento. Nel nostro caso, l'uso di preventDefault() non impedisce la prosecuzione della fase di bubbling dell'evento.

Ti consigliamo anche