Uvod

Događaji su pojave, koje su najčešće rezultat nečega što korisnik uradi (klik mišem, klik na tastaturi, drag and drop….), mada mogu biti izazvane i od strane sistema, browsera… Registrovanje dogadjaja na nekom HTML elementu podrazumeva vezivanje osluškivača dogadjaja (eng. event listener) za HTML elemenat i definisanje posledice tog događaja. Event listener nakon izvršenog dogadjaja poziva na akciju obrađivače događaja (eng. event handler). Event handler je callback funkcija koja se aktivira kao posledica nekog dogadjaja.

Registrovanje dogadjaja

JavaScript

Postoje više različitih metoda za registrovanje dogadjaja u JavaScript-u.

Inline event handler (preko HTML atributa)

Inline event handlers registruje dogadjaj kroz HTML atribut i pripada zastarelom načinu za registrovanje dogadjaja. HTML atribut se dobija tako što se uz naziv dogadjaja dodaje prefiks “on” tj. on{eventtype}.

Primer br.1

Primer br.2

Ovakav postupak i dalje može da “uradi posao” mada nije najbolja praksa jer ima više mana:

  • Ne odvaja JS od HTML-a
  • Sprečava dodavanje više “event handlers” (funkcija koje se izvršavaju nakon JS dogadjaja) po istom dogadjaju
  • Nije praktičano ukoliko treba da se primeni odredjeni dogadjaj na više istih vrsta HTML tag-ova, jer treba upisati kod u svaki tag.
  • Pri korišćenju inline event handler-a ključna reč THIS ukazuje na globalni objekat tj. widnow objekat (jer HTML atribut nije vlasnik funkcije nego samo function call-a).
  • U slučaju da je inline event handler ugradjen u tag <a href=””></a>, javljaju se problemi sa redosledom dogadjaja jer se takodje pokreće atribut href koji će otvoriti drugu stranicu.

Traditional event registration model

Ovaj model je bolji od inline event handler-a jer razdvaja JS od HTML koda i ključna reč THIS ukazuje na targetirani element ali i dalje ne može da se dodaje više od jedne funkcije na isti dogadjaj.

Jedini način da imamo više od jednog event handler-a na istom dogadjaju je pokazan na sledećem primeru:

“addEventListener” (W3C model)

Ovaj model je najprihvaćeniji način registrovanja dogadjaja i omogućava registrovanje više dogadjaja na jednom elementu. Metoda addEventListener podržava i bubbling i capturing redosled izvršavanja dogadjaja a to definiše kroz treći opcioni parametar. Ovaj parametar je boolean-ova vrednost koja definiše da li je redosled izvršenja capture ili ne. Stoga ako je ovaj parametar false znači da nije capturing već je bubbling, dok vrednost parametra true jasno definiše capturing. Defaultna vrednost je false, stoga ako se ne koristi ovaj treći opcioni parametar smatra se da je u pitanju bubbling redosled.

Primer br.1

Primer br.2

Da bi se izbeglo ponavljanje koda, preporuka je da se umesto anonimne funkcije koristi imenovana:

Event objekat

Kada god se desi neki dogadjaj (eng. event), javascript smešta sve relevantne podatke vezane za dogadjaj u event objekat (npr. gde je bio mouse pointer, kojim dugmetom je kliknuto, koje element je kliknut…). Ovaj event objekat se uvek prosledjuje callback funkciji a podacima smeštenim u njemu možemo pristupiti preko svojstva event objekta. Spisak svih svojstava event objekata možete pogledati ovde.

Primer

Primer

See the Pen Primer korišćenja event objekta by Web programiranje (@chos) on CodePen.


Redosled izvršavanja pri propagaciji

bubbling

Ukoliko imamo HTML elemente koji su “ugnježdjeni” jedan unutar drugog a svi imaju zakačen neki event listener, dogadjaji se ne izvršavaju odjednom nego prema odredjenom redosledu. Redosled izvršavanja dogadjaja može biti:
a) Bubbling
Ovakav redosled podrazumeva aktiviranje dogadjaja od unutrašnjeg elementa ka spoljnom (od deteta ka roditelju, pogledaj sliku). Skoro svi dogadjaji su bubbling osim par izuzetaka kao npr. focus dogadjaj kod koga je default-ni redosled capturing.
b) Capturing
Ovakav redosled podrazumeva aktiviranje dogadjaja od spoljnog ka unutrašnjem (od roditelja ka detetu)

Razlika izmedju THIS i event.target

Potrebno je naglasiti da u slučaju postojanja više HTML elemenata sa registrovanim dogadjaima, i medjusobno su ugnježđeni vrednost event.target ne ukazuje uvek isto što i ključna reč “this”!
Svojstvo objekata “event.target” ukazuje na element koji je inicirao dogadjaj dok “this” ukazuje na element na kome se trenutno izvršava obradjivač dogadjaja (event.currentTarget).

See the Pen Razlika izmedju THIS i event.target by Web programiranje (@chos) on CodePen.

Zaustavljanje propagacije

Ukoliko se iz nekog razloga pojavi potreba da se spreči “širenje uticaja dogadjaja” (eng. stop propagation) od targetiranog elementa duž DOM-a koristi se metoda event.stopPropagation(). Ali ako želimo da uključimo u zabranu i targetirani element (tj. da sprečimo da reaguje na dogadjaj), onda koristimo metodu event.stopImmediatePropagation()

Primer

Prevencija podrazumevanog ponašanja

Pri programiranju se javljau zahtevi za prevencijom podrazumevanog ponašanja DOM elemenata nakon nekog dogadjaja. Najčešće je to vezano za sprečavanje link taga da otvori stranicu ili da se forma submituje nakon klika na submit dugme. Najčešće se problem rešava sa sledećom sintaksom:

Primer

U ovome primeru je prikazano kako se sprečava podrazumevano ponašanje link taga:

Medjutim u slučaju da je DOM element kome želimo da promenimo podrazumevano ponašanje ugnježđen, a pri tom njegov roditeljski element takodje ima “zakačen” neki “eventListener”, sa prethodnim načinom nećemo sprečiti da se “okine” dogadjaj na roditeljskom elementu. Stoga je potrebno da sprečimo propagaciju sa event.stopPropagation(); i da u prethodni primer dodamo još jedan red:

Prethodni primer može da se uradi i na drugačiji način dodavanjem return false u okviru funkcije (govori funkciji da treba da se prekine izvršavanje, a samim tim neće doći do reakcije na dogadjaj). Ova linija koda menja obe linije koda iz prethodnog primera.

Dinamičko registrovanje osluškivača dogadjaja

Kada se koristeći JavaScript dinamički grade novi HTML elementi oni se ne “radjaju” sa prikačenim event listener-ima, stoga je potrebno dinamički dodati svakome novom elementu njegov event listener.

Primer

See the Pen Adding dinamic event handler by Web programiranje (@chos) on CodePen.

Još jedno legitimno rešenje za dodavanje event listener-a dinamički dodatim elementima je uz primenu event delagation što je objašnjeno u nastavku.

Event delegation

U slučaju da postoji puno HTML elemenata na koje treba dodati “event listener” kao npr. u slučaju liste sa puno članova, može doći nepotrebne potrošnje resusrsa. Stoga je korisno primeniti “event delegation” tehniku koja se zasniva na tome da se definiše samo jedan “event listener” na roditeljskom elementu. Princip na kojem radi ova tehnika se zasniva na osobini da ukoliko aktiviramo neki dogadjaj na elementu, zbog bubbling rasporeda aktiviranja dogadjaja, posle nekog vremena će taj dogadjaj biti “okinut” i na roditeljskom elementu. A da bi odredili koji je element bio okidač dogadjaja ova tehnika koristi “event.target” svojstvo. Pored jednostavnosti ove tehnike i smenjenje potrošnje resursa, prednost je što su na ovaj način obuhvaćeni i budući dinamički kreirani elementi.

Primer

Postupak počinje sa vezivanjem “event listener-a” za roditeljski element, nakon čega se po aktiviranom dogadjaju pretražuje da li je okidač bio naš tip elementa.

Najjednostavnija primena delegacije dogadjaja je uz korišćenje jQuery metode on(). Ova metoda kao opcioni argument prihvata selektor koji definiše “child” elemente na kojima očekujemo da budu okidači dogadjaja.

NAPOMENA:
Obratiti pažnju da se ove tehnika oslanja na bubbling redosled izvršavanja dogadjaja, ali neki dogadjaji nemaju takav raspored (blur, focus, load, unload…) pa kod njih ne može da se primenjuje. Takodje treba izbegavati dogadjaj “mousemove” jer bi on okidao previše dogadjaja i trošio resurse.

Problem sa gomilanjem osluškivača dogodjaja

U radu sa dogadjajima može doći do “nehotičnog” nagomilavanja “event listener-a”. Ovaj problem se može najbolje objasniti kroz naredni primer:

See the Pen Event problem by Web programiranje (@chos) on CodePen.

Kada se u prethodnom primeru klikne na dugme nakon višekratnog čekiranja checkbox-a, pojavljuju se alert prozori jedan za drugim (isti broj puta koliko je čekiran checkbox). Problem se javlja zbog toga što se pri svakom čekiranju dodaje novi event listener na elemenat dugmeta.
Ovaj problem može da se reši na više načina, ali je u narednom primeru prikazan brzi “hack”:

See the Pen Event problem rešen by Web programiranje (@chos) on CodePen.

Brzo rešenje ovog problema sa kojim sprečavamo gomilanje “event listener-a” je dodavanje metode off(). Sa off() metodom ukidamo sve click listener-e, a zatim sa metodom on() dodajemo novi event listener.

Debagovanje

Događaje možete da pregledate u okviru Elements Panel-a, gde se vide svi registrovani dogadjaji. Ako kliknete na “remove”, možete da uklonite registrovani event handler. Ovo je korisno za brzo proveravanje da li je event handler uzrok neočekivane greške. A klikom na link možete da vidite i samu handler funkciju.
Ako vaš kod koristi i neku biblioteku kao što je jQuery, DevTools može da sakrije event handlere biblioteke a da prikaže samo naše (ovo nije uvek izvodljivo). Ovo se dobija tako što se selektuje polje za potvrdu “Framework listeners”.

event tips

Podelite:

Ostavite komentar