Scriptorama.nl

Header image showing a keyboard, mouse, laptop and books on design patterns

Prototype: Event handling

In de afgelopen jaren is het gebruik van Javascript libraries, zoals bijvoorbeeld jQuery, YUI of Prototype sterk toegenomen. Mede door de optimalisatie van Javascript engines, zoals V8 in Chrome, SquirrelFish Extreme in Safari of TraceMonkey in Firefox, en de extreme toename van het gebruik van AJAX in webpagina's, wordt het gebruik van javascript en javascript libraries steeds populairder.

In deze gastpost op Scriptorama zal ik ingaan op de afhandeling van browser-events met behulp van de Prototype library. Er is niet direct een reden dat deze post over Prototype gaat en niet over bijvoorbeeld jQuery, elke javascript library heeft zijn eigen kwaliteiten.

Persoonlijk ben ik erg gecharmeerd van Prototype, omdat het een aantal bestaande javascript constructies (zoals o.a. Array, String en Event) uitbreidt met een aantal handige functies. Ik zal me vooral richten op de functies gerelateerd aan event-afhandeling, zoals observe en Event.element, met hier en daar een uitstapje naar andere gebieden in de wondere wereld van prototype. Ik neem in dit artikel aan dat je bekend bent met de basisprincipes van Javascript en ontwikkelt met firebug.

Prototype

Voordat we beginnen met prototype is het allereerst nodig om de library (prototype.js) op te nemen in de pagina. De laatste versie van prototype is te downloaden op http://prototypejs.org, of direct te laden vanaf de Google AJAX Libraries API:

HTML:
  1. <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js" type="text/javascript"></script>

Historie

Nog voor het AJAX-tijdperk, toen er nog een minder stricte scheiding bestond tussen de HTML en het gedrag (javascript) van een webpagina, was de volgende contructie een doorgaanse manier om een actie aan een HTML-element te koppelen:

HTML:
  1. <input onclick="alert('*klik*');" type="button" value="*klik*" />

 

Of nog erger, het misbruiken van de href eigenschap in het a element:

HTML:
  1. <a href="javascript:alert('*klik*');">*klik*</a>

Naarmate er een grotere scheiding ontstond tussen de HTML en javascript, deed de volgende constructie zijn intrede:

HTML:
  1. <a id="link" href="#">*klik*</a>

In combinatie met:

HTML:
  1. <script type="text/javascript"><!--
  2. window.onload = function() {
  3.     document.getElementById('link').onclick = function () { alert('klik'); }
  4. }
  5. // --></script>

De magie van observe()

Prototype maakt het echter mogelijk, om als het ware een element te 'observeren', en een bepaalde actie uit te voeren wanneer een event plaatsvindt. In code:

HTML:
  1. <p id="info">Info</p>
  2. <script type="text/javascript"><!--
  3. document.observe (
  4.     'dom:loaded',
  5.     function(event) {
  6.         /* HTML geladen, voer iets uit */
  7.         $('info').setStyle({backgroundColor : '#F00'});
  8.     }
  9. );
  10. // --></script>

Even wat toelichting op het bovenstaande script. Het document-element is door Prototype uitgebreid met de observe functie. De eerste parameter is het event waar we op wachten (in dit geval 'load'). De keuze voor een event kan worden gemaakt uit de DOM Level 2 Events (hier beschreven: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings) en een aantal toevoegingen van prototype zoals dom:loaded. Veel gebruikte events zijn bijvoorbeeld load, click, mouseover, focus en blur . De tweede parameter is de functie die uitgevoerd dient te worden wanneer het event zich voordoet. Hierbij kan een keuze worden gemaakt om de functie als een reference mee te geven, of zoals in het voorbeeld direct een functie als parameter te gebruiken. De functie wordt aangeroepen met als eerste parameters het event object. Dit object kan later worden gebruikt om gegevens over het event op te vragen, maar hier kom ik later op terug. De body van de functie wordt uitgevoerd wanneer het event zich voordoet. In dit geval is dit dus een verandering van achtergrondkleur van een p element met het id 'info'.

Maar er is meer...

Bovenstaand voorbeeld is maar een eenvoudige toepassing van event afhandeling met Prototype. Laten we eens kijken naar een wat complexer voorbeeld:

HTML:
  1. <ol id="list">
  2.     <li><a class="me" rel="Yeah!" href="#">Link 1</a></li>
  3.     <li><a class="me" rel="Click!" href="#">Link 2</a></li>
  4.     <li><a class="not-me" rel="No!" href="#">Link 3</a></li>
  5.     <li><a class="me" rel="Wow" href="#">Link 4</a></li>
  6. </ol>

Bovenstaand is een simpele ordered list met een stel list elements waarin zich een aantal links bevinden. Het weinig gebruikte rel attribuut gaan we in dit voorbeeld gebruiken om extra data bij het a element op te slaan. In AJAX-applicaties kan het rel-attribuut gebruikt worden om id's van bepaalde elementen op te slaan en deze als identificatie te gebruiken bij de verwerking aan de server-side. Aan de hand van een CSS Selector gaan we een aantal van deze links clickable maken:

JAVASCRIPT:
  1. $$('ol#list a.me').each(function(element) {
  2. /* Voor elk element dat voldoet aan 'ol.list a.me' */
  3. element.observe('mouseover', function(event) {
  4. /* Laad het element waar op geklikt is en voeg er en class aan toe */
  5. var target = Event.element(event).addClassName('over');
  6. }).observe('mouseout', function(event) {
  7. var target = Event.element(event).removeClassName('over');
  8. }).observe('click', function(event) {
  9. /* Doe een ajax request  */
  10. new Ajax.Request('ajax-request.php', { onSuccess : function(response) {
  11. console.log(response.responseText);
  12. }
  13. } );
  14.  
  15. /* Stop de verdere uitvoering van het event door je browser */
  16. event.stop();
  17. });
  18. });

Ok, wat toelichting op het bovenstaande voorbeeld. Allereerst wordt er een CSS selector gebruikt om een aantal elementen te selecteren (alle li-elementen met de class 'me' binnen ol met het id 'list'). In dit geval levert dit dus een array op met 3 elementen :Link 1, Link 2, Link 4. Vervolgens wordt de each functie van Prototype gebruikt om op ieder element een aantal acties uit te voeren.

Each() is een eenvoudige vervanging van de traditionele for-loop om door een aantal elementen te lopen. De eerste parameter van each is een callback function die voor elk van de elementen wordt aangeroepen. Dit element wordt daarna bij elke aanroep van deze functie als eerste parameter meegegeven (in dit voorbeeld 'element').

Vervolgens worden er 3 observes toegevoegd, 'mouseover', 'mouseout' en 'click'. De mouse-events zorgen ervoor dat een specifieke classname wordt toegekend/ verwijderd aan het element wanneer het event plaatsvindt. De 'element'-functie van Event wordt gebruikt om te achterhalen op welk element geklikt is (target). Het 'click' event wordt vervolgens ingevuld door een eenvoudige ajax request.

Uiteindelijk wordt de stop-functie gebruikt om de verdere uitvoering van het event te stoppen. Dit kan erg praktisch zijn wanneer het click-event plaatsvindt en het niet gewenst is dat de browser het event verder uitvoert (bijvoorbeeld bij het gebruik van a-elementen zoals in het voorbeeld) en een nieuwe pagina opvraagt.

this.bindAsEventListener

Wanneer we met OO-Javascript aan de gang gaan ontstaat er een probleem dat wordt opgelost met de functie bindAsEventListener. Neem het volgende voorbeeld:

HTML:
  1. <script type="text/javascript"><!--
  2. var object = {
  3.   foo : 'Yeah',
  4.   test : function() {
  5.      $('info').observe('mouseover', this.mouseOver); // FOUT
  6.   },
  7.   mouseOver : function(event) {
  8.     alert(this.foo);
  9.   }
  10. };
  11. object.test();
  12. // --></script>

Op het eerste gezicht zou het bovenstaande script 'Yeah' moeten weergeven wanneer er een mouseover op $('info') plaatsvindt. Echter, door de structuur van de taal javascript is het zo dat this geen referentie meer is naar object op het moment dat de eventhandler object.mouseOver op deze manier wordt aangeroepen. Om een lang verhaal kort te maken komt dit doordat de context (of 'scope') verandert wanneer een functie wordt uitgevoerd. Met behulp van de functie bindAsEventListener kunnen we dit corrigeren en zorgen dat this behouden blijft:

HTML:
  1. <script type="text/javascript"><!--
  2. var object = {
  3.   foo : 'Yeah',
  4.   test : function() {
  5.      $('info').observe('mouseover', this.mouseOver.bindAsEventListener(this)); // GOED
  6.   },
  7.   mouseOver : function(event) {
  8.     alert(this.foo);
  9.   }
  10. };
  11. // --></script>

Conclusie

In dit artikel is een aantal voorbeelden gegeven van het gebruik van de Prototype library met de focus op de afhandeling van events. Het geeft aan dat prototype niet alleen een aantal acties in Javascript vereenvoudigt (denk aan $, each en Ajax.Request), maar ook handige toevoegingen biedt (observe, setStyle).

Daarnaast laat het zien dat we bij het ontwikkelen van een echte AJAX-based killer-app niet meer om de populaire Javascript libaries heen kunnen. De documentatie van Prototype geeft een uitgebreid overzicht van alle functies in Prototype.

Reageer ook!

Lekker overzicht maar niet heus :) :|

Je doet elke keer een nieuwe Event registeren op het zelfde object. Zo word debuggen wel heel moeilijk.

Ik heb wat duidelijker gemaakt.
* En nu maar hopen dat Wordpress er in slaagt dit zonder een enig probleem weer te geven... *

JAVASCRIPT:
  1. $$('ol#list a.me').each(function(element)
  2. {
  3.     /* Voor elk element dat voldoet aan 'ol.list a.me' */
  4.     element.observe('mouseover', function(event)
  5.     {
  6.         /* Laad het element waar op geklikt is en voeg er en class aan toe */
  7.         var target = Event.element(event).addClassName('over');
  8.     });
  9.    
  10.     element.observe('mouseout', function(event)
  11.     {
  12.         var target = Event.element(event).removeClassName('over');
  13.     });
  14.    
  15.     element.observe('click', function(event)
  16.     {
  17.         /* Doe een ajax request  */
  18.         new Ajax.Request('ajax-request.php', {
  19.             onSuccess : function(response)
  20.             {
  21.                 console.log(response.responseText);
  22.             }
  23.         });
  24.  
  25.         /* Stop de verdere uitvoering van het event door je browser */
  26.         event.stop();
  27.     });
  28. });

OH DIT MEEN JE TOCH ZEKER ZELF NIET !!!
NU HAD IK EEN HEEL MOOI UITGELIJNDE CODE DOET DAT MIDDELS $@%!@$%~! Het weer naar normale tekst zetten, en dan plaats ik het nog wel tussen code blocks!

Iemand een handleiding hoe dit WP formaat werkt?

Dit is een leuke methode van events toevoegen. Echter loopt het aantal event handlers dat draait op je site nogal op op deze manier.

Een betere methode van events toevoegen is het zogenaamde "Event Delegation".

Een voordeel van deze methode is bijvoorbeeld dat je on-the-fly elementen kan toevoegen (bijvoorbeeld een menu item), zonder dat je daar de event handlers aan hoeft te hangen. Het event werkt automatisch.

Lees meer over event delegation op deze pagina:
http://www.chromasynthetic.com/blog/event-delegation-an-example/122/

"Iemand een handleiding hoe dit WP formaat werkt?"

Het werkt gewoon niet :P

Sebastiaan: [ php ] [ /php ] & [ javascript ] [ /javascript ] (zonder de spaties).

Hi,

Ik zoek naar een server-side script waarmee tekst in HTML van kleur verandert wanneer een opgegeven datum is gepasseerd.

Kan iemand mij daaraan helpen? Of op weg helpen?

Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>