JavaScript for Interactive Web Pages 1 Introductie Leerkern 7.1
9 10
Key JavaScript Concepts 10 Client-Side Scripting 10 7.1.2 Event-Driven Programming 11 7.1.3 A JavaScript Program 12 7.1.4 The Document Object Model (DOM) JavaScript Syntax 15 7.2.1 Types 15 7.2.2 Numbers and Arythmetic 16 7.2.3 Variables 16 7.2.4 Comments 17 7.2.5 Using DOM Objects 17 7.2.6 Debugging Common Errors 17 7.2.7 Strings 18 7.2.8 For Loops 18 7.2.9 The Math Object 18 7.2.10 Null and Undefined Values 19 7.1.1
7.2
Samenvatting Opdrachten Zelftoets
19 20
26
Terugkoppeling 1 2 3 4
8
27
Uitwerking van de opgaven 27 Uitwerking van de opdrachten 28 Uitwerking van de self-checks 29 Uitwerking van de zelftoets 32
13
Leereenheid 7a
JavaScript for Interactive Web Pages 1
INTRODUCTIE
JavaScript maakt het mogelijk om webpagina’s dynamisch te maken en om de gebruiker meer mogelijkheden tot interactie te bieden. Een aanzienlijk gedeelte van deze cursus heeft daarom JavaScript als onderwerp. JavaScript is een taal met veel mogelijkheden: mogelijkheden om geavanceerde dynamiek aan webpagina’s te geven, maar ook mogelijkheden om slecht leesbare en slecht onderhoudbare programma’s te schrijven en om fouten te maken. We besteden daarom in dit werkboek extra aandacht aan regels en gewoonten om goed leesbare en onderhoudbare code te schrijven. In deze leereenheid maakt u kennis met JavaScript en leert u eenvoudige functies te schrijven. LEERDOELEN
Na het bestuderen van deze leereenheid wordt verwacht dat u – kunt uitleggen wat het verschil is tussen server-side scripting en client-side scripting – eigenschappen van een DOM-object kunt veranderen – de acht fundamentele typen van JavaScript kent en ze kunt gebruiken (met uitzondering van de typen Array en Object die verderop in de cursus aan bod komen) – een JavaScriptbestand van commentaar kunt voorzien – de in het tekstboek genoemde operatoren kent en kunt gebruiken – het in het tekstboek genoemde control statement kent en kunt gebruiken – met behulp van de geleerde constructies eenvoudige functies kunt schrijven, en ze als event handler kunt binden aan een element uit een HTML-pagina – de betekenis van de volgende begrippen kent: event handler, binden van een event handler, event-driven programmeren, sequentieel programmeren, DOM-object, object chaining, statisch getypeerd, dynamisch getypeerd, impliciete typeconversie. Studeeraanwijzingen Voor deze leereenheid bestudeert u de paragrafen 7.1 en 7.2 van het tekstboek. We verwachten dat u voor het bestuderen van deze leereenheid ongeveer 6 uur zult nodig hebben.
OU
9
Webapplicaties: de clientkant
LEERKERN 7.1
Weblink: Geschiedenis van JavaScript
Key JavaScript Concepts
JavaScript is in 1995 ontwikkeld door Brendan Eich voor Netscape. Zijn opdracht was om een programmeertaal te ontwikkelen die gemakkelijk te gebruiken zou zijn door ongeschoolde programmeurs, en waarmee de code direct in de HTML-code geschreven zou kunnen worden. Toen hij een eind op weg was werd Java populair. Om van de populariteit van Java gebruik te maken is de naam van de door Brendan Eich ontwikkelde taal veranderd van LiveScript in JavaScript, en is de syntax van JavaScript zo ingericht dat die aan Java doet denken. JavaScript is echter een geheel andere taal! Meer over de geschiedenis van JavaScript in de weblinks bij deze leereenheid. Het feit dat JavaScript inderdaad gebruikt kan worden door ongeschoolde programmeurs, onder andere doordat het mogelijk is stukken code te knippen en te plakken en te bekijken wat er gebeurt, heeft als consequentie gehad dat JavaScript door ‘echte’ programmeurs lange tijd beschouwd is als een inferieur speelgoedtaaltje. Ook het feit dat het zo gemakkelijk is om uiterst slecht onderhoudbare code vol fouten te schrijven heeft daaraan bijgedragen. Sinds de opkomst van Ajax (waarover later in deze cursus) is er meer belangstelling voor JavaScript gekomen, en is er daarmee ook oog gekomen voor de mooie eigenschappen, de flexibiliteit en de kracht van de taal. Ook zijn er ideeën ontwikkeld over hoe je JavaScript zo kunt gebruiken dat je de valkuilen vermijdt.
ActionScript
Een andere implementatie van de ECMAScript-standaard is ActionScript, dat we in deze cursus niet behandelen. ActionScript wordt gebruikt om applicaties te ontwikkelen voor de Adobe Flash Player (Flash komt nog kort aan bod in leereenheid 11).
Functie en object
Het tekstboek zegt dat de ‘key construct’ in JavaScript de functie is. Zoals we later zullen zien, is een functie in JavaScript een object. Het is dan ook juister om te zeggen dat de basisconstructie in JavaScript het object is. 7.1.1
Server-side scripting
10
CLIENT-SIDE SCRIPTING
Server-side scripting wordt in het tekstboek in hoofdstuk 5 en 6 besproken, maar komt in deze cursus niet aan bod (in de cursus ‘Webapplicaties: de serverkant’ komt server-side scripting uiteraard wel aan bod). Voor deze cursus is het voldoende dat u weet dat het niet alleen mogelijk is om, zoals we in de voorbeelden en opdrachten gezien hebben, webpagina’s te bouwen door middel van HTML- en CSSbestanden, maar dat het ook mogelijk is om op de server een programma te executeren dat als output HTML en eventueel CSS heeft. De HTML staat dan dus niet ‘vast’ in een bestand, maar wordt geproduceerd op het moment dat een gebruiker via de browser een pagina opvraagt. Zo’n programma kan bijvoorbeeld een servlet zijn (een programmaatje in Java dat HTML produceert), of een script dat geïnterpreteerd wordt. In dat laatste geval is de term server-side scripting.
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
Aan de HTML die de browser ontvangt is niet te zien of deze afkomstig is van een HTML-bestand of een script of programma. Het is voor de JavaScript-programmeur dan ook niet van belang. Een aanwijzing kan de extensie van de URL zijn: eindigt die bijvoorbeeld op .php of op .jsp, dan is er een script dat de HTML produceert (de URL eindigt overigens niet altijd met een bestandsnaam met extensie, en de extensie .html houdt niet per definitie in dat er geen script aan te pas is gekomen). OPGAVE 7a.1
Sluiten server-side scripting en client-side scripting elkaar uit, of kan er bij een webpagina zowel server-side als client-side scripting aan te pas komen? Motiveer uw antwoord. OPGAVE 7a.2
Als u de bestanden bekijkt die bijvoorbeeld nodig zijn voor een blog in Wordpress (zie weblink), ziet u zowel bestanden met extensie .php (server-side scripts) als bestanden met extensie .js (client-side scripts). Die bestanden staan allemaal op de webserver. Waarom heten dan niet al die scripts server-side scripts? Weblink: Spelletjes
Weblink: Showcases Weblink: Chrome experiments
Om een idee te krijgen van wat er mogelijk is met JavaScript kunt u de site met spelletjes in JavaScript bekijken (zie de links bij deze leereenheid op studienet). Onder de weblink JavaScript showcases vindt u voorbeelden van hoe JavaScript gebruikt kan worden bij de navigatie en bij animaties. De Chrome Experiments zijn een verzameling van niet-alledaagse JavaScript-applicaties: ook de moeite waard om te bekijken. 7.1.2
Procedureel programmeren
Objectgeoriënteerd programmeren
Sequentieel programmeren
Event-driven programmeren
EVENT-DRIVEN PROGRAMMING
Met de term procedureel programmeren wordt bedoeld dat functies en procedures de basis vormen van een programma. Een programma bestaat uit een main die sequentieel wordt uitgevoerd, en waarin de in het programma gedefinieerde functies en procedures worden aangeroepen. Procedureel programmeren staat tegenover objectgeoriënteerd programmeren, waarin objecten de basis vormen en functies en procedures gebonden zijn aan objecten. JavaScript is, zoals we nog zullen zien, niet objectgeoriënteerd in die zin: functies hoeven niet gebonden te zijn aan objecten. Met de term sequentieel programmeren wordt bedoeld dat statements één voor één worden uitgevoerd, in de volgorde waarin ze in de code staan. Wat er in de main van een Java-programma staat wordt bijvoorbeeld sequentieel uitgevoerd. De term sequentieel programmeren heeft dus te maken met de aansturing. Die aansturing vindt bij event-driven programmeren op een andere manier plaats: de aansturing gebeurt door events. Event-driven programmeren past goed bij interactie met de gebruiker: elke actie van de gebruiker is een event. Statements binnen functies en statements die buiten functies om in een JavaScript-bestand staan, worden sequentieel uitgevoerd bij het inlezen, in de volgorde waarin ze gelezen worden. Er is dus in JavaScript ook sprake van een sequentieel programmeermodel, maar de standaardmanier waarop JavaScript gebruikt wordt is event-driven.
OU
11
Webapplicaties: de clientkant
Event handler
Een event handler is dus een functie die wordt aangeroepen op het moment dat er een event plaatsvindt. Een voorbeeld van zo’n event is de gebeurtenis dat de gebruiker op een knop klikt.
OPGAVE 7a.3
Hoort event-driven programmeren bij procedureel programmeren of bij objectgeoriënteerd programmeren? 7.1.3
Principe van gescheiden verantwoordelijkhe den
Principe van modulariteit
A JAVASCRIPT PROGRAM
De JavaScript-code wordt in een apart bestand geplaatst. Dat is een voorbeeld van een principe van software engineering: het principe van modulariteit. Dat principe is een speciaal geval van het principe van gescheiden verantwoordelijkheden. We gaan hier in op de manier waarop die principes betrekking hebben op het scheiden van HTML- en JavaScript-code: Het principe van gescheiden verantwoordelijkheden (in het Engels bekend als principle of the separation of concerns) zorgt ervoor dat het mogelijk wordt om ons op één aspect te concentreren. Dat principe wordt toegepast als we voor de structuur en de inhoud van een webpagina HTML gebruiken, en voor de stijl en de lay-out CSS. Datzelfde principe wordt ook toegepast als we voor gedrag en interactie JavaScript gebruiken. Bij het schrijven van de HTML-code hoeven we ons dan niet bezig te houden met gedrag en interactie. Het principe van modulariteit (in het Engels bekend als principle of modularity) is een speciale vorm van dat principe, en gaat iets verder: het stelt dat wat aan elkaar gerelateerd is bij elkaar moet staan, en wat niet gerelateerd is niet bij elkaar mag staan. Je gebruikt dus niet alleen HTML voor structuur en inhoud en CSS voor stijl en lay-out, maar je houdt de code ook geheel gescheiden van elkaar. Datzelfde geldt voor JavaScript: de JavaScript-code komt in een apart bestand te staan (of in een aantal aparte bestanden), en niet tussen de HTML in.
Programmeeraanwijzing Houd JavaScript-code gescheiden van de HTML, in één of meer aparte bestanden. Event handler binden
Een event handler is een functie waarvan het de bedoeling is dat die wordt uitgevoerd wanneer een bepaald event plaatsvindt. Een event handler wordt gebonden aan een bepaald type event van een element. In het voorbeeld in het tekstboek gebeurt dat in HTML:
In dit voorbeeld wordt, zodra de gebruiker op de button klikt, de functie sayMagicWord aangeroepen. De functie sayMagicWord is dus de event handler. NB: In Figure 7.4 in het tekstboek ziet u als titel van het alert-venster staan: ‘The page at http://localhost says:’. Als u dit voorbeeld op uw eigen systeem test door te dubbelklikken op uw aangepaste bouwsteen, zult u waarschijnlijk als titel zien: ‘De pagina op file://localhost meldt:’.
12
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
Het verschil is dat de auteurs voor het voorbeeld een lokale webserver hebben gebruikt (waardoor ze het HTML-bestand te zien krijgen via het http-protocol), terwijl u door te dubbelklikken een lokaal bestand bekijkt, waarbij het file-protocol wordt. In leereenheid 5 heeft u geleerd om met een lokale webserver te werken. OPGAVE 7a.4
Wat is in Example 7.4 de event handler, wat is het type event waarvoor de event handler is gebonden en wat is het element waaraan de event handler is gebonden? Schending van principes
In het tekstboek wordt al aangegeven dat er een betere manier is om een functie als event handler te binden aan een element. Waarom de hier gebruikte manier niet goed is, wordt duidelijk als we naar het principe van modulariteit kijken. Bij deze manier wordt er gedrag gespecificeerd door middel van HTML, en dat is een schending van het principe van modulariteit: om een overzicht te krijgen van het gedrag dat voor een pagina is gespecificeerd, is het nodig niet alleen het JavaScript-bestand te bekijken, maar ook het HTML-bestand. Onthoud dat u vanaf het moment dat u die betere manier heeft geleerd deze manier niet meer zult gebruiken! Bij de bouwstenen zit een uitwerking waarin u alvast kunt zien hoe event handlers gebonden kunnen worden aan elementen van de pagina zonder dat dat zichtbaar wordt in de HTML. We zullen die methode in de uitwerkingen blijven gebruiken, ook al zult u pas later zien waarom de methode zo in elkaar zit. 7.1.4
Object als waarde van variabele
THE DOCUMENT OBJECT MODEL (DOM)
In Example 7.7 in het tekstboek wordt een variabele gebruikt: var paragraph = document.getElementById("result");
Deze variabele paragraph krijgt als waarde het object dat wordt geleverd door document.getElementById("result"). Verderop in deze leereenheid komen variabelen expliciet aan bod. Waar we hier op wijzen is het feit dat met het veranderen van de waarde van de variabele (de property innerHTML van paragraph krijgt een andere waarde) ook de waarde van het pagina-element verandert. Dat is niet vanzelfsprekend. Als een variabele als waarde bijvoorbeeld een getal heeft (type Number), wordt er bij een toekenning van die waarde aan een andere variabele een kopie van die waarde gecreëerd:
FIGUUR 7a.1
OU
Toekenning, kopie van de waarde
13
Webapplicaties: de clientkant
Kopie van de waarde
Een variabele bestaat uit een naam en een waarde. Bij een toekenning krijgt de variabele in principe een kopie van de waarde: Bij het veranderen van de waarde van de kopie verandert de oorspronkelijke waarde niet mee, zoals figuur 7a.1 laat zien.
Referentie
Bij objecten werkt dat anders. De variabele paragraph krijgt als waarde niet een kopie van een object, maar een referentie (verwijzing) naar een object. Iets aan de variabele paragraph veranderen betekent in dat geval iets aan het oorspronkelijke object veranderen.
FIGUUR 7a.2
Toekenning, referentie naar de waarde
Figuur 7a.2 illustreert dat: we zien een variabele paragraph met als waarde null, een object waarvan we de naam niet weten: het paginaelement met de id "result", en een variabele met naam document dat wijst naar het object dat het pagina-element vertegenwoordigt (in werkelijkheid is die variabele document zelf ook een object, maar dat doet er voor deze toelichting niet toe). Na toekenning van het resultaat van document.getElementById("result") aan de variabele paragraph krijgt die als waarde niet een kopie van dat object, maar een referentie naar dat object (het bolletje met het pijltje in de tekening). Daardoor is het mogelijk om objecten in de pagina ook daadwerkelijk te veranderen: als de methode alleen een kopie van het object zou opleveren zou dat niet mogelijk zijn; de kopie van het object zou veranderen, terwijl het pagina-element onveranderd zou blijven. In plaats van: var paragraph = document.getElementById("result"); paragraph.innerHTML = "Please! ";
is het ook mogelijk om te schrijven: document.getElementById("result").innerHTML = "Please! ";
14
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
Object chaining
Deze constructie heet object chaining. Als een methode een object (preciezer geformuleerd: een referentie naar een object) teruggeeft, kun je dat object direct gebruiken, zonder de omweg van het eerst te moeten toekennen aan een variabele. Je kunt dat teruggegeven object gebruiken door er een methode van aan te roepen (die weer een object zou kunnen opleveren) of, zoals in dit voorbeeld, door er een attribuut van te gebruiken. Lange ketens van object chaining kunnen lastig leesbaar zijn, maar in dit geval laat de keten beter zien wat er gebeurt dan als er een extra variabele wordt gebruikt: zo is het inzichtelijker dat het object van de DOM zelf wordt veranderd. De DOM komt verderop in deze cursus nog uitgebreider aan bod. U heeft nu gezien dat DOM-objecten ‘nodes’ worden genoemd, dat een DOM-object een pagina-element representeert en dat een DOM-object veranderd kan worden via JavaScript waardoor het pagina-element verandert. 7.2
JavaScript syntax
7.2.1
TYPES
Het type van een waarde bepaalt wat er mee kan gebeuren. Twee waarden van type Number kunnen bijvoorbeeld met elkaar worden vermenigvuldigd; twee waarden van type String niet. Arrays en functions zijn in feite een speciaal type objecten. Er zijn dus strict gesproken zes typen (Number, Boolean, String, Object, null en undefined), terwijl het type Object een onderverdeling kent in drieën: Array, Function en het ‛gewone’ type Object. Het is gebruikelijk om te spreken over acht fundamentele typen, en Array en Function als gelijkwaardig aan Object te beschouwen; dat zullen we in deze cursus ook doen. We gaan hier nader in op de zin ‘JavaScript is loosely and dynamically typed’. Statisch getypeerd: koppeling van type aan variabele
Dynamisch getypeerd: koppeling van type aan waarde
Statisch getypeerd houdt in dat elke variabele een vast type heeft. Het type van een variabele kan dus niet tijdens de uitvoering van het programma veranderen. Bij het declareren van een variabele wordt vastgelegd van welk type hij is, en dat type houdt de variabele gedurende z’n gehele leven. JavaScript is dynamisch getypeerd. Dat houdt in dat bij elke waarde een type hoort. Het type van een variabele is afhankelijk van de waarde die er aan is toegekend. Omdat de waarde van een variabele tijdens z’n levensduur kan veranderen, kan ook z’n type tijdens z’n levensduur veranderen. Bij een dynamisch getypeerde taal is het niet nodig bij declaratie van een variabele vast te leggen van welk type hij is: dat wordt bepaald door de waarde die aan de variabele wordt toegekend. Een statisch getypeerde taal heeft onder andere als voordeel dat fouten snel zichtbaar worden; een dynamisch getypeerde taal heeft onder andere als voordeel dat casten (expliciet aangeven dat bijvoorbeeld een waarde van type Object behandeld moet worden alsof het type String object is) nooit nodig is: robuustheid tegenover flexibiliteit.
OU
15
Webapplicaties: de clientkant
Sterk getypeerd
Los getypeerd
Impliciete typeconversie
De term sterk (strong) getypeerd is niet goed gedefinieerd. De verschillende betekenissen hebben over het algemeen te maken met de mate waarin de compiler of de interpreter checkt of er fouten met typeringen zijn (bijvoorbeeld of bij de aanroep van een functie de typen van de actuele parameters overeenkomen met de typen van de formele parameters). Voor de term los (loosely) getypeerd geldt hetzelfde: er is geen eensluidende definitie van de term. De definities hebben over het algemeen te maken met een gebrek aan checken van typen door de compiler of interpreter. Zoals we nog zullen zien komt in JavaScript impliciete typeconversie voor. Dat houdt in dat het type van een waarde veranderd kan worden tijdens de duur van een programma, zonder dat de programmeur daartoe opdracht heeft gegeven. Een voorbeeld is het wisselen van het type van de waarde 3: het type van die waarde kan, afhankelijk van wat er op een bepaald moment nodig is, wisselen tussen string "3" en number 3. Soms wordt onder ‘los getypeerd’ verstaan dat een taal impliciete typeconversie kent. Impliciete typeconversie is, zoals we zullen zien, een bron van veel voorkomende fouten, terwijl het aan de andere kant minder programmeerwerk betekent: de string "3" hoeft nooit expliciet veranderd te worden in number 3 en andersom.
OPGAVE 7a.5
Kan impliciete typeconversie voorkomen bij een statisch getypeerde taal? Beargumenteer uw antwoord. 7.2.2
Object Number
Weblink: Number
NUMBERS AND ARYTHMETIC
De constanten in tabel 7.3 van het tekstboek zijn allemaal, zoals u aan de notatie kunt zien, properties van het object Number. Dat object is één van de globale objecten (globaal versus lokaal komt in een volgende leereenheid aan de orde) die JavaScript biedt. Op het verschil tussen het type Number en het globale object Number komen we uitgebreid terug in leereenheid 7c. In de referentie voor het object Number vindt u een compleet overzicht van de constanten. 7.2.3
VARIABLES
De term los getypeerd (loosely typed) kent te veel betekenissen om zinvol gebruikt te kunnen worden. Het is in dit geval nauwkeuriger om te spreken van dynamisch getypeerd: het type van de variabele hangt samen met het type van de waarde, en niet met de variabele zelf. In JavaScript is het toegestaan om statements niet met een puntkomma te laten eindigen. Over het algemeen gaat dat goed, maar er zijn situaties waarbij code anders geïnterpreteerd wordt dan als er netjes een puntkomma aan het einde van het statement zou staan. Om fouten te voorkomen eindigt u dus elk statement met een puntkomma, ook al klaagt JavaScript niet als u dat niet doet, en ook al werkt uw code vaak zonder problemen als u het nalaat.
16
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
Programmeeraanwijzing Eindig elk statement met een puntkomma. In het tekstboek staat uitgelegd waarom u het keyword var hoort te gebruiken als u een variabele declareert, ook al geeft JavaScript geen foutmelding als u het nalaat. Programmeeraanwijzing Declareer elke variabele expliciet met het keyword var. 7.2.4
COMMENTS
Eenregelig commentaar kan ook na een statement komen. Zo kunt u bijvoorbeeld kort commentaar schrijven bij de declaratie van een variabele: var counter = 0; // om het aantal blog-entries bij te houden 7.2.5
innerHTML value
USING DOM OBJECTS
Een extra vuistregel om te onthouden als u de innerHTML en als u de property value moet gebruiken is de volgende: wat de gebruiker intypt haalt u op via de property value; wat in de pagina zelf staat bereikt u met de property innerHTML.
OPGAVE 7a.6
Is de event handler in Example 7.14 en 7.15 gebonden aan het button element of aan het span element? 7.2.6
File protocol Http protocol
DEBUGGING COMMON ERRORS
Als u een HTML-bestand in Firefox opent dat refereert aan een nietbestaand JavaScript-bestand zal de Nettab van Firebug (Figure 7.9) niets laten zien. Dat komt omdat u het bestand niet via een webserver bekijkt, maar via de filebrowser: in de adresbalk van de browser ziet u als protocol staan file:// in plaats van http://. De browser gebruikt het file protocol om op de harde schijf te kijken. Er is bij het file protocol dus geen communicatie nodig met een webserver. Het http protocol gebruikt de browser om met een webserver te communiceren. De Nettab van Firebug laat http-requests zien (het http protocol werkt met http-requests en http-responses), maar dat gebeurt dus alleen als u een HTML-bestand bekijkt via een webbrowser. Daartoe zet u het bestand in de htdocs-directory van uw xamppinstallatie (zie leereenheid 5), en bekijkt u het bestand via http://localhost/bestand.html.
OU
17
Webapplicaties: de clientkant
7.2.7
STRINGS
Impliciete typeconversie
Het tekstboek zegt dat elke waarde een object is. In werkelijkheid zijn er meer typen dan alleen objecten, zoals we hebben gezien. De reden dat de waarde van type String zich in Example 7.23 gedraagt alsof het een string-object is, is dat JavaScript impliciete typeconversie toepast. Als u de property length opvraagt van een waarde van type String, wordt die waarde eerste geconverteerd naar een waarde van type String object. U kunt dus met waarden van type String werken alsof het stringobjecten zijn.
Literals hebben ook properties
De property length geeft niet alleen de lengte van de waarde van een variabele (als die waarde een string is), zoals in Example 7.23, maar ook de expressie "L. Croft".length geeft als resultaat 8. Ook voor ‘losse’ waarden, literals, geldt dus dat er impliciete typeconversie wordt toegepast als u een property ervan opvraagt.
OPGAVE 7a.7
Example 7.22 geeft onder andere aan hoe je in JavaScript een methode van een object aanroept. We hebben dat al eerder in deze leereenheid gebruikt. Welke methode, van welk object was dat? 7.2.8
FOR LOOPS
Een for loop wordt vaak gebruikt om iets te doen met alle elementen van een waarde van type String (of van type Array, dat later aan bod komt). Er wordt dan getest of de teller die in de for loop wordt gebruikt de lengte van de string of de array heeft bereikt. Zo’n for loop ziet er als volgt uit: // woord is de naam van een variabele van type String for (var i=0; i < woord.length; i++) { woord.charAt(i).toLowerCase(); // bijvoorbeeld } 7.2.9
HET MATH OBJECT
Er is een verschil tussen de properties en de methoden van het Mathobject, en de properties en methoden van string-objecten. De properties en methoden in deze paragraaf horen bij het voorgedefinieerde object Math. Er bestaat geen type Math, en waarden worden niet impliciet geconverteerd naar type Math object. Deze properties en methoden worden dus niet gebruikt op waarden, maar alleen op het Math-object zelf. Voor Math.random geldt dat bij de waarden die worden opgeleverd de 0 wel meetelt, maar de 1 niet.
18
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
OPGAVE 7a.8
a Wat gebeurt er precies als u een willekeurig geheel getal opvraagt tussen 1 en 10 op de volgende manier? var rand = parseInt(Math.random() * 10) + 1;
b Geeft u voor deze toepassing de voorkeur aan parseInt of aan Math.floor? Waarom? 7.2.10
NULL EN UNDEFINED WAARDEN
OPGAVE 7a.9
Gezien de volgende statements: 4 + deGroteOnbekende; var deGroteOnbekende = null; 4 + deGroteOnbekende;
a Wat is de waarde van de variabele deGroteOnbekende na elk statement uit de opdracht hiervoor? b Hoe verloopt de impliciete typeconversie in het eerste en in het derde statement?
SAMENVATTING
– JavaScript is een scripting-taal die vooral gebruikt wordt om webpagina’s interactief te maken. De syntaxregels laten over het algemeen meer vrijheid toe dan bij talen als Java of C#. – Vanuit de JavaScript-code gezien speelt de webpagina over het algemeen de rol van user interface. De volgorde van executie wordt dan bepaald door de gebruiker die via de muis of het toetsenbord interacties uitvoert met de pagina. De acties van de gebruiker zijn events; een programma waarbij de executievolgorde door events wordt bepaald heet event-driven. – Het Document Object Model (DOM) is een verzameling JavaScriptobjecten waarvan de code methoden kan aanroepen en properties kan veranderen. Daarmee kan de inhoud van een pagina worden veranderd. – Het DOM-object van elk HTML-element kan gebruikt worden om informatie over het element op te vragen, bijvoorbeeld om de tekst op te vragen of om te vragen of het element checked is; en het kan gebruikt worden om dynamisch de stijl van het element te veranderen. Een DOMobject is te benaderen door middel van document.getElementById. – JavaScript kent vijf primitieve types: Number, String, Boolean, null en undefined. Daarnaast kent JavaScript het type object, dat weer kan worden onderverdeeld in Array, Function en Object.
OU
19
Webapplicaties: de clientkant
OPDRACHTEN OPDRACHT 7a.1 Blog, bij 1.3
U schrijft in deze opdracht een event handler en bindt die aan een event. Het startpunt is een HTML-bestand met een style sheet: blog/index.html en blog/stijl.css. De pagina ziet er als volgt uit:
FIGUUR 7a.3
Blog pagina
In het bestand blog/blog.js ziet u staan: window.onload = function() { document.getElementById("pub").onclick = publiceer; };
Daarbij is het van belang geen haakjes achter de naam publiceer te schrijven: de property onclick krijgt hier een functie als waarde, en het is niet de bedoeling om die functie meteen uit te voeren (u leert daar meer over in leereenheid 7c). De code is een alternatief voor het binden van een event handler in de HTML-code. Het is dus een alternatief voor:
Schrijf nu een functie publiceer die een alert laat verschijnen met de melding: ‛We gaan nu publiceren’. U vindt een uitwerking bij de bouwstenen, in blog/blog.js. OPDRACHT 7a.2 Fout in de naam van de eventhandler, bij 1.3
20
In deze opdracht gebruikt u Firefox om een fout op te sporen. Als u Firebug nog niet geïnstalleerd hebt, doe dat dan nu (Extra -> Add-ons verkrijgen, zoeken op FireBug). Met behulp van Firebug is het gemakkelijker om fouten op te sporen. Open de pagina uit de vorige opdracht, en activeer Firebug met behulp van Extra -> Firebug -> Firebug openen, of door te klikken op het kevertje (bugje) rechts in de onderbalk van Firefox.
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
FIGUUR 7a.4
Bug van Firebug
Verander nu in het JavaScript-bestand de naam van de event handler waar die gebonden wordt aan de button in bijvoorbeeld publicees, ververs de pagina (F5) in Firefox en bekijk wat er gebeurt als u nu op de knop klikt. Niets... Firebug opent de console (die onder andere wordt gebruikt om details over fouten weer te geven) en geeft daarin aan: publicees is not defined. Dat kan een aanwijzing zijn dat de naam van de event handler niet overeenkomt met een gedefinieerde functie.
FIGUUR 7a.5
Firebug ontdekt een fout
Cache probleem Als u de naam van de event handler verandert en de pagina opnieuw laadt zult u in sommige gevallen zien dat Firefox nog steeds de oorspronkelijke, foutloze pagina toont. Dat komt doordat Firefox ziet dat u een pagina opvraagt die u al eerder heeft opgevraagd. Firefox gebruikt in zo’n geval de pagina die het al in het geheugen heeft: in de cache. Dat is lastig bij debuggen. Om zulke problemen te voorkomen kunt u de cache van Firefox uitschakelen: Extra -> Opties -> Geavanceerd, tab Netwerk, en dan 0 MB ruimte voor buffer kiezen. Een andere mogelijkheid is om te herladen met Control-F5 in plaats van met F5. Er is geen uitwerking bij deze opdracht. OPDRACHT 7a.3 Fout in de naam van het JavaScriptbestand, bij 1.3
a Herstel de naam van de event handler weer (test of alles nu weer gaat zoals het hoort), en verander nu de naam van het JavaScriptbestand in het src-attribuut van de script-tag in blof.js en bekijk wat er dan gebeurt als u de pagina heeft herladen en op de knop klikt. Precies hetzelfde, en Firebug geeft precies dezelfde melding. Deze twee fouten zijn dus lastig uit elkaar te houden.
OU
21
Webapplicaties: de clientkant
b Herstel de naam van het bestand in de head van het HTML-bestand weer (en test of alles weer werkt zoals het hoort). Voeg nu in het JavaScript-bestand bovenaan de volgende regel toe: alert("het
JavaScriptbestand is gevonden");
Bekijk wat er nu gebeurt als u de pagina herlaadt. Dit is een voorbeeld van sequentieel programmeren in JavaScript: de alert wordt uitgevoerd tijdens het inlezen van het bestand. Door middel van deze regel kunt u onderscheiden of er een fout is in de naam van een event handler, of dat het JavaScript-bestand in z’n geheel niet wordt geladen (in dat geval krijgt u de alert niet te zien). Er is geen uitwerking bij deze opdracht. OPDRACHT 7a.4 DOM, bij 1.4
In deze opdracht verandert u een DOM-element met behulp van JavaScript. U breidt het blogvoorbeeld uit. In plaats van een alert te laten verschijnen als de gebruiker op de knop klikt, laat u nu een nieuwe blogentry verschijnen met een vaste tekst. Het bovenste lege div-element geeft u daarom een id:
Uw taak is om de functie publiceer zo te veranderen dat het divelement de tekst ‛Hier komt een nieuwe entry’ gaat bevatten. Een uitwerking vindt u in de vorm van de bestanden blog/dom.html en blog/dom.js in de bouwstenen. OPDRACHT 7a.5 Waarden en variabelen, bij 1.4
U experimenteert in deze opdracht met waarden en variabelen. Houd de pagina uit de vorige opdracht geladen in Firefox, en open Firebug (door te klikken op het bugje rechtsonder of via het menu). Klik op de tab Console. U krijgt dan een venster met een invoerregel onderin. Als u iets invoert en op return klikt wordt het statement bovenin de console gezet en uitgevoerd, en als er een resultaat is wordt dat daar onder weergegeven.
FIGUUR 7a.6
22
OU
De console in Firebug
Leereenheid 7a JavaScript for Interactive Web Pages 1
Voer in de console de volgende expressies in en bekijk steeds het resultaat: var maandagmenu = "boerenkool"; var dinsdagmenu = maandagmenu; maandagmenu; dinsdagmenu; dinsdagmenu = "pannenkoeken"; maandagmenu; dinsdagmenu;
Er is geen uitwerking bij deze opdracht. OPDRACHT 7a.6 Refresh na verandering DOM, bij 1.4
In deze opdracht ziet u dat een verandering van de DOM tijdelijk is. Ververs de pagina (met F5) nadat u op de button van de pagina uit opdracht 7a.4 heeft geklikt en de inhoud van het div-element dus is veranderd door de event handler in JavaScript. Bij het verversen van de pagina blijkt duidelijk dat de HTML niet wordt veranderd door JavaScript: de pagina ziet er weer uit zoals hij er oorspronkelijk uit zag. Het is de DOM, het geheel van objecten dat de browser in het geheugen heeft, die verandert. Als u de pagina ververst leest Firefox de HTML opnieuw in, en bouwt opnieuw de DOM op. Bij deze opdracht is geen uitwerking. OPDRACHT 7a.7
Het type Number en operatoren, bij 2.2
Voer in de console van Firebug de volgende expressies in en bekijk het resultaat: Number.MAX_VALUE; Number.MIN_VALUE; 10/3; 10/0; 10/"3";
Wat gebeurt er bij het interpreteren van 10/"3";? U vindt een uitwerking van die vraag in de terugkoppeling. OPDRACHT 7a.8 Eigenschappen van DOM objecten, bij 2.5
De eigenschappen van DOM objecten in een HTML pagina zijn direct te bekijken met behulp van Firebug. Typ in de console van Firebug (nog steeds in blog/index.html): document.getElementById("nieuw").tagName;
Bekijk wat Firebug geeft als u op return klikt. Er is geen uitwerking bij deze opdracht.
OU
23
Webapplicaties: de clientkant
OPDRACHT 7a.9 Cat-dog, bij 2.5
In deze opdracht verandert u het src-attribuut van een element. Bij de bouwstenen vindt u een HTML-bestand met een img-tag, in catdog/kathond.html. Vul het bijbehorende JavaScript-bestand aan met een event handler om het plaatje te veranderen. Geef in de event handler de src-property van het element met id dier als waarde: "Wikilabrador.jpg";
Bij de bouwstenen vindt u een uitwerking, in cat-dog/kathond.js. OPDRACHT 7a.10 Value en innerHTML, bij 2.5
In deze opdracht gebruikt u de value van het ene element om de innerHTML van een ander element mee te vullen. In het blogvoorbeeld zet u wat de gebruiker in het textarea-element heeft ingevoerd nu tussen p-tags, en geeft u het div-element met id nieuw die string als waarde. Test de functie door iets in de textarea in te vullen en op Publiceer te klikken. U vindt een uitwerking bij de bouwstenen, in blog/value.js en blog/value.html. OPDRACHT 7a.11
De Net tab van Firebug, bij 2.6
Bij de opdrachten die u tot nu toe heeft uitgevoerd, heeft u gewoon kunnen dubbelklikken op een HTML-bestand, en heeft de browser het HTML-bestand via het filesysteem opgehaald. Om een HTML-bestand dat is verstuurd door een webserver te bekijken maakt u een directory JavaScript aan in de directory htdocs van uw xampp-directory (zie leereenheid 6). Zet de bestanden voor het blogvoorbeeld in die directory, maak een fout in de naam van het JavaScript-bestand zoals het in de HTML staat (maak er bijvoorbeeld valu.js van) en typ in de adresbalk van uw browser: http://localhost/JavaScript/value.html
Kijk dan wat de Net tab van Firebug zegt. U vindt een uitwerking van deze opdracht bij de terugkoppeling. OPDRACHT 7a.12 Niet bestaande pagina elementen, bij 2.6
24
Herstel in de HTML van het blogvoorbeeld eerst de naam van het JavaScript-bestand (en test of alles werkt). Verander dan in JavaScript document.getElementById("nieuw") in document.getElementById("nieu") en klik op de knop om te bekijken welke foutmelding Firebug laat zien. Gebruik vervolgens de console om, zoals in Figure 7.10, het element met de foutief gespelde en de goed gespelde id op te roepen. Er is geen uitwerking bij deze opdracht.
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
OPDRACHT 7a.13 JSLint, bij 2.6
Kopieer de code van het JavaScript-bestand van het blogvoorbeeld naar JSLint en klik op JSLint. JSLint zal als foutmelding geven: Error: Implied global: document
Dat komt doordat JSLint normaal gesproken document zal zien als een gewone variabelenaam, en die variabelenaam wordt gebruikt zonder dat hij netjes met var is gedeclareerd. Vink nu bij de Options aan: Assume a browser, en klik nogmaals op JSLint. De foutmelding over document is nu verdwenen, maar als u window.onload in uw JavaScript-bestand heeft staan zal JSLint nog steeds een foutmelding geven: Error: Implied global: window
Onderaan bij de Options kunt u zelf een lijst van variabelenamen opgeven die ‘predefined’ zijn. Als u daar window invult en weer op JSLint klikt zijn de foutmeldingen verdwenen. Er is geen uitwerking bij deze opdracht. OPDRACHT 7a.14 For loop, bij 2.7 en 2.8
Als u in het blogvoorbeeld een lang stuk tekst invoert, met enters om er niet één tekstbrij van te maken, verschijnt de tekst in het div-element wel als tekstbrij. HTML negeert enters immers. Check eerst of een entry inderdaad op die manier wordt verwerkt. Met behulp van een for loop kunt u in het blogvoorbeeld een enter zo interpreteren dat er met een nieuw p-element wordt begonnen. Daartoe splitst u eerst de tekst in een array van de stukken tussen de enters: var paragrafen = document.getElementById("tekst").value.split("\n");
Uw opdracht is om met behulp van een for loop alle zinnen tussen ptags te zetten. U vindt een uitwerking bij de bouwstenen, in blog/for.html en blog/for.js. OPDRACHT 7a.15 Een tekst veranderen, bij de gehele leereenheid
In de bouwstenen vindt u het bestand teksttool/teksttool.html. Als u dat in Firefox opent ziet u de volgende pagina:
FIGUUR 7a.7
OU
De pagina teksttool.html
25
Webapplicaties: de clientkant
Voeg aan het bestand code toe om een JavaScript-bestand te laden (met de naam teksttool.js), en verander de HTML-code zo dat u in staat zult zijn om de tekst in de textarea met JavaScript te kunnen veranderen. Creëer een bestand teksttool.js, en schrijf event handlers voor de knoppen in de pagina. Methoden voor strings kunt u vinden in de referentie voor strings (zie Table 7.12 of de link op de cursussite). Bind in het HTML-bestand die event handlers aan de juiste knoppen, of gebruik de manier die u in de opdrachten heeft geleerd om event handlers aan knoppen te binden. U vindt een uitwerking bij de bouwstenen, in teksttool/teksttool.js, en er is een toelichting in de terugkoppeling.
ZELFTOETS
1
Een HTML-pagina bevat het volgende fragment:
Schrijf een functie vatSamen die in dat p-element de tekst “Na het bestuderen van deze cursus kunt u functies schrijven in JavaScript” zet. 2
Wat zijn de acht fundamentele typen van JavaScript?
3
Wat is de waarde van de volgende expressie? parseInt("three") ;
4
Schrijf een for loop die exact 9 maal wordt doorlopen. Vermenigvuldig steeds de variabele som, met als beginwaarde 2, met zichzelf.
5
Wat houdt het feit dat JavaScript dynamisch getypeerd is in?
6
Gegeven is het volgende statement: var zin = "L. Croft is an actress.";
Vind met JavaScript de plaats van de eerste spatie in de tweede helft van de zin. 7
We willen een willekeurig getal tussen 1 en 50. De 1 doet dus mee; de 50 niet. Hoe verkrijgt u zo’n getal met behulp van parseInt en Math.random?
8
Een codefragment bevat het volgende statement: x = x + y;
Hoe kunt u dat korter opschrijven? 9
26
U heeft een HTML-pagina geschreven met een JavaScript-bestand, en u zet de bestanden op een webserver. Is het JavaScript-bestand daarmee een voorbeeld geworden van server-side scripting?
OU
Leereenheid 7a JavaScript for Interactive Web Pages 1
TERUGKOPPELING 1
7a.1
Uitwerking van de opgaven
Een pagina kan worden geproduceerd door een server-side script, en in de HTML die door dat script wordt geproduceerd kan gerefereerd worden aan een client-side script. Server-side scripting en client-side scripting sluiten elkaar dus zeker niet uit. Een voorbeeld is de documentatiepagina voor PHP, zie de weblink. Het is een URL met extensie .php: de pagina is dus geproduceerd door een client-side script. Als u de bron van de pagina bekijkt ziet u daar onder andere de regel: <script type="text/javascript" src="http://static.php.net/www.php.net/userprefs.js">
In de pagina wordt dus een client-side script gebruikt. 7a.2
Het verschil tussen client-side en server-side scripts zit hem niet in de plek waar ze staan (ze staan beide op de webserver), maar in de plaats waar ze worden uitgevoerd, of geïnterpreteerd. Server-side scripts worden op de server geïnterpreteerd; client-side scripts worden door de browser geïnterpreteerd.
7a.3
De vraag is in feite een strikvraag, want event-driven programmeren hoort bij geen van beide. U kunt zowel in een procedurele taal eventdriven programmeren (door een functie aan een event te binden) als in een objectgeoriënteerde taal (door de methode van een object aan een event te binden).
7a.4
De event handler is de functie sayMagicWord. Het type event waarvoor de event handler is gebonden is het click event. De event handler is gebonden aan het button-element.
7a.5
Voor een statisch getypeerde taal geldt: ‛Bij het declareren van een variabele wordt vastgelegd van welk type hij is, en dat type houdt de variabele gedurende z’n gehele leven.’ Een variabele kan dus niet van type veranderen. Impliciete typeconversie kan echter wel voorkomen. Een waarde van type byte kan bijvoorbeeld impliciet geconverteerd worden naar een waarde van type int of double.
7a.6
De event handler is gebonden aan het button-element; niet aan het span-element. Het span-element is in dit geval het lijdend voorwerp.
7a.7
Bij het verkrijgen van een DOM-object hebben we al de methode getElementById aangeroepen, op het object document, waarbij we een string als parameter meegaven die het id aangaf van het DOM-object dat we op het oog hadden. Het object document, dat de browser ter beschikking stelt, is dus een ‘normaal’ JavaScript-object.
OU
27
Webapplicaties: de clientkant
7a.8
a De expressie Math.random() levert een reëel getal op tussen 0 (inclusief) en 1 (exclusief). Dat getal wordt met 10 vermenigvuldigd, en wordt dus een reëel getal tussen 0 (inclusief) en 10 (exclusief). De functie parseInt verwacht een string, en krijgt in plaats daarvan een reëel getal. Impliciete typeconversie zal er voor zorgen dat er een string gemaakt wordt van het getal. Zo zal 9.747 veranderen in "9.747". De functie parseInt probeert die string te lezen als een number, en het resultaat af te ronden. parseInt(Math.random() * 10) levert dus een integer op tussen de 0 en de 9 (inclusief de 9). Door daar 1 bij op te tellen krijg je een integer tussen de 1 en de 10. b In dit geval verdient Math.floor de voorkeur: er vindt geen impliciete typeconversie plaats. Als het enigszins mogelijk is verdient het de voorkeur om voor een waarde functies te gebruiken die bij het type van die waarde horen. Bovendien geeft de naam van de functie duidelijk aan dat er niet afgerond maar ‘afgekapt’ zal worden.
7a.9
a De waarde van de variabele deGroteOnbekende is achtereenvolgens undefined, null en null. b In het eerste statement vindt er geen impliciete typeconversie plaats: de waarde undefined kan niet worden opgeteld bij 4. In het derde statement vindt er wel impliciete typeconversie plaats: de waarde null wordt veranderd in een number, en als waarde wordt 0 gebruikt. 2
7a.7
7a.11
Uitwerking van de opdrachten
Als de browser 10/"3"; interpreteert komt er 3.3333333333333335 uit. De browser heeft "3" dus geconverteerd naar een waarde 3 (van type Number). Er heeft dus impliciete typeconversie plaatsgevonden. Als u nu naar de Net tab van Firebug kijkt hoort u te zien: GET value.html 200 OK GET valu.js 404 Not Found
7a.15
Om een script aan de pagina toe te voegen is de volgende regel in de head van het HTML-bestand nodig: <script src="teksttool.js">
Om iets met de text area te kunnen gaan doen, moet u in JavaScript het element als object te pakken kunnen krijgen. Dat kan met de methode document.getElementById, maar dan heeft u een id nodig. Daarom is het nodig om de regel voor de text area te veranderen in: