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

HTML5 W3C logo

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.

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…

Autor pracuje ve společnosti Seznam na všem, co alespoň trochu souvisí s JavaScriptem. Ve volném čase se mimo jiné zabývá věcmi, které alespoň trochu souvisí s JavaScriptem. O obojím občas tweetuje jako @0ndras.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 3

Přehled komentářů

petr fořt diky
lukas svoboda supr clanek
Podbor Pochvala
Zdroj: https://www.zdrojak.cz/?p=9326