1 of 32

JavaScript: propagazione degli eventi + modali

2 of 32

Eventi in JavaScript

Se associate un event listener al "click“ su un elemento, cosa succede se l’utente clicca un figlio di quell’elemento?

3 of 32

Eventi in JavaScript

Esempio: se cliccate su <img>, verrà eseguita la funzione toggle? (CodePen)

4 of 32

Eventi in JavaScript

, un evento click impostato su un elemento verrà eseguito se cliccate su un figlio di quell’elemento.

Nell’esempio, se associate un listener all’evento click sul div, e l’utente clicca sull’img dentro il div, l’handler verrà eseguito

5 of 32

Event.currentTarget vs target

Potete accedere all’elemento cliccato o all’elemento al quale è associato l’event listener:

  • event.target: l’elemento che è stato cliccato, o in generale che ha “generato” l’evento
  • event.currentTarget: l’elemento al quale l’event handler è associato e che sta gestendo l’evento

6 of 32

Event listener multipli

Che succede se avete degli event listener associati sia ad un elemento che a un suo figlio?

  • Vengono eseguiti entrambi?
  • Quale viene eseguito prima?

7 of 32

Event bubbling

  • Entrambi i listener vengono attivati se cliccate sull’elemento interno
  • Di default, il listener dell’elemento più interno viene eseguito prima

Questo ordinamento degli eventi (dal più interno al più esterno) è chiamato bubbling.

div id="interno"

div id="esterno"

8 of 32

stopPropagation()

Per impedire che un evento risalga lungo la catena di genitori, possiamo utilizzare event.stopPropagation():

9 of 32

Event capturing

È possibile imporre che la propagazione dell’evento vada nella direzione opposta, aggiungendo un parametro a addEventListener:�

event.addEventListener(

'click', onClick, { capture: true} );

Questo ordinamento degli eventi (dal più esterno al più interno) è chiamato capturing. (CodePen)

div id=“interno"

div id=“esterno"

10 of 32

stopPropagation()

Possiamo usare event.stopPropagation() anche nel capturing.

11 of 32

Dettagli tecnici

Al verificarsi di un evento, il browser crea un "propagation path" dal target risalendo fino all’elemento html

(Con target si intende l’elemento cliccato, non quello a cui è associato l’event listener)

html

html

div id="interno"

html

body

div id="esterno"

12 of 32

"Capture phase"

Il browser parte dalla cima del propagation path e invoca tutti gli event listener con capture="true". Questa è la "capture phase" (w3c)

html

body

div id="esterno"

div id="interno"

13 of 32

"Target phase"

Quindi, il browser invoca gli event listener associati al target. Questa è la "target phase" (w3c)

div id="interno"

html

html

html

body

div id="esterno"

14 of 32

"Bubble phase"

Per gli eventi con bubbles=true (es. click), il browser risale lungo il propagation path e invoca gli event listener con capture="false". Questa è la "bubble phase" (w3c)

div id="interno"

html

html

html

body

div id="esterno"

15 of 32

preventDefault()

Quando un evento ha un comportamento predefinito dal browser (ad es., il click su un collegamento apre una nuova pagina), è possibile disabilitarlo tramite:

event.preventDefault()

16 of 32

In pratica

Cose di cui non preoccuparvi:

  • Probabilmente non avrete mai bisogno di usare capture, vi basterà solo il bubbling
  • Non vi serve davvero conoscere la sequenza delle tre fasi (“capture phase”, “target phase”, “bubble phase”)

Cose di cui preoccuparvi:

  • Vi serve capire come funziona il bubbling
  • stopPropagation() vi potrà tornare utile
  • preventDefault() anche di più

17 of 32

Esempio: album fotografico

Implementiamo una pagina che mostri un album di foto

18 of 32

Esempio: album fotografico

Implementiamo una pagina che mostri un album di foto

19 of 32

Album fotografico: HTML

Definiamo entrambe le “viste” :

  • La vista dell’album: “thumbnail” di ogni foto
  • La vista “modale”: una singola foto su sfondo semitrasparente
    • Nascosta di default

20 of 32

Album fotografico: CSS (album)

Il CSS per la vista dell’album è abbastanza immediato

21 of 32

Album fotografico: CSS (modale)

La vista modale è meno immediata, ma niente di nuovo.

22 of 32

Album fotografico: CSS (immagine)

Se le dimensioni originali dell’immagine rientrano nello spazio a disposizione, usa quelle; altrimenti, limita l’altezza e la larghezza e quelle del contenitore.

23 of 32

Album fotografico: CSS (modale)

Di default, la vista modale è nascosta tramite display: none;

24 of 32

Lista di foto

photo-list.js definisce una lista globale chiamata PHOTO_LIST contenenti i percorsi delle immagini da caricare.

25 of 32

Thumbnail delle foto

Riempiamo la vista dell’album iterando PHOTO_LIST e aggiungendo elementi <img> ad #album-view.

26 of 32

Click sulle foto

Quando un’immagine nella vista dell’album viene cliccata:

  • Creiamo un altro <img> con lo stesso src
  • Aggiungiamo il nuovo <img> a #modal-view
  • Mostriamo #modal-view

27 of 32

Chiudere la modale

Quando l’utente clicca sulla vista modale:

  • Nascondiamo la modale di nuovo
  • Rimuoviamo l’immagine contenuta tramite:�innerHTML = '';

Vediamo com’è a questo punto…

28 of 32

Problemi

  • Quando la modale è mostrata, non dovremmo poter scrollare
  • Se la viewport non corrisponde con l’inizio della pagina, la modale non la copre interamente

29 of 32

Posizionare la modale

window.pageYOffset rappresenta lo scostamente verticale della viewport rispetto all’inizio della pagina.

Quindi, possiamo posizionare la modale ad una distanza dall’inizio della pagina pari a quel valore!

30 of 32

Attribute style

Ogni HTMLElement ha un attributo style che permette di impostare direttamente le proprietà CSS dell’elemento:

element.style.top = window.pageYOffset + 'px';

In generale, non dovreste utilizzare l’attributo style; è meglio rimuovere e aggiungere classi tramite classList.

Ma se dobbiamo modificare le proprietà CSS in base a valori dinamici restituiti da JavaScript, dobbiamo per forza impostare l’attributo style direttamente.

31 of 32

Impedire lo scroll

La regola body { overflow: hidden; } disabilita lo scroll sulla pagina.

32 of 32

Chiudere la modale (2)

Quando chiudiamo la modale, ri-abilitiamo lo scroll della pagina.

Soluzione finale!