Javascript - window és this nem azonos
Sziasztok!
Egy nagyon idegesítő ie hibára keresek megoldást:
Tudja valaki, hogy mi lehet itt a this, mert ie-ben false-t ad vissza..
■ Egy nagyon idegesítő ie hibára keresek megoldást:
window.setListener=function ()
{
alert(this==window)
}
window.setListener();
{
alert(this==window)
}
window.setListener();
Tudja valaki, hogy mi lehet itt a this, mert ie-ben false-t ad vissza..
Gyorshegesztés
call
Nem
Érdekes, hogy elsiklottak e fölött..
szerk Bocs Poetro, a fő szálba akartam, csak már késő van...:-)
Hmm
Egyébként microsoft developer fórumba is írtam ezzel kapcsolatban.
Yepp
Kb így:
Az ember azt gondolná, hogy egy
(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
ami nem
2.)
Ami nem megy
{
alert(this==window)
}
window.custom()
3.)
Ami nem megy:
//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.
Nézegettem a jQurey kódját
Nem
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.
ie objektumodellje
Í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:
Működik?
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:
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.
eseménykezelő
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.
Miért?
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:
Ú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.
Cikk?
Üdv: Gábor.
:-)
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:
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:
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:
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).
Nagyon tetszik
Hehe
A koncepció, ahogy eseményt kezel nagyon egyszerű.
Vegyünk egy szimpla eseményt:
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
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.)
visszakeresés
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:
Yepp
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.
Lehet, hogy működik…
???
JS-ben "minden" Object*
Viszont kipróbáltam amit ajánlottam és valóban nem működik, ellenben elkezdtem kísérletezgetni és érdekes dologra lettem figyelmes:
* JavaScript-ben amennyire én tudom, egyedül a null és az undefined nem objektumok és ezáltal az Object leszármazottjai.
El vagy tévedve
{
...
}
Amit most írtál, arra ránézek mindjárt.
Ja, igaz
Típus konverzió
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 :-)
Aha
például ennek kéne működnie:
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:
dom.event.js
Nincs bent minden event, csak egy részük.
Névtér:
A {...} rész megadása:
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:
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:
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:
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:
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
Pár példa:
Példakódok a saját eseményekre:
Sima objektumnál: