Nieinwazyjny JavaScript - dodawanie zdarzeń
27 sierpnia 2006
Można śmiało powiedzieć, że cała magia JavaScriptu opiera się na zdarzeniach i akcjach wykonywanych po odebraniu któregoś z nich. Zdarzenia to na przykład wpisanie tekstu do formularza, kliknięcie w link, umieszczenie kursora nad obrazkiem, załadowanie dokumentu i tym podobne. Bez nich praktycznie nie istniałyby skrypty.
Aby obsłużyć jakieś zdarzenie potrzeba je przechwycić i wykonać odpowiedni kod. Netscape wprowadził do HTML-a nowe atrybuty obsługujące zdarzenia takie jak onclick, onmouseover, onsubmit — Microsoft podążył tym szlakiem i w końcu okazało się, że jest ich po prostu masa.
Mało kto zdaje sobie sprawę, że to rozwiązanie jest wręcz starożytne.
Pisząc stronę podchodzimy do niej z trzech stron:
- Tworzymy zawartość, wkładamy ją w odpowiednią strukturę - HTML
- Określamy sposób prezentacji treści - CSS
- Dodajemy warstwę zachowania (np.: podczas interakcji z użytkownikiem) - JavaScript
Umieszczając link wraz z atrybutem onclick robimy tak samo źle, jak umieszczając style liniowe poprzez style="". Jeśli uważasz, że można zrobić w niektórych miejscach wyjątek i określić prezentację w strukturze dokumentu, możliwe że dojdziesz do tych samych wniosków jeśli chodzi o JavaScript. Czasem wszakże potrzebujemy jeden mały onclick w jakimś zapomnianym miejscu strony. Moim zdaniem jednak warto podejść do zagadnienia nowocześnie i odrzucić on…… raz na zawsze. 1
Nigdy więcej onclick
Jak więc dodawać zdarzenia do dokumentu i je przechwytywać? Na początek musimy odnaleźć element bądź elementy, którymi należy się zająć. Widnieje tutaj jeszcze jedna zaleta nieinwazyjnego JavaScriptu - jeśli mamy sto elementów ze stoma onmouseover i onmouseout, to prowadzi to do kłopotów:
- HTML rośnie znacznie - sto razy kilkadziesiąt bajtów to kilka kilobajtów więcej w każdym dokumencie.
- Zmiana kodu JavaScript wymaga używania funkcji Znajdź i Zamień, czasem z wyrażeniami regularnymi a czasem jest to po prostu niemożliwe i trzeba edytować wszystko ręcznie.
Wystarczy jednak użyć mechanizmów wyszukiwania elementów w drzewie dokumentu aby pozyskać ich uchwyty albo całe ich tablice i przyporządkować do nich zdarzenie.
Funkcja addEvent
W idealnym świecie używalibyśmy metody DOM addEventListener, lecz Explorer honoruje tylko swoją metodę nazywaną attachEvent. Powstało bardzo dużo uniwersalnych funkcji, a swego czasu Peter Paul Koch urządził konkurs na najlepszą implementację funkcji addEvent. Wygrała taka:
function addEvent(obj, type, fn) {if (obj.addEventListener) {obj.addEventListener(type, fn, false);} else if (obj.attachEvent) {obj["e"+type+fn] = fn;obj[type+fn] = function() {obj["e"+type+fn](window.event); }obj.attachEvent("on"+type, obj[type+fn]);}}
Przyporządkowanie obsługi kliknięcia (onclick) następuje w ten sposób:
addEvent(element, "click", eventHandler)
W wywołaniu pomijamy prefiks "on", a jako trzeci argument przekazujemy nazwę funkcji (eventHandler), która ma być wywołana po kliknęciu w element.
Funkcja obsługująca ten klik powinna wyglądać mniej więcej tak:
function eventHandler(e) {// instrukcje(e.preventDefault) ? e.preventDefault() : (e.returnValue = false);}
Ostatnia linijka z powodzeniem wykonuje robotę za wszelkie return false; jakie dostawialiśmy na końcu onclick w linkach. Po prostu anuluje domyślą akcję, liczy się tylko to co znajduje się w środku funkcji eventHandler.
W co kliknęliśmy?
Co jednak, gdy chcemy wykonać operacje na elemencie z którego wywołujemy zdarzenie, albo chcemy pobrać jakieś jego właściwości? Zwykle robiło się to przez przekazanie zmiennej this. Tutaj będzie trzeba znaleźć element, którego zdarzenie obsługujemy w trochę bardziej skomplikowany sposób.
Funkcja addEvent umieszczona powyżej pozwala odwoływać się przez this, akapity poniżej są rozwiązaniem dla innych implementacji dodawania zdarzeń.
Jak już pisałem, Explorer ma swoje sposoby wykonywania różnych czynności. Zmienna e, używana w funkcji eventHandler zawiera informacje o tym zdarzeniu, a z nich możemy przez e.target wyciągnąć kliknięty element. Lecz nie dla IE. ;-)
function eventHandler(e) {var el;if (window.event && window.event.srcElement) { el = window.event.srcElement; }if (e && e.target) { el = e.target; }if (!el) { return; }(e.preventDefault) ? e.preventDefault() : (e.returnValue = false);}
Zmienna el przechowuje to samo co kiedyś przekazywane this. Przykład użycia.
Przekazywanie zmiennych
Czy da się jednak przekazać funkcji obsługującej zdarzenie jakąś zmienną? W konstrukcji, którą napisałem wyżej nie jest to możliwe, jednak istnieją proste, a zarazem kompleksowe rozwiązania. Można skorzystać z klasy Event biblioteki Prototype albo Yahoo! UI Library.
- 1) Wiem, sam muszę w HTML-u bloga wyrzucić obsługę zdarzeń z paru miejsc. ;)
Najmocniej przepraszam za tak rzadkie posty, strasznie dużo mam obecnie na głowie, mam też nadzieję że we wrześniu powrócę do regularnego pisania. :-(


