Javascript, problem z dodajanjem event listenerjev

Kot vse kaze, sem prejsnjo sredo slabo poslusal predavanje v Kiberpipi :)

Problem je sledec. Uporabljam nek javascript objekt, ki ob klicu neke metode doda event listener (ali attacha event, v primeru IE) nekemu elementu v DOM, v mojem primeru je to element BODY. Zelim, da se ob eventu klice metoda tega istega objekta.

To sem zelel narediti nekako takole:

document.body.addEventListener('click', this.hide, false);

Vse lepo in prav, metoda hide se ob kliku na BODY sicer izvede, vendar ne metoda na zeljeni instanci objekta, torej tisti, v kateri sem ta event listener dodal, ampak se ocitno kreira neka nova instanca, ali pa se metoda izvede nad instanco funkcije, ki doloca objekt, ali pa se kaj tretjega?

Vprasanje bi bilo, kako bi dosegel, da se ob eventu izvede metoda nad to isto instanco objekta?

13 odgovorov

Hmm, ocitno je nekaj tretjega :)

this je znotraj tiste metode tipa HTMLBodyElement, ceprav je to metoda nekega tretjega objekta. Stvar ima mogoce celo smisel, ce addEventListener doda BODY objektu metodo, ki se oglasa na doloceni event. Potem se seveda vse skupaj dogaja na instanci BODY elementa.

No, vprasanje tudi v tem primeru ostaja enako :)

Kontekst onclick eventa je dokument, ker se na njem zgodi event. V javascriptu je pač tako. :)

Lahko uporabiš closure, kjer ohraniš referenco:

document.body.addEventListener('click', function (obj){ return (function() { obj.hide() })}(this), false);

Če to razčleniš, dobiš tole:

document.body.addEventListener('click',
  function (obj) {
    return (
      function() {
        obj.hide()
      })
  }(this),
  false);

V besedah: na event click ti pripneš anonimno funkcijo s parametrom obj, ki jo že takoj izvedeš in ji podaš this, ona pa vrne novo anonimno funkcijo, ki ohrani parameter obj iz konteksta, kjer je bila klicana. Closures ftw!

Še drug primer in obširnejša razlaga: http://www.jibbering.com/faq/faq_notes/closures.html#clSto

lp, fat

Hvala, fatg, za tole razlago. V sredo me je predavanje malo razrajcalo, pa sem sel malo raziskovat se tele javascript objekte. Saj vem, da je bilo prav o tem govora tudi tam, ampak sem nekako pozabil. Vmes sem sicer nasel neko podobno resitev, ki tudi deluje:

var obj = this;
document.body.addEventListener('click', function () { obj.hide() }, false);

Se ti zdi to slabse? Isto? Ena nova referenca na objekt obstaja v obeh primerih, kajne?

če se ne motim, si s tem ustvaril "globalno" referenco, imenovano obj. Zadevo si nastavil v vrhnjem kontekstu (torej v objektu window). Ko se zgodi event, ti klic na funkcijo kliče v bistvu window.obj.hide(). Če vprašaš mene, je ta rešitev problematična, ker piše direktno v window. Če pa že itak imaš neko referenco na obstoječi objekt, potem pa le uporabi to.

Ne ne, referenco sem ustvaril znotraj metode objekta. Ce prav razumem, so spremenljivke, ki so definirane z var znotraj funkcije, tej funkciji lokalne, ja?

aja, znotraj si! potem si zgleda naredil closure in tista notranja funkcija drži referenco na obj. Na nek način enako, kot moja rešitev, samo za eno stopnjo manj komplicirano. Ne vem pa, če je ta način požegnan od profijev. Zgleda že v redu :).

lp

Resitev sem nasel tukaj, na Mozilli verjetno je kaksen profi, torej so verjetno zadevo tudi pozegnali :)

Je pa res, da ne govorijo prav tocno o tem problemu, ki sem ga imel jaz. Tudi meni se zdi tvoja resitev bolj profi, sem jo uporabil :)

Ha, fatg, en problem. Se ti kaj sanja, kako bi pa ta event listener removal? Metodi removeEventListener moram ravno tako podati referenco na neko funkcijo, kar pa tukaj tezko naredim, ce sem uporabil anonimno funkcijo, ni res?

Nasel resitev, se mi zdi kar elegantna in deluje:

function nekobjekt() {
}

nekobjekt.prototype.metoda1() {
    this.clickfunc = function (obj) { return function () { obj.metoda2() } }(this);
    document.body.addEventListener('click', this.clickfunc, false);
}

nekobjekt.prototype.metoda2() {
    document.body.removeEventListener('click', this.clickfunc, false);
}

Imam prav?

mislim, da to štima. Za remove potrebuješ referenco na funkcijo, ki jo ti držiš v this.clickfunc, torej bo ok. Za event jaz sicer uporabljam tole, ki je še cross-browser in pozna tudi remove. Gre v osnovi za enako finto in prav tako za remove potrebuješ referenco.

lp