Bubbling degli eventi in JavaScript

Events Bubbling. Un concetto con il quale prima o poi tutti gli sviluppatori che utilizzano un qualche linguaggio con supporto agli eventi si devono confrontare. Ma mentre il concetto è chiaro quando di parla di linguaggi orientati ad oggetti con supporto all’ereditarietà, la faccenda si fa più oscura quando di parla di Javascript.

Cos’è il bubbling di un evento

Possiamo relazionare il DOM della nostra pagina HTML come un elenco padre-figlio in cui il primo elemento è il tag html. Quando iscriviamo un evento ad un certo livello del DOM, sebbene l’evento sia effettivamente iscritto al solo elemento scelto, questo si propagherà verso l’alto in tutta la catena fino al tag più in cima.

<html onclick="console.log('html-click')">
	<body onclick="console.log('body-click')">
	<div onclick="console.log('div-1 clicked')">
		<div onclick="console.log('div-2 clicked')">
			<a onclick="console.log('last-child clicked')">clicca qui</a>
		</div>
	</div>
	</body>
</html>

La pressione di “clicca qui” nell’esempio sopra produrrà il seguente output:

last-child clicked
div-2 clicked
div-1 clicked
body-click
html-click

Bloccare la propagazione

Javascript mette a disposizione diversi strumenti per modificare il comportamento di un evento rispetto al contesto. In particolare per bloccare la propagazione dell’evento, ovvero per fare in modo che venga scatenato il solo “last-child clicked” occorre fare riferimento al metodo event.stopPropagation():

<html onclick="console.log('html-click')">
	<body onclick="console.log('body-click')">
	<div onclick="console.log('div-1 clicked')">
		<div onclick="console.log('div-2 clicked')">
			<a onclick="console.log('last-child clicked'); event.stopPropagation();">clicca qui</a>
		</div>
	</div>
	</body>
</html>

L’output sarà:

last-child clicked

Modificare la risposta del DOM all’evento

In effetti l’evento su un elemento del DOM agisce su un canale separato rispetto alle modifiche che un elemento è in grado di produrre sul DOM. In effetti se il nostro codice diventa:

...
<a onclick="console.log('last-child clicked'); event.stopPropagation();" href="http://www.google.it">clicca qui</a>
...

Si vede subito che stopPropagation non interrompe il caricamento del sito web inserito nell’attributo href. Per impedire qualsiasi risposta del DOM all’azione:

...
<a onclick="console.log('last-child clicked'); event.preventDefault();" href="http://www.google.it">clicca qui</a>
...

Che però non blocca la propagazione dell’evento. Combinando insieme:

...
<a onclick="console.log('last-child clicked'); event.stopPropagation(); event.preventDefault();" href="http://www.google.it">clicca qui</a>
...

In alcuni browser la somma di questi due metodi corrisponde a:

...
<a onclick="console.log('last-child clicked'); return false;" href="http://www.google.it">clicca qui</a>
...

Ma questa forma ridotta non mi sento di consigliarla poiché non supportata da tutti i browser (incluso Microsoft Edge).

Un piccolo scherzetto…

Cosa produce questo codice?

<html>
	<body>
		<a href="http://www.yahoo.it" onclick="console.log('yahoo clicked')">
			<a onclick="console.log('google clicked'); event.preventDefault();" href="http://www.google.it">motore di ricerca</a>
		</a>
	</body>
</html>

Verrebbe da pensare che al click il browser venga mandato su Yahoo… Eppure non succede niente!
Il motivo è semplice: non è possibile nidificare un anchor element dentro un altro.
E non solo non verrà cambiata pagina, ma non inizierà neppure la risalita dell’evento verso l’alto. Quindi occhio a formattare bene l’HTML 😉

Leave a Reply

Your email address will not be published. Required fields are marked *