ferrante.pl

  • blog
  • nauka, kurs javascript, kurs jquery
  • o mnie

Jak zrobić własny event handler?

  • JavaScript
  • Tech
data publikacji:

29/08

liczba komentarzy:
5

Jakiś czas temu zrobiłem swój własny event handler dla wszystkich niecodziennych zdarzeń (czyli innych niż domyślne, DOMowe), a że z równania “dawno nie pisałem” + “data ostatniego postu” wychodzi mi “wstyd” – chciałbym opisać proces tworzenia takiego skryptu.

Na wstępnie chciałbym zaznaczyć, że nie chcę zaprzestawać pisania bloga, ale z uwagi, że mam ograniczony wolny czas, postanowiłem pisać notki w maksimum 45 minut plus ewentualna korekta. Wierzę, że okaże się to solidnym antidotum na brak nowych postów. Tak więc – zaczynamy.

Pierwsze, co należy sobie uzmysłowić przy pisaniu event handlera to sposób jego działania. To jest relatywnie proste. Przyjmijmy, że mamy zdarzenie o nazwie item changed, które wskazuje, że coś tam nam się zmieniło w aplikacji – na przykład właściwość innerHTML jakiegoś elementu DOM. Analogicznie do zdarzeń typu onclick, mouseover i tak dalej – przypisujemy zdarzeniu item changed jakąś funkcję, która się odpali po każdym wystąpieniu… item changed. Z kolei to swoiste wystąpienie zdarzenia definiujemy za pomocą funkcji, takich jak fireEvent, fire, triggerEvent czy jakiejkolwiek, która będzie nam się podobać.

Pseudokod wygląda następująco:

BibliotekaOdEventow.przypiszJakiesZdarzenieFunkci(
nazwaZdarzenia,
funkcjaKtoraMaSieOdpalicPoWykryciuZdarzenia,
kontekstWJakimUruchomionaBedzieFunkcja);

W innej części aplikacji, jeśli zdarzenie wystąpiło, dajemy o tym znać części odpowiedzialnej za zdarzenia:

BibliotekaOdEventow.wywolajZdarzenie(nazwaZdarzenia);

W ten sposób funkcja, którą przypisaliśmy do zdarzenia o danej nazwie automatycznie uruchomi się po użyciu pseudo-metody wywolajZdarzenie.

System ten działa podobnie do standardowych zdarzeń DOM:

element.addEventListener("click", function() {}, false);

Tyle że w naszym wypadku musimy dać znać, że chcemy wywołać jakieś zdarzenie. W DOM naturalnie robi to za nas przeglądarka i silnik JS. Do dzieła!

Definiujemy najpierw obiekt – bibliotekę, która będzie odpowiedzialna za trzy zadania – dodanie zdarzenia, uruchomienie go i usunięcie.

var EventHandler = {
	_events: {}
};

Trochę pustawo – mamy jedynie jedną składową obiektu, którą jest _events. To w tym podobiekcie, robiącym za tablicę asocjacyjną, będziemy przetrzymywać wszystkie dodane zdarzenia. No właśnie, musimy je jakoś dodawać. Niech służy temu metoda bind:

var EventHandler = {
	_events: {},

	bind: function(name, callback, context) {
		if (arguments.length === 1 && typeof arguments[0] === "object") {
			var eventName = arguments[0].name;
			var callback = arguments[0].callback;
			var context = arguments[0].context;
		}

		var eventName = eventName || name;

		if (!eventName || !callback) {
			throw new Error("Name and callback are required!")
		} 

		if (!(typeof eventName == "string")) {
			throw new Error("Event name should be a string!");
		} 

		if (!(typeof callback == "function")) {
			throw new Error("Event callback should be a function!");
		}

		if (!eventName.replace(/\s/, "")) {
			throw new Error("Event name should not be an empty string!");
		}

		if (!this._events[eventName]) {
			this._events[eventName] = [];
		}

		this._events[name].push((!arguments[1]) ? arguments[0] : {name: eventName, callback: callback, context: context});
	},

Dzięki takiej, a nie innej konstrukcji funkcji bind, możemy jej użyć dwojako:

EventHandler.bind("item changed", function() { alert("Item is changed"); }, this);

EventHandler.bind({name: "item changed", callback: function() { alert("Item is changed"); }, context: this});

Co ciekawego robi funkcja bind? Najpierw sprawdza, czy argumentem jest obiekt, jeśli tak to przypisuje jego właściwości do zmiennych. Potem mamy serię sprawdzeń, czy podane parametry funkcji są właściwe. Jeśli nie – plujemy stosownym błędem, wbudowanym w silnik JS. Na końcu dodajemy do tablicy w _events nowy obiekt, reprezentujący zdarzenie.

Czas na metodę fire:

fire: function(name, data) {
	if (name && this._events[name]) {
		var data = data || {};

		for (var i = this._events[name].length-1; i > -1; i--) {
			var res = this._events[name][i].callback.call(this._events[name][i].context, data);

			if (res === false) {
				return false;
			}
		}
	}
},

Sprawdza ona, czy zdarzenie o podanej nazwie name zostało dodane i uruchamia wszystkie callbacki co do tego zdarzenia w podanym kontekście (context). Opcjonalnie możemy uruchomić fire z drugim parametrem. Wtedy zostanie on przekazany do każdego callbacka. Co ciekawe, jeśli nasza funkcja zwraca false – zatrzymuje uruchamianie pozostałych callbacków. Zdarzenia uruchamiane są bąbelkowo.

Usuwanie jest trywialne:

remove: function(name) {
	if (this._events[name]) {
		delete this._events[name];
	}
}

Jak to działa w praktyce? Na przykład tak:

var Controller = function() {
	this.itemValue = "value";

	this.setValue = function(value) {
		this.itemValue = value;

		EventHandler.fire("item changed", { newValue: value });
	};
};

var View = function() {

	this.alert = function(eventData) {
		alert("Nowa wartość to: " + eventData.newValue);
	};

	EventHandler.bind("item changed", this.alert, this);
};

var c = new Controller;
var v = new View;
c.setValue("new value");

Działa nieźle.

Warto dodać, że podobną funkcjonalność oferuje jQuery – możemy w łatwy sposób podpinać swoje zdarzenia, a “wyzwalać je” (co u nas robi funkcja fire) poprzez trigger.

Komentarze

  1. 1

    Bedą kolejne części jquery?

    autor komentarza:
    matiit
    data komentarza:
    29/08/2009 @ 22:00
  2. 2

    Teksty o jQuery nie stanowia dla mnie wyzwania – nawet nie wiem, co mialbym napisac. Jesli padna propozycje tematow to sie zastanowie :-)

    autor komentarza:
    ferrante
    data komentarza:
    29/08/2009 @ 23:05
  3. 3

    @matiit
    Ludzie, co wy byscie zrobili jakby John nie napisal jQuery? ;-)
    JS to taki (calkiem) prosty i fajny jezyk…

    autor komentarza:
    piotr
    data komentarza:
    31/08/2009 @ 13:16
  4. 4

    Bardzo fajny art – warto wiedziec jak zrobic takie rzeczy “od kuchni”.
    Prototype robi dokladnie to samo uzywajac wrapperow: Event.observe(‘twoj:event’) by ‘zlapac’ event, oraz funkcji .fire(‘twoj:event’) ktora go wywoluje. Bardzo przydatne przy skryptach ktore ladowane sa zbyt pozno (np. po document.ready/dom:loaded)

    autor komentarza:
    Lukasz
    data komentarza:
    01/09/2009 @ 12:30
  5. 5

    a moze cos o json, ajax w jquery? lub plugin?

    autor komentarza:
    agusssia
    data komentarza:
    09/09/2009 @ 22:27

Dodaj komentarz

idź do góry
  • blog
  • nauka, kurs javascript, kurs jquery
  • o mnie
O mnie

Damian Wielgosik - Programista i dziennikarz. Od siedmiu lat koder front-end i back-end. Lubi dobre technologie. mail skype

Kategorie
  • CSS
  • Drawter
  • ferrante.pl
  • JavaScript
  • jQuery
  • PHP
  • Publicystyka
  • Rozmowy rolowane
  • Software
  • Usability
  • Vademecum
  • W sieci...
Czytam
  • Costa
  • Ludwik
  • Rafał Kukawski
  • RAFi
  • Szafranek
  • Warszawa78

Powered by Wordpress.
Copyright by Damian Wielgosik 2007-2010
Design by Dez.