Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[JS] AddEvent, wat is de perfecte cross-browser oplossing?

Pagina: 1
Acties:

  • MarkvE
  • Registratie: Maart 2004
  • Laatst online: 30-01 17:16
Om dynamisch functies toe te voegen aan bepaalde elementen in een webpagina zonder de al bestaande events aan te tasten, maak ik gebruik van de AddEvent-methode die op het Internet vele malen is (her)bedacht door prominente JS-ers als Tino Zijdel (crisp), Dean Edwards en PPK, waarvan de laatste zelfs er een contest voor uitschreef, waarbij onder andere die van Tino er uit voort kwam.

Na veel nazoeken kom ik tot de conclusie dat de AddEvent van Tino wel tot de betere oplossingen hoort. Echter heb ik met het gebruik van deze methode nog problemen met de functionaliteit. Zo lijkt het er op dat de AddEvent niet functioneert in Safari. Op mijn Mac werkt deze namelijk alleen in Firefox (en andere nazaten zoals Camino). Omdat ik zelf als Mac-gebruiker zeer gesteld ben op Safari, wil ik deze browser niet negeren, echter lukt het me met geen mogelijkheid een "onclick" functie dynamisch toe te passen via de AddEvent of via een simpele testcase als:

JavaScript:
1
2
3
4
5
window.onload = function ()
{
    var link; // dit is een <a> element
    link.onclick = function () { alert ( 'Doe iets...' ); };
};


Kortom, is er een AddEvent oplossing die ook in Safari werkt? Is er anders een andere oplossing die wel in "alle" browsers werkt? Of is de Javascript-functionaliteit in Safari gewoon te brak?

Ik ben er nog niet uitgekomen!

Vormkracht10


  • orillion
  • Registratie: April 2006
  • Laatst online: 17:28
MarkvE schreef op zondag 09 maart 2008 @ 16:40:
Om dynamisch functies toe te voegen aan bepaalde elementen in een webpagina zonder de al bestaande events aan te tasten, maak ik gebruik van de AddEvent-methode die op het Internet vele malen is (her)bedacht door prominente JS-ers als Tino Zijdel (crisp), Dean Edwards en PPK, waarvan de laatste zelfs er een contest voor uitschreef, waarbij onder andere die van Tino er uit voort kwam.

Na veel nazoeken kom ik tot de conclusie dat de AddEvent van Tino wel tot de betere oplossingen hoort. Echter heb ik met het gebruik van deze methode nog problemen met de functionaliteit. Zo lijkt het er op dat de AddEvent niet functioneert in Safari. Op mijn Mac werkt deze namelijk alleen in Firefox (en andere nazaten zoals Camino). Omdat ik zelf als Mac-gebruiker zeer gesteld ben op Safari, wil ik deze browser niet negeren, echter lukt het me met geen mogelijkheid een "onclick" functie dynamisch toe te passen via de AddEvent of via een simpele testcase als:

JavaScript:
1
2
3
4
5
window.onload = function ()
{
    var link; // dit is een <a> element
    link.onclick = function () { alert ( 'Doe iets...' ); };
};


Kortom, is er een AddEvent oplossing die ook in Safari werkt? Is er anders een andere oplossing die wel in "alle" browsers werkt? Of is de Javascript-functionaliteit in Safari gewoon te brak?

Ik ben er nog niet uitgekomen!
Ik heb nooit problemen gehad met de addevent functie uit mootools.

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 14:23

crisp

Devver

Pixelated

Welke versie Safari heb je het hier specifiek over?

Intentionally left blank


  • MarkvE
  • Registratie: Maart 2004
  • Laatst online: 30-01 17:16
Ik heb alles getest in Safari 3.0.4 op Mac OS X Leopard 10.5.2.

Vormkracht10


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 14:23

crisp

Devver

Pixelated

MarkvE schreef op zondag 09 maart 2008 @ 17:00:
Ik heb alles getest in Safari 3.0.4 op Mac OS X Leopard 10.5.2.
Ah, recent dus, dus dat zou geen probleem op mogen leveren.
JavaScript:
1
var link; // dit is een <a> element

Ik neem aan dat je hier normaliter een referentie opvraagd mbv getElementById oid? Wat geeft een alert(link) na de opvraging?

Intentionally left blank


  • MarkvE
  • Registratie: Maart 2004
  • Laatst online: 30-01 17:16
Het gaat trouwens in deze vooral om het dynamisch toevoegen van de events aan een bepaald element. En dan specifiek over het "onclick" event aan een <a>-element.

Bijvoorbeeld het toevoegen van een onload-event aan het window-object lukt in alle browsers voor zover ik heb getest met de AddEvent methode van crisp. Echter het dynamisch toevoegen van een onclick event aan een <a>-element via AddEvent wordt in Safari niet gepakt, dan krijg ik een error op de regel (uit crisps-script) waarin het volgende staat:

JavaScript:
1
var handlers = element.events[type];

Vormkracht10


  • MarkvE
  • Registratie: Maart 2004
  • Laatst online: 30-01 17:16
crisp schreef op zondag 09 maart 2008 @ 17:01:
[...]

Ah, recent dus, dus dat zou geen probleem op mogen leveren.
JavaScript:
1
var link; // dit is een <a> element

Ik neem aan dat je hier normaliter een referentie opvraagd mbv getElementById oid? Wat geeft een alert(link) na de opvraging?
De referentie klopt, aangezien ik dit doe door een simpele aanvraag van alle <a>-elements in een pagina:

JavaScript:
1
2
3
4
5
6
var links = document.getElementsByTagName ( 'a' );

for ( link in links )
{
    AddEvent ( links [ link ], 'click', mijnfunctie );
};


Bovenstaande is een voorbeeld van hoe ik het doe in mijn code. Deze code voer ik dan uit na het laden van het document door middel van bovenstaande in een functie te zetten en dit dan aan te roepen via:

JavaScript:
1
AddEvent ( window, 'load', dynamischelinkfunctie );

Vormkracht10


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 14:23

crisp

Devver

Pixelated

MarkvE schreef op zondag 09 maart 2008 @ 17:05:
[...]


De referentie klopt, aangezien ik dit doe door een simpele aanvraag op alle <a>-element in een pagina:

JavaScript:
1
2
3
4
5
6
var links = document.getElementsByTagName ( 'a' );

for ( link in links )
{
    AddEvent ( links [ link ], 'click', mijnfunctie );
};


Is een voorbeeld van hoe ik het doe in mijn code. Deze code voer ik dan uit na het laden van het document door middel van bovenstaande in een functie te zetten en dit dan aan te roepen via:

JavaScript:
1
AddEvent ( window, 'load', dynamischelinkfunctie );
aj, ik denk dat je for-in meer oplevert dan enkel referenties naar A-elementen ;)

probeer dit eens:
JavaScript:
1
2
3
4
5
6
var links = document.getElementsByTagName ( 'a' );

for (var i = 0, l = links.length; i < l; i++)
{
    AddEvent ( links [ i ], 'click', mijnfunctie );
};

Intentionally left blank


  • MarkvE
  • Registratie: Maart 2004
  • Laatst online: 30-01 17:16
Dank!

Ik had in de tussentijd even snel een testcase gemaakt. En ik kreeg weer die fout van "undefined value" in Safari. Ik heb nu de for-lus vervangen in jouw variant en nu werkt het inderdaad.

Normaal gebruik ik altijd die variant, maar afgelopen week had ik deze ergens gezien en vond deze er wel makkelijk en kort uitzien, dus gebruikte ik die maar als standaard. Blijkt dus onder de kap nogal wat verschil in te zijn!

Vormkracht10


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 14:23

crisp

Devver

Pixelated

MarkvE schreef op zondag 09 maart 2008 @ 17:17:
Dank!

Ik had in de tussentijd even snel een testcase gemaakt. En ik kreeg weer die fout van "undefined value" in Safari. Ik heb nu de for-lus vervangen in jouw variant en nu werkt het inderdaad.

Normaal gebruik ik altijd die variant, maar afgelopen week had ik deze ergens gezien en vond deze er wel makkelijk en kort uitzien, dus gebruikte ik die maar als standaard. Blijkt dus onder de kap nogal wat verschil in te zijn!
Een for-in construct itereert over alle properties van een object. In dit geval krijg je dus ook bijvoorbeeld de 'length' property en nog een paar andere properties die dus geen HTML-elementen zijn. Test zelf maar uit:
HTML:
1
2
3
4
5
6
7
8
9
10
<!doctype html>
<body>
<a href="#"></a>
<script type="text/javascript">

var links = document.getElementsByTagName('a');
for (link in links)
    document.write(link + ': ' + links[link] + '<br>');

</script>


:)

Intentionally left blank


  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 14-11 16:23

Clay

cookie erbij?

Ik snapte het probleem nooit zo van die addEvent discussie :P in het event object zitten wat verschillende properties, en de scope van de handler verschilt, nou, dan schrijf je daar toch een wrappertje omheen? Stel je hebt die standaard functies:

JavaScript:
1
2
someElement.attachEvent( type, handler ); // IE
someElement.addEventListener( type, handler, bCapture); // echte browsers


type is voor IE met "on" erbij, en anders zonder. Ik zou een wrapper zonder "on" maken, maar wat dit punt betreft is het puur de voorkeur van de maker. Verder bestaat bCapture niet in IE, maar je moet al flink thuis zijn in javascript en het event model om daar ueberhaupt aan te willen zitten.

De scope van handler kan mij persoonlijk weinig schelen; de handler krijgt namelijk een event object binnen waar je met .target || .srcElement het element op kan vragen die het event triggerde. De kracht hiervan is bv dat je een hele treeview kan maken met 1 klik event op de omliggende container: super!

Die scope kan de wrapper dus a) afblijven, b) op iets generieks zetten, of c) instelbaar maken. Ik zou dan voor een combo van b en c gaan, en dan zou mn wrapper functie er zo uitzien:

JavaScript:
1
addEvent(element, type, handler[, scope]) { }


Zonder opgegeven scope kan de wrapper de scope van de handler naar element sturen, met scope kan die hem naar die opgegeven scope sturen, ala:

JavaScript:
1
2
var eventHandler = function() { handler.apply(scope || element, arguments); }
// en dan attachen


Daarna rest eigenlijk alleen nog het probleem van stopPropagation en preventDefault, maar die hoeven alleen maar gemapped te worden naar cancelBubble en returnValue, en dat is eigenlijk je kleinste zorg van alle :P

Memory leaks van circular references zijn inherent aan javascript (en programmeren) in het algemeen, en imo is het niet de taak van je event implementatie om dat op te lossen. Het is taak dat je dat zelf opruimt door events weer te detachen, zodat de garbage collector weet wat die los kan laten, en daar heb je een heel mooi event voor; onbeforeunload. Je wrapper hoeft alleen maar de functionaliteit te bieden events weer los te maken.

Ik wil hier absoluut niet voorstellen om nog een addEvent versie de wereld in te helpen, ik wil alleen de mythe ontkrachten dat er iets magisch obscuurs aan het event model mankeert wat voor normale stervelingen niet te doorgronden is. De meeste problemen komen gewoon voort uit onbegrip. Het gebruik van een library of helperclass lost daarbij alleen je probleem op als je snapt waarom dat ding geschreven is, en - belangrijker - wat die voor je doet.

en Safari doet dus gewoon HTMLElement.addEventListener

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • crisp
  • Registratie: Februari 2000
  • Laatst online: 14:23

crisp

Devver

Pixelated

Clay schreef op maandag 10 maart 2008 @ 10:06:
[...]
De scope van handler kan mij persoonlijk weinig schelen; de handler krijgt namelijk een event object binnen waar je met .target || .srcElement het element op kan vragen die het event triggerde. De kracht hiervan is bv dat je een hele treeview kan maken met 1 klik event op de omliggende container: super!
De scope is dus juist het hele probleem; in sommige gevallen wil je niet enkel weten welk element het event triggerde, maar ook welk element in scope is, en dat is niet noodzakelijkerwijs hetzelfde element(!).

Buiten dat ga je er, als je attachEvent/addEventListener wrapped, onterecht van uit dat ze functioneel (even buiten het capturing gebeuren) identiek zijn, hetgeen niet het geval is. Zo kan je met attachEvent bijvoorbeeld dezelfde handler op hetzelfde event op hetzelfde element meerdere keren toekennen, hetgeen met addEventListener niet kan.

Kortom: voor de simpele gevallen kan je inderdaad wel vooruit met een wrappertje om attachEvent/addEventListener, maar als je meer gaat doen met events loop je toch al gauw tegen de verschillen aan...

Intentionally left blank


  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 14-11 16:23

Clay

cookie erbij?

De scope is dus juist het hele probleem; in sommige gevallen wil je niet enkel weten welk element het event triggerde, maar ook welk element in scope is, en dat is niet noodzakelijkerwijs hetzelfde element(!).
Ja, afhankelijk van de soort triggert het event voor alle elementen binnen diegene waar je het aan attachte. Als jij vanuit de handler het element zoekt waaraan het event initieel attached was ipv de e.target, kan je dat dus zelf scopen. Imo zijn die niet "noodzakelijkerwijs" niet hetzelfde, maar godzijdank niet :P Het event krijg je in de handler al binnen als argument; dat de scope daar ook nog op zou staan is dus zinloos, net als dat het window zou zijn. Die scope is dus een prima kandidaat om op het element te zetten waaraan het event gehangen is.

En anders stuur je toch meteen een gescopede handler het event in die gewoon weer terugvalt op een of andere object instantie? OO ftw, scope is je vriend, geen "probleem" :P
Buiten dat ga je er, als je attachEvent/addEventListener wrapped, onterecht van uit dat ze functioneel (even buiten het capturing gebeuren) identiek zijn, hetgeen niet het geval is. Zo kan je met attachEvent bijvoorbeeld dezelfde handler op hetzelfde event op hetzelfde element meerdere keren toekennen, hetgeen met addEventListener niet kan.
Vet, die wist ik niet. IE telt ze ook echt; als je er van twee eentje weer detached blijft er ook echt 1 over. Bizar. Maar is dit ook nog ergens goed voor dan? Ik script best veel complexe dingen ;) en dit lijkt me echt een schitterende feature om hele obscure bugs in je app te krijgen.
Kortom: voor de simpele gevallen kan je inderdaad wel vooruit met een wrappertje om attachEvent/addEventListener, maar als je meer gaat doen met events loop je toch al gauw tegen de verschillen aan...
Ben ik het niet mee eens. Een wrapper moet de verschillen wegnemen tussen de browser implementaties van het event zonder functionaliteit van je af te pakken en kan daarbij nog wat extra features bieden om het leven makkelijker te maken. Ik beschrijf dan misschien een simpel wrappertje, het ding wat ik in de praktijk gebruik houdt juist rekening met die verschillen, daarom werkt die zo lekker ;)

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin

Pagina: 1