Přejít k navigační liště

Zdroják » JavaScript » DOM události: co o nich možná nevíte

DOM události: co o nich možná nevíte

Články JavaScript

Co všechno víte o zachytávání, bublání a zastavování událostí? Skrývá se v nich více „špeků“, než by jeden tušil.

Nálepky:

Povědomí vývojářů o DOM událostech (v článku mám na mysli ty od W3C/WHATWG) patří k základům v této branži. I středně pokročilý webař dokáže odvykládat základní fakta o tom, jak se události zpracují, kterými fázemi procházejí, že jejich uživatelské zpracování je možné zastavit a též určit, jak na ně bude reagovat prohlížeč sám. Mimo učebnicové příklady se ale čas od času objevují méně obvyklé situace, které jsou ve standardu popsány buď vágně, nebo málo, nebo tak malým písmem, že se jimi málokdo zabývá. Pojďme se podívat na tyto specialitky.

Termitologie

Nejprve si prosvištíme některá slovíčka, abychom si ujasnili názvosloví:

  • Událost (event) je zpravidla vyvolaná uživatelovou interakcí nebo uměle (dispatchEvent); v textu uvažuji pouze ty události, které mají povoleno bublání a zastavení bublání.
  • Posluchač (listener) události je voláním funkce addEventListener přiřazen k HTML prvku, typu události a jedné z fází zpracování události.
  • Cíl (target) události je ten HTML prvek, na kterém byla událost vyvolána.
  • Zachytávání (capture) je první fáze zpracování události, kdy jsou volány posluchače směrem od kořene dokumentu k cílovému prvku.
  • Bublání (bubble) je poslední fáze zpracování události, kdy jsou volány posluchače směrem od cílového prvku nahoru ke kořeni dokumentu.
  • Zastavení události je zavolání event.stopPropagation(). Nikterak to nesouvisí s metodou event.preventDefault, která v tomto článku nebude používána.
Eventflow

Obrázek byl převzat z webu w3.org

Neměnný seznam posluchačů

Začněme něčím jednodušším: co se stane, když během zpracování události přidáme nové či odebereme existující posluchače? Pokud tak činíme na jiném HTML prvku, zpracují se normálně. Pokud je ovšem chceme změnit na aktuálním (tj. tom, jehož posluchač je právě vykonáván), nic se nestane – soupis volaných posluchačů se pro každý HTML prvek vytvoří ještě před jejich zavoláním a v průběhu jejich vykonávání je neměnný. Pokud navíc některý z posluchačů vyhodí výjimku, vykonávání ostatních tím není nikterak ovlivněno. Toto chování je možné vidět na ukázce přidání posluchače a ukázce s výjimkou.

Zastavení události #1

Voláním metody stopPropagation zamezíme dalšímu procházení stromu dokumentu při volání posluchačů (tj. pokud tak učiníme ve fázi zachytávání, zpracování ani nepokračuje „dolů“ směrem k cíli události). Rozhodně tím však nezastavíme vykonávání posluchačů na tom HTML prvku, ve kterém se právě nacházíme (samosebou jen těch, které patří do aktuální fáze). Vidět je to na ukázce zastavení #1.

Pevné pořadí posluchačů

V jakém pořadí jsou vykonávány posluchače v rámci jednoho HTML prvku (ve stejné fázi)? Ač to nemusí být na první pohled ze specifikace patrné, je definováno jako pořadí, ve kterém byly posluchače přidány (experimentálně ověřeno diskuzí s autorem specifikace). Díky tomu je garantováno chování, které vidíme na ukázce pořadí.

Zastavení události #2

Pokud je definováno pořadí v rámci jednoho HTML prvku, můžeme se podívat na metodu stopImmediatePropagation. Její volání nejenže zabrání následnému procházení dalších HTML prvků, ale zastaví vykonávání i dalších posluchačů na prvku aktuálním. Je to vidět na ukázce zastavení #2.

Pořadí capture a bubble

Pojďme se nyní podívat na ten případ, kdy máme posluchače navěšeny přímo na cílový HTML prvek. Fáze zachytávání a bublání se k tomuto prvku vůbec nevztahují; jsou definovány jen pro jeho rodiče. Jakmile jsou na jeho rodičích vykonány posluchače v zachytávací fázi, posouvá se událost do mezistavu zvaného „AT_TARGET“ – vykonávání posluchačů na cílovém prvku. Teprve poté nastané třetí a poslední fáze, bublání (a účastní se jí opět pouze rodičovské prvky). Zajímavým důsledkem tohoto chování je skutečnost, že u posluchačů na cílovém prvku je irelevantní fáze, do které byly navěšeny. A protože záleží na jejich pořadí, může se klidně stát, že se posluchač capture vykoná až po posluchači bubble! Vidět je to na ukázce prostřední fáze.

Zastavení události #3

Na poslední ukázce zastavení #3 jsou na tělo dokumentu navěšeny dva posluchače: nejprve jeden capture, ve kterém je zpracování události zastaveno; poté jeden běžný bubble. Pokud klikneme na odstavec, chování intuitivně odpovídá první ukázce se zastavením – druhý posluchač nebude vykonán. Pokud ale klikneme mimo odstavec (tj. do textu přímo v těle), dojde k vykonání obou posluchačů – protože se jedná o vykonání na stejném cílovém prvku ve fázi AT_TARGET.

Závěrem

„Pravé“ DOM posluchače jsou v komunitě webových vývojářů známé především v teoretické rovině, protože se ve většině situací z historických důvodů používají různé cross-browser abstrakce. Díky tomu vidíme v praxi málokdy použití zachytávací fáze. Jak ale prohlížeče a jejich implementace DOM událostí konvergují, blíží se doba přímého využívání událostí. Hodí se proto tématu opravdu dobře rozumět a znát jeho záludnosti. Máte také nějakou oblíbenou, na kterou se v článku nedostalo? A dozvěděli jste se při čtení něco nového? Dejte vědět v komentářích pod článkem…

Komentáře

Subscribe
Upozornit na
guest
3 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
petr fořt

Pekne shrnuti, diky

lukas svoboda

Super clanek. Odkazy na jsfiddle jsou velmi komfortni. Diky!

Podbor

Díky za pěkný článek. Některé věci asi většina programátorů v JavaScriptu tak nějak „tuší“, ale vždy se hodí ujasnit si je přesně.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.