L’event propagation in JavaScript: bubbling e capturing

In Javascript la Bubbling consente di propagare l’evento dal centro verso l’esterno, la Capturing dall’esterno verso l’interno della struttura.

JavaScript è il mondo, non per nulla è uno dei linguaggi più utilizzati al mondo ed è alle base dei più conosciuti framework front-end. La sua conoscenza è imprescindibile, si può conoscere Angular, React ma senza sapere come sono implementanti è una conoscenza a metà. In quest’articolo analizzeremo una delle caratteristiche di JavaScript, l’event propagation, ovvero l’ordine con cui gli eventi occorrono. Esistono due tipi di propagazione:

  • Bubbling
  • Capturing

La tipologia di propagazione viene definita tramite il parametro useCapture della funzione addEventListener():

addEventListener(event, function, useCapture);

Per capire al meglio l’event propagation utilizzeremo un esempio con una serie di box contenuti uno dentro l’altro. Inizialmente i box saranno colorati con delle gradazioni di blu, quando gli event saranno scatenati utilizzeranno una gradazione del rosso. La struttura HTML sarà così definita:

<div class="hz-box" id="box1">
    <div class="hz-box" id="box2">
        <div class="hz-box" id="box3">
            <div class="hz-box" id="box4">
            </div>
        </div>
    </div>
</div>

Bubbling

Nell’event bubbling cliccando nel box al centro il click viene propagato anche a gli altri box, ma in che modo? Il primo evento click che verrà invocato sarà dal box più interno fino a quello più esterno. In generale gli eventi saranno scatenati in quest’ordine:

inner box -> outer box -> body -> document -> window

Per abilitare il bubbling dovremo passare false o null a addEventListener(), ad esempio:

box1.addEventListener('click', (event) => {
    // ...
}, false);

Oppure senza passare nessun parametro (comportamento di default che abilita la fase bubbling):

box1.addEventListener('click', (event) => {
    // ...
});

Per rendere dinamica l’event propagation andremo ad utilizzare dei setTimeout() a cui andremo ad aggiungere un delay tramite un counter per garantire l’ordine (ricorda che anche se dei setTimeout() sono stati definiti in un certo ordine nulla garantisce che termino nello stesso ordine!). All’interno del timeout andremo a cambiare il colore dello sfondo del box. Il codice è così definito:

const isCapturingFase = true; // Set to false or null to enable Bubbling phase
let count = 0;
const box1 = document.getElementById("box1");
const box2 = document.getElementById("box2");
const box3 = document.getElementById("box3");
const box4 = document.getElementById("box4");

box1.addEventListener('click', (event) => {
    console.log('Box1 event click fired');
    setTimeout(() => {
        box1.style.backgroundColor = "#ff9999";
    }, 1000 * ++count);
}, isCapturingFase);

box2.addEventListener('click', (event) => {
    console.log('Box2 event click fired');
    setTimeout(() => {
        box2.style.backgroundColor = "#ff3333";
    }, 1000 * ++count);
}, isCapturingFase);

box3.addEventListener('click', (event) => {
    console.log('Box3 event click fired');
    setTimeout(() => {
        box3.style.backgroundColor = "#cc0000";
    }, 1000 * ++count);
}, isCapturingFase);

box4.addEventListener('click', (event) => {
    console.log('Box4 event click fired');
    setTimeout(() => {
        box4.style.backgroundColor = '#660000';
    }, 1000 * ++count);
}, isCapturingFase);

Abbiamo aggiunto dei console.log() per renderci conto di quando l’evento click del box viene invocato. Eseguendo il codice avremo in console prima il click del box più interno fino a quello più esterno:

Box4 event click fired
Box3 event click fired
Box2 event click fired
Box1 event click fired

Visivamente avremo:
JavaScript bubbling

Puoi provare il codice al seguente link.

Capturing

Nell’event capturing JavaScript consente di fare scatenare il primo evento click invocato è a partire da quello più esterno a quello più interno. Avremo:

window -> document -> body -> outer box -> inner box

Per abilitare il capturing dovremo settare a true l’evento useCapture:

box1.addEventListener('click', (event) => {
    // ...
}, true);

Andando a settare a true la variabile del codice di esempio attiveremo la fase capturing:

const isCapturingFase = false;

In console avremo il click dal box più esterno a quello più interno:

Box1 event click fired
Box2 event click fired
Box3 event click fired
Box4 event click fired

Visivamente avremo:
JavaScript capturing

Puoi provare il codice al seguente link.

Differenza tra stopPropagation e preventDefault

Esistono delle funzioni che ci consentono di stoppare la propagazione degli eventi, in particolare esistono due funzioni:

  • stopPropagation() che consente di stoppare la propagazione indipendentemente dalle due fase (se capturing o bubbling)
  • preventDefault() ferma l’evento solo se in fase captuing bloccando solo il comportamento di default dell’elemento (ad es. spunta su una checkbox)

Vediamo un esempio pratico che stoppa la propagazione. Nell’HTML avremo:

<div class="hz-box" id="box1">
    <div class="hz-box" id="box2">
    </div>
</div>

Aggiungendo event.stopPropagation() nel click del box2:

box1.addEventListener('click', (event) => {
    console.log('box1');
});
box2.addEventListener('click', (event) => {
    console.log('box2');
    event.stopPropagation();
});


Cliccando nel box centrale avremo in console solo il click del box2 in quanto la propagazione viene stoppata e non raggiungerà mai il box1.

Per un esempio con preventDefault() bloccheremo il comportamento di default di una checkbox. Nell’HTML avremo:

<form>
    <label for="hz-checkbox">Click on the checkbox:</label>
    <input type="checkbox" id="hz-checkbox"/>
</form>


Andando a settare event.preventDefault() nell’evento click sulla checkbox e settando la capturing event:

const hzCheckbox= document.getElementById("hz-checkbox");
hzCheckbox.addEventListener("click", function(event) {
    console.log('preventDefault() blocks your click');
    event.preventDefault();
}, false);


Con questo codice non potremo mai flaggare la checkbox.

E’ possibile testare il codice per entrambi gli eventi al seguente link.

Pubblicato in Front-end, JavaScript

L’hoisting delle variabili var in JavaScript

L’hoisting delle variabili var in JavaScript consente di utilizzare le variabili prima della loro dichiarazione

Per utilizzare una variabile con var in JavaScript bisogna creare e inizializzare la variabile. Una variabile non ancora inizializzata ha undefined come valore (vedi: Il ciclo di vita delle var in JavaScript). Fin dalla prima versione di JavaScript è possibile utilizzare le variabili prima di dichiarare. Di conseguenza, il seguente codice è del tutto legittimo:

    console.log(myVar); // => undefined
    var myVar = 1;

L’interprete JavaScript è come se traducesse il codice in questo modo:

    var myVar;
    console.log(myVar); // => undefined
    myVar = 1;

La dichiarazione di myVar è stata spostata in cima al codice e dato che, nel ciclo di vita delle variabili dichiarate con var la fase di dichiarazione e inizializzazione avvengono insieme, la variabile assume il valore di undefined fin quando non gli verrà assegnato un valore.

JavaScript effettua l’hoisting solo per la dichiarazione delle variabili e non per l’inizializzazione, avremo quindi che:

    console.log(myVar); // => undefined
    var myVar = 1;
    console.log(myVar); // => 1

Solo var myVar viene spostato in cima dello scope, il suo assegnamento resta nello stesso punto di codice, quindi accendendo alla variabili prima del loro assegnamento ci verrà restituito undefined:

    var myVar;
    console.log(myVar); // => undefined
    myVar = 1;
    console.log(myVar); // => 1

Indice:

Pubblicato in Front-end, JavaScript Taggato con: , ,

L’hoisting delle classi in JavaScript

L’hoisting per classi non consente di accedere ad una classe prima delle sua dichiarazione a causa della temporal dead zone

Le class, introdotte da ECMAScript 6, cercano di simulare le “classi” di altri linguaggi consentendo di utilizzare super per accedere alle classi padre, definire metodi statici e estendere altre classi. Ecco un esempio di classe:

    class MyClass {
        constructor(name) {
            this.name = name;
        }
        print() {
            console.log(this.name);
        }
    }

    var myClass = new MyClass("Datrevo");
    myClass.print(); // => Datrevo

Leggi altro ›

Pubblicato in Front-end, JavaScript Taggato con: , , ,

L’hoisting delle costanti in JavaScript

Le costanti non sfruttano l’hoisting, per poterle utilizzare bisogna prima dichiararle e poi utilizzarle per non generare errori

In JavaScript è possibile creare ed utilizzare costanti all’interno del blocco scope. Le costanti devono essere dichiarare, inizializzate e assegnate nello stesso momento altrimenti verrebbe generato un errore:

    const MY_CONST; // => SyntaxError: Missing initializer in const declaration

    MY_CONST = 1;
    console.log(MY_CONST);

Leggi altro ›

Pubblicato in Front-end, JavaScript Taggato con: , , ,

L’hoisting delle variabili let in JavaScript

Le variabili let non sfruttano l’hoisting e non possono essere lette prima della loro dichiarazione per la temporal dead zone

In JavaScript, le variabili let sono dichiarate in cima al blocco di istruzioni di cui fanno parte. Accedere prematuramente alle variabili let genera un errore (e non un undefined come accade con le variabili definite con var) in quanto si trovano nella temporal dead zone e non possono ancora essere utilizzare (vedi: Il ciclo di vita di let, const e class in JavaScript).

    console.log(myVar); // => ReferenceError: myVar is not defined
    let myVar;

Leggi altro ›

Pubblicato in Front-end, JavaScript Taggato con: , ,

L’hoisting delle funzioni in JavaScript

L’hoisting delle funzioni in JavaScript consente di utilizzare le funzioni prima di dichiararle e consente di scrivere codice più leggibile

In JavaScript, grazie all’hoisting è possibile utilizzare una funzione indipendentemente da dove viene definita. Per le funzioni non viene restituito undefined o errori di accesso come per le variabili let (temporal dead zone). Il seguente codice viene correttamente eseguito:

    console.log(myFunction()); // => Datrevo
    function myFunction() {
        return "Datrevo";
    }

Il codice risulta equivalente a:

    function myFunction() {
        return "Datrevo";
    }
    console.log(myFunction()); // => Datrevo

Leggi altro ›

Pubblicato in Front-end, JavaScript Taggato con: , , ,

L’hoisting in JavaScript

L’hoisting consente di utilizzare variabili e funzioni prima della loro dichiarazione

In qualsiasi codice scritto con JavaScript le variabili sono molto utilizzate, per questo motivo è molto importante capire il concetto di hoisting. Per hoisting si intende quel meccanismo che muove la dichiarazione di variabili e funzioni all’interno dello scope della funzione in cui si trovano (o nello scope globale se si trovano in nessuna funzione). Leggi altro ›

Pubblicato in Front-end, JavaScript Taggato con: , , , , , ,

Utilizzando il sito, accetti l'utilizzo dei cookie da parte nostra. maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi