Witam, w kolejnej odsłonie mojego cyklu dotyczącego przygotowań do egzaminu “MCSD: 70-480 - Programming in HTML5 with JavaScript and CSS3”. Tak jak można przeczytać w nagłówku, tematem dzisiejszego wpisu jest wywoływanie i obsługa zdarzeń w JavaScript. Zapoznamy się więc ze sposobami rejestracji funkcji obsługujących zdarzenia, poznamy zagadnienie bąbelkowego zachowania się zdarzeń, dowiemy się także jakie zdarzenia udostępniane są przez elementy DOM. Zapraszam do lektury.

Zdarzenia elementów DOM

Elementy DOM dostarczają szereg zdarzeń, które wywoływane są kiedy zachodzi interakcja użytkownika ze stroną. Poniżej lista najważniejszych zdarzeń modelu DOM:

  • onAbort - wywoływane jest w momencie zaniechania ładowania strony
  • onBlur - wywoływany jest kiedy element przestaje być aktywny (traci “focus”)
  • obDblClick - zdarzenie podwójnego kliknięcia w obiekt
  • onChange - wywoływany jest w momencie gdy obiekt zmieni swoją zawartość
  • onClick - zdarzenie kliknięcia elementu
  • onError - wywoływany jest kiedy w skrypcie wystąpi błąd
  • onFocus - wywoływany jest kiedy element staje sie aktywny (uzyskuje “focus”, przeciwieństwo ‘onBlur’)
  • onKeyDown - wywoływany jest w momencie naciśnięcia klawisza klawiatury
  • onKeyUp - wywoływany jest w momencie puszczenia klawisza klawiatury
  • onLoad - wystepuje po załadowaniu elementu
  • onMouseOver - występuje w momencie najechania na element kursorem myszki
  • onMouseOut - występuje w momencie opuszczenia przez kursor myszki obiektu
  • onSelect - wywoływany jest kiedy zawartość obiektu zostanie zaznaczona
  • onSubmit - występuje w momencie zatwierdzenia formularza
  • onUnload - wywoływany jest gdy strona zostanie zmieniona (np. kliknięto link i następuje przekierowanie)

Widzimy, że jest tego trochę. W dalszej części posta, opisane zostanie jak obsługiwać takie zdarzenia.

Sposoby rejestracji funkcji obsługujących zdarzenie

Wywoływanie i obsługa zdarzeń w JavaScript to tak na prawdę dwa aspekty: sposoby rejestracji funkcji obsługi zdarzeń oraz szczegóły samej funkcji opisującej zdarzenie. W tym paragrafie skupimy się tylko na rejestracji.

Istnieje kilka sposobów rejestrowania funkcji, która obsługiwać ma dane zdarzenie. Poniżej przedstawiam poszczególne z nich.

Rejestracja inline

Najprostszy sposób to rejestracja “inline”. Spójrzmy na przykład:

<a href="index.html" onclick="alert('Kliknięto link strony głównej!')">
  Strona główna
</a>

W tym przypadku mamy do czynienia z rejestracją obsługi zdarzenia ‘onclick’ - do zdarzenia tego przypisany jest bezpośrednio kod, który ma się wykonać gdy zdarzenie wystąpi (w tym przypadku jest to zwykły alert). Oczywiście taki sposób obsługi zdarzeń nie jest prawidłowy, ponieważ miesza logikę zawartą w kodzie JavaScript z widokiem czyli treścią w HTML. Ponadto w tym przypadku nie mamy możliwości odwołania się do obiektu, który kody wywołał (poprzez słowo kluczowe ‘this’). W związku z tym nie będziemy poświęcać więcej uwagi temu sposobowi i przejdziemy dalej.

Przypisanie funkcji

Dużo lepszym sposobem przypisywania funkcji do zdarzeń jest zrobienie tego bezpośrednio w skrypcie. Spójrzmy na przykład:

var button = document.getElementById('button');

function eventHandler() {
    alert('wcisnieto przycisk');
}

button.onclick = eventHandler;

Jak widać, w przykładzie pobieramy obiekt przycisku oraz definiujemy funkcję ‘eventHandler’, która ma zostać wykonana gdy nastąpi zdarzenie. Samą rejestrację zdarzenia mamy w linii siódmej - wystarczy do właściwości ‘onclick’ elementu DOM przypisać nazwę funkcji.

Przypisanie funkcji anonimowej

To samo możemy również zrobić za pomocą funkcji anonimowej:

var button = document.getElementById('button');

button.onclick = function() {
    alert('wcisnieto przycisk');
};

Jeśli chcemy zaprzestać obsługi danego zdarzenia, wystarczy przypisać mu wartość ‘null’:

var button = document.getElementById('button');

button.onclick = null;

Każde zdarzenie, możemy również wywołać bezpośrednio z kodu skryptu - przykład poniżej:

var button = document.getElementById('button');

button.onclick = function() {
    alert('wcisnieto przycisk');
};

button.onclick();

Użycie funkcje addEventListener

Wszystko pięknie, ale widać od razu, że taki sposób na obsługę zdarzeń ma swoje wady - do każdego zdarzenia przypisać można tylko jedną funkcję realizującą jego obsługę. Czy jest więc możliwość przypisania do zdarzenia większej liczby “event handler’ów”, tak jak jest to możliwe np. w C#? Odpowiedź brzmi tak - spójrzmy na taki oto przykład:

var button = document.getElementById('button');

function firstEventHandler() {
    alert('pierwsza funkcja obslugujaca zdarzenie');
};

function secondEventHandler() {
    alert('a to druga funkcja obslugujaca zdarzenie')
};

button.addEventListener('click', firstEventHandler, false);
button.addEventListener('click', secondEventHandler, false);
button.addEventListener('click', function() {
    alert('a to funkcja anonimowa');
}, false);

Powyższy przykład pokazuje, że do rejestracji funkcji obsługi zdarzenia, można użyć metody ‘addEventListener’ - jako pierwszy parametr, przyjmuje ona typ zdarzenia, drugi to funkcja obsługująca zdarzenie. Trzeci parametr włącza lub wyłącza bąbelkowe zachowanie zdarzeń (o tym za chwilę). Przykład pokazuje, że za pomocą metody ‘addEventListener’ można rejestrować zarówno funkcje nazwane jak i anonimowe, zalecane jest jednak użycie funkcji nazwanych, ponieważ będzie wówczas możliwe jej późniejsze wyrejestrowanie.

Wyrejestrowywanie event handlerów

Skoro jesteśmy przy wyrejestrowywaniu “event handler’ów”… Służy do tego metoda ‘removeEventListener’. Jak zwykle w takich przypadkach, spójrzmy na przykład:

button.removeEventListener('click', firstEventHandler, false);
button.removeEventListener('click', secondEventHandler, false);

W powyższym przykładzie, możemy wyrejestrować tylko funkcje ‘firstEventHandler’ i ‘secondEventHandler’, ponieważ tak jak wspomniałem, trzecia z zarejestrowanych wcześniej funkcji jest anonimowa. Metoda ‘removeEventListener’ przyjmuje dokładnie takie same parametry jak metoda ‘addEventListener’.

Bąbelkowe wywoływanie zdarzeń

Przy okazji omawiania powyższych metod, wspomniałem o bąbelkowym przechwytywaniu zdarzeń. Oczywiście najlepiej zacząć od przykładu, najpierw kod HTML:

<div id="root" style="width: 100px; height: 100px; background: red">
  <div style="width: 75px; height: 75px; background: yellow">
    <div style="width: 50px; height: 50px; background: blue"></div>
  </div>
</div>

Jak widać, mamy trzy zagnieżdżone “div’y”, każdy kolejny mniejszy i w innym kolorze (tak aby można je było odróżnić i łatwo było w każdy kliknąć). Rozważmy teraz kod JavaScript:

var div = document.getElementById('root');

div.onclick = function() {
    alert('zdarzenie click!');
};

Widzimy, że do pierwszego, najwyższego w hierarchii bloku, przypisano obsługę zdarzenia ‘onclick’. Nie byłoby w tym nic dziwnego, jednak jeśli wypróbowalibyśmy ten przykład (można to zrobić tutaj), okazuje się, że zdarzenie przypisane do bloku o identyfikatorze “root” wywoływane jest również jeśli klikniemy w blok, który jest w jego wnętrzu.

A więc, bąbelkowe wywoływanie zdarzeń polega na przekazywaniu zdarzenia do kolejnych elementów w górę hierarchii dokumentu (aż do elementu ‘document’). Należy o tym pamiętać, ponieważ jeśli w naszym przykładzie, wszystkie bloki miałyby przypisaną swoją obsługę zdarzenia ‘onclick’, kliknięcie w blok znajdujący się najgłębiej spowodowałoby wywołanie wszystkich trzech funkcji obsługujących!

Funkcja obsługująca zdarzenie - parametry zdarzenia i słowo kluczowe ‘this’

Tak jak wspomniałem wcześniej, w tym paragrafie skupię się na aspektach związanych z samą funkcja obsługującą zdarzenie.

Zachowanie ‘this’

Wszystko o czym pisałem w poście na temat słowa kluczowego “this” ma zastosowanie również w przypadku “event handler’ów”. Dla potwierdzenia przykład:

var button = document.getElementById('button');

button.onclick = function() {
    this.value = 'Nacisnij mnie!';
};

Powyższy kod pokazuje, że w funkcji obsługującej zdarzenie ‘onclick’ przycisku, mamy za pomocą słowa kluczowego “this” dostęp do właściwości i metod elementu, który wywołał zdarzenie (w tym przypadku, w momencie kliknięcia przycisku, zmieniamy po prostu tekst znajdujący się na nim).

Dostęp do parametrów zdarzenia

Bardziej interesującym zagadnieniem jest dostęp, w funkcji obsługującej, do parametrów zdarzenia. Spójrzmy na taki kod:

var button = document.getElementById('button');

button.onclick = function(e) {
    alert(e.type);
}

Przykład ten nie robi niczego szczególnego, widzimy jednak, że funkcja obsługująca tym razem przyjmuje parametr wejściowy “e” - ten parametr jest przekazywany do funkcji obsługującej zdarzenie automatycznie, w momencie gdy ono nastąpi. Zawiera on szereg właściwości, które mogą być przydatne w trakcie obsługi zdarzenia (w przykładzie wyświetlany jest na przykład typ zdarzenia) - poniższy kod wyświetla wszystkie właściwości dostępne dla zdarzenia ‘onclick’ (oczywiście, trzeba uprzednio zdefiniować przycisk o identyfikatorze ‘button’ oraz paragraf o identyfikatorze ‘paragraph’ - do przetestowania tutaj):

var button = document.getElementById('button');
var paragraph = document.getElementById('paragraph');

button.onclick = function(e) {
    for (var i in e) {
        paragraph.innerHTML += i + '';
    }
};

W ramach tego wpisu nie będę się zagłębiał w omawianie poszczególnych właściwości zdarzeń, myślę że każdy może to zrobić w miarę potrzeb, we własnym zakresie. Warto jednak myślę, zwrócić uwagę na dwie przydatne metody dostępne w parametrze zdarzenia:

  • ‘preventDefault’ - zatrzymuje ona domyślne działanie zdarzenia (np. domyślnym działaniem zdarzenia kliknięcia linku jest przeniesienie na nową stronę); nie wszystkie domyślne zdarzenia da się zatrzymać, można jednak to sprawdzić dzięki właściwości ‘cancelable’, równiez dostępnej w parametrze zdarzenia
  • ‘stopPropagation’ - jak sama nazwa wskazuje, zatrzymuje ona propagowanie zdarzenia w górę (czyli przeciwdziała bąbelkowaniu zdarzeń)

Wywoływanie i obsługa zdarzeń w JavaScript - podsumowanie

To wszytko na dziś. Tak właśnie wygląda wywoływanie i obsługa zdarzeń w JavaScript. Myślę, że jest to tematyka dość podobna do rozwiązań dostępnych na przykład w C#, dlatego opisane zagadnienia są wystarczające do tego aby móc sobie z tym samodzielnie poradzić.

W kolejnym wpisie zajmę się obsługą wyjątków w JavaScript. Zapraszam!