Autor: Paweł Rajewski

Zadanie było proste. Tabelka z czerwonym tłem i linkiem pośrodku. Tło miało zmienić się na zielone po opuszczeniu tabelki przez kursor. Zadanie jak ulał dla zdarzenia onmouseout zachodzącego właśnie w momencie opuszczania obiektu przez kursor myszki. Napisałem:

<TABLE CELLSPACING="5" CELLPADDING="30" 
STYLE="background-color: red;" 
onmouseout="this.style.backgroundColor='green';"> 
<TR> 
<TD><A HREF="adres.html">LINK</A></TD> 
</TR> 
</TABLE> 

Na pierwszy rzut oka kod jest bezbłędny. A jednak nie działa poprawnie. Tło zmienia się na zielone w nieoczekiwanym momencie, tuż po wprowadzeniu kursora na czerwony obszar. A jeśli kursor od początku jest w tabelce, to po wskazaniu linku. Co się dzieje??? Przyczyną jest „bąblowanie” zdarzeń (ang. bubbling).

Na początek informacja podstawowa: ZDARZENIA ZACHODZĄ ZAWSZE. Jeśli wydaje ci się, że nie wpisując w ramach znacznika np. onmouseover=”…”, nie wywołujesz tego zdarzenia, jesteś w błędzie. Zapis onmouseover (lub inny odpowiedni dla innych zdarzeń) nie „wywołuje” zdarzenia, a jedynie określa sposób jego „wykorzystania”. Określa co dane zdarzenie ma zrobić w konkretnym przypadku. Gdy w danym znaczniku nie ma zapisu onmouseover, zdarzenie o tej nazwie i tak zajdzie, tylko nie wykona żadnej „czynności” – nie wywoła żadnych instrukcji ani funkcji.

Gdy więc umieścisz na stronie obrazek, lub jakikolwiek inny obiekt, i przesuniesz nad nim kursor myszki, zdarzenia sypną się jak z rękawa. I będzie ich więcej niż może ci się wydawać…

Dla początkującego twórcy aktywnych stron www, największą pułapką jest jednak nie ilość, lecz tytułowe „bąblowanie” zdarzeń. Bo to, że zdarzenia zachodzą zawsze, nie jest jeszcze problemem – najwyżej pozostaną niewykorzystane. Naprawdę zaskakujące jest to, że zdarzenia się poruszają! A dokładniej – wywołane w ramach jakiegoś obiektu „płyną” w górę struktury dokumentu oddziaływując po drodze na inne napotkane obiekty.

Jeśli nie jest to dla ciebie zrozumiałe, oto przykład:

<HTML> 
<BODY onmouseout="alert('Zdarzenie onmouseout!');"> 
<IMG SRC="obrazek.gif"> 
</BODY> 
</HTML> 

Przesuwaj kursor nad obrazkiem. Alert pojawi się dwa razy – raz, gdy kursor będzie opuszczał obiekt BODY („tło strony”) i wchodził nad obrazek, i drugi raz – gdy będzie opuszczał obrazek i powracał do obiektu BODY (aby to zaobserwować używaj Enter, a nie myszki, dla zlikwidowania okienka alertu). Pierwszy alert jest zrozumiały – obsługa onmouseout dla BODY została zdefiniowana. Ale skąd wziął się ten drugi alert? Obiekt IMG nie ma przecież zdefiniowanej żadnej obsługi onmouseout.

Jak wspomniałem wcześniej, to, że IMG nie ma obsługi onmouseout nie znaczy, że takie zdarzenie nie zachodzi. Zaszło więc, nie wykonało żadnej czynności, po czym… opuściło obiekt IMG i przepłynęło do obiektu bezpośrednio nadrzędnego, czyli w tym przypadku BODY. Ponieważ BODY miało zdefiniowaną obsługę onmouseout (wywołanie alertu), czynność ta została wykonana. Stąd dwukrotny alert – pierwszy to sygnał faktycznego opuszczenia obiektu BODY, a drugi to sygnał opuszczenia obiektu IMG, który przepłynął z IMG do leżącego wyżej w strukturze dokumentu obiektu BODY.

Gdyby obrazek był w komórce tabelki, komórka w rzędzie tabelki, rząd w tabelce, a tabelka w BODY, zdarzenie wywołane przez obrazek przepłynęłoby przez wszystkie te obiekty wywołując związane z nimi funkcje, gdyby takie były zdefiniowane. I to jest właśnie powód, dla którego prosty przykład podany na wstępie nie funkcjonuje.

Pajączek.pl - twórz poprawiaj publikuj

Kursor wchodzi na obszar tabelki (nad obiekt TABLE), ale zaraz potem go opuszcza wchodząc nad obiekt TD (komórka tabelki). Przy opuszczaniu obszaru TABLE zostaje wywołane zdarzenie onmouseout, które wykonuje zdefiniowaną dla niego funkcję zmiany koloru tła tabelki na zielony. Tło zmienia się więc w nieodpowiednim momencie. Przesuwając kursor dalej wchodzimy na obszar linku (obiekt A) opuszczając obiekt TD. Znowu zostaje wygenerowane zdarzenie onmouseout (tym razem sygnalizujące opuszczenie TD), które „nie mając nic do roboty” opuszcza obiekt TD i przechodzi do nadrzędnego TR. Tu także nic nie jest zdefiniowane, zdarzenie przepływa więc wyżej, do TABLE. Tutaj jest zadanie do wykonania – zmiana koloru (ponieważ jednak kolor zmienił się na zielony już wcześniej, efektu nie widać). Po wykonaniu „pracy” zdefiniowanej dla obiektu TABLE, zdarzenie płynie wyżej – do obiektu BODY. Tu również „nie ma pracy”, przesuwa się więc do obiektu najwyższego – document, gdzie kończy swoją drogę.

Taką trasę przebywają niemal wszystkie popularne zdarzenia generowane przez ruch myszki i wiele innych (są jednak zdarzenia, które nie „bąblują” np. onscroll).

Takie zachowanie zdarzeń może być utrapieniem (jak w przykładzie z tabelką), ale może też być przydatne. Gdyby nie to, chcąc wykryć np. kliknięcie na obszarze tabelki musiałbyś definiować onclick osobno dla każdego obiektu znajdującego się w jej wnętrzu (łącznie z komórkami TD, paragrafami P, linkami A itd. itp.). A „bąblowanie” pozwala na umieszczenie tej definicji raz, w znaczniku (obiekcie) TABLE, przez który muszą „przepłynąć” wszystkie „onclicki” wygenerowane na niższych poziomach w strukturze tabelki (czyli – wewnątrz tabelki). Tym sposobem na wyższym poziomie w strukturze dokumentu możesz przechwytywać zdarzenia płynące z poziomów niższych.

Zapewne będziesz przerażony wyobrażając sobie te dziesiątki zdarzeń przepływających przez wszystkie obiekty i wywołujących w nieoczekiwanych momentach twoje funkcje. Na szczęście jest na to metoda. Jeśli nie chcesz aby zdarzenie po wykonaniu swojego zadania popłynęło dalej, możesz unicestwić je kończąc funkcję instrukcją:

window.event.cancelBubble=true; 

Po napotkaniu takiej instrukcji zdarzenie „zatrzyma się” i zniknie.

<HTML> 
<BODY onmouseout="alert('Zdarzenie onmouseout!');"> 
<IMG SRC="obrazek.gif" 
onmouseout="window.event.cancelBubble=true;"> 
</BODY> 
</HTML> 

W tym przypadku alert pojawi się tylko raz – gdy kursor będzie opuszczał obiekt BODY i wchodził nad obrazek. Zdarzenie onmouseout wygenerowane przy opuszczaniu obrazka zostanie przechwycone i zatrzymane – nie przepłynie więc do obiektu BODY i nie wywoła drugiego, „fałszywego” alertu.

Zdarzenia to twory ulotne i krótkotrwałe, żyjące ułamki sekund pomiędzy wyzwoleniem i przepłynięciem swojej trasy „na powierzchnię” (do obiektu document). Ale w tych krótkich chwilach zdarzenia, niczym duchy, materializują się w postaci „namacalnych” obiektów „event” pojawiających się w momencie wygenerowania zdarzenia i znikających gdy kończy ono swoją trasę. Te tymczasowe obiekty mają kilka ciekawych właściwości (jedną z nich – cancelBubble – poznałeś wyżej) i przenoszą sporo informacji na temat „swojego” zdarzenia. Ale to już temat na zupełnie inny artykuł.

Powodzenia w opanowywaniu niesfornych zdarzeń.

Paweł Rajewski