Zdarzenie w języku JavaScript.

Zdarzenia elementów DOM

Elementy DOM dostarczają szereg zdarzeń, które wywoływane są kiedy zachodzi interakcja użytkownika ze stroną. Poniżej znajduje sie 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

Istnieje kilka sposobów rejestrowania funkcji, która obsługiwać ma dane zdarzenie. Najprostszy sposób to rejestracja „inline”. Spójrzmy na przykład:

1
<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.

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

1
2
3
4
5
6
7
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. To samo możemy również zrobić za pomocą funkcji anonimowej:

1
2
3
4
5
var button = document.getElementById('button');
button.onclick = function() {
    alert('wcisnieto przycisk');
};

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

1
2
3
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:

1
2
3
4
5
6
7
var button = document.getElementById('button');
 
button.onclick = function() {
    alert('wcisnieto przycisk');
};
 
button.onclick();

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.

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:

1
2
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’.

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

1
2
3
4
5
6
<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:

1
2
3
4
5
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’

W tym paragrafie skupię się na aspektach związanych z samą funkcja obsługującą zdarzenie.

Wszystko  ma zastosowanie również w przypadku „event handler’ów”. Dla potwierdzenia przykład:

1
2
3
4
5
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).

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

1
2
3
4
5
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):

1
2
3
4
5
6
7
8
var button = document.getElementById('button');
var paragraph = document.getElementById('paragraph');
 
button.onclick = function(e) {
    for (var i in e) {
        paragraph.innerHTML += i + '<br />';
    }
};

Warto jednak , 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ń)

Zdarzenia myszy

Przeglądarka może wygenerować następujące zdarzenia związane z użyciem myszki na stronie WWW:

  • onclick – pojedyncze kliknięcie (naciśnięcie i zwolnienie klawisza myszki);
  • ondblclick – podwójne kliknięcie;
  • onmousedown – wciśnięcie klawisza myszki;
  • onmouseup – zwolnienie klawisza myszki;
  • onmouseover – kursor myszki znalazł się w obszarze obiektu;
  • onmousemove – kursor myszki został przesunięty;
  • onmouseout – kursor myszki opuścił obszar obiektu.

Najpopularniejszym z tych zdarzeń jest onclick, które m.in. wykorzystywałem już w przykładach powyżej. Pozostałe zdarzenia są wykorzystywane rzadziej, ale mogą też się przydać jeżeli planujesz na swojej stronie WWW zastosować bardziej zaawansowane efekty, np. interfejs typu „przeciągnij i upuść”.

Zdarzenia myszy posiadają następujące parametry dostępne poprzez obiekt event:

  • clientX, clientY – względne współrzędne kursora myszki (względem obiektu który dostarczył procedurę obsługi zdarzenia);
  • screenX i screenY – ekranowe (bezwzględne) współrzędne kursora myszki;
  • button – numer klawisza myszki który był wciśnięty;
  • altKey – czy klawisz Alt jest wciśnięty;
  • ctrlKey – czy klawisz Control jest wciśnięty;
  • shiftKey – czy klawisz Shift jest wciśnięty.

Wymienione powyżej zdarzenia myszy mogą być wygenerowane każdy wyświetlany element strony, dlatego też odpowiednie funkcje obsługi zdarzeń można dołączyć do większości znaczników HTML (głównie poza tymi umieszczonymi w sekcji <head>).

Zdarzenia klawiatury

Drugą grupą zdarzeń są zdarzenia związane z klawiaturą. Nie są one tak popularne jak zdarzenia myszy – w praktyce poza aplikacjami dostępnymi przez WWW (np. czytniki poczty czy edytory tekstu) nie są one w ogóle stosowane. Lista zdarzeń klawiatury jest krótka i zawiera tylko trzy pozycje:

  • onkeypress – klawisz alfanumeryczny został wciśnięty;
  • onkeydown – klawisz został naciśnięty;
  • onkeyup – klawisz został zwolniony.

Zdarzenia klawiatury posiadają parametry altKey, ctrlKey oraz shiftKey określające czy dany klawisz specjalny był wciśnięty czy nie (analogicznie jak przy zdarzeniach myszy). Jeżeli zaś chodzi o informację który klawisz został naciśnięty, to tutaj jest różnica pomiędzy przeglądarkami, które używają do tego celu różnych pól w obiekcie zdarzenia – keyCode,which oraz charCode. Ten problem można jednak obejść stosując poniższy kawałek kodu JavaScript, które do zmiennej klawisz przypisze kod klawisza:

var klawisz = event.keyCode ? event.keyCode : 
    event.which ? event.which : event.charCode;

Zdarzenia klawiatury, podobnie jak zdarzenia myszy, mogą dotyczyć każdego wyświetlanego na stronie elementu, dlatego też podobnie jak w przypadku zdarzeń myszy odpowiednie procedury obsługi tych zdarzeń można dołączyć do większości znaczników HTML na stronie.

 

Zdarzenia kontrolek formularza

Zdarzenia mogą być także generowane przez kontrolki formularza. Oprócz wymienionych wcześniej zdarzeń myszy i klawiatury, kontrolki formularza mogą dodatkowo wygenerować następujące zdarzenia:

  • onfocus – kontrolka otrzymała fokus (dane z klawiatury będą trafiać właśnie do niej);
  • onblur – kontrolk straciła fokus;
  • onselect – kontrolka została wybrana (wybranie kontrolki nie zawsze wiąże się z przekazaniem jej fokusu);
  • onchange – zawartość kontrolki się zmieniła.

W praktyce zdarzenie onfocus można wykorzystać np. do podświetlenia za pomocą JavaScript pola do którego właśnie można wprowadzać dane, a zdarzenie onblur do usunięcia tego podświetlenia. To drugie zdarzenie można także wykorzystać do sprawdzenia czy dane wprowadzone do pola są poprawne – w przypadku błędnych danych skrypt może wyświetlić odpowiedni komunikat i ponownie przenieść fokus do pola, tak aby zablokować przejście do kolejnego pola formularza.

<input type="text" name="pole" onblur="sprawdzPole(this)">
function sprawdzPole(pole)
{
    if (!pole_jest_poprawne(pole))
    {
        alert('Pole jest źle wypełnione!');
        pole.focus();
    }
    return true;
}

W trakcie testów okazało się że powyższy przykład nie działa jednak poprawnie w Firefox’ie 3 – komunikat się pojawiał, ale fokus nie wracał do sprawdzanego pola (wygląda to na jakiś błąd w tej przeglądarce). IE7 i Opera 9.5 zachowywały się poprawnie.

Spośród pozostałych zdarzeń kontrolek formularza przydatne może być też onchange – zdarzenie to jest generowane za każdym razem gdy zawartość pola formularza się zmieni. Można to wykorzystać do automatycznej aktualizacji innej części strony w trakcie wprowadzania danych przez użytkownika, lub też np. do wyświetlania podpowiedzi dla pola tekstowego.