Šta su Event Bubbling, Capturing i Propagation

. . .
image
Photo by Aaron Burden

Šta su Event Bubbling, Event Capturing i Event Propagation i kako raditi sa njima

Event Propagation

Event Propagation je osobina eventa u Javascriptu. Propagation znaći širenje u Engleskom riječniku i to je upravo ono što radi Event Propagation, širi evente kroz elemente u svojoj hierarhiji. To širenje se dešava u tri faze, prva faza je Capturing, druga je Target a zadnja je Bubbling. Proći čemo detaljno šta su ove faze. Capturing je kada krenemo od početka dokumenta (window object) to elementa na kojem je izvršen neki event, što je u našem slučaju "td" element koje je ustvari druga faza to je Target, onda ulazimo u treću fazu koja je Bubbling i vraća se od "td" elementa na početak dokumenta odakle smo i krenuli. Moram isto spomenuti da se propagacija eventa može zaustaviti da ostane na elementu na kojem je izvršena tako što ćemo koristiti funkciju stopPropagation(). Ovo će zaustaviti da se event širi na elemente iznad njega.

NOTE: Veoma važno za znati je da ne svi eventi imaju propagaciju, na primjer blur, focus i scroll nemaju propagaciju. Ne šire evente na svoje parente, grandparente, etc. Također Event Propagation se nekad naziva i Event Delegation što je ustvari isto, samo drugi naziv.

Ispod je animacija koja prikazuje kako radi Event Propagation koristeći bubbling:

Animation by Fatima Trtak

Ovdje imamo "div" element i unutar tog elementa imamo tabelu sa redovima. I u tim redovima imamo checkbox, ako kliknemo na taj checkbox pokrenut će se porpagacija za taj element. I svaki element koji ima na sebi event će biti izvršen, krenut će od checkbox > td > tr > table > div > ... > window. Ovo može biti korisno kada npr. želimo da kreiramo event na svaki od redova u tabli, pa da ne bi kreirali za svaki red pojedinačno, kreiramo event na tabeli i tako ćemo imati jedan event za sve elemente u tabeli. Možda da kreiramo event koji će na klik na red dodati clasu "active", koja će promijeniti boju reda. Ovaj pristup nam može uštediti pisanje eventa za svaki red, a znamo da pisanje manje koda je bolje 😎 .

NOTE: Ako ste možda ćuli o preventDefault() i pomislili zašto da ne koristimo nju za zaustavljanje propagation, razlog je da ova funkcija radi samo za elemente koje imaju default ponašanje kao što je form, ali ako kliknemo na div za njega nemamo default ponašanje pa zato koristimo stopPropagation() da zaustavimo propagating.

Event Bubbling

Ovo je default ponašanje u modernim browserima, tako da ću prvo ovo preći. Event Bubbling je ponašanje koje izvršava evente od početka dokumenta, osim onih koji smo rekli da nemaju propagation (blur, focus, scroll). Ako kliknemo na posljednji element u dokumentu desit će se bubble tj. širenje eventa i izvršit će se svi eventi na parent elementima. Npr. ako kliknemo na checkbox u tabeli, to će uzrokovati izvršavanje eventa na tom checkbox (ako ga ima) i onda će pokušati izvršiti "tr" pa "table" i sva tako do vrha dokumenta. Ispod je animacija koja prikazuje ovo ponašanje:

Animation by Fatima Trtak

Kao što možete vidjeti klik na checkbox uzrokuje bubbling eventa sve do vrha. Tako da ako elementi iznad imaju na sebi event, npr. tabela div izvršit će i njihove evente. Ovo ponašanje se može sprijećiti ukoliko ne želimo da se izvrše svi eventi iznad možemo koristiti stopPropagation() i do će uzrokovati da bude izvršen event samo od checkbox i nijedan drugi. Ovo može biti korisno na tabelama gdje svaki red ima dugme za brisanje reda ili editovanje, a imamo i event na redu, pa bi klik na edit uzrokovao i izvršavanje eventa na redu ili bilo koji event iznad, što nije željeno ponašanje. I ukoliko ne iskoristimo stop propagation, izvršit će se otvaranje prozora za edit npr. i odmah če se izvršiti event na redu koji npr. otvara novu stranicu sa detaljima tog reda. Znaći naš klik na edit će biti kratko prikazan i uklonjen, tako da korisnik nebi mogao ni pristupiti edit prozoru.

Bubbling.html 🎈
1<div>
2 div
3 <table>
4 <tr>
5 <th>table</th>
6 </tr>
7
8 <tr>
9 <td id="checkbox">
10 <input type="checkbox" />
11 </td>
12 <td>input</td>
13 </tr>
14 <tr>
15 <td id="checkbox">
16 <input type="checkbox" />
17 </td>
18 <td>input</td>
19 </tr>
20 <tr>
21 <td id="checkbox">
22 <input type="checkbox" />
23 </td>
24 <td>input</td>
25 </tr>
26 </table>
27</div>
28
29<script>
30 function clicked() {
31 alert(this.tagName);
32 }
33
34 var elems = document.querySelectorAll('div, table, input');
35 for (let elem of elems) {
36 elem.addEventListener('click', clicked);
37 }
38</script>

Iznad imamo code koji možete kopirati i sami probati. Veoma je jednostavan, imamo div i table sa rows, također imamo click evente na div, table i inputs. Također imamo na dnu posta primjer koji možete koristiti da vidite kako ovaj code izgleda i ponaša se.

Event Capture

Ovaj event je dosta sličan Bubbling, ali za razliku od Bubbling kada kliknemo na checkbox u redu ono bi izvršilo evente od vrha dokumenta pa svo do targeta koji je checkbox, znaći event na checkbox bi se izvršio posljednji umjesto prvi. Ovo npr. može biti korisno kada želimo da obavjestimo prvo parenta i izvršimo event i onda da izvršimo child event. Ova faza mora biti enabled jer Bubbling je default ponašanje u browseru, a to možemo uraditi sa addEventListener("click", function(e){},true). Ova vrijednost true je vrijednost parametra useCapture koji ima default vrijednost false, zato se Bubbling koristi jer je Capture isključen po default. Nakon što stavimo vrijednost na true imamo Capture ponašanje u browseru:

Animation by Fatima Trtak

Vidite da je slično ponašanje kao kod Bubbling ali se eventi izvršavaju obrnuto, ispod je code koji je isti kao za bubbling ali smo uključili caputre fazu umjesto bubbling:

Capturing.html 📷
1<script>
2 function clicked() {
3 alert(this.tagName);
4 }
5
6 var elems = document.querySelectorAll('div, table, input');
7 for (let elem of elems) {
8 // We added third parameter 'true' to enable capturing
9 elem.addEventListener('click', clicked, true);
10 }
11</script>

A ovo je primjer kako da zaustavimo event bubbling ili capture koristeći stopPropagation:

StopPropagation.html
1<script>
2 function clicked(e) {
3 alert(this.tagName);
4 //Prevent event propagation
5 e.stopPropagation();
6 }
7
8 var elems = document.querySelectorAll('div, table, input');
9 for (let elem of elems) {
10 elem.addEventListener('click', clicked, true);
11 }
12</script>

Ovo je primjer sa kojim se možete igrati, možete koristiti dugme iznad da promijeniti ponašanje iz "bubbling" u "capturing" i onda klikajte na elemente checkbox, div, table itd. da vidite koji se eventi izvršavaju. Nakon klika dobit će te alert za svaki izvršeni event.I

div
table
input
input
input

Zaključak

Ovo je bila interesantna tema za pisati, i nadam se da je ponašanje evenata sada jasnije. Ako mislite da je ovo bio koristan post, podijelite ga i sa drugima da i oni mogu vidjeti i naučiti iz njega.