Events

We schreven tot hiertoe code die verwerkt werd vanaf dat de website in de browser geopend werd. Dat is geen interactieve situatie. We reageren met onze scripts niet op wat de gebruiker doet. We moeten daarom onderzoeken wat de gebruiker doet, wat de gebeurtenissen zijn, de 'events'.

Misschien hebben we JavaScript geschreven dat pas uitgevoerd mag worden als de gebruiker van onze pagina op een link klikt. Of als de gebruiker een formulier heeft ingevuld en wil doorsturen, wil ik als JavaScripter er zeker van zijn dat alle velden correct zijn ingevuld. Misschien willen we er zeker van zijn dat de pagina volledig geladen is, alle afbeeldingen en alle CSS bestanden geladen zijn. Al deze zaken kunnen we opvangen aan de hand van gebeurtenissen of events.

Hoe schrijf je zo'n event. Wel, eigenlijk nooit. Deze gebeurtenissen zijn reeds aanwezig. Wanneer de pagina laadt, dat is een event. Wanneer de gebruiker klikt, dat is een event. Wanneer de gebruiker zijn muis verplaats, dat zijn een heleboel events. Wanneer de gebruiker zijn pagina scrollt, dat is een event. Als hij of zij op een tekstveld in een formulier klikt, dat is een event. Als die een woord intypt zijn het een heleboel events. Events zijn er heel de tijd. Je moet beslissen op welke we willen reageren, naar welke we willen luisteren.

Events zijn ingebouwd in JavaScript. Er zijn speciale gereserveerde woorden die ze omschrijven. Je ziet ze meestal beginnen met het woord on. onload, onclick, onmouseover, onblur, onfocus, zijn er enkele en we zien er nog een aantal in de loop van deze cursus.

Maar je schrijft het event zelf niet. Je schrijft een event handler of een event listener (luisteraar), welke term je ook prefereert. Je schrijft een functie die gaat luisteren naar dat ene event, die ene gebeurtenis. We gaan drie manieren zien hoe we deze event listeners kunnen schrijven.

De eerste is de simpelste maar ook de lelijkste en de meest onhandige. We kunnen namelijk JavaScript-code rechtstreeks in je HTML-code schrijven. Stel dat je een knop hebt in je HTML-code en we schrijven het volgende:

<button onclick="alert('Hello world!');">
     Run some JavaScript
</button>

Dit is slecht om verschillende redenen. JavaScript wordt nu gemengd met HTML-code en dat maakt het moeilijker op grotere pagina's om ordelijk te werken. Net zoals je CCS en HTML-code best niet mengt. Terwijl je verschillende lijnen JavaScript code kan schrijven op deze manier binnen de tags, kan je je afvragen wie er zo wil werken. Herbruikbare code wordt het ook niet echt. Maar soms zie je deze methode wel gebruiken.

De tweede manier is om de naam van het element op te halen en achter een punt de naam van het event dat je wil gaan behandelen.

  • window.onload is het event als de pagina volledig is geladen.
  • nameField.onblur bedoelt het wegklikken van een in dit geval een tekstveld.
  • myElement.onclick bedoelt dat er op dat ene element wordt geklikt.
  • ... Wanneer de gebruiker een DOM-element aanklikt, willen we iets doen en dus schrijven we het volgende:
myElment.onclick = function() {
	//code die uitgevoerd zal worden
};

De functie-vorm die we hier gebruiken heet een anonieme functie, een functie zonder naam. Deze wordt heel veel gebruikt in JavaScript. Het ziet er een beetje vreemd uit omdat we de functie geen naam geven. Een functie is normaal gezien een blok code die we een naam geven zodat we die later, wanneer we die nodig hebben, kunnen oproepen. Maar omdat we ze hier enkel oproepen bij de klik, moeten we er hier ook geen naam aan geven. Tegelijk moeten we deze code afsluiten met een punt-komma. Ook al schrijven we een functie en ook al eindigen we met accolades, het blijft één zin en die moeten we steeds afsluiten met een punt-komma. Doen we dit niet, dan werkt het script niet altijd.

Herbruikbaar wordt deze functie uiteraard ook niet.

Wat belangrijk is om weten is dat deze functie niet wordt uitgevoerd totdat de gebruiker op dat ene DOM-element klikt.

De derde manier om naar een event te luisteren is werkelijk een luisteraar toe te voegen aan een element. We gebruiken hiervoor de methode addEventListener() die drie argumenten nodig heeft. vb:

myElement.addEventListener("click", myFunction, false);

We koppelen hier een eventListener aan een element dat nu kan luisteren naar dat ene event dat als eerste parameter wordt meegegeven. Merk op dat het woord 'on' weg valt en dat het te beluisteren event tussen aanhalingstekens staat. Als tweede argument wordt de uit te voeren functie meegegeven. Deze functie wordt apart geschreven. Het derde argument, false, moet er staan maar wordt enkel true gezet bij hele speciale gevallen die we hier niet gaan verklaren. Een voordeel is dat we via removeEventListener() ook weer een handler kunnen verwijderen van een element.

myElement.removeEventListener("click", myFunction, false);

Een zeer groot nadeel is dat we hier moeten rekening houden met de verschillende browsers. Het is zo dat in Internet Explorer de addEventListener() methode niet bestaat. Daar gebruikt men attachEvent(). Dit moeten we oplossen door JavaScript code te schrijven die gaat onderzoeken op welke browser we zitten en of de methode bestaat. Omdat dit te ingewikkeld wordt, is het beter om externe bibliotheken te gebruiken zoals jQuery waar we later in deze cursus op terugkeren. Deze bibliotheek kan voor ons de verschillen tussen de browsers oplossen.

Daarom gaan we de tweede vorm gebruiken, de anonieme functie, om events te gaan behandelen.

Werken met onclick events

De meest voorkomende events zijn wanneer een pagina in de browser laadt en wanneer een gebruiker ergens op klikt in de pagina. We onderzoeken deze in bijgevoegde HTML-pagina.

Merk op dat er in de pagina een script staat op regel 28.

<button onclick="alert('Hello, world');">Run Some JavaScript</button>

Deze 'inline'-methode willen we niet langer gebruiken. We gaan de tweede vorm gebruiken die we hierboven hebben gezien. Onderaan vinden we reeds een link naar het bestand script.js. Dit bestand is normaal gezien leeg.

onclick is een veel voorkomend event. Je zou kunnen denken dat je enkel kan klikken op een knop of hyperlink, maar eigenlijk is dit niet altijd nodig. We kunnen overal op klikken, we moeten alleen kunnen zeggen wat dat is.

Het top-level in JavaScript is window, daaronder vind je het document waarin dan weer je ganse HTML-pagina staat oftewel de DOM. document is een element dat we rechtstreeks kunnen aanspreken, zonder dit te moeten ophalen via getElementById() of getElementsByTagName(). Om te luisteren of er op het document werd geklikt, kunnen we het volgende schrijven:

document.onclick = function() {
	alert("Er werd op het document geklikt");
};

In de browser merken we nu dat als we waar dan ook in het document klikken, we de alert-box krijgen. Let wel, als je op de reeds aanwezige knop klikt, dan krijg je eerst de alert die daar bij hoort, daarna de alert die bij het document hoort. Want, ook op een knop klikken is op het document klikken. Natuurlijk is klikken op het document niet altijd handig. We schrijven het volgende:

let myImage = document.getElementById("mainImage");
myImage.onclick = function() {
	alert("Je klikte op de afbeelding");
};

Op deze manier wordt het duidelijk dat we via JavaScript de mogelijkheid kunnen inbouwen dat we elk element kunnen aanspreken en klikbaar maken.

Werken met onload events

Tot hiertoe hebben we steeds onze scripts, of onze link naar het script.js bestand, onderaan de HTML-pagina gezet. Dit omdat we er zeker van zouden zijn dat alle elementen in de pagina zeker geladen zouden zijn voordat we een script uitvoeren. Nochtans is dit geen garantie. Het kan heel goed zijn dat als je CSS bestanden, afbeeldingen en script bestanden gaat laden, dat niet alles binnen is terwijl je toch al scripts wil laten uitvoeren. En toch zie je online heel veel scripts bovenaan staan.

Als we dit script inderdaad bovenaan zetten door de link naar script.js bovenaan te zetten, dan merken we dat het script niet langer blijkt te werken. Dit omdat het script reeds wordt uitgevoerd vooraleer er een id met mainImage bestaat, en dus kan de event luisteraar er niet aan gekoppeld worden, en dus ook de functie niet... Daarom moeten we steeds gebruik maken van het onload event dat pas opgeroepen wordt als de pagina en al zijn bijbehorende en noodzakelijke bestanden volledig geladen zijn.

In het script.js bestand kunnen we nu het volgende schrijven:

window.onload = function() {
	// bereidt alles voor wat je nodig hebt
};

Om alles voor te bereiden, zullen we een functie maken die de ganse initiatie (voorbereiding) doet voor onze scripts.

function init(){
    let myImage = document.getElementById("mainImage");
    myImage.onclick = function(){
        alert("Je klikte op de afbeelding");
    }
}
window.onload = funcion(){
    init();
};

Op deze manier wordt de luisteraar voor de mainImage geactiveerd als het document volledig binnen is.

De onblur en onfocus events.

Deze twee events worden veel gebruikt bij formulieren. Ongetwijfeld heb je tijdens het invullen van een web-formulier al wel gezien dat er tekst in de vakken staat die verklaart wat je moet invullen en die verdwijnt als je op het veld klikt of er via de tab toets in terecht komt. Als je er dan terug uit gaat, via een klik of via de tab-toets en je hebt niets ingevuld, verschijnt de tekst opnieuw. Dit wordt bereikt via de onfocus en onblur events. Bij het aanklikken gebruiken we het onfocus event, bij het wegklikken het onblur event.

let emailField = document.getElementById("email");
emailField.onfocus = function() {
	if (emailField.value == "your email") {
		emailField.value = "";
	}
};
emailField.onblur = function() {
	if (emailField.value == "") {
		emailField.value = "your email";
	}
};

Bekijk het script.js bestand om te zien hoe dit bereikt wordt. Merk op dat er hier geen gebruik wordt gemaakt van innerHTML bij het invullen van de velden, maar wel van value omdat het hier gaat over de ingevulde waarde van het tekstveld.

Let ook op de werking van de if-structuur. Er wordt wel degelijk gevraagd wat er in het veld staat, niet of dat het al dan niet is ingevuld. Als we dit wel zouden doen, dan zouden we ook een reeds ingevulde waarde wissen. Om echter na te kijken of de ingevulde waarde werkelijk een geldig e-mail is, hebben we 'regular expressions' nodig. Dit is echter voor een later hoofdstuk.

Timers

Het komt veel voor om JavaScript functies met vertraging uit te voeren of om die om de 60 seconden te laten uitvoeren. Om nieuwe foto's te laden na een tijdje in functie van een slideshow. Of om een klok te maken. Hiervoor gebruiken we timers.

Een eerste manier om een timer te maken en te starten is setTimeout(). Dit statement heeft 2 parameters nodig: de functie die moet uitgevoerd worden na een bepaalde tijd, die gegeven wordt als tweede parameter in milliseconden. vb:

function simpleMessage() {
	alert("timed out alert");
}
setTimeout(simpleMessage, 5000);

Duidelijk is dat bij uitvoering van dit script, de alert-box pas zal komen na 5 seconden na laden van de pagina. Dit gebeurt slechts 1 keer.

Als we iets meerdere keren willen doen gebeuren, telkens na een bepaalde tijd, dan hebben we een interval nodig. setInterval() heeft de zelfde parameters nodig en blijft de functie herhalen telkens de vooropgestelde tijd behaald werd. Laat ons deze gebruiken in een slideshow voorbeeld. In de bijgevoegde HTML-code gaan we de afbeelding met id mainImage om de drie seconden veranderen met een andere die we uit een Array halen. We maken eerst de variabelen en de functie aan die we nodig hebben, en starten dan de timer door setInterval() aan te roepen.

let myImage = document.getElementById("mainImage");
let imageArray = [
	"_images/overlook.jpg",
	"_images/winery_sign.jpg",
	"_images/lunch.jpg",
	"_images/bigSur.jpg",
	"_images/flag_photo.jpg",
	"_images/mission_look.jpg"
];
let imageIndex = 0;
function changeImage() {
	myImage.setAttribute("src", imageArray[imageIndex]);
	imageIndex++;
	if (imageIndex >= imageArray.length) {
		imageIndex = 0;
	}
}
// setInterval is also in milliseconds
setInterval(changeImage, 3000);

Merk op dat we gebruik maken van setAttribute() om de bron van de img-tag aan te passen. Zinnig en handig is ook wel dat we een setInterval() ook kunnen doen stoppen door clearInterval() te gebruiken. Zo kunnen we deze slideshow stoppen als een gebruiker op de afbeelding klikt. Om dit te kunnen doen moeten we het setInterval() statement opvangen in een variabele. Dat is trouwens altijd een betere werkwijze. We voegen het volgende aan het script toe in script.js

let myInterval = setInterval(changeImage, 3000);
myImage.onclick = function() {
	clearInterval(myInterval);
};

De reden waarom we setInterval() moeten opvangen in een variabele is simpel. We kunnen, als we dat willen, veel meer timers starten via setInterval(). Als we ze in een variabele steken, kunnen we ze gericht oproepen om ze te stoppen.