JavaScript voor beginners: Events

In de vorige aflevering hebben we gezien hoe we in JavaScript de elementen van een HTML-formulier kunnen benaderen. Daarbij kwam al even ter sprake dat er een speciale "event" of gebeurtenis plaatsvindt als de gebruiker op een knop in het formulier drukt. In deze aflevering zullen we uitgebreid ingaan op events en de "event handlers" die we gebruiken om op gebeurtenissen te reageren.

Programmeren met behulp van events

Vroeger werkten programma's zeer rechtlijnig: eerst werd stuk A uitgevoerd, vervolgens stuk B, en tenslotte stuk C of D, afhankelijk van de waarde die de gebruiker voor een bepaald veld had ingevoerd. Die eenvoud verdween met het populair worden van de grafische gebruikersinterface.

Plotseling kon de gebruiker niet alleen met behulp van het toetsenbord teksten invoeren en binnen het programma navigeren, maar het was nu ook mogelijk om allerlei handelingen te verrichten met de muis. Natuurlijk moest het programma adequaat reageren op de handelingen van de gebruiker, en "events" oftewel gebeurtenissen bleken een handige manier om daar voor te zorgen.

Programmeertalen die events ondersteunen, voeren achter de schermen voortdurend een lus uit om te kijken welke gebeurtenissen er optreden. Voorbeelden van gebeurtenissen zijn: het indrukken van een toets, het aanklikken van een knop met de muis, het openen of sluiten van een venster, of het verstrijken van een bepaalde hoeveelheid tijd.

Het enige dat de programmeur in zo'n taal hoeft te doen, is: opgeven welke code moet worden uitgevoerd als de gebeurtenis optreedt, oftewel: een "event handler" specificeren. Dankzij het event-model kunnen we in een taal als Visual Basic of Delphi op vrij simpele wijze programma's voor Windows schrijven, en ook JavaScript ondersteunt events.

Om in onze programma's gebruik te kunnen maken van events, moeten we dus weten welke gebeurtenissen wanneer kunnen optreden. Helaas is dit voor een deel afhankelijk van de browser, de versie van JavaScript en het besturingssysteem waarmee we werken. We zullen ons echter concentreren op die gebeurtenissen die in alle versies van JavaScript kunnen optreden.

Formulieren

Veel JavaScript-events hebben betrekking op formulieren; in de vorige aflevering hadden we het al over het click-event dat optreedt als de gebruiker op een knop drukt. Om op die gebeurtenis te kunnen reageren, moeten we dus een event-handler opgeven; dat kan op verschillende manieren. Laten we aannemen dat onze HTML-pagina een formulier bevat met de naam "klant"; op dat formulier bevindt zich een knop die als volgt is gedefinieerd:

<INPUT TYPE="button" NAME="druk" VALUE="Bereken premie!">

Als de gebruiker op deze knop drukt, lijkt er niets te gebeuren, want knoppen van het type button hebben, anders dan submit- en reset-knoppen, geen "ingebouwd" gedrag. Er gebeurt echter wel degelijk iets: er vindt namelijk een click-event plaats. Dit wordt duidelijk als we voor de knop een event-handler opgeven. We kunnen bijvoorbeeld schrijven:

<INPUT TYPE="button" NAME="druk" VALUE="Bereken premie!" onClick="alert('Premie berekend!')">

Drukt de gebruiker nu op de knop, dan wordt er een alert-box getoond. We kunnen de uit te voeren code ook in een afzonderlijke functie "bereken" stoppen, en de event handler als volgt opgeven:

<INPUT TYPE="button" NAME="druk" VALUE="Bereken premie!" onClick="bereken()">
Dit is in veel gevallen overzichtelijker, en bovendien kan dezelfde functie zo fungeren als event handler voor verschillende gebeurtenissen. Het is echter wel duidelijk dat de tweede methode niet fundamenteel van de eerste verschilt.

Er is echter (in JavaScript 1.1 en hoger) nog een manier om een event handler op te geven; hierbij blijven HTML-code en JavaScript strikt van elkaar gescheiden. We definiëren in HTML nu alleen het TYPE, de NAME en de VALUE van onze knop, zoals in het eerste voorbeeld hierboven. In JavaScript schrijven we vervolgens:

document.klant.druk.onclick = function (){alert('Premie berekend!')}

Bij deze schrijfwijze wordt er dus een functie toegekend aan een eigenschap van een object. Een JavaScript-object dat overeenkomt met een HTML-element (zoals een knop), heeft namelijk een of meer eigenschappen die overeenkomen met de event handler-attributen van dat element.

We moeten hier wel "onclick" schrijven en niet bijvoorbeeld "onClick": JavaScript maakt, anders dan HTML, onderscheid tussen hoofd- en kleine letters, en de eigenschappen die event handlers representeren, moeten worden geschreven met kleine letters.

Het grote voordeel van deze derde schrijfwijze is, dat we op elk moment een nieuwe event handler op kunnen geven door een nieuwe waarde aan de desbetreffende eigenschap toe te kennen, terwijl HTML-attributen niet meer kunnen worden gewijzigd.

Alle voorbeelden tot nu toe hadden betrekking op het click-event. Dat treedt op als de gebruiker een knop (submit, reset of button), een checkbox of een radiobutton aanklikt, maar ook als de click-methode van het overeenkomstige JavaScript-object wordt aangeroepen, bijvoorbeeld met: document.klant.druk.click().

In de vorige aflevering hebben we gezien dat knoppen ook een focus- en een blur-methode hebben, waarmee ze de focus krijgen of juist verliezen. Het komt nu wellicht niet meer als een verrassing dat er ook focus- en blur-events bestaan die we met respectievelijk "onfocus" en "onblur" kunnen afhandelen.

Functieresultaat

De event handler die we hiervoor gebruikten, was een eenvoudige functie die geen resultaat teruggaf. Het kan echter zinvol zijn om wel een resultaat terug te geven. Dat is met name het geval als sprake is van objecten met "ingebouwd" gedrag, zoals submit-knoppen. Als de gebruiker op een submit-knop drukt, dan wordt de inhoud van het formulier automatisch verstuurd. Dat is heel mooi... tenzij er fouten zijn gemaakt bij het invullen van het formulier!

Het zou geen zin hebben om formulieren met behulp van JavaScript te valideren, als we niet konden voorkomen dat foutieve gegevens werden verstuurd. Gelukkig is er wel een manier om het automatische versturen af te breken: geeft de event handler namelijk de waarde "false" terug, dan wordt het formulier niet verstuurd. We geven daartoe onze event handler bijvoorbeeld als volgt op: onClick="return bereken()", en we laten de functie "bereken" true teruggeven als de invoer in orde is, of false als er een probleem is.

Het click-event vindt plaats voordat het formulier wordt opgestuurd; van dat feit kunnen we ook gebruik maken om gegevens te formatteren (en bijvoorbeeld alle kleine letters in hoofdletters om te zetten) voordat ze worden verstuurd.

Focus- en blur-events treden niet alleen op bij knoppen, checkboxen en radiobuttons, maar ook bij alle andere form-objecten, met uitzondering van "hidden" velden. Click-events kennen deze objecten echter niet; in plaats daarvan hebben ze een change-event.

Als de gebruiker bijvoorbeeld een waarde invult in een veld van het type text, password, textarea of fileupload, dan treedt er een change-event op. Dat gebeurt overigens eenmalig, op het moment dat de gebruiker het veld wil verlaten, en niet telkens wanneer er een toets wordt ingedrukt.

Het event treedt alleen op als er met het toetsenbord een waarde wordt ingegeven, en niet als er in JavaScript een waarde aan het veld wordt toegekend. Bij het select-object vindt het change-event plaats zodra de gebruiker een optie selecteert of deselecteert.

Niet alleen de verschillende elementen van een formulier kennen events, maar ook het formulier zelf: er bestaat een reset- en een submit-event.

In de vorige aflevering zagen we dat we een formulier in JavaScript op twee manieren kunnen versturen: we kunnen de submit-methode van het formulier aanroepen of de click-methode van zijn submit-knop.

Er is echter een belangrijk verschil tussen beide alternatieven: in het eerste geval treedt er geen submit-event op, in het tweede geval wel. Eventuele validaties die we in de event handler hebben opgenomen, worden in het eerste geval dus ook niet uitgevoerd!

Window-events

Hoewel veel gebeurtenissen betrekking hebben op formulieren of de elementen die daar deel van uitmaken, zijn er ook events die horen bij andere JavaScript-objecten. Met name het window-object kent enkele events waar we in onze programma's nuttig gebruik van kunnen maken.

Zodra een document volledig geladen is, treedt er een load-event op. De event handler voor deze gebeurtenis geven we op in de BODY-tag van ons HTML-document, bijvoorbeeld als volgt:

<BODY onLoad="alert('Welkom op mijn pagina!')">

Er is ook een unload-event dat optreedt vlak voordat we de pagina verlaten. Ook voor deze gebeurtenis geven we de event handler op in de BODY-tag:

<BODY onUnload="alert('Graag tot binnenkort!')">

Ook het window-object kent focus- en blur-events; deze treden bijvoorbeeld op als de gebruiker tussen twee vensters heen en weer schakelt. De bijbehorende event handlers gaan weer in de BODY-tag.

Link-events

Ook links kennen enkele events, waarvan het click-event waarschijnlijk de meest voor de hand liggende is: deze gebeurtenis vindt plaats als de gebruiker een link in het document aanklikt. Normalerwijze gaat de gebruiker in zo'n geval naar een nieuwe pagina, maar dat gebeurt niet (vanaf JavaScript 1.1) als de event handler de waarde "false" teruggeeft.

Daarnaast kent het link-object twee gebeurtenissen die te maken hebben met de positie van de muiscursor ten opzichte van de link. Er treedt een mouseover-event op wanneer de muiscursor zich boven de link bevindt, en een mouseout-event wanneer de muiscursor van de link wordt weggehaald.

Het standaard "gedrag" van een mouseover-event is, dat de URL die bij de link hoort, in de statusregel wordt getoond. Dit kunnen we echter onderdrukken door de event handler de waarde "true" terug te laten geven. Handlers voor link-events geven we op in de anchor-tag, bijvoorbeeld als volgt:

<A HREF="uitgang.htm" onClick="return confirm('U staat op het punt de site te verlaten, wilt u dat echt?')">Uitgang</A>

Met confirm, een methode van het window-object, kan de gebruiker zijn keuze bevestigen door op "OK" te drukken of annuleren door op "Cancel" te drukken. In het eerste geval wordt de waarde "true" geretourneerd en gaat de gebruiker naar pagina "uitgang.htm", in het tweede geval wordt "false" teruggegeven en blijft de gebruiker op de huidige pagina.

Timer-events

In recente versies van JavaScript zijn er nog veel meer events die optreden als het venster van de browser wordt vergroot of verkleind, als er een toets wordt ingedrukt of als de gebruiker de muiscursor boven een afbeelding beweegt. Op deze events zullen we hier echter niet ingaan, maar wel op een laatste groep gebeurtenissen die te maken hebben met het verstrijken van een bepaalde hoeveelheid tijd: de timer-events.

Om gebruik te kunnen maken van timer-events, moeten we eerst opgeven met welke tussenpozen die events zullen optreden. Dat doen we met de methode "setTimeout" van het window-object. De parameters die we aan deze methode meegeven, zijn de uit te voeren JavaScript-code (met het oog op de leesbaarheid zullen we hier meestal de naam van een functie gebruiken) en de lengte van het gewenste interval in milliseconden (dus 1000 voor een seconde en 60000 voor een minuut).

De methode geeft een waarde terug: het zogenaamde "timeout ID". Deze waarde kunnen we gebruiken om voortijdig het optreden van het timer-event te onderdrukken; we geven daartoe het timeout ID als parameter mee aan clearTimeout, een andere methode van het window-object.

Met setTimeout wordt slechts een enkel timer-event "voorbereid"; willen we dat een dergelijk event herhaaldelijk optreedt, dan moeten we in de uit te voeren code, die we als parameter aan setTimeout meegeven, opnieuw setTimeout aanroepen. (Vanaf JavaScript 1.2 is er een methode setInterval die zelf voor herhaalde uitvoering zorgt.)

De beste manier om timer-events te leren begrijpen, is aan de hand van een voorbeeld:

<HTML>
<SCRIPT>
<!-- Afschermen voor oude browsers:
function Initialiseer()
{
t = 10;
k = setTimeout('ToonKlok()',1000);
}


function ToonKlok()
{
t = t - 1;
if (t == 0)
  {
  document.tijdbom.klok.value = 'BOEM!';
  }
else
  {
  document.tijdbom.klok.value = t;
  k = setTimeout('ToonKlok()',1000);
  }
}


// Einde afscherming -->
</SCRIPT>


<BODY onLoad="Initialiseer()">
<H1>Tijdbom...</H1>


<FORM NAME="tijdbom">
<INPUT TYPE="text" NAME="klok" VALUE=10><BR>
<INPUT TYPE="button" NAME="stop" VALUE="Stop!" onClick="clearTimeout(k);document.tijdbom.klok.value='De bom is gestopt...'"><BR>
</FORM>
</BODY>
</HTML>


We gebruiken een invoerveld om te tonen hoe de tijdbom aftelt van tien naar nul; zodra we bij nul zijn aangeland, tonen we het woord "BOEM!" om de explosie te simuleren. Merk op hoe in ToonKlok steeds opnieuw de methode setTimeout wordt aangeroepen. De retourwaarde van die methode bewaren we in variabele k; als de gebruiker tijdig op de stop-knop drukt, dan zetten we de klok stil met clearTimeout(k).

Conclusie

We hebben kennis gemaakt met de verschillende events die in JavaScript kunnen plaatsvinden, en gezien hoe we daar op kunnen reageren met behulp van event handlers. In de volgende aflevering zullen we het hebben over de manier waarin we in JavaScript omgaan met frames en navigatie.