JS - BASISCURSUS

Bronnen: Softwarebus nummers 6 van 2006 en 1 en 2 van 2007.

Deze basiscursus is geen cursus in de strikte zin van het woord. De bedoeling van deze basiscursus is niet meer dan kennis te maken met JavaScript en de mogelijkheden daarvan.

Natuurlijk is er niets op tegen om in een voorkomend geval een scriptje ergens van het web te plukken om dat in je eigen home page te gebruiken. Maar veel leuker is het om tot in elk detail zelf te kunnen bepalen wat er moet gebeuren en daarvoor het script te schrijven. De daarvoor benodigde kennis is te onderscheiden in HTML, CSS en Javascript. Hoe HTML, CSS en Script tot onderlinge samenhang zijn gekomen, is gedefinieerd in het Document Object Model, kortweg DOM, dat dus veel slimmer is dan de afkorting doet denken.

Het Document Object Model
Wat is eigenlijk een object? Wel, alles is een object. Uw auto is een object. De hamburger die u eet, is een object en u bent zelf een object, namelijk het object 'mens'. Ook abstracte zaken zijn objecten. Al hetgeen taalkundig op ons afkomt aangeduid door een zelfstandig naamwoord, is een object.
Een object heeft eigenschappen. U kunt blond, bruin of roodharig zijn en dus hebt u een eigenschap 'haarkleur'. Oh, u bent inmiddels kaal en denkt dus dat u die eigenschap niet meer hebt? Dat is dan een ernstige denkfout, want u hebt het nu over een heel andere eigenschap: de hoeveelheid haar en die is dan bij u ongeveer nul! Maar laten we eens aannemen dat u te lang haar hebt en dientengevolge een bezoek aan de kapper overweegt. Dan gaat u de hoeveelheid haar dus verminderen. Hoe doet u dat? Met een schaar, een tondeuse of door naar de kapper te gaan. Juist ja, dat is een methode! Maar u bent nu tevens een activiteit aan het ontplooien. Het 'naar de kapper gaan' is een bezigheid oftewel in onze terminologie: een gedrag.
- er zijn objecten (objects)
- objecten hebben eigenschappen (properties)
- objecten gedragen zich op een bepaalde manier. (behaviors)
- zowel de eigenschappen als de gedragingen kunnen we aanpassen volgens een methode (method)

Javascript en zijn klonen zijn objectgeoriënteerde programmeertalen. Hoe die programmeertaal in elkaar moet zitten, is gedefinieerd in een standaard: ECMA-262.

Het DOM doet niets anders dan de combinatie HTML en CSS samenvoegen tot het object 'document' met methoden, gedragingen en eigenschappen die worden beheerst met ECMA Script. Javascript is een verschijningsvorm daarvan.
Noteer ook dat de eerste D voor 'Document' staat en niet voor 'Window'. Er zijn dan ook een heleboel zaken buiten het object 'document' ongedefinieerd en browser-afhankelijk. Wel is het zo dat voor een groot aantal eigenschappen en methoden van het object 'Window'  de facto een standaard is ontstaan waar alle brouwers min of meer aan voldoen.
Behalve ECMA-262 hebben we te maken met de HTML5-specificatie en met Cascading Style Sheets (CSS) Wat is de relatie van deze standaards tot het DOM?

HTML 5
HTML was oorspronkelijk een manier om teksten in verschillende documenten of in hetzelfde document naar elkaar te laten verwijzen. Hypertekst dus. Daarnaast bevatte het aanwijzingen voor de opmaak: lettertype, lettergrootte en opsommingen, later ook gevolgd door de mogelijkheid figuren in te voegen. Om dergelijke zaken mogelijk te maken, waren en zijn elementen voorzien van attributen. Verleden tijd: 'waren', omdat veel daarvan nu worden afgeraden. Maar ook tegenwoordige tijd: 'zijn', omdat het onmogelijk is browsers te gaan maken die ze niet meer ondersteunen. Internet zou ontoegankelijk worden. Een voorbeeld is het element <P> om een paragraaf aan te duiden. Het kan worden voorzien van het attribuut 'align' om de tekst naar links of naar rechts uit te richten of om de tekst uit te vullen. (justify).
Maar als we het element <P> als een object beschouwen, dan is het attribuut 'align' dus een eigenschap van dat object.
Er zijn echter een heleboel andere eigenschappen van een paragraaf, waar we totaal geen invloed op hebben. Welk lettertype moet worden gebruikt? Welke kleur? Welke lettergrootte? Daar blijkt HTML ongestructureerd te zijn. Ze moeten buiten het element <P> worden opgegeven met een element <FONT ...>
Het is de toevoeging van Cascading Style Sheets (CSS), die daarin verandering brengt. CSS dient om HTML-elementen, die we nu liever objecten noemen, te voorzien van alle mogelijke eigenschappen die we via de klassieke attributen niet hadden. Daarbij raken sommige elementen overbodig. Het element FONT verliest bijvoorbeeld zijn betekenis doordat lettertypen een eigenschap van tekstobjecten worden.

Een paragraaf op een andere manier.
Voorbeeld 1 laat zien hoe je het uiterlijk van een paragraaf op een andere manier kunt bepalen. In het element <P> is het attribuut style opgenomen. Waarmee we meer kunnen dan de oorspronkelijke attributen van het element <P>. Merk bijvoorbeeld op dat we nu de afstand tussen de regels kunnen bepalen.

<p style="font-family: Arial, Helvetica, sans-serif; font-size: 13px; color: #990000; text-align: justify; line-height: 25px">

Door het attribuut style te gebruiken kunnen we nog veel meer, zoals aangeven dat de tekst vet of cursief moet worden afgedrukt. Maar is het nu prettig om elke paragraaf van die enorme lange regel met het attribuut style te voorzien? Gelukkig niet.
Zoals we het hier hebben gedaan is het handig om eenmalig een speciale alinea op te maken. Maar als we deze zelfde opmaak nog eens moeten gebruiken in hetzelfde document, hebben we daarvoor een handiger methode. We zetten de inhoud van het attribuut style tussen accolades en geven het een naam. We maken er een 'class' van. Vervolgens vertellen we de alinea dat deze class moet worden gebruikt om deze paragraaf op te maken.
Voorbeeld 2 laat het zien. We kunnen nu meerdere paragrafen van dezelfde opmaak gebruik laten maken door het attribuut class="speciaal" toe te voegen. Let er op dat een stijl wordt gedefinieerd met een punt voor de naam, maar dat we die niet gebruiken in het attribuut 'class'.

<style type="text/css">
<!--
.speciaal {font-family: Arial, Helvetica, sans-serif; font-size: 13px; color: #990000; text-align: justify; line-height: 25px}
-->
</style>
<body>
<p class="speciaal">Tekst .... </p>

Een andere mogelijkheid ontstaat als we alle paragrafen van het document dezelfde opmaak willen geven. In dat geval veranderen we de naam van de definitie .speciaal in p en nu dus zonder punt. Het ontbreken van de punt geeft aan dat we de complete definitie van het element <P> willen wijzigen.

<style type="text/css">
<!--
p { font-family: Arial, Helvetica, sans-serif; font-size: 13px; color: #000000; text-align: justify; line-height: normal; fontweight: normal; }
-->
</style>

Een gelukkig huwelijk : Style en Javascript
Het slimme van CSS is nu, dat we in Javascript toegang tot de complete style sheet van een element hebben. Het DOM definieert elk element als een object en style is daarin een eigenschap van dat object. Dat betekent dat we de opmaak met Javascript kunnen veranderen. Stel dat we de speciale aandacht van de lezer willen vestigen op een paragraaf, bijvoorbeeld door deze hinderlijk te laten knipperen. We gaan dat doen in Voorbeeld 3 en of dat als zodanig zinvol is, laten we nu even buiten beschouwing. Het gaat om de methoden.

<html>
<head>
<title>Voorbeeld 3</title>
<style type="text/css">
<!--
p {font-family: Arial, Helvetica, sans-serif; font-size: 13px;
color: #000000; text-align: justify; line-height: normal;
font-weight: normal;}
-->
</style>

<script type=text/javascript>
<!--
var hulp;
var tid;
function opvallend() {
hulp=document.getElementById('flits');
hulp.style.color='red';
hulp.style.fontWeight='bold';
hulp.style.lineHeight='30px';
tid=setTimeout("gewoon()",1000)
}

function gewoon() {
with(hulp.style) {
color='black';
fontWeight='normal';
lineHeight='normal'
tid=setTimeout("opvallend()",1000)
}
}
//-->
</script>
</head>
<body>

<p>tekst</p>

<p ID=flits>Let op:<br>
Hier komt de knipperende tekst.
</p>

<p>Nog een paragraaf met tekst</p>

<script type=text/javascript>
<!--
hulp=document.getElementById('flits');
tid=setTimeout("opvallend()",1000)
//-->
</script>
</body>
</html>

Bovenaan voorbeeld 3 staat de style sheet van figuur 3. Daarmee is het element <P> door ons opnieuw gedefinieerd.
Alle alinea's (paragrafen) van het document zullen automatisch opgemaakt worden volgens deze style sheet. Maar in werkelijkheid krijgt elke alinea een eigen kopie van deze stijl. Om de eerste paragraaf dynamisch te maken moeten we hem eerst als object toegankelijk maken door hem een ID te geven. Een ID (identifier) geeft die paragraaf een unieke identiteit. We veranderen <p> in <p ID=flits>. Op twee plaatsen voegen we nu script aan het document toe. Om te beginnen in de sectie <head> en tenslotte een deel helemaal aan het einde van de sectie <body>.
Dat een deel van het script aan het einde van het document is gezet, heeft een belangrijke reden. Veel browsers beginnen een script meteen uit te voeren nog voor het complete document is geladen. Als het document is geladen, is de eerste opdracht die wordt uitgevoerd, een pointer maken naar de bewuste paragraaf. Dat gebeurt met de opdracht hulp=document.getElementById('flits').
Na het uitvoeren van deze opdracht is de variabele 'hulp' als het ware het geopende deksel van de paragraaf die we 'flits' hebben genoemd. De naam wordt hier tussen aanhalingstekens gebruikt om aan te geven dat we letterlijk de naam 'flits' bedoelen. Zouden we de aanhalingstekens weglaten, dan zou het script zoeken naar een variabele met de naam flits, deze niet vinden en dus 0 (nul) gebruiken waarna een foutmelding zou volgen. Let er ook op dat alles in Javascript hoofdlettergevoelig is. Hoofdletters en kleine letters zijn dus niet aan elkaar gelijk.
De schrijfwijze van getElementById is niet toevallig. Het is in Javascript gebruikelijk identifiers, dat zijn namen van methoden, variabelen en objecten, altijd te laten beginnen met een kleine letter, maar vervolgens met een hoofdletter aan te geven waar in de uitspraak een nieuw woord begint. Dus eigenlijk heet deze functie in de Engelse taal uitgesproken 'Get element by id'). Aan de punt tussen document en getElementById zien we dat de functie een methode is van het object document.
De volgende opdracht zet in de variabele 'tid' een teller om na een seconde de functie 'opvallend' te gaan uitvoeren. setTimeout heeft als argumenten de naam van de na verloop van tijd uit te voeren opdracht en die bewuste tijd in milliseconden. Een seconde na het laden van het document wordt dus de functie opvallend() uitgevoerd.
De functie opvallend doet niets anders dan de waarden in de style sheet van de paragraaf veranderen. En hier zien we dus dat style een subobject van hulp is en dat alles wat in figuur 1 is opgegeven weer subobjecten van style zijn. Veel identifiers in CSS bevatten een koppelteken, dat echter in Javascript als minteken zou worden gelezen. Als we zouden schrijven hulp.style.font-weight='bold' dan zou het script zoeken naar de eigenschap 'font' van het object hulp.style om van diens waarde dan een hoeveelheid uit de variabele 'weight' af te trekken. Dat levert dan uiteraard een foutmelding op. De conventie voor zulke gevallen is dan:
- laat het koppelteken weg.
- schrijf het teken na het koppelteken als hoofdletter.
De functie opvallend() eindigt met de opdracht om na 1 seconde de functie gewoon() te starten die alles weer ongedaan maakt, om na 1 seconde de functie opvallend() weer te starten. Dat gaat oneindig door tot de browser wordt afgesloten.
In de functie opvallend() zijn de opdrachten allemaal bestemd voor het object hulp.style en dat moeten we voor elke regel opnieuw opgeven. Het kan ook anders zoals in de functie gewoon() waar we met with(hulp.style) hebben aangeven dat alles tussen { en } betrekking heeft op eigenschappen van het object hulp.style. Een verzameling opdrachten tussen { en } wordt beschouwd als één opdracht. We noemen dat een compound-opdracht. (compound statement).

Een DIV is belangrijker dan u denkt!
Heel vaak zien we in html een gedeelte dat staat tussen <DIV> en </DIV> en dat wordt dan meestal gebruikt om tekst uit te lijnen, zoals in <DIV align="center">. Maar we kunnen er veel meer mee.
Een Division oftewel afdeling is in feite een rechthoek. Daar merken we niets van, omdat die rechthoek zichzelf aan de tekst aanpast. Maar we kunnen hem ook dwingen zijn afmetingen aan ons aan te passen en zelfs kunnen we zijn positie bepalen.
In voorbeeld 4 zien we drie veronderstelde trefwoorden. Door op een trefwoord te klikken wordt de bijbehorende tekst zichtbaar. Door nogmaals te op het trefwoord te klikken zal de tekst weer verdwijnen.
Waar het hier om gaat is de mogelijkheid om het element DIV zichtbaar te maken of te verbergen. Daarvoor zijn drie grootheden in de style sheet van het element beschikbaar. We gebruiken hier 'display'. Als we display="none" opgeven zal het element volmaakt onzichtbaar zijn. Als we het display='block' opgeven (de standaardwaarde), zal het element normaal zichtbaar zijn.
De andere variabele heet 'visibility' (zichtbaarheid) en kan de waarden 'visible' (zichtbaar) of 'hidden' (verborgen) hebben. Het verschil met 'display' is dat een bij gebruik van visibility het element alleen zijn inhoud onzichtbaar maakt, maar wel plaats blijft innemen.
In voorbeeld 4 hebben we de drie standaard onzichtbare alinea's in een DIV-element gezet met verwijzing naar de style sheet in het attribuut 'Style':
<div ID="demo1" style="display: none">
Het trefwoord daarboven is voorzien van een link naar de functie die de alinea zichtbaar maakt, als deze onzichtbaar is of juist doet verdwijnen als ze zichtbaar was. Hierna de code:

<html>
<head>

<title>Voorbeeld 4</title>
<style type="text/css">
<!--
p {font-family: Arial, Helvetica, sans-serif; font-size: 13px; 
   color: #000000; text-align: justify; line-height: normal; 
   font-weight: normal;}
body {
  background-color: #FFFFFF;

  padding-top: 10px;
  padding-right: 30px;
 padding-bottom: 10px;
  padding-left: 30px}
-->
</style>
<script type=text/javascript>

function showText(what) {
   currentLayer=document.getElementById(what);
   with (currentLayer.style) {
     if (display=='none') {
       display='block' }
     else {
        display='none'
     }
  }
}


//-->
</script>

</head>
<body>
<p><a href="javascript:showText('demo1')">Niets op tegen</a></p>
<div ID="demo1" style="display: none">
<p>Natuurlijk is er niets.</p></div>

<p><a href="javascript:showText('demo2')">Javascript</a></p>
<div ID="demo2" style="display: none">
<p>Javascript is een scripttaal. </p></div>

<p><a href="javascript:showText('demo3')">Specificaties</a></p>
<div ID="demo3" style="display: none">
<p>Specificaties zijn een probleem.</p></div>

</body>
</html>

Deze functie heeft een parameter 'what', waarin de ID van het betreffende DIV-element wordt overgedragen. Na het voorgaande zou de code voor zich moeten spreken. In de code heeft één van de variabelen de naam 'currentLayer' en we zullen nu zien dat met het element DIV wel degelijk 'lagen' over elkaar heen kunnen worden gelegd.

Layers (Lagen)
Soms is het wenselijk om bij een stukje tekst wat extra informatie toe te voegen. Zo kan er ergens uitleg staan over het gebruik van Internet en daarbij komt de lezer voor het eerst de term IP-adres tegen. Wat wordt daar eigenlijk onder verstaan? Dan is het handig als je een venstertje kunt laten verschijnen op het moment dat iemand met de muis over die term beweegt. Dat zoiets nogal wat voeten in de aarde heeft, komt deels doordat sommige leveranciers van browsers lijden aan chronische hardleersheid, deels doordat het DOM niet in alles erg slim is. Maar om al die problemen kunnen we heen.
Voorbeeld 5 is een werkend voorbeeld.
We hebben daarin op twee plaatsen het woord 'IP-Adres' gemarkeerd waarop ter demonstratie hetzelfde venstertje moet verschijnen als we er met de muis overheen gaan.

Het element DIV dat de tekst voor het venstertje bevat, moet om te beginnen een style sheet als attribuut hebben. Anders hebben we geen toegang tot de grootheden in de style sheet, als we ze willen lezen. Ook dan is het in HTML mogelijk om leesbare code te schrijven.
Hierna wordt de complete DIV getoond.

<div ID="note1" style="
position: absolute;
top: 10; left: 10; width: 300px; height : 100px;
background-color : yellow;
visibility: hidden;
border: #000000;
border-style: solid;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
padding-left: 5px;
padding-right: 5px;
padding-top: 5px;
padding-bottom: 5px "> 
<p>Een IP-adres is het adres waaronder een computer op
internet wordt gevonden. Het bestaat uit 4 groepen
van getallen, elk van 0..255, gescheiden door een punt.
</p>
</div>

Normaliter zou de tekst gewoon in het document verschijnen op de plaats waar ze staat. Maar nu staan in de style sheet de afmetingen (width, height) en de plaats waar de linkerbovenhoek in het document moet beginnen. (top, left) Deze laatste twee worden normaliter genegeerd, maar de clou van deze DIV is de regel 'position: absolute'™ die vertelt dat dit venster op een aangeven positie moet staan. Bij 'position absolute' neemt de layer, want dat is het nu geworden, geen plaats in, in het document. De layer vormt echt een 'laag' die over het document heen valt. Maar dat zien we niet in voorbeeld 5 omdat de layer onzichtbaar is gemaakt. In de style sheet met 'visibility hidden'.
Een aantal mogelijk nog onbekende grootheden in de style sheet, zijn border en padding. We kunnen met 'border' de kleur, dikte en het type van de rand om de layer opgeven. Dat gebeurt afzonderlijk voor bovenkant, rechterkant, onderkant en linkerkant. (top, right, bottom, left) Ook kunnen we met 'padding' opgeven hoever de inhoud van de layer, van elke rand vandaan moet blijven. De link die de layer moet oproepen als de muis er overheen gaat, is deze:
<span class="explain" onMouseOver="showNote('note1',event)" onMouseOut="hideNote('note1')">
Het element 'SPAN', waarvoor meestal ook 'div' wordt misbruikt, staat toe een stukje tekst dat onderdeel is van een ander element zoals een paragraaf, andere eigenschappen te geven. Ook SPAN is een object. En hier krijgen we voor het eerst ruzie met Microsoft.
Volgens het DOM zou in 'onMouseOver' de parameter 'event' het mouse event moeten overdragen aan de functie showNote. Terzijde: Het met de muis over het gemarkeerde woord gaan is een gebeurtenis. In programmeertalen noemen we die bij hun Engelse naam: event. En ook de muis wordt dan als 'mouse' aangeduid.
Helaas had Microsoft het op zich uitstekende idee dat dit niet nodig zou moeten zijn. In elke functie die in Internet Explorer wordt aangeroepen met een mouse event, zijn alle gegevens over de muis reeds beschikbaar in een objectvariabele met de naam 'event'. Hoe slim dat ook mag zijn, het is gewoon helemaal fout, want niet conform het DOM, dat ook door Microsoft is geaccepteerd. Het gevolg is dat we nu afzonderlijke code moeten schrijven vanwege deze ontwerpfout in Internet Explorer.

var msGarbage = navigator.appName.indexOf("Microsoft
Internet Explorer") > -1;

function showNote(what, eventDOM) {
   currentLayer=document.getElementById(what);
   if (msGarbage) {
     xPos = event.clientX + document.body.scrollLeft;
     yPos = event.clientY + document.body.scrollTop}
  else {
    xPos = eventDOM.pageX;
    yPos = eventDOM.pageY
  }
  with (currentLayer.style) {
     noteWidth = parseInt(width)+10;
    noteHeight = parseInt(height+10);
    if (xPos > noteWidth ) {
      xPos = xPos - noteWidth-10 }
    else {
       xPos += 10
    } 
   if (yPos > noteHeight) {
      yPos = yPos - noteHeight-10 }
    else {
      yPos +=10
    }
     top = yPos;
     left= xPos;
    visibility = 'visible'
   }
}

function hideNote(what) {
   currentLayer=document.getElementById(what);
   currentLayer.style.visibility='hidden' 
}

In het voorbeeld wordt, als de muis van het trefwoord af wordt bewogen, door de functie hideNote de layer weer onzichtbaar gemaakt door visibility="hidden" te maken en je zou nu verwachten dat de functie showNote alleen maar visibility="visible" zou hoeven maken. Maar dan zou de layer in de linker bovenhoek van het document verschijnen en dat is buitengewoon zinloos als we ergens onderaan een document bezig zijn, dat veel langer is dan het schermvenster. Dan zien we niets, want de layer valt buiten beeld. De layer moet naast de muiscursor verschijnen!
Verder moet de layer zodanig onder of boven de muiscursor en links of rechts van de muiscursor verschijnen, waar ie niet geheel of gedeeltelijk buiten beeld valt. Tevens mag de layer niet over de muiscursor heen vallen, want dat zou een 'event mouse out' tot gevolg hebben, waardoor de layer meteen weer verdwijnt.
Vandaar dat we in onMouseOver behalve de ID van de op te roepen layer ook het mouse-event meegeven, want we willen weten waar de muiscursor zich bevindt.
Bij het laden van het document wordt de variabele met de toepasselijke naam msGarbage geïnitialiseerd met een vlag die aangeeft of de browser misschien Internet Explorer is.
Hebben we te maken met een nette browser, (msGarbage=false), dan hebben we een event-object in de variabele eventDOM en daarin als behaviors de positie van de muiscursorc in event.DOM.pageX en event.DOMpageY. Maar hebben we te maken met Internet Explorer (msGarbage=true), dan wordt de muiscursor berekend op een andere manier die hier niet in detail wordt besproken. Van belang is alleen dat we de positie van de muis xPos en yPos hebben. Nu kunnen we gaan bepalen waar het venstertje moet verschijnen.
Daartoe moeten we weten hoe breed en hoe hoog het venster is. Dat is te vinden in de style sheet en om daar die informatie uit te kunnen halen moest deze voluit als attribuut in het element DIV worden opgenomen. Als we gegevens uit een style sheet lezen, krijgen we altijd een tekenreeks terug. Als een tekenreeks bestaat uit alleen cijfers, kunnen we daar direct mee rekenen, maar bedenk dat de grootheden height en width staan opgeslagen als een tekenreeks met de toevoeging 'px'.
Javascript heeft een ingebouwde functie m=parseInt(n) die alleen het eerste numerieke deel van n retourneert in m.
Die gebruiken we dus hier en tellen er meteen 10 pixels bij op, zodat berekenen van de beschikbare ruimte naast, boven of onder de cursor rekening houdt met een eerbiedige afstand van de layer tot de muiscursor.
Daarna kijken we of er ruimte links van de muiscursor is. Zo ja, dan zetten we de layer daar neer. Zo nee, dan zetten we hem rechts van de muiscursor. Hetzelfde doen we met de keuze boven of onder de muiscursor.
En als nu aan beide kanten niet genoeg plaats is? Ja, dan hebben we te maken met een veel te klein beeldscherm, 800x100% is al extreem uit de tijd. Of we hebben het venster onzinnige afmetingen gegeven.

Een hekel aan popups?
Wat is er nu precies tegen popupvensters? Eigenlijk niets, ware het niet dat deze faciliteit vooral bekend is geworden vanwege het misbruik. Omstreden branches zoals seks en gokken, mogen dan roepen dat ze volkomen legaal zijn, met de agressieve manier waarop zij zich vaak presenteren, sorteren ze echt geen bijval. Je bezoekt een website en komt tot de ontdekking dat je niet bent geïnteresseerd in hetgeen daar wordt geboden, dus volgt een klik op X. Maar het venster dat wordt afgesloten, roept meteen twee of meer andere vensters op die proberen je aandacht te trekken. Ook het sluiten van die vensters roept weer andere vensters op. Het gevolg is dat alle moderne browsers zijn voorzien van een popup-blocker. Dat maakt het zinvol gebruik van popupvensters onmogelijk. Wederom functionaliteit die onbruikbaar is geworden door misbruik.

De browser Opera had daarvoor een andere oplossing: alle geopende vensters stonden binnen een eigen werkruimte van de browser en alle vensters waren dus met één muisklik te sluiten. De gebruikers waren er niet allemaal blij mee en in versie 6 werd dit principe overboord gegooid, om in versie 7 weer terug te keren in een aangepaste vorm, die de gebruiker de keuze laat. Een andere ontwikkeling is 'tabbed browsing', dat we kennen uit Mozilla en Firefox en inmiddels door Opera en zelfs door Microsoft is overgenomen. Het is in feite een slimmere variant op hetgeen ooit door Opera werd geïntroduceerd. Blijft het feit dat we opgescheept zitten met de onmogelijkheid om strikt functioneel gebruik te maken van popupvensters. De oplossing is het gebruik van layers. Een popup-blocker laat die met rust en het ding is ongevaarlijk omdat het binnen de werkruimte van een en dezelfde browsersessie wordt geopend.

Popupvenster met een layer,
Voorbeeld 6 laat een pagina zien die verschijnt met de bedoelde popup. De bedoeling is dat deze op zijn plaatst blijft staan als de gebruiker de gebruiker de verticale rolbalk bedient. Daartoe is de eigenschap 'position' in de style sheet van de layer ingevuld als 'fixed'. Inderdaad,de bedoeling van 'position:fixed', is dat een object muurvast op de plaats blijft staan ten opzichte van de linker bovenhoek van het venster. Een sluiticoontje dat de gebruiker de gelegenheid geeft de popup te sluiten, maakt de zaak af.
Maar het is wederom Microsoft die hier liederlijk aan het klunzen is geweest. Voorbeeld 6 werkt namelijk prima in elke browser, behalve Internet Explorer, want Microsoft heeft hier CSS volkomen fout geïnterpreteerd en geeft het object een vaste plaats in het document in plaats van het venster. In Internet Explorer 7.0 heeft Microsoft die fout maar half gecorrigeerd. Door te verklaren dat de webpagina strikt volgens het DOM is opgezet, werkt het dan wel. Dus als voorbeeld 6 in IE7 niet werkt, kunnen we dat verhelpen door deze regel bovenaan het document te zetten:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
Zie voorbeeld 7 voor het resultaat.

Internet Explorer tot de orde roepen.

In voorbeeld 7 hebben we dan de versie die ook in Internet Explorer werkt. We maken daarbij gebruik van niet-standaard mogelijkheden die Internet Explorer bezit. Om te beginnen moeten we de style sheet verplaatsen naar de header. De DIV-declaratie wordt niet meer dan <DIV ID=Layer1> en we gaan een conditionele style gebruiken. Dat is een mogelijkheid die alleen Internet Explorer heeft. Hieronder is de complete sectie <head> van voorbeeld 7 weergegeven.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">

<html>
<head>
<style type="text/css">

p {font-family: Arial, Helvetica, sans-serif; font-size: 13px;
 color: #000000; text-align: justify; line-height: normal;
  font-weight: normal;}
body { padding-top: 10px; 
 padding-right: 30px; 
 padding-bottom: 10px; 
 padding-left: 30px;
  background: url(yellow.jpg) fixed }

.lClass {
  position: fixed; width:400px; height:300px; z-index:1;
  left: 100px; top: 50px; 
  border: 1px none #000000; visibility: visible;
}
</style>

<!--[if lt IE 7]>
<style type="text/css">
.lClass {
  position: absolute;
  top: expression(eval(document.documentElement.scrollTop+50));
}
</style>
<![endif]-->

<script type=text/javascript>

function noLayer() {
  var theLayer=document.getElementById('Layer1');
  theLayer.style.visibility='hidden'
}
</script>

</head>

Om te beginnen is de achtergrondkleur van het document nu aangebracht door een plaatje van dezelfde kleur. Dit omdat de regel background nodig is om het document niet te laten flikkeren tijdens scrollen. Dat zit hem in de toevoeging 'fixed' maar het gebruik van een achtergrondplaatje overruled de achtergrondkleur. Zie de style sheet voor 'body'.
Hetgeen tussen de regels <!--[if lt IE 7]> en <!--[endif]--> wordt uiteraard door elke browser als commentaar opgevat. Maar niet door Internet Explorer. Die ziet hetgeen daar tussen staat als conditioneel. Het zijn 'directives'. De code tussen deze regels wordt alleen uitgevoerd als de versie van Internet Explorer minder dan 7 is (lt staat voor less than). De code overruled een deel van de style sheet voor de layer. De grootheid 'position' wordt 'absolute' en de top van de layer wordt dynamisch opgegeven. Alleen Internet Explorer heeft die mogelijkheid.
We gaan er hier niet verder op in, maar volstaan met het geven van deze 'work around'. De vele niet-standaardfuncties van Internet Explorer vallen buiten het bestek van deze reeks.

Bewegende popup (1)
Voorbeeld 7 is wat saai. In voorbeeld 8 laten we de popup pas een seconde na het laden van de pagina verschijnen. Tevens verschijnt hij vanuit de linker bovenhoek. Daarmee zijn we beland in de wereld van de bewegende objecten.
Een oplossing voor de fout in Internet Explorer 6 wordt nu erg lastig, dus we beperken ons eerst tot het absoluut positioneren van de layer als IE 6 wordt aangetroffen. Bij het laden van de pagina is de layer onzichtbaar (visibility:hidden) en het coördinaat van de linker bovenhoek is niet gedefinieerd.
Geheel onderaan Voorbeeld 8 staat het stukje javascript dat de layer na 1 seconde zichtbaar gaat maken. De functie die dat gaat doen is popUpLayer met de subfunctie moveLayer.
Zie hieronder:

function moveLayer() {
   redo=false;
   with (theLayer.style) {
    x=parseInt(left);
     if (x<50) {
      x +=10;
      left=x;
      redo=true
    }
    y=parseInt(top)
    if (y<50) {
      y+=10;
      top=y
      redo=true
   }
   if (redo) {
    setTimeout('moveLayer()',10)
   }
  }
}

function popUpLayer() {
   theLayer = document.getElementById('Layer1');
   with (theLayer.style) {
    left = "-500px";
    top = "-500px";
    visibility='visible'
  }
   moveLayer()
}

Het laten bewegen van een object, is niets anders dan het coördinaat van de linker bovenhoek (de oorsprong van het object) in zijn style sheet wijzigen. We lezen de actuele positie van 'top' of 'left' en als die nog niet de gewenste waarde heeft tellen we er een hoeveelheid bij. Er zijn twee valstrikken waardoor een degelijke code vaak niet werkt:
Ten eerste is het pas mogelijk waarden uit een style sheet te lezen als deze dynamisch zijn gewijzigd. Dat is bij gebruik van een class of gebruik van de identifier (#objectID) nooit het geval. Alleen als de style sheet als attribuut 'style' in het element zelf is gedefinieerd, hebben we dat probleem niet. Dit is de meest voorkomende valstrik omdat de code gewoon niet werkt, zonder dat ergens een foutmelding verschijnt. De beginwaarden voor 'top' en 'left' worden hier dan ook dynamisch ingesteld op 500 pixels, waarna de layer zichtbaar wordt gemaakt, maar uiteraard buiten beeld valt.
De tweede valstrik is het lezen van de actuele waarde van 'top' of 'left'. Het misverstand is dat zulks een getal als antwoordt oplevert. Het is echter een string, vanwege de toevoeging px of andere dimensie. Er moet dan ook gebruik worden gemaakt van de in javascript ingebouwde functie parseInt.

De scripttaal
Nu we gezien hebben dat er met javascript heel wat te doen valt, wordt het tijd iets meer te vertellen over de taal. Wie geen ervaring heeft met programmeren, want dat is het, moet niet schrikken! Het leren van een programmeertaal zoals javascript is niet moeilijk en lukt het snelst door het gewoon te doen. Probeer daarom de code die je hier ziet te begrijpen. Een computerprogramma kan nog zo complex zijn, het valt altijd uiteen in navolgende reeksen van opdrachten.
Opeenvolging: opdrachten volgen elkaar gewoon één voor één op. Voorbeeld:
X=5;
Y=6;
document.write(x+y)
Een minuscuul script dat uiteindelijk de som 11 zal afdrukken.
Keuze: afhankelijk van een bepaalde conditie wordt gekozen uit een van de mogelijkheden. We zien dat in de blokken if (conditie) met het alternatieve blok else. Zowel na if als else moet één opdracht volgen. Nadat een programmadeel is doorlopen als gevolg van een keuze, ook wel splitsing genaamd, moet altijd weer het gemeenschappelijk deelworden vervolgd (verzameling). Voorbeeld:
if (X>0) document.write("X is groter dan nul") else document.write("X is kleiner dan nul")
Herhaling: zolang een bepaalde conditie niet is bereikt wordt een opdracht herhaald.
while (x<10) x+=1
Hier wordt telkens 1 bij x opgeteld tot deze de waarde 10 heeft. Belangrijk is te zien dat er helemaal niets gedaan zal worden als x aan het begin reeds 10 is!
Deze drie structuren bepalen elk programma en elke programmeertaal of scripttaal heeft materiaal aan boord om ermee te werken. Een schijnbaar probleem is dat in de keuze en de herhaling altijd slechts sprake is van één opdracht. Daarvoor zijn echter oplossingen in elke taal ingebouwd. In javascript hebben we te maken met twee van die zaken.
Om te beginnen de samengestelde opdracht (compound statement) waarbij meerdere opdrachten tot één geheel worden samengevoegd. Daartoe worden ze tussen accolades geplaatst en onderling met gescheiden door een puntkomma of door naar een nieuwe regel te gaan.
Ten tweede is daar het complete subprogramma\, in javascript 'function' genaamd. Er zijn diverse voorbeelden van samengestelde opdrachten en functies in tot nu gebruikte code te vinden.

Ingebouwde opdrachten.
Elke programmeertaal moet natuurlijk wel over een aantal basisopdrachten beschikken. In javascript kennen we navolgende taalelementen:
Variabelen zijn geheugenlocaties waar we iets kunnen bewaren. Over de plaats van die locaties maken we ons in javascript niet druk, dat doet de taal wel voor ons. Er zijn twee soorten variabelen:
Enkelvoudig: de variabele kan een getal of een tekst of een booleaanse waarheid (true of false) bevatten.
Matrices of arrays: Hou het voorlopig maar op tabellen van gegevens. We werken er op dit moment nog niet mee.
Aan een variabele wordt eenvoudig een waarde toegekend door het misbruiken van het teken = waar vooral wiskundige scherpslijpers nogal moeite mee hebben. Spreek het uit als 'wordt gelijk aan' en niet als 'is gelijk aan'. Als we het wiskundige begrip 'is gelijk aan' bedoelen, gebruiken we dat teken dubbel: ==

Javascript kan rekenen: de belangrijkste rekenkundige opdrachten zijn * voor vermenigvuldigen, / voor delen en natuurlijk + en - voor optellen en aftrekken. Daarnaast zijn er opdrachten speciaal bedoeld om een variabele een hogere of lagere waarde te geven dan de huidige:
x++ is equivalent aan x=x+1 (x wordt x plus één dus)
x+=10 is equivalent aan x=x+10
De operator + wordt ook gebruikt om teksten samen te stellen:
tekst = 'piet' + ' ' + 'jansen' heeft tot gevolg dat de variabele tekst als inhoud 'piet jansen' zal hebben. Door tekst tussen dubbele of enkele aanhalingstekens te plaatsen, geven we aan dat het om letterlijke tekst gaat, dus niet om namen van variabelen of opdrachten.

Tenslotte zijn er een heleboel standaardopdrachten, in feite 'methoden' die we hier niet allemaal kunnen noemen. Ze staan uitvoerig beschreven in het document ECMA262 dat van het internet te halen is.
Genoeg saaie stof over de scripttaal. We gaan er in een later stadium wel mee verder. De bedoeling is dat zaken die we over de taal zelf vertellen, door jou worden herkend omdat je er al iets mee gedaan hebt.

Bewegende popup (2)
Die club in Redmond blijft ons dwars zitten, want die popup uit voorbeeld 8, die vanuit de linker bovenhoek verschijnt, heeft wel tot gevolg dat we het weer zonder vaste positie in Internet Explorer 6 moeten stellen. Daar gaan we iets aan doen in Voorbeeld 9. We hebben nu voor de verticale positie andere code nodig, als IE6 wordt gedetecteerd.
De speciale style sheet voor IE6 kennen we nog uit voorbeeld 7, maar nu is de offset vanaf de top van het venster niet meer gedefinieerd met de harde constante 50, maar een variabele y. De bewuste regel wordt nu:
top: expression(eval(document.
documentElement.scrollTop+y));

En om de top te verplaatsen moet nu de inhoud van die variabele y worden gewijzigd.

var theLayer;
var y=-500;
var okBrowser=true;

function noLayer() {
   theLayer.style.visibility='hidden'
}


function moveLayer() {
   redo=false;
   with (theLayer.style) {
    x=parseInt(left);
    if (x<50) {
      x +=10;
      left=x;
      redo=true
    }
   if (okBrowser) {
     y=parseInt(top)
   }
   if (y<50) {
     y+=10;
     if (okBrowser) {
       top=y
     }
   redo=true
   }
   if (redo) {
     setTimeout('moveLayer()',10)
  }
 }
}

function popUpLayer() {
  if (navigator.appVersion.indexOf('MSIE 6') >0) {
    okBrowser = false
  }
  theLayer = document.getElementById('Layer1');
  with (theLayer.style) {
   left = "-500px";
   if (okBrowser) {
    top  = "-500px" }
   else {
    y=-500
   }
   visibility='visible'
  }
  moveLayer()
}

We gebruikten y reeds als locale variabele in de functie moveLayer. Nu wordt ie echter als globale variabele gedeclareerd met de initiële waarde -500 . verder hebben we een vlag okBrowser nodig die initieel de waarde true heeft, ten teken dat we een deugdelijke browser en niet IE6 verwachten.
De functie moveLayer leest en schrijft nu alleen de waarde 'top' van de style sheet, als we met een deugdelijke browser te maken hebben. In alle andere gevallen laten we 'top' met rust.
De functie popUpLayer moet nu eerst worden voorzien van een test op het type browser. Zet eens in een tekstbestand alleen deze regel:
<script type=text/javascript>document.
write(navigator.appVersion)</script>
Wijzig de naam van het bestand in 'test.html' en start het om te zien wat we precies deden in figuur 9. Het object 'navigator' dankt zijn naam aan het programma waar javascript ooit begon: in Netscape Navigator. De eigenschappen app.Version en appName zijn tekenreeksen die ons alle vertellen over het type browser. We maken gebruik van een ingebouwde methode van elk stringobject in javascript:
indexOf(P) geeft de positie aan waar de substring P in het stringobject begint. Als de substring niet bestaat, wordt de waarde -1 geretourneerd. In ons geval betekent een waarde groter dan 0 dat de string 'MSIE 6' aanwezig is en we dus met de 'probleembrowser' te doen hebben. De verschillende acties voor okBrowser=true of false zullen duidelijk zijn.

Luxaflex.
Soms is het leuk een pagina te openen met een effen wit of gekleurd scherm waarop nog niets te zien is. Vervolgens gaat de luxaflex open en wordt het onderliggende document zichtbaar. Voorbeeld 10 demonstreert wat de bedoeling is.
In voorbeeld 10 hebben we 6 layers, elk met een breedte van 1/6 van het actuele venster naast elkaar gezet. De layers worden dynamisch geschreven. Het probleem is namelijk dat we moeten achterhalen hoeveel 1/6 van het scherm in pixels is, zodat we de breedte van elke layer niet als constante kunnen invullen. Om het moeilijk te maken wijkt ook hier Internet Explorer weer af van de standaard, zodat we voor IE de breedte van het scherm op een iets andere manier moeten bepalen:

if (navigator.appVersion.indexOf('MSIE') >0 ) {
layer16=Math.round(document.body.offsetWidth/6) }
else {
layer16=Math.round((innerWidth-20)/6)
}

In Internet Explorer beschikt het element body een eigenschap offsetWidth die nauwkeurig is. Standaardbrowsers geven de breedte van het venster, waar de breedte van de rolbalken moet worden afgetrokken. Een aantal mathematische (rekenkundige) bewerken is beschikbaar in een module Math, waarvan we hier de functie round gebruiken om af te ronden. Na het laden van de pagina zien we niets omdat de zes layers het hele document verbergen. Aan het einde van het document helemaal onderaan staat de code die het verwijderen van de layers gaat starten:

setTimeout('getLayers()',500);

var layers=new Array;
var layer16;


function dropLayers() {
   for (i=1;i<7;i++) {
    with (layers[i].style) {
     theWidth=parseInt(width);
     theWidth-=4;
     if (theWidth >=0) {
       width=theWidth+'px' }
     else {
       visibility='hidden'
     }
   }
  }
  if (theWidth >=0) {
    setTimeout('dropLayers()',1)
  }
}

function getLayers() {
  for (i=1;i<7;i++) {
    theName='Layer'+i;
    layers[i]=document.getElementById(theName)
  }
  document.body.style.visibility='visible';
  dropLayers()
}

De code staat hierboven. Eerst worden pointers naar de 6 layers in een array gezet. Daarna gaat de functie droplayers() de layers stap voor stap smaller maken tot ze uiteindelijk onzichtbaar zijn geworden.

Nuttige websites:

 

Terug naar de top