ugrás a tartalomhoz

Javascript - window és this nem azonos

inf · 2009. Jún. 1. (H), 18.19
Sziasztok!

Egy nagyon idegesítő ie hibára keresek megoldást:

window.setListener=function ()
{
  alert(this==window)
}
window.setListener();


Tudja valaki, hogy mi lehet itt a this, mert ie-ben false-t ad vissza..
 
1

Gyorshegesztés

inf · 2009. Jún. 1. (H), 18.28

<script>

Function.prototype.bind=function (o)
{
	var m=this;
	return function ()
	{
		return m.apply(o,arguments);
	}
};

window.setListener=function ()
{
	alert(this==window)
}.bind(window);
window.setListener();

</script>
Ettől függetlenül még érdekelne, hogy mi az az object.
2

call

Poetro · 2009. Jún. 1. (H), 19.04
window.setListener.call(window);
3

Nem

Ustak · 2009. Jún. 1. (H), 22.16
találtam mást, csak a Te prog.hu-s hozzászólásodat a témában, pedig idegen nyelven (angol :-)) is nézelődtem, vagy húszféle search - term kombót kipróbálva...
Érdekes, hogy elsiklottak e fölött..
szerk Bocs Poetro, a fő szálba akartam, csak már késő van...:-)
4

Hmm

inf · 2009. Jún. 2. (K), 01.37
Hát nehéz erre a témára olyan egyedi kulcsszót kitalálni, amire találatokat is dob a tutorialokon meg egyeben kívül.
Egyébként microsoft developer fórumba is írtam ezzel kapcsolatban.
5

Yepp

inf · 2009. Jún. 2. (K), 01.58
Yepp, a bind is ugyanezt csinálja. (Amúgy a bind azért kell, mert ciklusban másolok a window objectre event kezeléssel kapcsolatos függvényeket... Szóval most az Object.extend-em elég csúnyán néz ki, mert az ie-nél kivételezni kell a window objectre.)

Kb így:

	//object extending
	//ie has 2 bugs
		//toString,valueOf,toSource are not represented in for-in
		//in methods of window this won't be the window object, so window must be bound on the methods
	if (!BrowserFeatures.HTMLElement)
	{
		var hiddenProperties=["toString","valueOf","toSource"];
	}
	Object.extend=function (context,source,overWrite)
	{
		for (var property in source)
		{
			if ((overWrite && source[property]!==Object.prototype[property])  || 
				!(property in context) || 
				(property in context && context[property]===Object.prototype[property]))
			{
				//ie bound bug
				if (!BrowserFeatures.HTMLElement && context===window && is.Function(source[property]))
				{
					context[property]=source[property].bind(window);
					continue;
				}
				context[property]=source[property];
			}
		}
		//ie represent bug
		if (!BrowserFeatures.HTMLElement)
		{
			for (var i=0; i<hiddenProperties.length; i++)
			{
				var property=hiddenProperties[i];				
				if (source[property]!==Object.prototype[property] && (overWrite || context[property]===Object.prototype[property]))
				{
					//ie bound bug
					if (context===window && is.Function(source[property]))
					{
						context[property]=source[property].bind(window);
						continue;
					}
					context[property]=source[property];
				}
			}
		}
		return context;
	};
Az egyező részeket azért nem akarom külön függvénybe tákolni, mert IE-nél a HTMLElement sem létezik, aztán manuálisan másolom be ezzel a függvénnyel minden egyes elembe onload-kor a paramétereket. Így is elég lassú, a ciklus, szóval nincs kedvem még 2 függvényhívást betenni elemenként.

Az ember azt gondolná, hogy egy

Object.extend=function (context,source,overWrite)
{
	for (var property in source)
	{
		if (!(property in context) || overWrite)
		{
			context[property]=source[property];
		}
	}
	return context;
}
bőven elég, aztán jön a pofára esés, meg az IE szörnyűségei....

(A másik IE bug, hogy a toString stb tulajdonságokat nem veszi bele for-in-be, szóval azokat is külön kell másolni...)

De még tudnék sok ilyet sorolni, ami megkeseríti az ember életét. 1500 sor crossBrowser kód után már úgy gondolom, hogy túl vagyok a nehezén.


Na azért sorolok pár bugot, hogy mások is tanuljanak belőle:

1.)
Ami működik
<custom></custom>

ami nem
<custom />


2.)
Ami nem megy
window.custom=function ()
{
 alert(this==window)
}
window.custom()


3.)
Ami nem megy:
location.hash=custom;
//máshol a history-ben ugrik, ie-nél nem - iframes trükközéssel megoldható


4.)
HTMLElement hiányzik, manuálisan kell kiegészíteni az új tulajdonságokat. Meg úgy egyébként sem szabványos néhány dolog a dom kezelésében.

5.)
attachEvent-nél nem a megadási sorrendben futtatja az eseménykezelőket, szóval ha olyan kiegészítőt akar írni az ember, ami onload-ra fut minden előtt, akkor néz egy nagyot.

Egyelőre most több nem jut eszembe, de vannak meglepő dolgok.
6

Nézegettem a jQurey kódját

Ustak · 2009. Jún. 2. (K), 15.25
és találtam benne egy ilyet:

....
inArray: function( elem, array ) {
		for ( var i = 0, length = array.length; i < length; i++ )
		// Use === because on IE, window == document
			if ( array[ i ] === elem )
				return i;

		return -1;
	},
....
Nem lehet hogy a fenti dologba is belejátszik? Persze lehet hogy hülyeség... Majd windows alatt leszek próbálkozom.
7

Nem

inf · 2009. Jún. 2. (K), 16.47
Szia!

Ez egy újabb bug, amiről nem tudtam, de nem játszik bele a dologba, viszont annyit segítettél, hogy módosítani kell ===-re, mert különben összekeverheti a kettőt.

Amit én írtam, ott úgy néz ki, hogy csinál egy klónt a window objectről, és azt használja this-nek, mert sem a windowval sem a documenttel nem egyenlő a this ezzel a megadásmóddal, viszont a tartalma teljes mértékben megegyezik a window tartalmával.
8

ie objektumodellje

vbence · 2009. Jún. 2. (K), 18.24
Én is beleütköztem a problémába korábba az IE-vel kapcsolatban. magamnak kb így indokoltam: az IE nem DOM alapokon lett tervezve. Használ ugyan objektukokat, de ezek nem a DOM objektumai. A DOM objektumok, amiket szkrptekkel elérsz csupán wrapperek, vagy dekorátorok.

Így elő fordulhat, hogy egy elméleti DOM objektumot (akár node-ot akár magát a windowt) több különböző szkript objektum testesítsen meg. Két node összehasonlításának elősegítésére hoztam létre az alábbit:

DesIE.actualNode = function (elem) {
	if (typeof(elem.parentNode) == "undefined" && typeof(elem.firstChild) == "undefined")
		return window;
	return elem.previousSibling ? elem.previousSibling.nextSibling : (elem.parentNode ? elem.parentNode.firstChild : elem.firstChild.parentNode);
}
Tehát a példa így festene:

alert(DesIE.actualNode(this) == DesIE.actualNode(window));
9

Működik?

inf · 2009. Jún. 2. (K), 19.10
Nem próbáltam ki a mintádat, de tudtommal IE csak parentElement-et tud, legalábbis a régebbi verziók biztosan, te pedig parentNode-t használsz. De lehet, hogy fordítva mondom :-P Utánaolvasok.

Ok, én tudtam rosszul, ff tolja a parentElement-et, ie meg a parentNode-t...


Egyébként eléggé limitált a módszered használhatósága, én írtam egy cross-browser event interfacet, amit rá lehet húzni bármelyik objectre, viszont ha az object a dom része, akkor a definiált dom eseménykezelőkre rácsatlakozik.
(Nem olyan nagy kunszt felvenni a létező 20-30 eseményt, meg a hozzájuk tartozó event objecteket létrehozni.)

Az objectekhez tartozó eseménykezelőket egy tree-ben tárolom, ahol az azonosítók sorban:
element,type,method

A te változatoddal ha az element valamilyen általam létrehozott object, akkor nem megyek semmire, mert azt is a window eseménykezelői közé pakolná. Ha az element-hez kötöm a hozzá tartozó eseménykezelőket, akkor mondjuk nincs szükség erre az összehasonlításra, de ebben az esetben beszennyezném a globális névteret.

Szóval vannak olyan esetek, amikor jobb inkább binddel bemaszkolni a this-t, a window-nál, aztán annyi. Azt legalább automatikusan meg lehet oldani, és nem kell odafigyelni, hogy mindig kiírjam a DesIE.actualNode-t.
10

eseménykezelő

vbence · 2009. Jún. 2. (K), 19.32
Hogy hogynem én is esemény-absztrakcióra használtam a dologt. Fontos a node életciklusát szem előtt tartani: a létrehozott node-odhoz hozzáadhatsz eseménykezelőket, viszont ilyenkor még semmiképpen nem kell felkészülni események feldolgozására (hacsak nem mutation eventekről van szó, ezek crossbrowser emulálása viszont a lehetetlennel határos).

Onnantól, hogy a dokumentum fájába helyezted a node-ot, már működik a fenti módszer az azonosításra.
11

Miért?

inf · 2009. Jún. 3. (Sze), 02.39
Miért lenne lehetetlen?
Ha mindkét(ff,ie) böngészőben működik valami, akkor nagyon szimplán meg tudom csinálni. (most már...)

Például oninput-ra:

HTMLElement.Implements(HTMLEventInterface);
.
.
.
	var TextEvent=Class.Create(
	{
		static_prepareConfig: function ()
		{
			if (!BrowserFeatures.HTMLElement)
			{
				this.type="propertychange";
			}
		},
		initialize: function (element,name,eventSource)
		{
			this.parent.initialize.apply(this,arguments);//target,method,stopPropagation,preventDefault
			if (!BrowserFeatures.HTMLElement && eventSource.propertyName!=="value")
			{
				this.cancelEvent=true;
			}
		}
	}).Extends(Event);

.
.
.
	var HTMLEvents=
	{
		.
		.
		.
		input: TextEvent,
		.
		.
		.
	};
        eventTree.createEvents(HTMLElement,HTMLEvents);
...

$("textarea#valami").setListener("input",function ()
{
 alert(this.value);
});
A prepareConfig átírja az element.addEventListener előtt az element,type,listener paramétereket amennyiben szükséges. Az initialize meg magának az event objectnek a konstruktora...
Úgy döntöttem, hogy inkább saját Event osztályokat csinálok, mert máshogy elég gáz crossBrowser eseménykezelést írni.
(egyelőre ie,ff,op alatt megy az oninput, chrome meg safari nem viszi)

Mondjuk a mutation eventek ennél gondolom komplikáltabbak, de azokat nem álltam neki kidolgozni, mert sosem használtam még őket. Nem is nagyon tudok olyan helyzetet elképzelni, ahol hasznukat venném.


szerk:
Haha meg néztem JQuery-t, amit ők 700 sorban oldottak meg, azt én 320 sor alatt hoztam, bár ez az ő kódolási stílusukban 300 sor alatt lenne. Szóval büszke vagyok.
Így belegondolva nem is lassú a kódom (bár attól féltem), az események hozzáadása, ami kicsit lassú, de azon meg tudok még tuningolni, ha nem vagyok megelégedve vele. Egyébként JQuery forrása szerintem borzalmas. Prototype.js ezerszer szebb.
12

Cikk?

Ustak · 2009. Jún. 3. (Sze), 16.19
Ha nem túl copyright a dolog én szívesen elolvasnám, annak is örülnék ha csak annyi lenne a cikk hogy "ezt én így csinálom:" copy+paste forrás -ha nincs időd megírni :-) - és az is érdekelne (ha nem túl copy-copy right) hogy mi az a gyakorlati alkalmazás, melynek háttereként megéri írni egy ilyet.
Üdv: Gábor.
13

:-)

inf · 2009. Jún. 4. (Cs), 00.08
Egyelőre fejlesztési fázisban van a dolog, szóval még nem adok ki forrást róla, viszont ha adsz egy email címet, akkor július közepe-vége fele el tudom küldeni doksikkal meg ilyesmivel. Szabadon felhasználható lesz majd úgyis, meg azon az oldalon is fent lesz a leírása, amihez fejlesztem.

A dolog úgy indult, hogy egy híroldalt, amiben van néhány közösségi, és egyéb funkció, szóval egy elég komplikált oldalt kezdtem el fejleszteni saját részre, szóval félig hobbiként, félig azért, hogy komolyabb dolgokat is csináljak, és legyen referenciám.
Az oldal nemzetközi lesz, célközönség megvan, meg nagyjából megvannak a kapcsolati rendszerek, szóval komoly látogatottságú oldalakon lesz meghírdetve, ha elindul. (Ezt a részét nem én intézem.) A lényeg ezzel kapcsolatban, hogy van esély rá, hogy azonnal komoly látogatottsága lesz az oldalnak, ha meg befut, akkor pedig folyamatosan.
Na most a komoly látogatottsággal együtt jár a sok letöltés, neten pedig az adatforgalmat komoly pénzekben mérik.

Az adatforgalom csökkentésére találtam ki, hogy inkább jsonnal viszem át azokat a dolgokat, amiket módosítani szeretnék, mert a html-hez képest szerintem még az is kevesebb adatátvitel. Nyilván elsőre nem, mert le kell tölteni a js fájlokat, stb.. viszont utána már pusztán az adat megy át a html sablon nélkül, és még abból is csak konkrétan az, ami frissül.

Szóval a cél az volt, hogy egy elég tömör kódtárat hozzak létre a számomra fontos funkciókkal, crossBrowser legyen (mmint a frissebb böngészőket vigye, úgy mint ff2,ie8,chrome stb.. ie6 nem cél) és mellette még könnyen kezelhető, gyors, és szerver oldalról könnyen frissíthető. Szal nem az a típusú cucc, hogy működik, kész jólvan, hanem most pl 3adszor néztem át az eseménykezelős motort, és írtam újra az egészet.
Az egész vége tetszett a legjobban, teszteltem 1000 div-re az onmouseover hozzáadását, aztán nézegettem az eseményekhez tartozó adatok tárolását.
Ugye eseményeket úgy tárolsz le, hogy van egy fád 3 kulcssal: element,type,method. Ezek közül az elementet simán beraktam egy object kulcsú hash-be elsőre, ami működött is, és nem kellett foglalkoznom vele, hogy mit csinál. Aztán az átnézésnél jöttem rá, hogyha az elementnek egy tulajdonságához kötöm az adatok tárolását, akkor sokkal gyorsabb lesz a kikeresés és hozzáadás. Szóval át írtam ezt:

	var events=Class.Singleton(
	{
		static_classes: 
		[
			Reference,
			Hash,
			Reference
		],
.
.
.
.
	},Tree);
erre:

	var events=Class.Singleton(
	{
		static_classes: 
		[
			Class.Create(
			{
				set: function (eventListeners,element)
				{
					element.events=eventListeners;
				},
				get: function (element)
				{
					return element.events;
				}
			}),
			Hash,
			Reference
		],
.
.
.
.
	},Tree);
és 5x-ös lett az 1000div-hez az onmouseover hozzáadásának a sebessége :-) Na ez tetszett.

Kis magyarázat:
[colorer]
Class.Singleton(osztályForrása,szülőOsztályHaVan)
[/colorer]
Ez egy singleton osztályt hoz létre a forrás és a szülőosztály alapján, és egyből példányosítja. Nyilván ha singleton-t akarsz létrehozni, akkor legtöbbször kapásból csinálni akarsz róla egy példányt, és csak azt használni.

A Tree egy többdimenziós adattároló, vagy lehet fának is mondani. A static_classes paraméterből nyeri az adatokat arról, hogy az egyes dimenziókon az adattárolás milyen típusú objectekkel történik.
Ilyen object lehet pl:
Array: integer -> object
Hash: string -> object
Reference: object -> object
Azért mindig objectet írok, mert jelen esetben :
Reference->Hash->Reference adattárolás volt az első
itt az első Reference-ből az element alapján lekéri az ahhoz tartozó Hash-t, ami a típusokat tárolja, a Hash-ből a type alapján lekéri a másik Reference-et, ami pedig a method alapján tárolja le az eseményhez tartozó beállításokat.
Szóval mondjuk Tree.get([element,type,method]) lekéri az "events" tárolóból az ezekhez tartozó beállításokat.

Erre azért van szükség, mert browsertől függően mondjuk változhat a type vagy az element, a method-ot meg én változtatom meg, amikor hozzáadom a saját event objectet létrehozó részt. Pl a fentebbi oninputos példámban az alap type az "input", viszont ha a browser ie, akkor ez változik "propertychange"-re, ezt a változtatást a static_prepareConfig függvény végzi. Szóval amikor hozzáadom az eseményt addEventListener-rel vagy az attachEventtel, azelőtt gyakorlatilag minden megadott paraméter megváltozhat, és emiatt szükségem van a megváltozott paraméterek megadására, hogy ugyanazzal az [element,type,method] hármassal később mondjuk törölhető legyen az esemény a listából. (nyilván a törléshez a módosított element,type,method változókat kell használnom, nem azokat, amik alapján letároltam a dolgokat.)

Térjünk vissza a Tree-hez. Megadtam neki a 3 kulcshoz a 3 tárolót típusonként (element=object=Reference,type=string=Hash,method=function=Reference). Ez szépen ment is, viszont a Reference szerkezete olyan, hogy tömbben tárolja az adatokat [[kulcs1,érték1],[kulcs2,érték2]...] formában. A kulcs az element az érték meg nyilván a Hash object, ami az aktuális típusokat tárolja.
A Tree megszabott függvényeken keresztül kommunikál az objektumokkal:
get(kulcs) -> lekérés, set(érték,kulcs) -> beállítás, remove(kulcs) -> törlés.. etc.
Itt csak a get-re és a set-re van szükség az element esetében, mert ha egyszer létrehozok egy tárolót egy elementhez, akkor utána már nem törlöm, csak lekérem.

Az egész element-hez tartozó Hash kikérést egy új osztállyal egyszerűsítettem, ami az element.events-be menti a Hash-t, és onnan is kéri vissza. Nyilván ez sokkal gyorsabb, mint a tömbben tárolás, és tömbön ciklussal végigmenős kikeresés, ezért gyorsult 5x-ösére az eseménykezelők hozzáadása a divekhez.


Szóval a beállítások tárolása így zajlik, a teljes events singleton így néz ki:

	var events=Class.Singleton(
	{
		static_classes: 
		[
			Class.Create(
			{
				set: function (eventListeners,element)
				{
					element.events=eventListeners;
				},
				get: function (element)
				{
					return element.events;
				}
			}),
			Hash,
			Reference
		],
		setListener: function (element,name,method,config)
		{
			this.set(config,[element,name,method]);
		},
		removeListener: function (element,name,method)
		{
			return this.remove([element,name,method]);
		},
		fireListeners: function (element,name,eventSource)
		{
			this.invoke(function ()
			{
				this.initialize(eventSource);
			},[element,name]);
		},
		createEvent: function (element,name,eventClass)
		{
			this.setProperty(eventClass,[element,name,"eventClass"]);
		},
		createEvents: function (element,source)
		{
			Object.toHash(source).each(function (eventClass,name)
			{
				this.createEvent(element,name,eventClass);
			},this);
		},
		getEventClass: function (element,name)
		{
			var eventClass=this.getProperty([element,name,"eventClass"]) || 
				this.getProperty([HTMLElement,name,"eventClass"]) ||
				CustomEvent;
			return eventClass;
		}
	},Tree);
(a "name" és "type" egyébként fel van cserélve benne ahhoz képest, ahogy mondtam. )

A Tree-n tárolom az egyes eseménytípusokhoz tartozó osztályokat is, mert ott meg [element,type/name] a kulcsok, amik meghatározzák azt, hogy milyen osztálynak kell az eventObjectet létrehozni. Amit az oninputhoz megadtam: TextEvent az például egy eventObject osztály. Abból az initialize végzi a hagyományos event object (eventSource) alapján a paraméterek másolását. Mutatok olyat, amin jobban látszik:

	var MouseEvent=Class.Create(
	{
		initialize: function (element,name,eventSource)
		{
			this.parent.initialize.apply(this,arguments);
			if (this.type=="mouseover" || this.type=="mouseout")
			{
				this.relatedTarget=BrowserFeatures.HTMLElement
					?eventSource.relatedTarget
					:this.type=="mouseover"
						?eventSource.fromElement
						:eventSource.toElement;
				//fixes mouseover-mouseout bubbling bug
				if (this.relatedTarget && (this.relatedTarget.isAncestor(element) || this.target.isAncestor(element)))
				{
					this.cancelEvent=true;
					return;
				}
			}
			if (BrowserFeatures.HTMLElement)
			{
				this.layerX=eventSource.layerX;
				this.layerY=eventSource.layerX;
				this.pageX=eventSource.pageX;
				this.pageY=eventSource.pageY;
			}
			else
			{
				this.layerX=eventSource.offsetX;
				this.layerY=eventSource.offsetY;
				this.pageX=eventSource.clientX+(document.documentElement && document.documentElement.scrollLeft || document.body && document.body.scrollLeft || 0)-(document.documentElement.clientLeft || 0);
				this.pageY=eventSource.clientY+(document.documentElement && document.documentElement.scrollTop || document.body && document.body.scrollTop || 0)-(document.documentElement.clientTop || 0);
			}
			switch (this.type)
			{
				case "click":
				case "dblclick":
				case "mousedown":
				case "mouseup":
					this.which=(BrowserFeatures.HTMLElement?eventSource.which:eventSource.button)==1
						?1
						:2;//too many browser differences in code by right,middle buttons
			}
		}
	}).Extends(Event);
Na nem rizsázok többet. A lényeg, hogy JQueryben meg Prototype.js-ben is úgy álltak neki az eseménykezelés témához, hogy beleraktak sok if-et, meg a sima event objecten (vagy másnéven ie-ben window.event-en) átírták a tulajdonságokat a nekik tetszőre. Én a fejlesztés során azt vettem észre, hogy ez a fajta megoldás komolytalan.
A konkrétum ami miatt úgy döntöttem, hogy inkább mindent újraírok az az volt, hogy az event.type tulajdonság read-only mind a két böngészőben, szóval mondjuk oninput-ra nem tudom átírni ie-ben az onpropertychange-et. Emellett még volt egy-két zavaró dolog, ami miatt kb lehetetlen volt normálisan crossBrowser kódot fejleszteni.
Szóval a listener, meg minden egyéb tárolását teljesen újraírtam, a hagyományos eseménykezelők pedig az újraírt résszel kommunikálnak. Ezek után egyébként megvan a véleményem a böngésző fejlesztőkről (leginkább a microsoftnál bűntetnék).
21

Nagyon tetszik

Ustak · 2009. Jún. 4. (Cs), 21.22
még legalább egyszer el kell olvasnom!
22

Hehe

inf · 2009. Jún. 4. (Cs), 23.54
:D Direkt bonyolultan mondtam el :-P
A koncepció, ahogy eseményt kezel nagyon egyszerű.

Vegyünk egy szimpla eseményt:
element.addEventListener(name,method);

Ha ez cross-browser lenne, akkor tetszene, de sajnos nem az, és még az addEventListenerrel rendelkező böngészőknél is vannak eltérő nevű események.

Ezek miatt az eltérések miatt hoztam létre egy új függvényt, a setListenert, ez a függvény hozza összes a böngészőnkénti eltéréseket az egyes események nevében, illetve a kontextusban.
Legyen a példánk a window.onload, ez ieben

document.attachEvent("onreadystatechange",function ()
{
	if (document.readyState=="complete")
		method.call(this);
});
tehát:

{
	context: document,
	type: "onreadystatechange",
	listener: function ()
	{
		if (document.readyState=="complete")
			method.call(this);
	}
}
, ff-ben

window.addEventListener("DOMContentLoaded",method);
tehát:
{
	context: window,
	type: "DOMContentLoaded",
	listener: method
}
, a többi böngészőben pedig

window.addEventListener("load",method);
tehát:
{
	context: window,
	type: "load",
	listener: method
}
. A legegyszerűbb ezeket egységesen behozni a
window.setListener("load",method);
alá.

Az event objecten belüli eltérések miatt pedig saját eventClass-eket csináltam. Ezek a window.event, vagy tovább adott "e" object tulajdonságait egységesítik.

Mivel a kontextus, a típus és az eseménykezelő is megváltozhat, ezért le kell tárolni a megadott és a megváltozott formát is. A megadott forma alapján lehet kikeresni a megváltozott formákat, és a megváltozott formákkal lehet törölni az eseményt szintén új removeListener függvénnyel.

Pl:
window.onload-ra egy kis szemléltető példa (nem teszteltem, csak a módszert akarom bemutatni rajta.)

var prepareConfig=function ()
{
	this.context=browser.ie
		?document
		:window;
	
	this.type=browser.ie
		?'readystatechange'
		:browser.ff
			?'DOMContentLoaded'
			:this.name;
			
	this.listener=browser.ie
		?function (eventObject)
		{
			if (document.readyState=="complete")
			{
				this.method.call(this,eventObject);
			}
		}
		:this.method;
};


var eventClass=function (element,name,eventSource)
{
	this.target=element;
	this.type=name;
	this.preventDefault=function ()
	{
		if (browser.ie)
		{
			eventSource.returnValue=false;
		}
		else if (eventSource.preventDefault)
		{
			eventSource.preventDefault();
		}
	};
};
	

window.setListener=function (name,method)
{
	var element=this;
	var config=prepareConfig.call(
	{
		element: element,
		name: name,
		method: method
	});
	var context=config.context,listener=config.listener,type=config.type;
	if (!element.events)
	{
		element.events={};
	}
	if (!element.events[name])
	{
		element.events[name]=[];
	}
	var initialize=function (eventSource)
	{
		if (browser.ie6)
		{
			eventSource=window.event;
		}
		var eventObject=new eventClass(element,name,eventSource);
		listener.call(element,eventObject);
	};
	config.initialize=initialize;
	element.events[name].push(config);
	if (browser.ie)
	{
		context.attachEvent('on'+type,initialize);
	}
	else
	{
		context.addEventListener(type,initialize);
	}
};

window.removeListener=function (name,method)
{
	var element=this;
	if (!element.events)
	{
		return;
	}
	if (!element.events[name])
	{
		return;
	}
	var configs=element.events[name];
	var config;
	for (var i=0; i<configs.length; i++)
	{
		var currentConfig=configs[i];
		if (currentConfig.method===method)
		{
			config=currentConfig;
			delete(configs[i]);
			break;
		}
	}
	if (!config)
	{
		return;
	}
	var type=config.type,context=config.context,intialize=config.initialize;
	if (browser.ie)
	{
		context.detachEvent('on'+type,initialize);
	}
	else
	{
		context.removeEventListener(type,initialize);
	}
};


var element=window;
var name="load";
var method=function (eventObject)
{
	alert("betöltődött\n típus:"+eventObject.type);
};
element.setListener(name,method);
Így talán kicsit egyszerűbb megérteni.
16

visszakeresés

vbence · 2009. Jún. 4. (Cs), 10.00
én írtam egy cross-browser event interfacet, amit rá lehet húzni bármelyik objectre, viszont ha az object a dom része, akkor a definiált dom eseménykezelőkre rácsatlakozik


Ha jól értem, az első (teszemazt onclick) eseménykezelő hozzácsatoláskaor egy adott node-hoz felrakod a saját kezelődet, ami meghívja user által megadott kezelőt. Amikor a második kezelő kerülne fel (ugyanazon node ugyanazon eseményére), akkor már csak a meglévő eseménykezelődhöz adod hozzá az adott kezelőt, magyarán az attachEvent csak az első esetben fog lefutni.

Ezesetben érdekel, hogyan keresed vissza, hogy az adott node-hoz már van kezelő? Ugyanis ebben az esetben jön elő a témaindítóban szereplő probléma. Vagyis hogy az == operátor nem megbízható IE esetén.

(Persze az esetedben nem végzetes, ha mégegyszer lefut az attachEvent, legfeljebb nem hozza meg a várt sebességnövekedést).

Példa:

<html>
    <body>
        <script>
            addEvent(document.body, "click", function { alert(1); } );

            function f() {
                addEvent(document.body, "click", function { alert(2); } );
            }
        </script>
    </body>
    
        . . .

        <button onclick="f();">második kezelő</button>
</html>
Itt az lesz a helyzet, hogy (IE alatt) a második esetben a body nem ugyanazon objektum lesz, mint az elsőben. Ennek lekezelése az érdekes téma.
24

Yepp

inf · 2009. Jún. 5. (P), 06.27
Jah, úgy, ahogy mondod.
Viszont én kicsit túlbonyolítottam, mert mindent attach-eltem. Egyszerűbb, ha csak összegyűjtöd a függvényeket, aztán egy központit attach-elsz.

A lényeg, hogy hagytam a window hasonlítgatását, és inkább csináltam egy tárolót a window.events néven. Így sokkal gyorsabb kikeresni, mintha egy központi tárolóba szednéd össze az element-eket. Cserébe bele kellett nyúlni a globális névtérbe, dehát ezvan. A sebességért megérte, mert 5x lett gyorsabb tőle a kód. Azóta mondjuk a régi tárolóformát upgradeltem, szóval igazából csak 2x lett gyorsabb a kód, de azért is megérte.
(Egyébként még így is a bind függvénnyel adtam hozzá a setListenert, mert úgy garantáltan a window objektum lesz a this, és nem más. Majd még bind nélkül is tesztelem, valszeg úgy is megy ezzel a globális névtér szennyezős módszerrel.)

Az egy függvény attach-elésével viszont sokkal jobban le fog egyszerűsödni a kép, szóval az is gyorsítani fog majd a dolgon. Azt hiszem majd holnap átírom olyanra az egészet, aztán bemásolom ide, ami már készen van. Valszeg csak az alaprendszert fogom ideírni, ami olyan 200 sor, az eseményekre specifikus dolgokat, meg az osztályok kezelését, meg a gyűjtemények kezelését inkább kihagyom, mert összesen simán kitesznek 1000 sort.
14

Lehet, hogy működik…

Adam · 2009. Jún. 4. (Cs), 00.16
Lehet, hogy működik, nem tudtam most kipróbálni IE hiányában:

window.prototype.myFunc = function()
{
    // this;
}

window.myFunc();
Hiszen elméletileg egy, azaz egy és ugyanazon window objektumot kellene használnia az IE-nek is mindenhol…
15

???

inf · 2009. Jún. 4. (Cs), 00.30
Ez kizárt dolog, hogy működjön. A window object, prototype-ja pedig csak function-nek van.
17

JS-ben "minden" Object*

Adam · 2009. Jún. 4. (Cs), 13.27
JavaScript-ben minden objektumnak van prototype-ja. A Function az Object leszármazottja! Próbáld csak ki:

Object.prototype.a = function()
{
	alert('a');
}

var myFunc = new Function();
myFunc.a();
Meg fogsz lepődni, de meg fog jelenni az alert.

Viszont kipróbáltam amit ajánlottam és valóban nem működik, ellenben elkezdtem kísérletezgetni és érdekes dologra lettem figyelmes:

var w = window;
w.a = function()
{
	w == this; // true
	window == this; // true
	this == w; // false
	this == window; // false
	w == window; // true
};
w.a();
Szóval ha a w == this true, akkor a this == w miért false? (A tesztet IE6-tal csináltam.)

* JavaScript-ben amennyire én tudom, egyedül a null és az undefined nem objektumok és ezáltal az Object leszármazottjai.
18

El vagy tévedve

inf · 2009. Jún. 4. (Cs), 18.45
Te erősen el vagy tévedve. Az Object egy függvény (más nyelvben osztálynak mondanák), a window, amit előzőleg írtál viszont egy objektum (más nyelven példány). A prototype az osztályhoz tartozó dolog, ami a nem statikus public változókat írja le, és az osztályon keresztül érhető el. Te viszont a példányból akarod elérni a prototype-ot, ami nem fog sikerülni, mert ugyebár a window az példánya valamilyen osztálynak. IEben ha jól tudom a window.constructor null, szóval nem adják ki, hogy milyen osztálynak a példánya, ffben meg talán lehet módosítani annak az osztálynak a prototype-ját, de akkor is úgy kell, hogy
window.constructor.prototype.a=function ()
{
...
}

Amit most írtál, arra ránézek mindjárt.
19

Ja, igaz

inf · 2009. Jún. 4. (Cs), 18.49

<script>

window.s=function ()
{
	alert(window==this)
	alert(this==window)
}
window.s();


</script>
Ha fordított sorrendben írom, akkor rendesen true-t ad. Kösz szépen. Akkor ezek szerint a window object a this, és az összehasonlító függvény a rossz.
20

Típus konverzió

Ustak · 2009. Jún. 4. (Cs), 20.33
Kipróbáltátok a "===" operátorral is? Szerintem itt valami típus konverzió gubanc lehet, ugyanis elég bohókás algoritmus alapján hasonlít a == operátor, és ez már sok volt az ie programozóinak :-)
11.9.1 The Equals Operator ( == )
The production EqualityExpression : EqualityExpression == RelationalExpression is evaluated as follows:
1. Evaluate EqualityExpression.
2. Call GetValue(Result(1)).
3. Evaluate RelationalExpression.
4. Call GetValue(Result(3)).
5. Perform the comparison Result(4) == Result(2). (Section 11.9.3.)
6. Return Result(5).

11.9.3 The Abstract Equality Comparison Algorithm
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as
follows:
1. If Type(x) is different from Type(y), go to step 14.
2. If Type(x) is Undefined, return true.
3. If Type(x) is Null, return true.
4. If Type(x) is not Number, go to step 11.
5. If x is NaN, return false.
6. If y is NaN, return false.
7. If x is the same number value as y, return true.
8. If x is +0 and y is −0, return true.
9. If x is −0 and y is +0, return true.
10. Return false.
11. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and
same characters in corresponding positions). Otherwise, return false.
12. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
13. Return true if x and y refer to the same object or if they refer to objects joined to each other (section 13.1.2).
Otherwise, return false.
14. If x is null and y is undefined, return true.
15. If x is undefined and y is null, return true.
16. If Type(x) is Number and Type(y) is String,
return the result of the comparison x == ToNumber(y).
17. If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
18. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
19. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
20. If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
21. If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
22. Return false.



ECMAScript szabvány :-)
23

Aha

inf · 2009. Jún. 5. (P), 00.14
Jah, egyedül a window==this formában ad truet, a maradék 3ban pedig false-t.

például ennek kéne működnie:

<script>
var x=[];
x.save=function (w,o)
{
	for (var i=0; i<this.length; i++)
	{
		var current_w=this[i][0];
		if (current_w===w)
		{
			this[i][1]=o;
			alert("felülírva");
			return;
		}
	}
	this[this.length]=[w,o];
	alert("új");
};

x.save(window,123);

window.add=function (o)
{
	x.save(this,o);
};
window.add(543);
</script>
current_w==w -> jó
w==current_w -> rossz
current_w===w -> rossz
w===current_w -> rossz

Inkább maradok a bind-os megoldásnál ezek után, mert az == nekem nem elég a változók azonosítására, és az sem tiszta ugye, hogy mikor melyik oldalon lesz a this.

Szóval:

<script>
var x=[];
x.save=function (w,o)
{
	for (var i=0; i<this.length; i++)
	{
		var current_w=this[i][0];
		if (current_w===w)
		{
			this[i][1]=o;
			alert("felülírva");
			return;
		}
	}
	this[this.length]=[w,o];
	alert("új");
};

x.save(window,123);

Function.prototype.bind=function (w)
{
	var m=this;
	return function ()
	{
		return m.apply(w,arguments);
	};
}

window.add=function (o)
{
	x.save(this,o);
}.bind(window);
window.add(543);
</script>
25

dom.event.js

inf · 2009. Jún. 6. (Szo), 09.18
Nos elkészültem a stuffal. Az egész néhány függvényemre épül.
Nincs bent minden event, csak egy részük.


Package(function ()
{	
	var events=Class.Singleton(
	{
		static_classes: 
		[
			Class.Create(
			{
				set: function (eventListeners,element)
				{
					element.events=eventListeners;
				},
				get: function (element)
				{
					return element.events;
				}
			}),
			Hash,
			Array
		],
		getEventConfig: function (element,name)
		{
			return events.getProperty([element,name,"eventConfig"]);
		},
		setEventConfig: function (element,name,eventConfig)
		{
			events.setProperty(eventConfig,[element,name,"eventConfig"]);
		},
		buildEventConfig: function (element,name)
		{
			var eventConfig=this.getEventConfig(element,name);
			if (!eventConfig)
			{
				var configTemplate=this.getEventConfig(HTMLElement,name);
				if (!configTemplate)
				{
					throw new Exception("Event called: element.on'"+name+"' doesn't exists.");
				}
				var eventConfig=Object.extend({element: element},configTemplate);
				this.setProperty(eventConfig,[element,name,"eventConfig"]);
			}
			if (!eventConfig.virtual)
			{
				if (is.Function(eventConfig.prepare))
				{
					eventConfig.prepare();
				}
				if (!eventConfig.context)
				{
					eventConfig.context=element;
				}
				if (!eventConfig.type)
				{
					eventConfig.type=name;
				}
			}
			return eventConfig;
		},
		getListeners: function (element,name)
		{
			return events.get([element,name]);
		},
		addListener: function (element,name,method)
		{
			this.set(method,[element,name,-1]);
		},
		removeListener: function (element,name,method)
		{
			this.remove(this.find(function (listener)
			{
				return listener===method;
			},this,[element,name]));
		},
		createEvents: function (element,source)
		{
			Object.toHash(source).each(function (eventClass,name)
			{
				var eventConfig=
				{
					name: name,
					eventClass: eventClass,
					checkBefore: eventClass.checkBefore,
					checkAfter: eventClass.checkAfter,
					prepare: eventClass.prepare,
					virtual: eventClass.virtual
				};
				events.setEventConfig(element,name,eventConfig);
			});
		}
	},Tree);


	var HTMLEventInterface=
	{
		setListener: function (name,method)
		{
			var element=this;
			if (!is.Function(method))
			{
				return method;
			}
			var eventConfig=events.buildEventConfig(element,name);
			if (!eventConfig.controller)
			{
				var checkBefore=eventConfig.checkBefore,
					eventClass=eventConfig.eventClass,
					checkAfter=eventConfig.checkAfter,
					virtual=eventConfig.virtual,
					context=eventConfig.context,
					type=eventConfig.type;
				
				var controller=function (eventSource)
				{
					var listeners=events.getListeners(element,name);
					if (!listeners || !listeners.length)
					{
						return;
					}
					if (!BrowserFeatures.HTMLElement && !eventSource)
					{
						eventSource=window.event;
					}
					if (is.Function(checkBefore) && !checkBefore.call(element,eventSource))
					{
						return;
					}
					var eventObject=is.Instance(eventSource,CustomEvent)
						?eventSource //by triggerEvent
						:new eventClass(element,name,eventSource); //by normal event handling
					if (is.Function(checkAfter) && !checkAfter.call(element,eventObject))
					{
						return;
					}
					listeners.invoke(function ()
					{
						this.call(element,eventObject);
					});
				};
				eventConfig.controller=controller;
				if (!virtual)
				{
					if (BrowserFeatures.HTMLElement)
					{
						context.addEventListener(type,controller,false);
					}
					else
					{
						context.attachEvent("on"+eventConfig.type,controller);
					}
				}
			}
			events.addListener(element,name,method);
			return element;
		},
		removeListener: function (name,method)
		{
			var element=this;
			events.removeListener(element,name,method);
			return element;
		},
		createEvent: function (name,settings)
		{
			var element=this;
			if (!settings)
			{
				settings={};
			}
			var eventClass=is.Function(settings.initialize)
				?Class.Create(
				{
					initialize: settings.initialize	//function (element,name,eventSource), this=eventObject
				}).Extends(CustomEvent)
				:CustomEvent;
			var eventConfig=
			{
				name: name,
				eventClass: eventClass,
				checkBefore: settings.checkBefore, //function (), this=element -> true/false
				checkAfter: settings.checkAfter, //function (eventObject), this=element -> true/false
				prepare: settings.prepare, //function (), this=eventConfig; rewrites eventConfig.type &| eventConfig.context
				virtual: is.Null(settings.virtual)?true:settings.virtual //boolean, if true, then event can be triggered just manually
			};
			events.setEventConfig(element,name,eventConfig);
			return element;
		},
		triggerEvent: function (name,eventSource)
		{
			var element=this;
			var eventConfig=events.getEventConfig(element,name);
			if (eventConfig && is.Function(eventConfig.controller))
			{
				var eventObject=new CustomEvent(element,name,eventSource || {});
				eventConfig.controller(eventObject);
			}
			return element;
		}
	};
	
	var Event=Class.Create(
	{
		initialize: function (element,name,eventSource)
		{
			this.type=name;
			this.target=element;
			this.preventDefault=BrowserFeatures.HTMLElement
				?function ()
				{
					if (eventSource.preventDefault)
					{
						eventSource.preventDefault();
					}
				}
				:function ()
				{
					eventSource.returnValue=false;
				};
			this.stopPropagation=BrowserFeatures.HTMLElement
				?function ()
				{
					if (eventSource.stopPropagation)
					{
						eventSource.stopPropagation();
					}
				}
				:function ()
				{
					eventSource.cancelBubble=true;
				};
		},
		static_virtual: false
	});
	
	var CustomEvent=Class.Create(
	{
		initialize: function (element,name,eventSource)
		{
			this.type=name;
			this.target=element;
			Object.extend(this,eventSource);
		},
		static_virtual: true
	}).Extends(Event);
	
	
	var UIEvent=Class.Create().Extends(Event);
	
	var TextEvent=Class.Create(
	{
		static_prepare: function ()
		{
			if (!BrowserFeatures.HTMLElement)
			{
				this.type="propertychange";
			}
		},
		static_checkBefore: function (eventSource)
		{
			return (BrowserFeatures.HTMLElement?true:eventSource.propertyName==="value") || is.Instance(eventSource,CustomEvent);
		}
	}).Extends(Event);
	
	var KeyboardEvent=Class.Create(
	{
		initialize: function (element,name,eventSource)
		{
			this.parent.initialize.apply(this,arguments);
			this.which=eventSource.which || eventSource.charCode || eventSource.keyCode || 0;
		}
	}).Extends(Event);
	
	var MouseEvent=Class.Create(
	{
		initialize: function (element,name,eventSource)
		{
			this.parent.initialize.apply(this,arguments);
			if (this.type=="mouseover" || this.type=="mouseout")
			{
				this.relatedTarget=BrowserFeatures.HTMLElement
					?eventSource.relatedTarget
					:this.type=="mouseover"
						?eventSource.fromElement
						:eventSource.toElement;
			}
			if (BrowserFeatures.HTMLElement)
			{
				this.layerX=eventSource.layerX;
				this.layerY=eventSource.layerX;
				this.pageX=eventSource.pageX;
				this.pageY=eventSource.pageY;
			}
			else
			{
				this.layerX=eventSource.offsetX;
				this.layerY=eventSource.offsetY;
				this.pageX=eventSource.clientX+(document.documentElement && document.documentElement.scrollLeft || document.body && document.body.scrollLeft || 0)-(document.documentElement.clientLeft || 0);
				this.pageY=eventSource.clientY+(document.documentElement && document.documentElement.scrollTop || document.body && document.body.scrollTop || 0)-(document.documentElement.clientTop || 0);
			}
			switch (this.type)
			{
				case "click":
				case "dblclick":
				case "mousedown":
				case "mouseup":
					this.which=(BrowserFeatures.HTMLElement?eventSource.which:eventSource.button)==1
						?1
						:2;//too many browser differences in code by right,middle buttons
			}
		},
		static_checkAfter: function (eventObject)
		{
			//fixes mouseover-mouseout bubbling bug:
				//if you set a listener to an element's onmouseover, 
					//and you move the cursor on the child of the element, 
						//then it will cause a fast mouseout and re-mouseover event
			return !(this.relatedTarget && (this.relatedTarget.isAncestor(element) || this.target.isAncestor(element)));
		}
	}).Extends(Event);
	
	var MouseMultiWheelEvent=Class.Create().Extends(Event);
	
	var MouseWheelEvent=Class.Create(
	{
		static_prepare: function ()
		{
			if (BrowserFeatures.XUL)
			{
				this.type="DOMMouseScroll";
			}
		},
		initialize: function (element,name,eventSource)
		{
			this.parent.initialize.apply(this,arguments);
			this.delta=is.Number(eventSource.wheelDelta)
				?Math.sgn(eventSource.wheelDelta*-1)
				:Math.sgn(eventSource.detail);
		}
	}).Extends(Event);
	
	var MutationEvent=Class.Create().Extends(Event);
	
	var MutationNameEvent=Class.Create(
		//DOMNodeInserted: MutationEvent
		//DOMAttrModified: MutationEvent
		
		//DOMElementNameChanged: MutationNameEvent
		//DOMAttributeNameChanged: MutationNameEvent
	).Extends(Event);
	
	var HTMLEvents=
	{
		load: UIEvent,
		focus: UIEvent,
		blur: UIEvent,
		load: UIEvent,
		unload: UIEvent,
		beforeunload: UIEvent,
		abort: UIEvent,
		error: UIEvent,
		select: UIEvent,
		change: UIEvent,
		submit: UIEvent,
		reset: UIEvent,
		resize: UIEvent,
		scroll: UIEvent,
		//contextmenu: UIEvent,
		
		input: TextEvent,
		
		mouseover: MouseEvent,
		mouseout: MouseEvent,
		mousemove: MouseEvent,
		mousedown: MouseEvent,
		mouseup: MouseEvent,
		click: MouseEvent,
		dblclick: MouseEvent,
		
		
		keydown: KeyboardEvent,
		keyup: KeyboardEvent,
		keypress: KeyboardEvent,
		
		mousewheel: MouseWheelEvent,
		
		mousemultiwheel: MouseMultiWheelEvent
	};
		
	HTMLElement.Implements(HTMLEventInterface);
	//összes elementhez hozzáadja az interface-t
	HTMLElement.createEvent=HTMLEventInterface.createEvent;
	//lehetőség arra, hogy új, összes elementre vonatkozó eseményt hozzunk létre

	events.createEvents(HTMLElement,HTMLEvents);
	//alap események hozzáadása az összes elementhez
	
	Object.implement(window,HTMLEventInterface);
	
	events.createEvents(window,
	{
		load: Class.Create(
		{
			prepare: function ()
			{
				if (BrowserFeatures.HTMLElement)
				{
					if (BrowserFeatures.XUL)
					{
						//firefox
						this.type="DOMContentLoaded";
					}
					//safari,opera,chrome
				}
				else
				{
					//internet explorer
					this.type="readystatechange";
					this.context=document;
				}
			},
			checkBefore: function ()
			{
				return BrowserFeatures.HTMLElement?true:document.readyState=="complete";
			}
		}).Extends(UIEvent)
	});
	
	if (!BrowserFeatures.HTMLElement)
	{
		window.setListener("load",function ()
		{
			HTMLElement.__applyChanges();
		});
	}
	
	$.HTMLEventInterface=HTMLEventInterface;
});
Nos elmagyarázom, ami nincs benne.

Névtér:

Package(function (){...});
//ez gyakorlatilag az alábbinak felel meg
(function ()
{
...
})();
Osztálykezelés:

Class.Create({...}); //új osztály létrehozása, kibővíti az osztályt függvénnyekkel
$class.Extends(parentClass); //mint más nyelvekben az extends
$class.Implements({...}); //interface-t illeszt az osztályra
Class.Singleton({...},parentClass); //singleton class-t hoz létre, és egyből példányosítja
Általánosan igaz, hogy az extends nem ír felül, az implements meg felülír már létező tualjdonságokat. Az Object.extend ill. Object.implement függvények is ugyanígy működnek, csak objektumokra. (a window objektumnál, ha függvényt adok meg, akkor a függvényt hozzáköti a window-hoz a bind nevű függvénnyel, ezt prototype.js-ben megtalálhatjátok.)
A {...} rész megadása:

var a=Class.Create({
    static_property: 123,
    initialize: function (){alert("új példány")},
    property: 5
});
alert(a.property); //123
var b=new a(); //új példány
alert(b.property); //5
HTMLElement:
A HTMLElement-et kibővítettem az osztály függvényeivel. IE6nál például nincs HTMLElement osztály, ott létrehoztam egyet, és az __applyChanges függvény hívásával másolom át az összes létező DOM node-ba az átírt dolgokat. Egyelőre az összes IEnél ezt csinálja.

BrowserFeatures:

BrowserFeatures.HTMLElement -> van e HTMLElement alapból (ie-nél nincs)
BrowserFeatures.XUL -> XUL controllerek vannak e (ff-nél van csak)
Például a DOMContentLoaded és a DOMMouseScroll is XUL események. (Egyébként nem tudok semmit arról, hogy mi az, hogy XUL, de jól hangzik :D)

Tree:
Az egész lényege, hogy egy Tree-ben lehet letárolni az eseménykezelőket, és az esemény típusokhoz tartozó adatokat.

Az eseménykezelőkhöz 3 szint mély/magas fa kell, a kulcsok sorban, és a típusaik:
element  ->  object
type     ->  string
method   ->  function

Ez a 3 azonosító van, ami meghatároz egy eseménykezelőt. Nézzük sorban:
Az elementhez tartozó gyűjtemény a típusokat tárolja, a kulcs az element nála, az érték meg a típusokat tároló gyűjtemény. A tárolásra egy speciális osztályt hoztam létre, ami az element.events-be rakja az értéket, és onnan is kéri vissza. Ha nagyon sok element van, akkor nem éri meg letárolni egy nagy tömbben a hozzájuk tartozó típusokat.
A type-hoz tartozó gyűjteményben a kulcs szöveges, tehát itt egy Hash object jó a tárolásra. (Ezzel kapcsolatban prototype.js-ben vagy JQuery-ben is van leírás sztem.)
A method-nál a kulcs függvény, de mivel saját magát azonosítja, ezért mehet tömbbe.

Az eseménytípusok adataihoz 2 mélységű fa kell, a két kulcs:
element -> object
name -> string

mivel ugyanaz, mint fent, ezért a kettőt le lehet tárolni ugyanabban a fában a methodhoz tartozó tömb egy tulajdonságaként. A tulajdonságnak ez eventConfig nevet adtam. A Tree-hez tartozó függvényeket használó részt külön kigyűjtöttem az events object-be, szóval annak az átírásával ki lehet váltani a Tree osztályom használatát.

Az eseménytípusok adatai:
element: -> az adott elem
name: -> a típus neve
context: -> általában az adott elem, de felül lehet írni, erre hívódik az attachEvent
type: -> erre hívódik az attachEvent, normál esetben ugyanaz, mint a name
eventClass: -> event osztály, ezzel hozza létre az egységesített esemény objektumot
checkBefore: -> ha nem true-t ad vissza megszakad az eseménykezelés az esemény objektum létrejötte előtt
checkAfter: -> ha nem true-t ad vissza az esemény objektum létrejötte után szakad meg az eseménykezelés, nyilván az eseménykezelők olyankor nem futnak le
prepare: -> a type és context értékeket ezzel lehet beállítani az adott objektumra. ez csak akkor fut le, amikor az első listenert hozzáadják az element-hez
virtual: -> ha true az értéke, akkor csak a triggerEvent függvénnyel lehet kiváltani az eseménykezelést, saját esemény típusoknál kell használni


Az event interface

element.setListener(name,method); //eseménykezelő hozzáadása
element.removeListener(name,method); //eseménykezelő elvétele
element.createEvent(settings); //saját eseménytípus hozzáadása
element.triggerEvent(name,eventObject); //esemény kiváltása
26

Pár példa:

inf · 2009. Jún. 6. (Szo), 10.14
Jah egyébként HUPSZ, a window.onload-nál úgy néz ki lemaradt a static_ kulcsszó, szóval a checkBefore és a prepare nem fut le a fenti kódban.

Példakódok a saját eseményekre:

Sima objektumnál:

var o=Object.extend({},$.HTMLEventInterface);
o.createEvent("hupsz");
o.setListener("hupsz",function (e)
{
    alert(e.hupsz);
});
o.triggerEvent("hupsz",
{
    hupsz: "valami"
});
Sima obi + dom esemény:

var o=Object.extend({},$.HTMLEventInterface);
o.createEvent("hupsz");
o.setListener("hupsz",function (e)
{
    alert(e.hupsz);
});

$("#cucc").setListener("mouseover",function ()
{
    o.triggerEvent("hupsz",{hupsz: "valami"});
});