In una delle precedenti lezioni abbiamo visto come i metodi .on() e .off() siano diventati, a partire dalla versione 1.7 di jQuery, i protagonisti assoluti nella gestione degli eventi. Questi metodi, infatti, non solo hanno sostituito .bind() e .unbind(), ma hanno altresì rimpiazzato metodi del calibro di .live(), .delegate() e .die().
Per dirla in altri termini (forse più comprensibili ai lettori che per la prima volta si affacciamo all’utilizzo di questo framework) i nuovi metodi .on() e .off() diventano fondamentali anche nella gestione della event delegation.
Questa tecnica consente di associare una funzione (handler) ad un determinato nodo dell’albero del DOM per poi intercettare l’evento relativamente a tutti i nodi figli (presenti e futuri). Tutto ciò è possibile grazie ad un effetto tipico di Javascript che prende il nome di event bubbling o event propagation, ovvero la propagazione di un evento in risalita lungo tutto l’albero del DOM (cioè sino alla root).
Questa tecnica offre diversi vantaggi:
- riduce il consumo di risorse di memoria in quanto non si aggancia un handler ad ogni elemento ma solo al nodo genitore;
- consente di lavorare anche su elementi attualmente non presenti nel DOM (che, ad esempio, saranno caricati dinamicamente in seguito).
Facciamo un esempio basandoci su un semplice codice HTML di un ipotetico menu composto da un elenco puntato:
<html>
<body>
<div id="contenitore">
<ul id="menu">
<li><a href="link-1.html">Item #1</a></li>
<li><a href="link-2.html">Item #2</a></li>
<li><a href="link-3.html">Item #3</a></li>
</ul>
</div>
</body>
</html>
Ora supponiamo di voler scatenare una data azione al click sui link appartenenti al nostro menu. Seguendo quanto imparato sino ad ora utilizzeremmo un codice del genere:
$('#menu a').on('click',function() {
// ... faccio qualcosa ...
});
Questo codice, è bene precisarlo, non ha nulla di sbagliato ma potrebbe non essere adeguato alle nostre esigenze. Si supponga, ad esempio, di avere, in giro per la nostra pagina web, una qualche funzione che, dinamicamente, interviene sul nostro menu aggiungendo un quarto link al menu. Ad esempio:
$('#menu').append('<li><a href="link-4.html">Item #4</a></li>');
Ora, la nostra funzione per intercettare i click sui link del menu non funzionerebbe sul nuovo link inserito dinamicamente, questo perchè, attraverso il codice visto sopra, abbiamo "attaccato" un handler a ciascun link materialmente presente nel DOM al momento del caricamento del codice Javascript.
Come fare, quindi, a gestire anche gli elementi che vengono inseriti in un secondo momento? L’event delegation è la risposta al nostro quesito.
Se noi avessimo "attaccato" l’handler al nodo genitore avremmo potuto sfruttare il bubbling per lavorare su ogni nodo figlio anche se successivamente aggiunto. Vediamo un esempio di codice:
$('#menu').on('click','a',function() {
// ... faccio qualcosa ...
});
Attraverso questo codice abbiamo utilizzato .on() per agganciare il nostro handler al menu (abbiamo eliminato dal selettore il riferimento a <a>) ed abbiamo specificato "a" come secondo argomento passato al metodo.
Così facendo il nostro metodo resterà in ascolto e verificherà se i click sull’elemento con id "menu" si è scatenato a seguito della pressione di un link (come specificato nel secondo parametro) oppure no ed eseguirà la funzione di callback solo in caso affermativo.
Con questo codice, quindi, non è importante che il link fosse già presente nel DOM all’atto del caricamento del codice Javascript essendo l’handler associato non ai singoli link ma alla lista che li contiene.
In questa logica i link del menu non sono più l’elemento cui associare l’handler ma i "grilletti" che scatenano l’evento che riguarda il loro contenitore. Sfruttando opportunamente questa tecnica, quindi, potremo gestire gli eventi di ciascun nodo del DOM senza doverci preoccupare che questo sia effettivamente presente al caricamento del codice Javascript.
Una piccola nota finale: se non sappiamo a quale nodo applicare il metodo .on() avremo sempre la possibilità di utilizzare questa tecnica agganciando l’handler all’intero documento, in questo modo:
$(document).on('click','#elemento_nuovo', function() {
// ... faccio qualcosa ...
});
Bloccare la propagazione di un evento
Abbiamo visto come ogni evento su un singolo elemento della pagina si propaghi in risalita lungo l’albero del DOM sino a raggiungerne la radice (l’intero documento, cioè). Questo fenomeno, detto bubbling degli eventi, consente di sfruttare la tecnica della event delegation vista sopra.
In taluni casi, tuttavia, può essere utile o necessario bloccare la propagazione di un evento ed impedire che questo risalga lungo il DOM. Per farlo è possibile applicare il metodo .stopPropagation() ad uno specifico evento. Ad esempio:
$('#menu a').click(function(e) {
e.stopPropagation();
// ... faccio qualcosa ...
});
Con questo codice impedisco che l’evento click su uno dei link del nostro menu risalga verso il suo contenitore ed oltre.
Si noti che la funzione di callback del metodo .click() è munita di un parametro (che io ho chiamato "e") il quale rappresenta il nostro evento ed è l’oggetto al quale andremo ad applicare il metodo .stopPropagation().