Skip links en inhoudsopgave

Uitleg menu voor kinderen met tekening en pop-ups. Elk puzzelstukje is een link

Laatst aangepast: .

Menu in de vorm van een puzzel.

Korte omschrijving

Achter elk puzzelstukje zitten een link naar een andere pagina en een – voor (de meeste) kinderen – grappige pop-up. Link en pop-up worden pas zichtbaar bij klikken op of aanraken van een puzzelstukje, of door gebruik van de Tab-toets + Enter.

BELANGRIJK

Deze uitleg hoort bij het voorbeeld dat in de download zit. Het voorbeeld uit de download verschilt iets van het voorbeeld hier op de site. In de download ontbreekt bijvoorbeeld de navigatie voor de site. Ook in de kopregels zit vaak wat verschil. Daarnaast kunnen er nog andere (meestal kleine) verschillen zijn.

Als je deze uitleg leest naast de broncode van het voorbeeld op de site, kan het dus bijvoorbeeld zijn dat 'n <h1> uit de CSS bij 'n <h2> uit de HTML hoort. Maar het gaat niet om hele grote, fundamentele afwijkingen.

Als je dit lastig vindt, kun je bovenaan de pagina de hele handel downloaden. In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.

Als je deze handleiding graag uitprint (zonde van het bos), gebruik dan de pdf in de download. Deze pagina is niet geoptimaliseerd voor printen, de pdf kan wel makkelijk worden geprint.

Alles op deze site kan vrij worden gebruikt, met drie beperkingen:

* Je gebruikt het materiaal op deze site volledig op eigen risico. Het kan prima zijn dat er fouten in de hier verstrekte info zitten. Voor eventuele schade die door gebruik van materiaal van deze site ontstaat, in welke vorm dan ook, zijn www.css-voorbeelden.nl en medewerkers daarvan op geen enkele manier verantwoordelijk.

* Deze uitleg wordt min of meer regelmatig bijgewerkt. Het is daarom niet toegestaan deze uitleg op welke manier dan ook te verspreiden, zonder daarbij duidelijk te vermelden dat de uitleg afkomstig is van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie is te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.

* Het kan zijn dat materiaal is gebruikt dat van anderen afkomstig is. Dat materiaal kan onder een bepaalde licentie vallen, waardoor het mogelijk niet onbeperkt gebruikt mag worden. Als dat zo is, wordt dat vermeld onder Inhoud van de download en licenties.

Een link naar www.css-voorbeelden.nl wordt trouwens altijd op prijs gesteld.

Alle code is geschreven in een afwijkende lettersoort en -kleur. De code die te maken heeft met de basis van dit voorbeeld (essentiële code), is in de hele uitleg onderstippeld blauw. Alle niet-essentiële code is bruin. (In de inhoudsopgave staat alles vanwege de leesbaarheid in een gewone letter.)

Opmerkingen

Links in deze uitleg, vooral links naar andere sites, kunnen verouderd zijn. Op de pagina met links vind je steeds de meest recente links.

Dit voorbeeld is gemaakt op een systeem met Linux (KDE neon). Daarbij is vooral gebruik gemaakt van Visual Studio Code, GIMP en Firefox met extensies. De pdf-bestanden zijn gemaakt met LibreOffice.

Vragen of opmerkingen? Fout gevonden? Ga naar het forum.

Achterliggend idee

Boven een afbeelding van een puzzel wordt een ongeordende lijst <ul> geplaatst. Met behulp van display: grid; worden elk van de twaalf in die <ul> zittende <li>'s boven een van de twaalf puzzelstukjes geplaatst. Elke <li> wordt even groot gemaakt als een puzzelstukje. Hierdoor ontstaat een rooster, waarvan elk vakje precies één puzzelstukje bestrijkt.

Binnen elke <li> staat gelijk bovenin een aankruisvakje.. Als dat wordt aangevinkt, wordt boven het puzzelstukje een <div> getoond (in grotere browservensters geheel of deels buiten het puzzelstukje), waarin een of meer afbeeldingen, tekst en een link zitten. Althans: die afbeeldingen en link zitten in slechts vier van de <li>’s, in de andere acht staat alleen de tekst dat dit puzzelstukje nog niet klaar is.

Die <div> is normaal genomen niet zichtbaar, omdat die links buiten het scherm is geparkeerd. Omdat de <div> altijd aanwezig is, al zie je die niet, kunnen Schermlezers de inhoud ervan gewoon voorlezen.

Omdat binnen elke <li> bovenin een aankruisvakje staat, kan met de Tab-toets door de puzzelstukjes worden gelopen. De bij het aankruisvakje met de focus horende <label> krijgt een blauwe rand, zodat zichtbaar is welk aankruisvakje de focus heeft. Omdat deze <label> even groot is als het bijbehorende puzzelstukje, lijkt het puzzelstukje een rand te hebben. Hierdoor is duidelijk zichtbaar, waar je met de Tab-toets bent. Door op de Spatiebalk of op Enter te drukken, wordt het aankruisvakje aan- of uitgevinkt en de bijbehorende <div> getoond of verborgen.

Door het aanraken of -klikken van de <label> wordt het bijbehorende aankruisvakje aangevinkt, waardoor de <div> met inhoud zichtbaar wordt. Omdat de <label> even groot is als een puzzelstukje, lijkt het net alsof je een puzzelstukje moet aanraken om de bijbehorende <div> te tonen.

Anders dan normaal het geval is, hebben de <label>’s geen tekst. Het gaat hier om puzzelstukjes en je moet zelf maar ontdekken, wat er achter een stukje zit. Voor schermlezers is met behulp van aria-label wel een tekst binnen de <label>’s aangebracht, omdat een schermlezer nou eenmaal niet ziet dat het om twaalf stukje van een puzzel gaat.

Voor schermlezers is ook de inhoud van de <div>’s soms wat aangepast. Bij het tweede puzzelstukje bijvoorbeeld wordt de tekst gevormd door 64 aparte plaatjes met elk een bewegende letter. Je kunt bij elk plaatje een alt-tekst met de letter toevoegen, maar schermlezers zouden dan die 64 letters een voor een voorlezen. Daarom worden die plaatjes voor schermlezers verborgen en vervangen door een ’normale’ tekst in een <p>. Die <p> wordt buiten het scherm geparkeerd, zodat deze niet zichtbaar is, maar wel wordt voorgelezen.

Omdat de <div>’s vaak helemaal of gedeeltelijk over een (groot) deel van de puzzel heen staan, zouden soms puzzelstukjes niet aangeraakt of -geklikt kunnen worden. Dit wordt voorkomen door het gebruik pointer-events: none;: een aanraking of klik gaat door de <div> heen naar het onderliggende puzzelstukje. Door de in de <div>’s zittende <a>’s pointer-events: auto; te geven, werken die links wel gewoon bij een aanraking of klik.en puzzel wordt een ongeordende lijst <ul> geplaatst. Met behulp van display: grid; worden elk van de twaalf in die <ul> zittende <li>’s boven een van de twaalf puzzelstukjes geplaatst. Elke <li> wordt even groot gemaakt als een puzzelstukje. Hierdoor ontstaat een rooster, waarvan elk vakje precies één puzzelstukje bestrijkt.

Omdat de <div>’s vaak helemaal of gedeeltelijk over een (groot) deel van de puzzel heen staan, zouden soms puzzelstukjes niet aangeraakt of -geklikt kunnen worden. Dit wordt voorkomen door het gebruik pointer-events: none;: een aanraking of klik gaat door de <div> heen naar het onderliggende puzzelstukje. Door de in de <div>’s zittende <a>’s pointer-events: auto; te geven, werken die links wel gewoon bij een aanraking of klik.

Kort overzicht van problemen die met JavaScript worden opgelost

Hieronder staat een kort lijstje met dingen die zonder JavaScript (veel) minder handig werken of onduidelijk zijn. Bij Zonder JavaScript is deze zelfde lijst te vinden, maar dan met veel meer toelichting, bijbehorende oplossingen, en dergelijke.

Omdat dat nogal ’n lang verhaal is, staat dezelfde lijst hier ook, maar dan veel beknopter.

De voorvoegsels -moz- en -webkit-

Voordat een nieuwe CSS-eigenschap wordt ingevoerd, is er in de regel een experimentele fase. Browsers passen het dan al toe, maar met een aangepaste naam. Tijdens deze fase kunnen problemen worden opgelost en worden veldslagen uitgevochten, over hoe de standaard precies moet worden toegepast.

Als iedereen het overal over eens is en alle problemen zijn opgelost, wordt de officiële naam uit de standaard gebruikt.

De belangrijkste browsers hebben elk een eigen voorvoegsel:

Firefox: -moz-, naar de maker: Mozilla.

Op webkit gebaseerde browsers, zoals Edge, Google Chrome, Opera, Safari en Android browser: -webkit-.

(Google Chrome is van webkit overgestapt op een eigen weergave-machine: Blink. Blink gaat geen voorvoegsels gebruiken. Het is echter een aftakking van webkit, dus het zal nog wel even duren voor -webkit- hier helemaal uit is verdwenen. Ook Opera en Edge gebruiken Blink.)

In oudere code kun je ook nog de voorvoegsels -o- (Opera) en -ms- (Microsoft Internet Explorer) tegenkomen.

Zodra de experimentele fase voorbij is, wordt het voorvoegsel weggelaten. Omdat dat moment niet bij alle browsers hetzelfde is, zet je nu ook al de officiële naam erbij. Deze wordt als laatste opgegeven. Bijvoorbeeld Safari herkent -webkit-appearance. Zodra Safari appearance gaat herkennen, zal dit -webkit-appearance overrulen, omdat het er later in staat. Dat ze er beide in staan, is dus geen enkel probleem.

In dit voorbeeld wordt hyphens gebruikt.

hyphens

Op dit moment moet je nog het volgende schrijven:

{-webkit-hyphens: ...; hyphens: ...;}

In de toekomst kun je volstaan met:

{hyphens: ...;}

Inmiddels is de algemene mening dat ’vendor prefixes’, zoals deze voorvoegsels in het Engels heten, geen groot succes zijn. Eén van de grootste problemen: veel sitemakers gebruiken alleen de -webkit-variant. Daar kwamen ze in het verleden nog mee weg, omdat Apple op mobiel zo’n beetje ’n monopolie had, en omdat ook Google Chrome in het verleden met -webkit- werkte. Inmiddels is dat niet meer zo, maar deze gewoonte bestaat nog steeds. Waardoor ’n site alleen in op webkit georiënteerde browsers goed is te bekijken.

Dit is zo’n groot probleem dat andere browsers soms de variant met -webkit- ook maar zijn gaan implementeren, naast de standaard. Want als ’n site het niet goed doet in ’n bepaalde browser, krijgt in de regel niet de site maar de browser de schuld.

Vanwege alle problemen met ’vendor prefixes’ worden deze niet meer gebruikt. Nieuwe, experimentele CSS-eigenschappen zitten inmiddels achter een zogenaamde vlag: de gebruiker moet iets veranderen in de instellingen, waarna de eigenschap gebruikt (en getest) kan worden. Als alles werkt, zoals het hoort te werken, schakelt de browsermaker de vlag standaard in.

Voorlopig zijn we echter nog niet van deze voorvoegsels af. Als je ze gebruikt, gebruik dan álle varianten, en eindig met de variant zonder voorvoegsel, zoals die uiteindelijk ooit gebruikt gaat worden. Als je alleen de -webkit- gebruikt, ben je in feite ’n onbetaalde reclamemaker voor Apple.

Semantische elementen en ARIA

Deze twee onderwerpen zijn samengevoegd, omdat ze veel met elkaar te maken hebben.

Semantische elementen

De meeste elementen die in HTML worden gebruikt, hebben een semantische betekenis. Dat wil zeggen dat je aan de gebruikte tag al (enigszins) kunt zien, wat voor soort inhoud er in het element staat. In een <h1> staat een belangrijke kop. In een <h2> staat een iets minder belangrijke kop. In een <p> staat een alinea. In een <table> staat een tabel (en geen lay-out, als het goed is!). Enzovoort.

Door het op de goede manier gebruiken van semantische elementen, kunnen zoekmachines, schermlezers, enzovoort de structuur van een pagina begrijpen. De spider van een zoekmachine is redelijk te vergelijken met een blinde. Het is dus ook in je eigen belang om semantische elementen zo goed mogelijk te gebruiken. Een site die toegankelijk is voor mensen met een handicap, is in de regel ook goed te verwerken door een zoekmachine en maakt dus een grotere kans gevonden en bezocht te worden.

Als het goed is, wordt het uiterlijk van de pagina bepaald met behulp van CSS. Het uiterlijk staat hierdoor (vrijwel) los van de semantische inhoud van de pagina. Met behulp van CSS kun je een <h1> heel klein weergeven en een <h6> heel groot, terwijl schermlezers, zoekmachines, en dergelijke nog steeds weten dat de <h1> een belangrijke kop is.

Slechts enkele elementen, zoals <div> en <span>, hebben geen semantische betekenis. Daardoor zijn deze elementen uitstekend geschikt om met behulp van CSS het uiterlijk van de pagina aan te passen: de semantische betekenis verandert niet, maar het uiterlijk wel. Voor een schermlezer of zoekmachine verandert er (vrijwel) niets, voor de gemiddelde bezoeker krijgt het door de CSS een heel ander uiterlijk.

(De derde laag, naast HTML voor de inhoud en CSS voor het uiterlijk, is JavaScript. Die zorgt voor de interactie tussen site en bezoeker. De min of meer strikte scheiding tussen CSS en HTML aan de ene kant en JavaScript aan de andere kant is met de komst van CSS3 en HTML5 veel vager geworden. Je kunt nu bijvoorbeeld ook met CSS dingen langzaam verplaatsen en met HTML deels de invoer in formulieren controleren.)

Html5 heeft een aantal nieuwe elementen, die speciaal zijn bedoeld om de opbouw van een pagina aan te geven. Deze elementen gedragen zich als een gewone <div>, maar dan een <div> met een semantische betekenis. Hierdoor kunnen schermlezers, zoekmachines, en dergelijke. beter zien, hoe de pagina is samengesteld. De meeste schermlezers kunnen dit soort elementen ook gebruiken om snel door de pagina te navigeren. In dit voorbeeld worden hiervan alleen <main> en <nav> gebruikt.

<main>

Hierbinnen staat de belangrijkste inhoud van de pagina (in dit voorbeeld is dat de puzzel met alles erop en eraan).

<nav>

Ook <nav> gedraagt zich als een gewone <div>, maar dan een <div> met een semantische betekenis: navigatie. Hierdoor kunnen schermlezers, zoekmachines, en dergelijke. gelijk zien dat hierin links zijn ondergebracht, waarmee je naar andere pagina’s en dergelijke. kunt gaan.

<nav> geeft alleen aan dat hierin een of andere vorm van navigatie is ondergebracht, maar niet wat voor navigatie. In het voorbeeld is dit geen probleem, omdat er maar één <nav> is. Bovendien staat in de (onzichtbare) <h1> gelijk onder <main> een omschrijving. Voor schermlezers is daardoor duidelijk, om wat voor soort menu het gaat.

Op de site staan echter twee menu’s: het menu van het voorbeeld en het menu van de site. Op de site is hierdoor niet duidelijk, wat voor soort menu er in de <nav> zit. Daarom is op de site gelijk onder beide <nav>’s een <h2> met een korte omschrijving van het menu geplaatst. Met de ARIA-code aria-labelledby wordt de <h2> aan de bijbehorende <nav> gekoppeld. De <h2>’s zijn onzichtbaar, maar een schermlezer leest ze gewoon voor.

ARIA-codes

De afkorting ARIA staat voor Accessible Rich Internet Applications.

Er worden in dit voorbeeld drie ARIA-codes gebruikt: aria-expanded, aria-hidden en aria-label.

aria-expanded

Schermlezers lezen netjes alle tekst en dergelijke. in de <div>’s voor, ook als je die niet ziet. Op het scherm is te zien of een <div> zichtbaar is of niet, maar een schermlezer weet dat niet.

Met behulp van aria-expanded kan aan een schermlezer worden doorgegeven, of de <div> zichtbaar is of niet. De schermlezer zal dit dan melden. (Hoe dat precies wordt gemeld, verschilt per schermlezer.)

aria-expanded="false" geeft aan dat de <div> is verborgen, aria-expanded="true" geeft aan dat de <div> wordt getoond. Bij openen van de pagina wordt geen enkel <div> getoond. Bij het eerste puzzelstukje ziet dat er zo uit:

<input id="een" type="checkbox" aria-label="Puzzelstukje een" aria-expanded="false">

Het veranderen van ’false’ in ’true’ en omgekeerd gebeurt met behulp van JavaScript. Ook zonder JavaScript werkt alles, alleen zal de schermlezer dan altijd melden dat de <div> niet wordt getoond, omdat de waarde bij aria-expanded altijd ’false’ blijft.

Een door JavaScript in de code aangebrachte wijziging is niet in de broncode van de site te zien. Om de door JavaScript aangebrachte wijziging te zien, moet je de code bekijken in het ontwikkelgereedschap van de browser.

aria-hidden

Met behulp van aria-hidden="true" kan een deel van de code worden verborgen voor schermlezers en dergelijke., zodat dit niet wordt voorgelezen. Op de normale weergave op het scherm heeft dit verder geen enkele invloed.

Bij het tweede puzzelstukje wordt aria-hidden gebruikt:

<span id="twee-tekst" aria-hidden="true">

Binnen deze <span> staat de tekst ’Niet echt geschikt voor volwassenen, maar kinderen vinden zoiets juist leuk’. Deze tekst van wordt gevormd door 64 losse, bewegende plaatjes met in elk plaatje één letter. Je zou bij elk plaatje de letter met een alt-tekst kunnen vermelden, maar schermlezers lezen dan alle 64 letters los van elkaar voor: de volledige tekst van 64 letters wordt gespeld.

Om dit te voorkomen wordt de <span> met de 64 plaatjes met behulp van aria-hidden verborgen.

Gelijk boven de <span> staat een <p> met ongeveer dezelfde zin, maar dan in gewone tekst. Deze <p> wordt buiten het scherm geparkeerd en is daardoor onzichtbaar, maar wordt wel gewoon door schermlezers voorgelezen.

Bij het negende puzzelstukje zit in de <div> een tekst die steeds wordt herhaald, terwijl die steeds kleiner wordt. Een schermlezer zou al deze herhalingen voorlezen. Om dit te voorkomen wordt die tekst voor schermlezers verborgen:

<span id="negen-tekst" aria-hidden="true">

Omdat dit echt flauwekul-tekst is, wordt voor schermlezers geen vervangende tekst opgegeven.

aria-label

Elk aankruisvakje heeft een bijbehorende <label>. In die <label> zit normaal genomen de tekst, die bij het aankruisvakje wordt getoond. Hier zit geen tekst in de <label>, omdat het om puzzelstukjes gaat. Hierdoor is er voor schermlezers geen tekst om voor te lezen, waardoor alleen maar twaalf keer iets als ’checkbox’ wordt voorgelezen. Dat is natuurlijk veel te vaag. Daarom krijgt elk aankruisvakje een aria-label. Je ziet zo’n aria-label niet, maar het wordt gewoon voorgelezen. Bij het eerste puzzelstukje ziet dat er zo uit:

<input id="een" type="checkbox" aria-label="Puzzelstukje een" aria-expanded="false">

Een schermlezer leest hier ’Puzzelstukje een’ voor. Zo staat bij elk aankruisvakje een ander label.

Bij het zesde puzzelstukje zit in de <div> een link met de tekst ’Wegwezen!’. In combinatie met de afbeelding is het duidelijk, dat hiermee het menu wordt verlaten. Om dit ook voor schermlezers, die de afbeelding niet zien, duidelijk te maken, wordt met aria-label de tekst ’Verlaat dit menu’ toegevoegd:

<a href="014-files-dl/menu-014-uitgang-dl.html" aria-label="Verlaat dit menu">Wegwezen!</a>

Schermlezers lezen nu niet ’Wegwezen!’, maar ’Verlaat dit menu’ voor.

Bij het negende puzzelstukje is de tekst van de link ’E-mail’. Ook dat is mogelijk niet duidelijk genoeg voor schermlezers, daarom wordt ook hier een aria-label toegevoegd:

<a href="014-files-dl/menu-014-mail-dl.html" aria-label="Stuur ons een mail">E-mail</a>

Nu is duidelijk dat het om het versturen van een mail gaat, en niet om het lezen ervan of zoiets.

Muis, toetsenbord, touchpad en aanraakscherm

Vroeger, toen het leven nog mooi was en alles beter, waren er alleen monitors. Omdat kinderen daar niet af konden blijven met hun tengels, besloten fabrikanten dan maar aanraakschermen te gaan maken, omdat je die mag aanraken. Het bleek makkelijker te zijn om volwassenen ook te leren hoe je ’n scherm vies maakt, dan om kinderen te leren met hun vingers van de monitor af te blijven.

Zo ontstonden aanraakschermen en kreeg het begrip ellende een geheel nieuwe lading. In de perfecte wereld van vroeger, waarin alleen desktops bestonden, werkten dingen als hoveren, klikken en slepen gewoon. Zonder dat je eerst ’n cursus hogere magie op Zweinstein hoefde te volgen. Zelfs in JavaScript was het nog wel te behappen, ook voor mensen die toevallig niet Einstein heten.

Op dit moment kun je computerschermen ruwweg in twee soorten indelen: schermen die worden aangeraakt, en schermen die worden bediend met hulpmiddelen als een toetsenbord, muis of touchpad. Omdat ook computerschermen zich kennelijk vermengen, bestaan er inmiddels ook schermen die zowel van aanraken als van muizen houden.

Hieronder staat een lijstje met dingen die zijn aangepast voor de verschillende soorten schermen, zodat dit voorbeeld overal werkt. Een touchpad werkt ongeveer hetzelfde als een muis. Als hieronder iets over een muis staat, geldt dat ook voor een touchpad.

:hover

Omdat :hover mogelijk niet werkt, als CSS uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :hover tonen.

Je hovert over een element, als je met behulp van muis of touchpad de cursor boven dat element brengt. Hoveren kan over álle elementen. Het wordt veel gebruikt om iets van uiterlijk te laten veranderen, een pop-up te laten verschijnen, en dergelijke..

In dit voorbeeld wordt :hover niet gebruikt voor het tonen van de <div>’s. Het gaat om puzzelstukjes, en die kunnen alleen worden bediend met een klik, een aanraking, Enter of de Spatiebalk. Als de <div>’s ook zichtbaar zouden worden bij hoveren over een puzzelstukje, zou dat een vreselijk onrustig beeld opleveren. Bovendien zijn dan op iOS en iPadOS extra aanpassingen nodig, omdat :hover daarop ook werkt bij een aanraking.

Wel krijgt het puzzelstukje waarover wordt gehoverd een blauwe rand (eigenlijk krijgt de <label> bij dat puzzelstukje die blauwe rand, maar omdat die <label> even groot is als het puzzelstukje, lijkt het puzzelstukje die rand te krijgen). Dit levert geen onrustig beeld op, omdat de inhoud van de <div> niet wordt getoond. Op iOS en iPadOS levert dit ook geen problemen op, omdat bij aanraken van een puzzelstukje de bijbehorende <div> wordt getoond, waarbij eventueel andere getoonde <div>’s weer worden verborgen.

:focus

Omdat :focus mogelijk niet werkt, als CSS uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :focus tonen.

De meeste mensen gaan met een muis naar een link, invoerveld, en dergelijke.. Waarna ze vervolgens klikken om de link te volgen, tekst in te voeren, of wat ze ook maar willen doen. (Dit geldt uiteraard niet voor aanraakschermen.)

Er is echter ’n tweede manier om naar links, invoervelden, en dergelijke. te gaan: met behulp van de Tab-toets kun je naar links, invoervelden, en dergelijke. ’springen’.

Waar je staat, wordt door alle browsers aangegeven met een of ander kadertje. De link en dergelijke. met het kadertje eromheen ’heeft focus’. Dat wil zeggen dat je die link volgt, als je op de Enter-toets drukt, of dat je in dat veld tekst kunt gaan invoeren, enzovoort.

Het kadertje dat de focus aangeeft, moet nooit zonder meer worden weggehaald. Gebruikers van de Tab-toets hebben dan geen idee meer, waar ze zijn.

Bij gebruik van de muis of een aanraakscherm is het kadertje niet nodig, want je mag toch hopen dat iemand weet, waar iemand iets heeft heeft aangeraakt of ‑geklikt. Als je dat niet meer weet, valt te vrezen dat een kadertje ook geen redding meer biedt.

In het verleden was dat vaak een probleem, omdat dat kadertje nou niet bepaald erg mooi is. En dus eigenlijk alleen bij gebruik van de Tab-toets nodig is. Dit is opgelost met de pseudo-class :focus-visible: de focus wordt alleen nog aangegeven, als een toetsenbord wordt gebruikt. Bij een klik of een aanraking zie je het kadertje niet meer.

Inmiddels is :focus-visible in alle iets nieuwere browsers de standaard, dus eigenlijk hoef je :focus en/of :focus-visible alleen nog te gebruiken, als je bijvoorbeeld het uiterlijk van het kadertje wilt aanpassen.

In dit voorbeeld krijgt de <label> bij het aankruisvakje met de focus een blauwe rand, zodat te zien is waar je met de Tab-toets bent aangekomen. Omdat de <label>’s even groot zijn als het bijbehorende puzzelstukje, lijkt het puzzelstukje die blauwe rand te krijgen. In dit geval wordt hier gewoon :focus voor gebruikt, want ook bij gebruik van de muis of een aanraakscherm moet je kunnen zien bij welk puzzelstukje de getoonde <div> hoort.

Als een <div> zichtbaar is, kan deze – vooral in kleinere browservensters – een (groot) deel van de puzzel afdekken. Daardoor zou je soms niet kunnen zien, waar de Tab-toets is aangekomen. Daarom zijn de <div>’s een klein beetje doorzichtig gemaakt: nu kun je de blauwe rand van de <label> bij het aankruisvakje met de focus nog enigszins zien.

:active

Omdat :active mogelijk niet werkt, als CSS uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :active tonen.

Een element is actief, als de muis wordt ingedrukt boven dat element. Op sommige aanraakschermen is het element actief, als het wordt aangeraakt.

:active wordt niet gebruikt in dit voorbeeld.

De code aanpassen aan je eigen ontwerp

Toegankelijkheid en zoekmachines

De tekst in dit hoofdstukje is een algemene tekst, die voor elke pagina geldt. Eventueel specifiek voor dit voorbeeld geldende problemen en eventuele aanpassingen om die problemen te voorkomen staan bij Bekende problemen (en oplossingen).

Toegankelijkheid (in het Engels 'accessibility') is belangrijk voor bijvoorbeeld blinden die een schermlezer gebruiken, of voor motorisch gehandicapte mensen die moeite hebben met het bedienen van een muis. Een spider van een zoekmachine (dat is het programmaatje dat de site indexeert voor de zoekmachine) is te vergelijken met een blinde. Als je je site goed toegankelijk maakt voor gehandicapten, is dat gelijk goed voor een hogere plaats in een zoekmachine. Dus als je 't niet uit sociale motieven wilt doen, kun je 't uit egoïstische motieven doen.

(Op die plaats in de zoekmachine heb je maar beperkt invloed. De toegankelijkheid van je site is maar één van de factoren, maar zeker niet onbelangrijk.)

Als je bij het maken van je site al rekening houdt met toegankelijkheid, is dat nauwelijks extra werk. 't Is ongeveer te vergelijken met inbraakbescherming: doe dat bij 'n nieuw huis en 't is nauwelijks extra werk, doe 't bij 'n bestaand huis en 't is al snel 'n enorme klus.

Enkele tips die helpen bij toegankelijkheid:

Getest in

Laatst gecontroleerd op 28 juli 2026.

Onder dit kopje staat alleen maar, hoe en waarin is getest. Alle eventuele problemen, ook die met betrekking tot zoomen, lettergroottes, toegankelijkheid, uit staan van JavaScript en/of CSS, enzovoort staan iets hieronder bij Bekende problemen (en oplossingen). Het is belangrijk dat deel te lezen, want uit een test kan ook prima blijken dat iets totaal niet werkt!

Dit voorbeeld is getest op de volgende systemen:

Desktopcomputers

Linux (KDE neon 24.04) (2560 x 1080 px, resolutie: 96 ppi):
Firefox, Google Chrome en Vivaldi, in grotere en kleinere browservensters.
In Vivaldi is ook ruimtelijke navigatie (’Spatial Navigation’) getest.

Laptops

Windows 11 (1920 x 1080 px, resolutie: 165 ppi):
Firefox, Google Chrome en Edge, in grotere en kleinere browservensters.

macOS 11.7.11 ('Big Sur') (1440 x 900 px, resolutie: 96 ppi):
Firefox, Safari, Google Chrome en Microsoft Edge, in grotere en kleinere browservensters.

macOS 26.5.1 (’Tahoe’) (2500 x 1600 px, resolutie: 227 ppi):
Firefox, Safari, Google Chrome en Microsoft Edge, in grotere en kleinere browservensters.

Tablets

iPad met iOS 12.5.8 (2048 x 1536 px, device-pixel-ratio: 2:
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

iPad met iPadOS 18.7.9 (2160 x 1620 px, resolutie: 264 ppi):
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

Android 6.0 ('Marshmallow') (1920 x 1200 px, resolutie: 224 ppi):
Samsung Internet, Firefox en Chrome (alle portret en landschap).

Android 8.1 ('Oreo') (1920 x 1200 px, resolutie: 218 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Android 13 (2000 x 1200 px, resolutie: 225 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Smartphones

iPhone met iOS 15.8.8 (1334 x 750 px, resolutie: 326 ppi):
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

iPhone met iOS 26.5 (1334x750, resolutie: 326 ppi): Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

Android 7.0 ('Nougat') (1280 x 720 px, resolutie: 294 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Android 10 (2220 x 1080 px, resolutie: 570 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Android 14 (2408 x 1080 px, resolutie: 401 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Er is op de aan het begin van dit hoofdstukje genoemde controledatum getest in de meest recente versie van de browser, die op het betreffende besturingssysteem kon draaien. Het aantal geteste browsers en systemen is al tamelijk fors, en als ook nog rekening gehouden moet worden met (zwaar) verouderde browsers, is het gewoon niet meer te doen. Surfen met een verouderde browser is trouwens vragen om ellende, want updates van browsers hebben heel vaak met beveiligingsproblemen te maken.

In- en uitzoomen en - voor zover de browser dat kan - een kleinere en grotere letter zijn ook getest. Er is ingezoomd en vergroot tot zover de browser kan, maar niet verder dan 200%.

Er is getest met behulp van muis en toetsenbord, behalve op iOS, iPadOs en Android, waar een aanraakscherm is gebruikt. Op Windows 11 is getest met touchpad, toetsenbord, muis, en - waar dat zinvol was - op een combinatie daarvan. Op macOS 11.7.11 en 26.5.1 is getest met (een combinatie van) toetsenbord, touchpad en muis.

Als in een voorbeeld JavaScript is gebruikt, is ook getest of het werkt zonder JavaScript. Dat is alleen gedaan in de browsers, waarin in de instellingen JavaScript kan worden uitgeschakeld.

Ook is getest zonder CSS en - als afbeeldingen worden gebruikt - zonder afbeeldingen.

Schermlezers en dergelijke

Naast deze 'gewone' browsers is ook getest in Lynx, WebbIE, NVDA, TalkBack, VoiceOver, Orca en Verteller.

Lynx is een browser die alleen tekst laat zien en geen CSS gebruikt. Er is getest op Linux.

WebbIE. is een browser die gericht is op mensen met een handicap. Er is getest op Windows 11.

NVDA is een schermlezer, zoals die door blinden wordt gebruikt. Er is getest op Windows 11 in combinatie met Firefox.

TalkBack is een in Android ingebouwde schermlezer. Er is getest in combinatie met Chrome op Android 6.0, 7.0, 8.1, 10, 13 en 14

VoiceOver is een in iOS en macOS ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS 12.5.8, 15.8.8 en 26.5, iPadOS 18.7.9, macOS 11.7.11 en 26.5.1.

Orca is een schermlezer voor Linux. Er is getest in combinatie met Firefox op KDE neon 24.04.

Verteller (Narrator) is een in Windows 11 ingebouwde schermlezer. Er is getest in combinatie met Edge.

(Voor de bovenstaande programma's zijn links naar sites met uitleg en dergelijke te vinden op de pagina met links onder Toegankelijkheid → Schermlezers, tekstbrowsers, en dergelijke.)

Als het voorbeeld in deze programma's toegankelijk is, zou het in principe toegankelijk moeten zijn in alle aangepaste browsers en dergelijke. En dus ook voor zoekmachines, want een zoekmachine is redelijk vergelijkbaar met een blinde.

Eventuele problemen in schermlezers (en eventuele aanpassingen om die te voorkomen) staan iets hieronder bij Bekende problemen (en oplossingen).

Waar dat zinvol was, is ook nog getest op combinaties als een grote letter in een schermlezer met toetsenbordbediening.

Alleen op de hierboven genoemde systemen en browsers is getest. Er is dus niet getest op bijvoorbeeld 'n Blackberry. Er is een kans dat dit voorbeeld niet (volledig) werkt op niet-geteste systemen en apparaten. Om het wel (volledig) werkend te krijgen, zul je soms (kleine) wijzigingen en/of (kleine) aanvullingen moeten aanbrengen, bijvoorbeeld met JavaScript.

Er is ook geen enkele garantie dat iets werkt in een andere tablet of smartphone dan hierboven genoemd, omdat fabrikanten in principe de software kunnen veranderen. Dit is anders dan op de desktop, waar browsers altijd (vrijwel) hetzelfde werken, zelfs op verschillende besturingssystemen. Iets wat in Samsung Internet op Android werkt, zal in de regel overal werken in die browser, maar een garantie is er niet. De enige garantie is het daadwerkelijk testen op een fysiek apparaat. En aangezien er duizenden mobiele apparaten zijn, is daar geen beginnen aan.

De HTML is gevalideerd met de HTML-validator, de CSS met de CSS-validator van w3c. Als om een of andere reden niet volledig gevalideerd kon worden, wordt dat bij Bekende problemen (en oplossingen) vermeld.

Nieuwe browsers worden pas getest, als ze uit het bèta-stadium zijn. Anders is er 'n redelijke kans dat je tegen 'n bug zit te vechten, die voor de uiteindelijke versie nog gerepareerd wordt.

Dit voorbeeld is alleen getest in de hierboven met name genoemde browsers. Vragen over niet-geteste browsers kunnen niet worden beantwoord, en het melden van fouten in niet-geteste browsers heeft ook geen enkel nut. (Melden van fouten, problemen, enzovoort in wel geteste browsers: graag! Dat kan op het forum.)

Bekende problemen (en oplossingen)

Waarop en hoe is getest, kun je gelijk hierboven vinden bij Getest in.

Als je hieronder geen oplossing vindt voor een probleem dat met dit voorbeeld te maken heeft, kun je op het forum proberen een oplossing te vinden voor je probleem. Om forumspam te voorkomen, moet je je helaas wel registreren, voordat je op het forum een probleem kunt aankaarten.

Bij toegankelijkheid is er vaak geen goed onderscheid te maken tussen oplossing en probleem. Zonder (heel simpele) aanpassingen heb je vaak 'n probleem, en omgekeerd. Daarom staan wat betreft toegankelijkheid aanpassingen en problemen hier bij elkaar in dit hoofdstukje.

Voor zover van toepassing wordt eerst het ontbreken van JavaScript, CSS en/of afbeeldingen besproken. Vervolgens problemen en aanpassingen met betrekking tot toegankelijkheid voor specifieke groepen bezoekers, toetsenbordnavigatie, tekstbrowsers, schermlezers en zoomen en andere lettergrootte.

Als in een onderdeel geen problemen aanwezig zijn, staat in een smal groen kadertje 'Geen problemen'. Bij een onderwerp over toegankelijkheid zijn er soms geen problemen, maar alleen aanpassingen. Ook in dat geval staat bovenaan in een smal groen kadertje 'Geen problemen'. Daaronder staan dan de aanpassingen.

Als in een onderdeel één of meer problemen worden besproken, staat van elk probleem in een breed rood kadertje een korte samenvatting daarvan.

Als bij het probleem een oplossing wordt gegeven, staat de samenvatting in een rode stippellijn. Bij een onderwerp over toegankelijkheid zijn er soms, naast de opgeloste problemen, ook aanpassingen. In dat geval staan staan die aanpassingen boven de kadertjes met opgeloste problemen.

Als bij het probleem geen oplossing is gevonden, staat de samenvatting in een rode ononderbroken lijn. Bij een onderwerp over toegankelijkheid zijn er soms, naast de problemen, ook aanpassingen. In dat geval staan staan die aanpassingen boven de kadertjes met problemen.

Zonder JavaScript

Probleem: als een nieuwe <div> wordt getoond, wordt een eerder getoonde <div> niet gesloten.

Alle divs gelijktijdig zichtbaar.

De <div>’s worden elk afzonderlijk getoond en verborgen met behulp van een eigen aankruisvakje. Daardoor kan een <div> alleen weer worden verborgen, als het aankruisvakje wordt uitgevinkt. Als een nieuwe <div> wordt getoond, blijft het aankruisvakje van andere getoonde <div>’s gewoon aangevinkt. Waardoor de bijbehorende <div>’s gewoon zichtbaar blijven. Op deze manier kun je zelfs alle twaalf <div>’s gelijk tonen. Op de afbeelding is dat precies wat er gebeurt: in een smartphone zijn alle twaalf <div>’s zichtbaar.

Dit ziet er niet echt overzichtelijk uit. De links werken nog wel, maar latere <div>’s komen over eerdere <div>’s heen te staan, dus de links zijn voor een (groot) deel niet of nauwelijks zichtbaar.

Als een aankruisvakje wordt aangevinkt en daardoor een <div> zichtbaar wordt, worden met behulp van JavaScript andere aankruisvakjes uitgevinkt. Daardoor is er nooit meer dan één <div> gelijktijdig zichtbaar.

Probleem: het is onduidelijk dat een <div> kan worden getoond en verborgen met de Spatiebalk.

Een aankruisvakje kan standaard worden aan- of uitgevinkt door het indrukken van de Spatiebalk (en natuurlijk door een klik of aanraking). Hier is volstrekt onduidelijk dat het eigenlijk om aankruisvakjes gaat en dat je dus de Spatiebalk moet gebruiken om iets te tonen of te verbergen. Bovendien gaat het hier om een menu, en normaal genomen is dat te bedienen met Enter.

Met behulp van JavaScript kan een aankruisvakje ook worden aan- of uitgevinkt door Enter in te drukken. Waardoor de <div>’s ook kunnen worden getoond en verborgen door het indrukken van Enter. Nu werken de puzzelstukjes zoals een gewoon menu (hoewel het uiterlijk natuurlijk niet echt gewoon is voor een menu).

Probleem: een getoonde <div> kan niet worden gesloten door het indrukken van Escape.

Een getoonde <div> lijkt heel erg op een pop-up. Heel vaak kan een pop-up worden gesloten door het indrukken van Escape. Hier is dat niet het geval, omdat de <div>’s worden getoond en verborgen door het aan- of uitvinken van een aankruisvakje. En het indrukken van Escape werkt daar niet bij.

Met behulp van JavaScript worden alle aankruisvakjes uitgevinkt door het indrukken van Escape. Hierdoor wordt een eventueel getoonde <div> door het indrukken van Escape verborgen.

Probleem: een getoonde <div> kan niet worden gesloten door een aanraking of klik buiten de puzzel.

Een pop-up, waar een getoonde <div> erg op lijkt, kan vaak weer worden verborgen door het aanraken of -klikken van het scherm ergens buiten die pop-up. Hier werkt dat niet, want een aankruisvakje reageert niet op een aanraking of klik elders op het scherm.

Met behulp van JavaScript worden alle aankruisvakjes uitgevinkt, als je het scherm ergens buiten de puzzel aanraakt of -klikt. Je moet wel buiten de puzzel aanraken of klikken, want binnen de puzzel wordt een andere <div> getoond, omdat onder elk puzzelstukje een aankruisvakje zit.

Probleem: voor schermlezers is onduidelijk, of een <div> wordt getoond of niet.

Daarom wordt dit voor schermlezers aangegeven met aria-expanded. Bij openen van de pagina zijn alle <div>’s verborgen en staat in elke <input> aria-expanded="false". Zodra een aankruisvakje is aangevinkt en daardoor de bijbehorende <div> wordt getoond, wordt dit met behulp van JavaScript veranderd in aria-expanded="true". Schermlezers geven hierdoor aan, of er iets zichtbaar is of niet.

Zonder CSS

Geen problemen.

Zonder CSS werkt alles: tekst, afbeeldingen en links zijn aanwezig. Uiteraard ziet het er niet meer uit als een puzzel.

Zonder afbeeldingen

Geen problemen.

Zonder afbeeldingen toont puzzelstukje twee alleen heel kleine tekst.

Ook zonder afbeeldingen zijn tekst en links nog aanwezig. Omdat de aankruisvakjes gewone HTML zijn, werken ook die nog.

Maar het wordt zonder afbeeldingen wel behoorlijk onduidelijk. De puzzel is een zwart vlak. En de tekst bij puzzelstukje twee bestaat uit afbeeldingen. Zonder afbeeldingen wordt de alt-tekst getoond, en die is heel klein, zoals op de afbeelding is te zien. Maar de link staat er nog netjes en werkt nog.

Zonder CSS én zonder afbeeldingen

Geen problemen.

Zelfs zonder afbeeldingen én zonder CSS werkt alles nog. Alleen moet je wel ’n beetje masochistisch zijn om dit te doen, want je krijgt de gecombineerde ellende van geen CSS en geen afbeeldinge.

Toetsenbordnavigatie

Geen problemen.

Hoewel het er anders uitziet, zitten onder de puzzel twaalf doodnormale aankruisvakjes. Bij gebruik van de Tab-toets krijgen deze aankruisvakjes in de normale volgorde de focus. Welk aankruisvakje de focus heeft, wordt aangeven met een blauwe rand. Omdat de aankruisvakjes even groot zijn als het bijbehorende puzzelstukje, lijkt het alsof het puzzelstukje de focus heeft.

Als een <div> niet wordt getoond, wordt de link in de <div> genegeerd. Pas als een <div> wordt getoond door het aanvinken van een aankruisvakje, wordt met de Tab-toets die link bezocht.

Tekstbrowsers

Geen problemen.

In Lynx is alle tekst aanwezig en werken de links. De aankruisvakjes werken ook, maar dat heeft geen nut, omdat tekst en links altijd zichtbaar zijn.

In WebbIE zijn tekst en links niet zichtbaar, maar bij aanvinken van een aankruisvakje wordt de bijbehorende <div> getoond en werkt de daarin zittende link.

Schermlezers

Geen problemen.

Ook met schermlezers is dit menu te bedienen. Wel zijn er met behulp van aria-expanded, aria-hidden en aria-label en wat JavaScript enkele aanpassingen gemaakt. Zonder deze aanpassingen zijn sommige teksten onduidelijk en is ook niet duidelijk of een <div> wordt getoond of niet.

Zoomen en andere lettergrootte

Probleem: een (sterk) vergrote letter levert soms in kleinere browservensters problemen op.

Zoomen levert nergens problemen op. Als de puzzel te groot wordt voor het browservenster, kan worden gescrold.

Smartphone met Android 7

In Firefox vallen (grote) delen van de tekst weg bij een lettergrootte van meer dan 150 procent. Zoomen levert geen problemen op.

Smartphone met Android 10

In Edge, Firefox en Google Chrome vallen (grote) delen van de tekst weg bij een lettergrootte van meer dan 160 procent. Zoomen levert geen problemen op.

Smartphone met Android 14

In Edge kunnen letters in portretstand probleemloos tot tweehonderd procent worden vergroot. In landschapsstand vallen bij een lettergrootte van meer dan 180 procent (grote) delen van de tekst weg. Zoomen levert geen problemen op.

In Firefox vallen (grote) delen van de tekst weg bij een lettergrootte van meer dan 170 procent. Zoomen levert geen problemen op.

In Google Chrome in portretstand kan de tekst tot 180 procent worden vergroot, in landschapsstand tot 160 procent. Daarboven vallen (grote) delen van de tekst weg. Zoomen levert geen problemen op.

In Samsung Internet kan de lettergrootte in portretstand tot tweehonderd procent worden vergroot. In landschapsstand vallen bij een lettergrootte van meer dan 180 procent (grote) delen van de tekst weg. Zoomen levert geen problemen op.

Overige problemen

Geen problemen

Een aantal validators geeft een foutmelding, omdat de <label>’s bij de <input>’s geen tekst hebben. Normaal genomen is dat een fout, maar hier is het juist de bedoeling: het gaat om puzzelstukjes.

Voor schermlezers is dit wel een probleem, maar dit wordt opgelost door gebruik van aria-label met tekst.

Wijzigingen

Alleen grotere wijzigingen worden hier vermeld, geen dingen als een link die is geüpdatet.

:

Nieuw opgenomen.

11 augustus 2008:

De nieuwe zoomfunctie van Firefox 3 sloopte dit menu nogal. Door de veranderingen die hiervoor nodig waren bleek het vergroten/verkleinen van letters ook ineens 'n stuk beter te werken. Tja.

Het volgende is veranderd:

31 maart 2009:

Tekst aangepast aan de nieuw verschenen Internet Explorer 8. De code is niet veranderd.

26 juli 2011:

25 december 2013:

1 augustus 2015:

28 juli 2026:

Volledig herschreven. De belangrijkste veranderingen:

Inhoud van de download en licenties

De inhoud van deze download kan vrij worden gebruikt, met drie beperkingen:

* Sommige onderdelen die van 'n andere site of zo afkomstig zijn, vallen mogelijk onder een of andere licentie. Dat is hieronder bij het betreffende onderdeel te vinden.

* Je gebruikt het materiaal uit deze download volledig op eigen risico. Het kan prima zijn dat er fouten in de hier verstrekte code en dergelijke zitten. Voor eventuele schade die door gebruik van materiaal uit deze download ontstaat, in welke vorm dan ook, zijn www.css-voorbeelden.nl en medewerkers daarvan op geen enkele manier verantwoordelijk.

* Dit voorbeeld (en de bijbehorende uitleg en dergelijke) wordt min of meer regelmatig bijgewerkt. Het is daarom niet toegestaan dit voorbeeld (en de bijbehorende uitleg en dergelijke) op welke manier dan ook te verspreiden, zonder daarbij duidelijk te vermelden dat voorbeeld, uitleg, en dergelijke afkomstig zijn van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie is te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.

Een link naar www.css-voorbeelden.nl wordt trouwens altijd op prijs gesteld.

menu-014-dl.html: de pagina met het voorbeeld.

menu-014.pdf: deze uitleg (aangepast aan de inhoud van de download).

menu-014-inhoud-download-en-licenties.txt: een kopie van de tekst onder dit kopje (Inhoud van de download en licenties).

014-css-dl:

menu-014-dl.css: stijlbestand voor menu-014-dl.html.

menu-014-hulp-dl.css: stijlbestand voor de pagina’s achter het menu.

014-pics:

menu-014-a.gif, menu-014-b.gif, ..., tot en met menu-014-z.gif: de dansende letters. De dansende letters komen van calendarofupdates.com. Deze site bestaat niet meer, daarom zijn in de download de niet in het voorbeeld gebruikte letters ook bijgesloten, zodat het alfabet volledig is.

menu-014-ijspret.jpg: afbeelding waaronder het menu zit. De afbeelding komt van karenswhimsy.com/public_domain_images. Het puzzelpatroon is aangebracht met Gimp. De gebruiksvoorwaarden van de afbeelding zijn te vinden op karenswhimsy.com/image-ordering.shtm#terms.

menu-014-krokodil.gif en menu-014-olifant.png: de afbeeldingen met de bewegende krokodil en de (niet-bewegende) olifant. Deze komen van de niet meer bestaande site www.davidpye.com. Het wolkje bij de krokodil is aangebracht met Gimp.

menu-014-mail.gif: de afbeelding van de mailbox komt van de niet meer bestaande site www.hellasmultimedia.com.

menu-014-uitgang.jpg: het plaatje met de uitgang. Waar dit plaatje ooit vandaan is gehaald, is inmiddels niet meer bekend. In ieder geval niet van een of ander eng bedrijf met pitbulls en deurwaarders. Maar als je dit dus verder wilt gebruiken, is dat op eigen risico.

014-js:

menu-014.js: javascript voor het voorbeeld.

014-files-dl:

menu-missende-letters-dl.zip: niet-gebruikte dansletters (zodat het alfabet compleet is). Deze letters zijn afkomstig van het niet meer bestaande calendarofupdates.com.

menu-014-dansletters-dl.html: hulppagina achter de link bij de dansletters.

menu-014-krokodil-dl.html: hulppagina achter de link bij de krokodil.

menu-014-mail-dl.html: hulppagina achter de link bij de mail.

menu-014-uitgang-dl.html: hulppagina achter de link bij de uitgang.

HTML

De code is geschreven in een afwijkende lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code), is in de hele uitleg onderstippeld blauw. Alle niet-essentiële code is bruin. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)

In de HTML hieronder wordt alleen de HTML besproken, waarover iets meer is te vertellen. Een <h1> bijvoorbeeld wordt in de regel niet genoemd, omdat daarover weinig interessants valt te melden. (Als bijvoorbeeld het uiterlijk van de <h1> wordt aangepast met behulp van CSS, staat dat verderop bij de bespreking van de CSS.)

Zaken als een doctype en charset hebben soms wat voor veel mensen onbekende effecten, dus daarover wordt hieronder wel een en ander geschreven.

<!doctype html>

Een document moet met een doctype beginnen om weergaveverschillen tussen browsers te voorkomen. Zonder doctype is de kans op verschillende (en soms volkomen verkeerde) weergave tussen verschillende browsers heel erg groot.

Geldige doctypes vind je op www.w3.org/QA/2002/04/valid-dtd-list.

Gebruik het volledige doctype, inclusief de eventuele url, anders werkt het niet goed.

Het hier gebruikte doctype is dat van HTML5. Dit kan zonder enig probleem worden gebruikt: het werkt zelfs in Internet Explorer 6.

<html lang="nl">

Het attribuut lang="nl" bij <html> geeft aan dat de pagina in het Nederlands is. De taal is van belang voor schermlezers, automatisch afbreken, automatisch genereren van aanhalingstekens, juist gebruik van decimale punt of komma, en dergelijke.

<meta charset="utf-8">

Zorgt dat de browser letters met accenten en dergelijke goed kan weergeven.

utf-8 is de beste charset (tekenset), omdat deze alle talen van de wereld (en nog heel veel andere extra tekens) bestrijkt, maar toch niet meer ruimte inneemt voor de code, dan nodig is. Als je utf-8 gebruikt, hoef je veel minder entiteiten (&auml; en dergelijke) te gebruiken, maar kun je bijvoorbeeld gewoon ä gebruiken.

Deze regel moet zo hoog mogelijk komen te staan, als eerste regel binnen de <head>, omdat de regel anders door sommige browsers niet wordt gelezen.

In HTML5 hoeft deze regel niet langer te zijn, dan wat hier staat.

<meta name="viewport" content="width=device-width, initial-scale=1">

Mobiele apparaten variëren enorm in grootte. En dat is een probleem. Sites waren, in ieder geval tot enkele jaren geleden, gemaakt voor desktopbrowsers. En die hebben, in vergelijking met bijvoorbeeld een smartphone, heel brede browservensters. Hoe moet je op 'n smartphone een pagina weergeven, die is gemaakt voor de breedte van een desktop? Je kunt natuurlijk wachten tot álle sites zijn omgebouwd voor smartphones, tablets, enzovoort, maar dan moet je waarschijnlijk heel erg lang wachten.

Mobiele browsers gokken erop dat een pagina een bepaalde breedte heeft. Safari voor mobiel bijvoorbeeld gaat ervan uit dat een pagina 980 px breed is. De pagina wordt vervolgens zoveel versmald dat deze binnen het venster van het apparaat past. Op een iPhone wordt de pagina dus veel smaller dan op een iPad. Vervolgens kan de gebruiker inzoomen op het deel van de pagina dat deze wil zien.

Dit betekent ook dat bij het openen van de pagina de tekst meestal heel erg klein wordt weergegeven. (Meestal, want niet alle browsers en apparaten doen het op dezelfde manier.) Niet erg fraai, maar bedenk maar 'ns 'n betere oplossing voor bestaande sites.

Nieuwe sites of pagina's kunnen echter wel rekening houden met de veel kleinere vensters van mobiele apparaten. In dit voorbeeld bijvoorbeeld worden de <div>’s met de tekst, afbeeldingen en links in smallere vensters boven het menu getoond, en niet ernaast.

Maar die stomme mobiele browser weet dat niet, dus die gaat ervan uit dat ook deze pagina 980 px breed is, en verkleint die dan. Dat is ongeveer even behulpzaam als de gedienstige kelner die behulpzaam de stoel naar achteren trekt, net als jij wilt gaan zitten.

Om de door de browser aangeboden hulp vriendelijk maar beslist te weigeren, wordt deze tag gebruikt. Hiermee geef je aan dat de pagina is geoptimaliseerd voor mobiele apparaten.

De kreet width=device-width zegt tegen de mobiele browser dat de breedte van de weer te geven pagina gelijk is aan de breedte van het apparaat. Als een iPad in portretstand bijvoorbeeld 768 px breed is, wordt de pagina ook 768 px breed.

Er staat nog een tweede deel in de tag: initial-scale=1. Sommige mobiele apparaten zoomen een pagina gelijk in of uit. Ook weer in een poging behulpzaam te zijn. Ook dat is hier niet nodig. Er is ook een instructie om zoomen helemaal onmogelijk te maken, maar die wordt niet gebruikt. De bezoeker kan zelf nog gewoon zoomen, wat belangrijk is voor mensen die wat slechter zien.

<input id="een" type="checkbox" aria-label="Puzzelstukje een" aria-expanded="false">

<label for="een"></label>

Er zijn twaalf <input>’s, voor elk puzzelstukje een. Deze twaalf <input>’s zijn precies hetzelfde opgebouwd. Alleen het volgnummer van de id en de tekst bij aria-label is anders. Daarom wordt hier alleen de eerste <input> besproken.

Elke <input> heeft een bijbehorende <label>. De <label> en de <input> worden gekoppeld met behulp van het for-attribuut bij de <label> en de id bij de <input>:

<input id="een"> <label for="een"></label>

Normaal genomen staat in de label een tekst, die op het scherm zichtbaar is. In formulieren bijvoorbeeld kan in de <label> iets staan als ’Voornaam:’. Hier staat in de <label> geen tekst, omdat het hier om puzzelstukjes gaat. De <label>’s zijn even groot als het bijbehorende puzzelstukje, dus eigenlijk is het puzzelstukje hier de ’tekst’ in de <label>.

Schermlezers hebben hier niets aan, want die zien dat puzzelstukje helemaal niet. Daarom is in de <input> de ARIA-code aria-label met de tekst ’Puzzelstukje’ en het volgnummer van het puzzelstukje aangebracht. Deze tekst wordt door een schermlezer voorgelezen.

Schermlezers melden ook of een aankruisvakje is aangevinkt of niet. Maar daarmee is nog niet duidelijk, of de <div> met de tekst, de afbeeldingen en de link wordt getoond of niet. Daarvoor wordt de ARIA-code aria-expanded gebruikt. Bij openen van de pagina heeft aria-expanded overal de waarde ’false’. Zodra een aankruisvakje wordt aangevinkt, wordt de bijbehorende <div> getoond, en wordt de waarde bij aria-expanded met behulp van JavaScript veranderd in ’true’. Als een aankruisvakje weer wordt uitgevinkt, verandert de waarde weer in ’false’.

<span id="een-tekst-1">Wil jij de tanden</span>

<span id="een-tekst-2">van Krokkie</span> <span id="een-tekst-3">poetsen,</span> <span id="een-tekst-4">terwijl je</span> <span id="een-tekst-5">intussen</span> <span id="een-tekst-6">gezellig over</span> <span id="een-tekst-7">z'n hoofdje kroelt? Ga dan</span> <span id="een-tekst-8">snel naar Krokkie toe!</span> <span id="een-tekst-9">(Alleen voor kinderen met drie armen!)</span>

Binnen deze negen <span>’s staat de tekst die bij het eerste puzzelstukje wordt getoond, als het aankruisvakje bij dit puzzelstukje wordt aangevinkt. De tekst kan niet in één <span> worden neergezet, omdat de negen regels op een onregelmatig gevormde achtergrond staan. Daardoor moeten positie en lengte van elke regel worden aangepast. Door elke regel in een eigen <span> neer te zetten, wordt dat mogelijk.

<a href="014-files-dl/menu-014-krokodil-dl.html">Ja!  Krokodillen aaien! Leuk!</a>

De link naar de pagina achter het eerste puzzelstukje. De tekst hierin is ’Ja! Krokodillen aaien! Leuk!’. In sommige browsers wordt deze tekst in kleinere browservensters bij een grotere lettergrootte op meerdere regels neergezet. Om dat te voorkomen staat tussen sommige woorden geen gewone spatie, maar een ’non-breaking space&nbsp;. Nu blijven de woorden altijd op dezelfde regel staan.

<p>Dansende letters met als tekst "Niet echt geschikt voor volwassenen, maar kinderen vinden zoiets juist leuk"</p>

<span id="twee-tekst" aria-hidden="true">

De tekst bij het tweede puzzelstukje wordt getoond in de vorm van bewegende afbeeldingen: één plaatje voor elke letter. Dat zijn in totaal 64 afbeeldingen. Schermlezers zien die plaatjes niet, dus die kunnen de letters niet voorlezen. Bij elke afbeelding staat een alt-tekst met daarin de letter. Schermlezers kunnen die alt-tekst wel voorlezen. Dat is hier echter geen goed idee, want schermlezers lezen die 64 alt-teksten elk apart voor: de tekst wordt letter voor letter gespeld.

Daarom wordt de <span> met de 64 afbeeldingen voor schermlezers met behulp van de ARIA-code aria-hidden="true" verborgen. In plaats daarvan staat voor schermlezers gelijk boven die <span> een <p> met daarin tekst voor schermlezers. De <p> wordt buiten het scherm geparkeerd, waardoor deze onzichtbaar is. Voor schermlezers maakt dat niets uit: de tekst wordt gewoon voorgelezen.

<span>

<img src="014-pics/menu-014-n.gif" width="29" height="31" alt="n"> <img src="014-pics/menu-014-i.gif" width="19" height="30" alt="i"> <img src="014-pics/menu-014-e.gif" width="23" height="44" alt="e"> <img src="014-pics/menu-014-t.gif" width="29" height="30" alt="t"> </span> 

Elk woord van de tekst bij het tweede puzzelstukje staat in een aparte <span>.Hierdoor kunnen de woorden op de juiste plaats worden gepositioneerd. In bovenstaande <span> zit het eerste woord: ’niet’.

Achter sommige <span>’s staat &nbsp;, een ’vaste spatie’ of ’non-breaking space’. Hierdoor blijven de twee woorden voor en achter de spatie altijd op dezelfde regel staan. Zonder deze spatie zouden bij een grotere letter de woorden op twee regels komen te staan, waardoor in een kleiner browservenster aan de onderkant woorden onder het venster kunnen verdwijnen.

Achter sommige andere <span>’s staat een <br>, waardoor de twee woorden voor en achter de <br> juist altijd op twee regels komen te staan.

<a href="014-files-dl/menu-014-uitgang-dl.html" aria-label="Verlaat dit menu">Wegwezen!</a>

De tekst in de link bij het zesde puzzelstukje is ’Wegwezen!’. Dit is bedoeld om het menu te verlaten. Voor schermlezers is die tekst nogal onduidelijk. Er zit weliswaar ook een afbeelding bij, maar daar heeft een schermlezer niets aan. Bovendien is het gebruikelijk om de pagina te verlaten in de onderste ingang van een menu.

Daarom wordt voor schermlezers met behulp van de ARIA-code aria-label de tekst ’Verlaat dit menu’ opgegeven. Deze duidelijker tekst wordt nu voorgelezen in plaats van het vage ’Wegwezen!’.

<a href="014-files-dl/menu-014-mail-dl.html" aria-label="Stuur ons een mail">E-mail</a>

<span id="negen-tekst" aria-hidden="true">

De tekst bij het negende puzzelstukje wordt steeds herhaald en steeds kleiner. Als je die tekst ziet, kun je gelijk stoppen als je merkt dat de tekst steeds wordt herhaald. Een schermlezer leest echter trouwhartig alle herhalingen voor, tot de tekst echt helemaal is voorgelezen. Om dat te voorkomen wordt de tekst met de ARIA-code aria-hidden verborgen.

Nu staat in de link alleen nog maar de tekst ’E-mail’. Dat is nogal vaag. Daarom wordt met behulp van de ARIA-code aria-label voor schermlezers de tekst ’Stuur ons een mail’ toegevoegd. Nu wordt deze tekst voorgelezen in plaats van het vage ’E-mail’.

<script src="014-js/menu-014.js"></script>

Vrijwel onderaan de pagina staat een koppeling naar een extern JavaScript.

Dit script gebruikt een aantal onderdelen van de pagina: <body>, <main> en de twaalf <input>’s. Om deze onderdelen te kunnen gebruiken, moeten ze eerst door de browser worden gemaakt. Daarom staat het script helemaal onderaan de HTML: je weet dan zeker, dat alle elementen al zijn aangemaakt.

Bovendien stopt de opbouw van de pagina, zodra de browser een script ontmoet, omdat het script eerst wordt uitgevoerd. Het zou immers kunnen dat het script (heel) belangrijk is voor de weergave. Dat is hier niet het geval. Door het script onderaan de pagina te zetten, voorkom je ook die in dit geval overbodige vertraging.

Als het script op slechts één pagina wordt gebruikt, zoals hier het geval is, kun je het hele script ook tussen <script> en </script> zetten, onderaan de pagina zelf. Als het op meer pagina’s wordt gebruikt, is het beter om het in een eigen bestand te zetten. Het hoeft dan maar één keer te worden gedownload.

In dit geval had het script dus gewoon binnen de pagina kunnen worden gezet. Maar het is vanwege de overzichtelijkheid toch in een apart bestand gezet: nu kun je het apart bekijken.

CSS

De code is geschreven in een afwijkende lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code) is in de hele uitleg onderstippeld blauw. Alle niet-essentiële code is bruin. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)

Omdat deze site nou eenmaal (voornamelijk) op CSS is gericht, wordt hieronder álle CSS besproken.

De CSS voor de hulppagina’s achter de links is heel simpel, daarom wordt die hier niet besproken.

Technisch gezien is er geen enkel bezwaar om de CSS in het stijlbestand allemaal achter elkaar op één regel te zetten:

div#header-buiten {position: absolute; right: 16px; width: 100%; height: 120px; background: yellow;} div p {margin-left 16px; height: 120px; text-align: center;}

Maar als je dat doet, krijg je gegarandeerd hele grote problemen, omdat het volstrekt onoverzichtelijk is. Beter is het om de CSS netjes in te laten springen:

div#header-buiten { position: absolute; right: 16px; width: 100%; height: 120px; background: yellow; }
div p { margin-left: 16px; height: 120px; text-align: center; }

Hiernaast is het heel belangrijk voldoende commentaar (uitleg) in het stijlbestand te schrijven. Op dit moment weet je waarschijnlijk (hopelijk...), waarom je iets doet. Maar over vijf jaar kan dat volstrekt onduidelijk zijn. Op deze site vind je nauwelijks commentaar in het stijlbestand, maar dat heeft een simpele reden: deze uitleg is in feite één groot commentaar.

Op internet zelf is het goed, als het stijlbestand juist zo klein mogelijk is. Dus voor het uploaden kun je normaal genomen het beste het commentaar weer verwijderen. Veel mensen halen zelfs alles wat overbodig is weg, voordat ze het stijlbestand uploaden. Inspringingen bijvoorbeeld zijn voor mensen handig, een computer heeft ze niet nodig.

Je hebt dan eigenlijk twee stijlbestanden. De uitgebreide versie waarin je dingen uitprobeert, verandert, enzovoort, met commentaar, inspringingen, en dergelijke. Dat is de mensvriendelijke versie. Daarnaast is er dan een stijlbestand dat je op de echte site gebruikt: een gecomprimeerde versie.

Dat comprimeren kun je met de hand doen, maar er bestaan ook hulpmiddelen voor. Op de pagina met links kun je onder het kopje Gereedschap → Snelheid testen en verbeteren, gzip, CLS, comprimeren (inclusief theorie), en dergelijke links naar sites vinden, waar je bestanden kunt comprimeren.

(Stijlbestanden op deze site zijn niet gecomprimeerd. Omdat het vaak juist om de CSS gaat, kunnen mensen dan zonder al te veel moeite de CSS bekijken.)

CSS voor alle vensters

/* menu-014-dl.css */

Om vergissingen te voorkomen is het een goede gewoonte bovenaan het stijlbestand even de naam neer te zetten. Voor je het weet, zit je anders in het verkeerde bestand te werken.

body

Het element waarbinnen de hele pagina staat. Veel instellingen die hier worden opgegeven, worden geërfd door de nakomelingen van <body>. Ze gelden voor de hele pagina, tenzij ze later worden gewijzigd. Dit geldt bijvoorbeeld voor de lettersoort, de lettergrootte en de voorgrondkleur.

font-family: Arial, Helvetica, sans-serif;

Als Arial is geïnstalleerd op de machine van de bezoeker, wordt deze gebruikt, anders Helvetica. Als die ook niet wordt gevonden, wordt in ieder geval een schreefloze letter (zonder dwarsstreepjes) gebruikt.

margin: 0;

Standaardmarge verwijderen.

Normaal genomen is dat niet zo heel belangrijk, maar hier wel. In heel kleine browservensters is er al heel weinig ruimte voor alle tekst en dergelijke. Als <body> dan een marge krijgt, is er nog minder ruimte.

main

Alle <main>’s. Dat is er altijd maar één. Binnen <main> staat de belangrijkste inhoud van de pagina. Hier is dat de hele pagina.

background: black;

Achtergrondkleur zwart.

display: flex;

De waarde flex is onderdeel van een hele reeks eigenschappen en waarden, die bij elkaar ’flexbox’ worden genoemd. Met display: flex; wordt <main>> in een zogenaamde ’flex container’ veranderd. Dit maakt het veel makkelijker om de directe kinderen van dit element, de ’flex items’, op een bepaalde plaats neer te zetten. Hier heeft <main> twee directe kinderen: <nav> en <h1>. Maar <h1> wordt iets verderop absoluut gepositioneerd, waardoor die niet meer niet meetelt als flex item en alleen <nav> overblijft als flex item.

Met flex-direction kan de richting van de ’hoofdas’ (in het Engels ’main axis’) worden opgegeven: regel of kolom. De andere richting heet ’dwarsas’ (in het Engels ’cross axis’). De standaardinstelling is flex-direction: row;, en die wordt hier niet aangepast.

De waarde van flex-direction is van belang voor een aantal andere eigenschappen van flexbox, zoals het iets hieronder gebruikte justify-content.

height: 100vh;

Het menu moet halverwege het browservenster komen te staan, zowel horizontaal als verticaal.

Zonder hoogte wordt niet het hele browservenster gevuld.

<main> is een blok-element en wordt daarom standaard niet groter dan nodig is om de inhoud ervan weer te geven. In kleinere browservensters is dat geen probleem, omdat het menu het hele venster vult.

Maar in grotere browservensters komt de inhoud van <main> (de afbeelding met het menu) bovenin het venster te staan. Omdat <main> niet hoger wordt dan het menu, wordt ook de zwarte

achtergrond van <main> niet hoger dan de hoogte van het menu, zoals op de afbeelding is te zien. Om dit te voorkomen wordt <main> even hoog gemaakt als de hoogte van het venster: 100 vh.

De eenheid vh is gebaseerd op de hoogte van het venster van de browser. 1 vh is 1% van de hoogte van het venster, en 100 vh is 100% van de hoogte. Nu vult <main> de volle hoogte van het venster, waardoor de achtergrond over de volle hoogte van het venster zwart wordt.

Omdat <main> nu de volle hoogte van het browservenster vult, kan nu ook iets verderop bij align-self: center; het menu halverwege deze hoogte worden neergezet, wat in hogere vensters van belang is.

De breedte van <main> hoeft niet te worden aangepast, want standaard wordt een blok-element even breed als de ouder. Hier is die ouder <body>, ook een blok-element, dat dus standaard ook weer even breed wordt als ouder <html>. <html> is het buitenste element, en wordt daarom standaard even breed als het venster van de browser.

justify-content: center;

Dit is een van de eigenschappen die bij flexbox horen. Centreer de in <main> zittende flex items in de richting van de hoofdas. Hier is die richting horizontaal: centreer de flex items horizontaal binnen <main>. Hier zit maar één flex item binnen <main>: de <nav> met het menu. Hierdoor staat <nav> altijd horizontaal gecentreerd binnen <main>, ongeacht de breedte van <main>. En omdat <main> even breed is als het browservenster, staat <nav> met het menu altijd horizontaal gecentreerd binnen het venster.

overflow: hidden;

De eigenschap overflow regelt wat er gebeurt, als de inhoud van een blok-element groter is dan dat element zelf. Standaard is de waarde ’visible’: ook al past de inhoud niet in het blok-element, geef het toch weer. Mogelijk wordt dan de lay-out verstoord, maar er verdwijnt in ieder geval geen tekst of zo.

Hier is met behulp van CSS geregeld dat alle tekst, afbeeldingen en links altijd binnen het venster van de browser komen te staan. (In kleine vensters kan bij een grotere letter wel wat tekst verdwijnen, dit wordt beschreven bij Zoomen en andere lettergrootte.)

De tekst bij puzzelstukje negen is heel erg lang. Die tekst bestaat uit een aantal keren dezelfde tekst, die naar onder toe steeds kleiner wordt en onderaan het browservenster verdwijnt. Als overflow de standaardwaarde visible zou blijven houden, zou de volledige tekst met alle herhalingen zichtbaar worden. Als dat niet binnen het venster past (en het past nooit, dat is juist de bedoeling), wordt daardoor een verticale scrollbalk zichtbaar. Hier is dat niet nodig en wordt dat met overflow: hidden; voorkomen: het teveel aan tekst aan de onderkant van het venster wordt niet getoond.

h1

Alle <h1>’s. Dat is er maar één: de belangrijkste titel van de pagina.

position: absolute;

Om de <h1> op een bepaalde plaats neer te kunnen zetten. Hoewel de <h1> een direct kind is van de met display: flex; in een flex container veranderde <main>, voorkomt de absolute positie de <h1> werkt als een flex item.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als zo’n voorouder er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.

left: -20000px;

Ver links buiten het browservenster neerzetten. De <h1> is nu onzichtbaar, maar schermlezers lezen de <h1> gewoon voor.

nav

Alle <nav>’s. Dat is er hier maar één: de <nav> met het menu.

height: 480px;

Hoogte.

max-height: 100%;

Een hoogte in procenten is normaal genomen ten opzichte van de ouder. Die ouder is hier <main>. Iets hierboven bij main heeft <main> een hoogte van 100 vh gekregen: even hoog als het venster van de browser. Daardoor wordt de <nav> met het menu nooit hoger dan het venster, ook niet als dit lager is dan de hier gelijk boven opgegeven hoogte van 480 px.

align-self: center;

De ouder van <nav> is <main>, die bij <main> met display: flex; in een flex container is veranderd. De directe kinderen van <main> worden hiermee in de richting van de dwarsas van <main> gecentreerd. Dat is hier de horizontale richting. Hier is maar één direct kind van <main>: de <nav>. (De <h1> is ook een direct kind van <main>, maar die is iets hierboven absoluut gepositioneerd en is daardoor geen flex item). Daardoor staat de <nav> altijd horizontaal gecentreerd binnen <main>, ongeacht de breedte van het venster van de browser.

position: relative;

Om nakomelingen van <nav> te kunnen positioneren ten opzichte van <nav>, moet <nav> zelf een zogenaamd ’containing block’ voor die nakomelingen vormen. Dit kan door aan <nav> bepaalde eigenschappen te geven. Een van die eigenschappen is een relatieve positie. Een relatieve positie heeft verder geen invloed op de <nav> zelf, omdat niets voor top en dergelijke. wordt opgegeven.

nav > img

Elke <img> die een direct kind is van een <nav>. Er is maar één <nav> aanwezig, de <nav> met het menu.

<nav> <img> </nav>

In de code hierboven is de <img> een direct kind van de <nav>: er zit geen enkel ander element tussen ouder <nav> en kind <img>.

<nav> <div> <img> </div> </nav>

In de code hierboven is de <img> geen direct kind van de <nav>, omdat er een <div> tussen zit. In dit voorbeeld is er maar één <img> een direct kind van de <nav>: de afbeelding met de puzzel.

max-width: 100%;

De afbeelding met de puzzel is 384 px breed, dat is in de HTML opgegeven:

<img src="014-pics/menu-014-ijspret.jpg" alt="Puzzel met twaalf stukjes: jongen op ijs die tegen bok aan gaat botsen." width="384" height="480">

In browservensters smaller dan 384 px zou hierdoor een deel van de afbeelding buiten het venster komen te staan. Om dat te voorkomen, wordt een maximumbreedte van 100% opgegeven. Zodra in de CSS bij een afbeelding een breedte wordt opgegeven, ’wint’ deze van een in de HTML opgegeven breedte.

De ouder van de <img> is <nav>, dus de <img> wordt nu nooit breder dan de <nav>. Omdat <nav> een blok-element is, wordt dit standaard even breed als de ouder ervan: <main>. Ook <main> is een blok-element en wordt dus standaard even breed als ouder <body>, ook een blok-element, dat dus standaard ook weer even breed wordt als ouder <html>. <html> is het buitenste element, en wordt daarom standaard even breed als het venster van de browser.

max-height: 100%;

Hiervoor geldt ongeveer hetzelfde verhaal als gelijk hierboven bij max-width: 100%;. De hoogte van de afbeelding wordt nooit meer dan 100% van de hoogte van ouder <nav>. <nav> heeft bij max-height: 100%; zelf een maximale hoogte gekregen van 100%, die ook weer geldt ten opzichte van ouder <main>. <main> is bij height: 100vh; even hoog gemaakt als het venster van de browser, ongeacht de hoogte van dat venster en wordt dus nooit hoger dan het venster. Hierdoor wordt de <img> ook nooit hoger dan het venster.

ul

Alle <ul>’s. Dat is er maar één: het hele menu zit erin, met voor elk puzzelstukje een <li>.

display: grid;

De waarde grid is onderdeel van een hele reeks eigenschappen en waarden, die bij elkaar ’grid lay-out’ worden genoemd. Met display: grid; wordt de <ul> in een zogenaamde ’grid container’ veranderd. Dit maakt het veel makkelijker om de directe kinderen van dit element, de ’grid items’, op een bepaalde plaats neer te zetten. De directe kinderen van <ul> zijn hier de twaalf <li>’s: voor elk puzzelstukje een.

Met behulp van grid kunnen elementen makkelijk horizontaal en verticaal op de juiste plaats worden neergezet, zonder dat hiervoor ingewikkelde constructies met float, position, en dergelijke nodig zijn. Elementen kunnen allerlei verschillende maten hebben, waarbij ze toch netjes op de juiste plaats kunnen worden neergezet. Ook kan bijvoorbeeld worden opgegeven dat een element twee keer zo breed is als een ander element, of dat een element een bepaald percentage van de hoogte krijgt.

Omdat hier vier keer drie puzzelstukjes onder elkaar komen te staan, zijn deze meer uitgebreide mogelijkheden hier allemaal niet nodig. Hier gelijk onder wordt alleen die verdeling in vier keer drie opgegeven.

grid-template-columns: repeat(3, 1fr);

Hiermee wordt de verdeling van de grid items in kolommen geregeld. Met behulp van de repeat()-functie kun je aangeven, hoe vaak een bepaalde verdeling van de kolommen moet worden herhaald.

Het getal voor de komma geeft het aantal herhalingen aan: drie keer. Wat achter de komma staat wordt dus drie keer herhaald.

Wat achter de komma staat bepaalt de breedte van de kolommen. Hier wordt de eenheid fr gebruikt, een afkorting van ’fraction’. Deze eenheid wordt (op dit moment) alleen bij een grid lay-out gebruikt.

Grid items zichtbaar gemaakt met zwart kader.

fr geeft het deel van de beschikbare ruimte aan dat elk grid item mag gebruiken. Omdat hier als enige waarde 1fr staat, wordt elk grid item even breed. Dit wordt drie keer herhaald. Er komen dus op elke regel drie even brede grid items te staan. Hier zijn dat drie <li>’s, precies evenveel als er puzzelstukjes op een regel staan.

Regels worden hier worden niet opgegeven. Bij een grid lay-out maakt de browser dan automatisch zelf regels aan. Er zijn twaalf grid items, twaalf <li>’s, en er passen er drie op een regel. Daardoor worden vier regels gemaakt. Omdat geen hoogte is opgegeven voor de regels worden de regels automatisch even hoog.

Op de afbeelding is de omtrek van de twaalf grid items, de twaalf <li>’s, zichtbaar gemaakt met een zwarte rand. Alle twaalf <li>’s zijn precies even hoog en even breed. Precies de bedoeling, wants nu passen ze precies onder de puzzelstukjes.

width: 100%; height: 100%;

Een breedte en hoogte in procenten zijn normaal genomen ten opzichte van de ouder van het element. Die ouder is hier <nav>, dus de <ul> wordt even breed en hoog als <nav>.

list-style-type: none;

Standaardbolletjes e.d bij een <ul> weghalen.

margin: 0;

Geen marge.

padding: 0;

Geen padding.

position: absolute;

Om de <ul> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Hier is dat ouder <nav>, die bij nav relatief is gepositioneerd.

In de HTML staat binnen de <nav> als eerste element de <img> met de puzzel, pas daaronder staat de <ul>. Normaal genomen zou de <ul> daardoor onder de <img> komen te staan. Nu kan de <ul> bovenop de <img> worden geplaatst, waardoor de <li>’s in de <ul> bovenop de puzzelstukjes komen te staan.

top: 0;

Helemaal bovenin de <nav> neerzetten. Daar staat ook de <img> met de puzzel.

li

Alle <li>’s. Dat zijn er hier twaalf: voor elk puzzelstukje een.

position: relative;

Om nakomelingen van de <li> te kunnen positioneren ten opzichte van de <li>, moet de <li> zelf een zogenaamd ’containing block’ voor die nakomelingen vormen. Dit kan door aan de <li> bepaalde eigenschappen te geven. Een van die eigenschappen is een relatieve positie. Een relatieve positie heeft verder geen invloed op de <li>’s zelf, omdat niets voor top en dergelijke. wordt opgegeven.

input

Alle <input>’s. Dat zijn er twaalf: voor elk puzzelstukje een.

position: relative;

Gelijk hieronder wordt een z-index opgegeven. Een z-index werkt alleen bij bepaalde eigenschappen. Een van die eigenschappen is een relatieve positie. Omdat verder niets voor top en dergelijke. wordt opgegeven, heeft dit verder geen invloed op de positie van de <input>’s.

z-index: -1; Inputs zichtbaar gemaakt.

Normaal genomen worden elementen in de volgorde van de HTML op het scherm gezet. Wat later in de HTML staat, wordt over eerdere elementen gezet. Zonder correctie zouden de <input>’s boven de afbeelding met de puzzel komen te staan, zoals op de afbeelding het geval is.

Om te zorgen dat de <input>’s helemaal onderaan komen te staan en daardoor door de afbeelding met de puzzel worden verborgen, krijgen ze een negatieve z-index.

Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een relatieve positie. Die is gelijk hierboven aan de <input> gegeven, dus dat is geregeld.

label

Alle <label>’s. Elk van de twaalf <label>’s hoort bij een van de twaalf <input>’s.

width: 100%; height: 100%;

Een breedte en hoogte in procenten zijn normaal genomen ten opzichte van de ouder van het element. Die ouder is hier de <li>, waar de <label> in zit, dus de <label> wordt even breed en hoog als de <li>.

Bij grid-template-columns: repeat(3, 1fr); zijn de twaalf <li>’s even breed en hoog gemaakt als de twaalf puzzelstukjes. Hier worden de <label>’s even breed en hoog gemaakt als de <li>’s, zodat ook de <label>’s elk een puzzelstukje afdekken. Omdat de <label>’s elk aan een <input> zijn gekoppeld, kan de <input> worden aan- en uitgevinkt door op de <label> te klikken of deze aan te raken. Het hele puzzelstukje reageert dus op een klik of een aanraking.

position: absolute;

Om de <label> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Hier is dat ouder <li>, die bij li relatief is gepositioneerd.

Een <label> is van zichzelf een inline-element, waardoor eigenschappen als breedte en hoogte niet gebruikt kunnen worden. Door de <label> absoluut te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.

left: 0;

Op de afbeelding hebben de <label>’s een rode rand gekregen, zodat ze zichtbaar zijn. Ook de <input>’s zijn zichtbaar gemaakt: de witte vierkante blokjes.

In de HTML komen de <label>’s na de <input>’s, dus ook op het scherm worden ze achter de <input>’s gezet. Dat is wat er op de afbeelding gebeurt: de <label>’s schuiven de breedte van de <input>’s naar rechts, waardoor ze niet meer goed staan ten opzichte van het bijbehorende puzzelstukje.

Door de <label>’s helemaal links binnen de <li>’s te zetten, komen ze weer netjes boven de bijbehorende puzzelstukjes te staan.

div

Alle <div>’s. Dat zijn er twaalf, bij elk puzzelstukje één.

opacity: 0.85;

Klein beetje doorzichtig maken. Als een <input> de focus heeft, wordt de bijbehorende <div> getoond en krijgt de bijbehorende <label> een blauwe rand. (Omdat de <label> even groot is als het puzzelstukje, lijkt het of puzzelstukje die rand krijgt).

Meestal is nog goed te zien, bij welk puzzelstukje een getoonde <div> hoort. Maar soms bedekt de <div> een groter deel van de puzzel en is dat moeilijk te zien. Door de <div> iets doorschijnend te maken is de blauwe rand van het bij de <div> horende puzzelstukje nog te zien.

display: none;

Verbergen. Pas als de bijbehorende <input> is aangevinkt, moet de <div> worden getoond.

box-sizing: border-box;

Hier iets onder wordt een padding opgegeven. Standaard wordt een padding bij de breedte en hoogte opgeteld. De <div> zou daardoor iets breder en hoger worden dan het puzzelstukje, waar de <div> bij hoort.

Met de hier opgegeven waarde bij box-sizing komt de padding bínnen de opgegeven breedte en hoogte te staan, waardoor het element niet breder en hoger wordt dan het bijbehorende puzzelstukje.

min-height: 100%; Div drie is te laag.

Een <div> wordt standaard niet hoger dan nodig is om de inhoud ervan weer te kunnen geven. Bij <div>’s met minder inhoud ziet dat er niet goed uit, zoals op de afbeelding is te zien.

Hier wordt de <div> minstens even hoog gemaakt als ouder <li>. Bij grid-template-columns: repeat(3, 1fr); zijn de <li>’s even breed en hoog gemaakt als de twaalf puzzelstukjes, waardoor de <div> ook altijd minstens even hoog is als het puzzelstukje.

In de breedte is geen aanpassing nodig, want een blok-element wordt standaard even breed als de ouder ervan.

font-size: 0.85em;

Iets kleinere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

-webkit-hyphens: auto; hyphens: auto;

Hier staat in feite twee keer hetzelfde: hyphens: auto;. Waarom dat zo is, staat bij De voorvoegsels -moz- en -webkit-.

Er is soms weinig ruimte voor tekst. Daardoor kan een woord als ’puzzelstukje’ al snel te lang worden, vooral in kleinere browservensters. Deze regel zorgt ervoor dat woorden mogen worden afgebroken.

Dit werkt alleen als voor de af te breken tekst het lang-attribuut aanwezig is en de afbreek-bibliotheek bij de opgegeven taal is geïnstalleerd.

Bovenaan de pagina staat <html lang="nl">. Hierdoor weet de browser dat deze pagina Nederlandstalig is en worden de woorden correct afgebroken (als de Nederlandstalige afbreek-bibliotheek is geïnstalleerd, maar in Nederland zal dit vrijwel altijd het geval zijn. En dit voorbeeld zal vermoedelijk niet in Oeganda of China worden bekeken.)

padding: 5px;

Kleine afstand tussen tekst in en buitenkant van de <div>.

pointer-events: none;

Deze CSS-eigenschap moet niet worden verward met pointer-events uit JavaScript, die iets heel anders zijn.

Als er wat meer tekst, afbeeldingen, en dergelijke. in een <div> zitten, kan die behoorlijk groot worden. Daardoor kunnen puzzelstukjes die niet bij de <div> horen worden afgedekt door de <div>. De bij die puzzelstukjes horende <input> kan dan niet meer worden aan- of uitgevinkt, omdat een aanraking of klik wordt afgevangen door de erboven zittende <div>.

Met deze regel wordt een klik of een aanraking van de <div> genegeerd en doorgegeven aan het element onder de <div> (en hetzelfde geldt voor hoveren over de <div>).

Boven de twaalf puzzelstukjes zitten twaalf bijbehorende <label>’s, die elk even groot zijn als het puzzelstukje. Hierdoor is de hele puzzel afgedekt door <label>’s. Het deel van de <div> dat boven de puzzel staat, staat dus altijd boven een <label>. Aanraken van, klikken op of hoveren over een <div> wordt daardoor altijd doorgegeven aan de daaronder zittende <label>. Hierdoor kan de bij de <label> horende <input> toch worden aan- en uitgevinkt, ook als deze onder een <div> zit. Ook wordt bij hoveren over de <div> de blauwe rand rondom de onder de <div> zittende <label> gewoon zichtbaar. (Dit wordt verderop bij input:focus + label, label:hover geregeld.)

position: absolute;

Om de <div> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Hier is dat ouder <li>, die bij li relatief is gepositioneerd.

top: 0;

Helemaal bovenin de <li> neerzetten.

z-index: 10;

Normaal genomen worden elementen in de volgorde van de HTML op het scherm gezet. Wat later in de HTML staat, wordt over eerdere elementen gezet. Om te zorgen dat de <div> altijd bovenaan staat, krijgt deze een hogere z-index.

Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een absolute positie. Die is iets hierboven aan de <div> gegeven, dus dat is geregeld.

li:nth-of-type(3n) div

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen ’n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de selector :nth-of-type() worden alleen elementen van een bepaald soort geteld.

li: alle <li>’s.

:nth-of-type: een zogenaamde pseudo-class. nth wil zeggen ’de zoveelste’, of-type betekent ’van de soort’. Aangezien dit achter li staat, gaat het hier dus om één (of meer) <li>’s.

(3n): tussen de haakjes staat aangegeven, om welke <li> of <li>’s het gaat.

Het getal ’3’ is een gewoon getal, daar is verder weinig geheimzinnigs aan.

De ’n’ is een soort teller, die steeds met 1 wordt verhoogd. Bij de eerste keer is de ’n’ 0, bij de tweede keer 0 + 1 = 1, bij de derde keer 1 + 1 = 2, enzovoort.

3n betekent, net als in de wiskunde, 3 x de waarde van ’n’. De eerste keer is dat dus 3 x 0 = 0, de tweede keer 3 x 1 = 3, de derde keer 3 x 2 = 6, enzovoort. Deze berekening levert een reeks getallen op, beginnend met 0, die steeds 3 hoger worden: 0, 3, 6, 9, 12, ...

De eerste keer is het resultaat 3 x 0 = 0. Oftewel: de nulde <li>. Die is er niet, dus dit selecteert nog geen enkele <li>.

De tweede keer is het resultaat 3 x 1 = 3. Oftewel: de derde <li>.

De derde keer is het resultaat 3 x 2 = 6. Oftewel: de zesde <li>.

Bij de laatste berekening is het resultaat 3 x 4 = 12. Hoger is niet zinvol, want bij de volgende berekening zou de uitkomst 3 x 5 = 15 zijn, en er zijn maar 12 <li>’s.

In dit geval gaat het om de derde, zesde, negende en twaalfde <li>. Dit zijn de vier <li>’s in de rechterkolom.

Deze selector telt alleen de <li>’s die dezelfde ouder hebben. Alle twaalf <li>’s hebben hier als ouder dezelfde <ul> en tellen dus mee. Maar stel dat de eerste zeven <li>’s in een andere <ul> stonden, en de vijf ook. In dat geval zou je twee series hebben: één van zeven <li>’s, en één van vijf <li>’s.

div: de <div>’s die in de geselecteerde <li> zitten. Hier zit in elke <li> maar één <div>.

De pseudo-class nth-of-type() kan onverwachte bijwerkingen hebben. In dit geval is er maar één serie <li>’s in het voorbeeld. Maar als ergens anders ook nog een serie <li>’s zou zitten, (zoals op de site soms daadwerkelijk het geval is), zou deze selector ook voor die serie <li>’s gelden. (Op de site is dit opgelost door de selector te veranderen door in dat geval aan de <ul> een id te geven: ul#met-id li:nth-of-type(3n) div. Er is maar één <ul> met id="met -id". Op een soortgelijke manier kunnen al dit soort conflicten worden opgelost.)

De hele selector bij elkaar betekent: de <div>’s in elke derde <li>, de <div>’s die bij de puzzelstukjes in de rechterkolom horen.

right: 0; div negen staat buiten het browservenster.

Als een <div> wordt getoond, wordt die standaard links binnen ouder <li> getoond. In smallere browservensters is dat in de rechterkolom een probleem, omdat een <div> met iets meer inhoud dan voor een (groot) deel rechts buiten het venster komt te staan en daardoor niet meer helemaal zichtbaar is. Dat is wat op de afbeelding gebeurt met de <div> bij het negende puzzelstukje.

Om dit te voorkomen wordt de <div> binnen de puzzel neergezet met right: 0;. Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Hier is dat de <li>, waar de <div> in zit. Deze heeft bij li een relatieve positie heeft gekregen. Nu wordt de <div> vanaf de rechterkant van de <li> neergezet en past daardoor altijd binnen het browservenster, ook in smallere vensters.

li:nth-of-type(n + 7) div

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(3n) div {right: 0;

Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen ’n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type() worden alleen elementen van een bepaald soort geteld.

li: alle <li>’s.

:nth-of-type: een zogenaamde pseudo-class. nth wil zeggen ’de zoveelste’, of-type betekent ’van de soort’. Aangezien dit achter li staat, gaat het hier dus om één (of meer) <li>’s.

(n + 7): tussen de haakjes staat aangegeven, om welke <li> of <li>’s het gaat.

Het getal ’7’ is gewoon een getal, daar is verder weinig geheimzinnigs aan.

De ’n’ is een soort teller, die begint bij 0, en steeds met 1 wordt verhoogd. Bij de eerste keer is de ’n’ 0, bij de tweede keer 1, bij de derde keer 2, enzovoort.

De eerste keer is het resultaat 0 + 7 = 7. Oftewel: de zevende <li>.

De tweede keer is het resultaat 1 + 7. Oftewel: de achtste <li>.

De derde keer is het resultaat 2 + 7 = 9. Oftewel: de negende <li>.

Bij de laatste berekening is het resultaat 5 + 7 = 12. Hoger is niet zinvol, want bij de volgende berekening zou de uitkomst 6 + 7 = 13 zijn, en er zijn maar twaalf <li>’s.

In dit geval gaat het om de zevende en latere <li>. Dit zijn de zes <li>’s in de onderste helft van de puzzel.

Deze selector telt alleen de <li>’s die dezelfde ouder hebben. Alle twintig <li>’s hebben hier als ouder dezelfde <ul>, want er is maar één <ul>, en tellen dus mee. Maar stel dat er nog een <ul> met <li>’s zou zijn, dan zou je twee series hebben: één in de eerste <ul>, en één in de tweede <ul>.

div: de <div>’s die in de geselecteerde <li> zitten. Hier zit in elke <li> maar één <div>.

De pseudo-class nth-of-type() kan onverwachte bijwerkingen hebben. In dit geval is er maar één serie <li>’s in het voorbeeld. Maar als ergens anders ook nog een serie <li>’s zou zitten, (zoals op de site soms daadwerkelijk het geval is), zou deze selector ook voor die serie <li>’s gelden. (Op de site is dit opgelost door de selector te veranderen door in dat geval aan de <ul> een id te geven: ul#met-id li:nth-of-type(n + 7) div. Er is maar één <ul> met id="met -id". Op een soortgelijke manier kunnen al dit soort conflicten worden opgelost.)

De hele selector bij elkaar betekent: de <div>’s in de laatste zeven <li>’s, de <div>’s in de onderste helft van de puzzel.

top: auto;

Bij div zijn de <div>’s met top: 0; aan de bovenkant van de bijbehorende <li> neergezet. Hier gelijk onder worden ze met bottom: 0; aan de onderkant van dezelfde bijbehorende <li> neergezet. Aan de bovenkant én aan de onderkant vastmaken kan alleen maar, als de <div> precies even hoog is als de <li>, en dat is nergens het geval.

Bij een conflict tussen top en bottom wordt bottom genegeerd en zou het eerdere top: 0; dus ’winnen’. Met deze regel wordt top teruggezet naar de standaardinstelling auto, waardoor de bottom gelijk hieronder werkt.

bottom: 0; div tien staat te laag.

In lagere browservensters kan de inhoud van de <div>’s bij de onderste zes puzzelstukjes onder het venster terecht komen, waardoor een (groot) deel van de inhoud kan verdwijnen, zeker bij een grotere letter.

Op de linkerafbeelding is de <div> bij het tiende puzzelstukje zichtbaar. De letter is vergroot. Als de <div> aan de bovenkant van de <li> wordt neergezet, verdwijnt een deel van de tekst onder het browservensters.

Op de rechterafbeelding is de <div> met behulp van bottom: 0; aan de onderkant van de <li> gepositioneerd. Bij een grotere letter groeit de <div> nu niet naar beneden, maar vanaf de onderkant naar boven. De volledige tekst staat nu binnen het browservenster.

(Voor sommige <div>’s die al af zijn, wordt de positie verderop voor speciaal die <div>’s nog apart aangepast).

.nog-niet-af

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(3n) div {right: 0;}

li:nth-of-type(n + 7) div {top: auto; bottom: 0;}

Alle elementen met class="nog-niet-af". Dit zijn de <div>’s, waarin nog niets is aangebracht, behalve een korte standaardtekst.

background: rgba(255 255 255 / 0.8);

Vaak wordt voor ’n kleur de hexadecimale notatie gebruikt, iets als color: #33ab01;. Daarbij worden niet alleen cijfers, maar ook letters gebruikt. 0 tot en met 9 werken precies hetzelfde als altijd, maar na de 9 komen nog A, B, C, D, E en F.

Als je telt, is ’t dus: 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12, enzovoort. Daarbij is A gelijk aan het tientallige 10, B aan 11, enzovoort. Op deze manier kun je met twee cijfers en/of letters 256 mogelijkheden aangeven: van 00 tot en met FF.

De eerste drie getallen bij rgb() geven de kleur aan. Deze lopen van 0 tot en met 255, dus ook hiermee kun je 256 mogelijkheden aangeven. En omdat er drie getallen zijn levert dat 256 x 256 x 256 = 16.777.216 mogelijke kleuren op, net iets meer dan het aantal kleurpotloden in de gemiddelde kleurdoos van ’n kind.

De notatie bij rgb() geeft dus precies evenveel mogelijkheden als de hexadecimale.

Het eerste getal staat voor rood, het tweede voor groen en het derde voor blauw (feitelijk de Engelse namen, maar de eerste letter is toevallig in het Nederlands hetzelfde). Uit deze drie kleuren zijn alle kleuren op een monitor opgebouwd.

255 wil zeggen volledig aanwezig, 0 wil zeggen helemaal ontbrekend.

rgb(255 255 255) levert wit op, rgb(0 0 0) zwart.

In plaats van getallen mag je ook percentages gebruiken, bijvoorbeeld: rgb(10% 20% 100%).

Omdat in dit voorbeeld drie keer 255 is opgegeven, levert dit een witte kleur op.

Als de kleur doorzichtig moet zijn, is nog een vierde getal aanwezig. Dit wordt van de eerste drie getallen gescheiden door een schuine streep /. Dit vierde getal staat voor het alfa-kanaal. Hiermee wordt de doorzichtigheid aangegeven. Dit getal loopt van 0 naar 1. Volledig doorzichtig is 0, volledig ondoorzichtig is 1.

Het getal voor het alfa-kanaal wordt als decimale breuk aangegeven, dus bijvoorbeeld 0.5 wil zeggen halfdoorzichtig. Let erop dat je ’n punt gebruikt, de Amerikaanse manier om breuken aan te geven. Als je ’n komma gebruikt, denkt de browser dat er twee verschillende getallen staan. Je kunt de doorzichtigheid, net als bij de eerste drie getallen voor de kleuren, ook als een percentage aangeven: rgb(100% 100% 100% / 50%) levert een halfdoorzichtig wit op.

In dit voorbeeld is de achtergrondkleur enigszins doorzichtig: 0.8. Hierdoor zie je de blauwe rand rondom het puzzelstukje dat de focus heeft nog enigszins door de achtergrond heen.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.

Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.

Door beide op te geven, is redelijk zeker dat achtergrond‑ en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important heeft gebruikt in een eigen stijlbestand, is er nog niets aan de hand, want dan veranderen achtergrond‑ en voorgrondkleur geen van beide.

div a

Alle <a>’s binnen een <div>.

Slechts vier van de twaalf <div>’s bevatten een link, de andere <div>’s bevatten alleen maar een korte standaardtekst. Als dit menu helemaal af zou zijn, zou binnen alle twaalf <div>’s een link zitten.

background: linear-gradient(to bottom, #fff 0%, #bbb 100%);

Als achtergrond wordt een gradiënt gebruikt: een verlopende kleur.

Je kunt een gradiënt zelf uitvogelen, maar vaak is het makkelijker om gebruik te maken van een gradiënt-editor. Op de pagina met links zijn onder het kopje CSS → Online uitproberen, code genereren, en dergelijke links naar gradiënt-editors te vinden.

In dit geval is geen gradiënt-editor gebruikt, omdat het hier om een heel simpele gradiënt gaat.

linear-gradient valt in een aantal delen uiteen:

to bottom: de kant waar de gradiënt naartoe gaat. Omdat alleen de onderkant is opgegeven, loopt de gradiënt loodrecht van boven naar onder. Eigenlijk hoeft to bottom niet te worden opgegeven, want dat is de standaardwaarde: van boven naar beneden. Voor de duidelijkheid is dat toch gedaan.

#fff: de eerste kleur is #fff (wit).

0%: de plaats waar de eerste kleur van gradiënt begint. 0% is helemaal aan de bovenkant. Omdat dit de standaardwaarde is, mag je dit ook weglaten.

#bbb: de tweede (en laatste) kleur is #bbb (lichtgrijs).

100%: de plaats waar de gradiënt eindigt. 100% is helemaal aan de onderkant. Omdat dit de standaardwaarde is, mag je dit ook weglaten.

De geleidelijke overgang van de kleur #fff helemaal bovenaan naar hier #bbb aan de onderkant wordt door de browser geregeld.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.

Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.

Door beide op te geven, is redelijk zeker dat achtergrond‑ en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important heeft gebruikt in een eigen stijlbestand, is er nog niets aan de hand, want dan veranderen achtergrond‑ en voorgrondkleur geen van beide.

box-shadow: 4px 6px 4px;

Met behulp van box-shadow kan een schaduw aan een element worden gegeven.

Eerste waarde: schaduw 4 px naar rechts verplaatsen.

Tweede waarde: schaduw 6 px naar beneden verplaatsen.

Derde waarde: de schaduw vervaagt over een dikte van 4 px.

Je kunt nog een aantal andere waarden opgeven bij box-shadow, maar die worden hier niet gebruikt. Omdat geen kleur is opgegeven, wordt de kleur van de tekst gebruikt: zwart.

border: black solid 1px;

Zwart randje van 1 px dik.

border-radius: 10em / 6em;

De naam van deze eigenschap is wat slecht gekozen, want er worden niet alleen borders afgerond, maar ook dingen als achtergronden.

Je kunt voor alle vier de hoeken andere waarden opgeven. En voor elke hoek kun je dan ook nog een andere waarde voor de horizontale en de verticale richting opgeven.

De horizontale en verticale waarde wordt gescheiden door een schuine streep. Voor de streep staat de horizontale waarde, achter de streep de verticale. Als er maar één waarde is opgegeven, geldt die voor horizontaal en verticaal. (Als een element bijvoorbeeld 50 px breed en hoog is en je geeft alleen 25 px op, kun je op deze manier dus een volmaakte cirkel maken.)

Hier is de waarde in horizontale richting 10 em, in verticale richting 6 em. Omdat er maar één waarde per richting staat, geldt die voor alle vier de hoeken. In horizontale richting is de ronding haast twee keer zo lang als in verticale richting, wat een ovaal oplevert.

pointer-events: auto;

Deze CSS-eigenschap moet niet worden verward met pointer-events uit JavaScript, die iets heel anders zijn.

Eerder is bij div met pointer-events: none; opgegeven dat aanraken of -klikken van of hoveren over een <div> wordt genegeerd. Dit geldt ook voor de nakomelingen van de <div>, waaronder deze links. Maar bij een link is het natuurlijk de bedoeling dat die bij aanraken of -klikken wordt gevolgd.

Daarom wordt hier de standaardwerking van aanraken, klikken en hoveren weer hersteld: nu werkt de link weer als een gewone link.

#een-tekst

Het element met id="een-tekst". De <span> waar de tekst in de <div> bij het eerste puzzelstukje in zit.

display: block; margin-top: -215px; De tekst staat niet naast de krokodil, maar eronder.

In de <div> bij het eerste puzzelstukje zit als eerste element de afbeelding met de krokodil en de wolk, die als lichtgrijze achtergrond onder de tekst staat. Als dat niet wordt gecorrigeerd, komt de tekst niet op, maar onder die lichtgrijze achtergrond te staan. De <span> met de tekst staat in de HTML onder de afbeelding met de krokodil, dus die komt eronder te staan. Dit is wat op de afbeelding gebeurt.

Door de <span> met een negatieve marge aan de bovenkant 215 px naar boven te zetten, komt de tekst netjes boven de lichtgrijze achtergrond te staan.

Een <span> is een inline-element. Daardoor werkt een verticale marge niet. Door de <span> met display: block; in een soort blok-element te veranderen, werkt de negatieve marge aan de bovenkant wel.

#een-tekst span

Alle <span>’s binnen het element met id="een-tekst". Elke regel van de tekst in de <div> bij het eerste puzzelstukje staat in een eigen <span>. Daardoor kan de lengte van elke regel worden aangepast aan de breedte van de lichtgrijze achtergrond. De meeste eigenschappen voor die <span>’s zijn hetzelfde en kunnen hier in een keer voor alle <span>’s worden opgegeven.

background: #e7e7e7;

Achtergrondkleur. Deze kleur is hetzelfde als die van het wolkje op de afbeelding met de krokodil, dus normaal genomen is deze achtergrondkleur overbodig. Bij een (hele) grote letter kan tekst echter iets buiten het wolkje komen te staan, en dan is de achtergrondkleur wel nodig voor voldoende contrast tussen de tekst en de afbeelding met de puzzel.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.

Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.

Door beide op te geven, is redelijk zeker dat achtergrond‑ en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important heeft gebruikt in een eigen stijlbestand, is er nog niets aan de hand, want dan veranderen achtergrond‑ en voorgrondkleur geen van beide.

display: block;

In elke <span> zit een regel. De breedte van die regel moet worden aangepast aan de breedte van het lichtgrijze wolkje op de hoogte waar de <span> staat. Daarom krijgt elke <span> later een eigen breedte. Zonder die aangepaste breedte wordt de weergave van de tekst, vooral bij een grotere letter, een puinhoop.

Een <span> is een inline-element, en die kunnen geen breedte krijgen. Door de <span>’s met display: block; te veranderen in een soort blok-element, kunnen ze wel een breedte krijgen.

font-size: 0.95em;

Iets kleinere letter, zodat de tekst binnen de lichtgrijze achtergrond past.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

line-height: 1.25em;

Regelhoogte.

Omdat geen gewone hoogte voor de <span>’s is opgegeven, is dit ook gelijk de hoogte van de <span>’s. De regelhoogte wordt hier iets verhoogd, omdat de tekst in combinatie met de gelijk hierboven opgegeven iets kleinere lettergrootte dan het best op de lichtgrijze achtergrond blijkt te passen. Dat is tamelijk lastig vanwege alle mogelijke maten browservensters en omdat de bezoeker ook nog een andere lettergrootte opgegeven kan hebben.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px de regelhoogte niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid voor de regelhoogte wordt gebruikt.

#een-tekst-1

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#een-tekst span {background: #e7e7e7; color: black; display: block; font-size: 0.95em; line-height: 1.25em;}

Het element met id="een-tekst-1". De <span> waarin de eerste regel tekst van de <div> bij het eerste puzzelstukje zit.

width: 14ch;

Breedte. Omdat het lichtgrijze wolkje onregelmatig is gevormd, moet de breedte van elke regel worden aangepast. De eenheid ch is gebaseerd op de breedte van de ’0’ van de gebruikte lettersoort. Hierdoor verandert de breedte mee met de lettergrootte.

Omdat rekening moet worden gehouden met allerlei maten van het browservensters en andere lettergroottes, is het vrij moeilijk deze tekst op de juiste plaats neer te zetten. De weinig gebruikte eenheid ch blijkt hier het meest geschikt voor te zijn.

margin-left: 120px; Tekst staat niet naast de krokodil, maar eroverheen.

Zonder correctie wordt de tekst gewoon links in de <div> neergezet. Daardoor komt de tekst niet over het lichtgrijze wolkje, maar over de krokodil heen te staan.

Door aan elke <span> een aangepaste marge aan de linkerkant te geven, komt de tekst op de juiste plaats boven het lichtgrijze wolkje te staan.

Op de afbeelding is de tekst te zien, als geen marges aan de linkerkant worden opgegeven.

#een-tekst-2

{width: 10ch; margin-left: 134px;} #een-tekst-3 {width: 8ch; margin-left: 147px;} #een-tekst-4 {width: 7ch; margin-left: 153px;} #een-tekst-5 {width: 8ch; margin-left: 165px;} #een-tekst-6 {width: 11ch; margin-left: 150px;} #een-tekst-7 {width: 24ch; margin-left: 80px;} #een-tekst-8 {width: 18ch; margin-left: 80px;}

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#een-tekst span {background: #e7e7e7; color: black; display: block; font-size: 0.95em; line-height: 1.25em;}

De <span>’s met de tweede tot en met de achtste regel tekst in de <div> bij het eerste puzzelblokje. Elke <span> krijgt een aangepaste breedte en linkermarge, zodat de tekst overal boven het witte blokje staat. Meer uitleg hierover is te vinden bij #een-tekst-1, de <span> met de eerste regel tekst.

span#een-tekst-9

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#een-tekst span {background: #e7e7e7; color: black; display: block; font-size: 0.95em; line-height: 1.25em;}

De <span> met id="een-tekst-9". De <span> met de negende, onderste regel.

Eerder is bij #een-tekst span een lettergrootte van 0,95 em opgegeven. In die selector zit één id en één element (span). Zou je bij de selector hier alleen #een-tekst-9 opgeven (zoals bij de eerste acht <span>’s binnen span#een-tekst hier gelijk boven), dan ou deze selector alleen één id hebben.

Een selector met een id én een element heeft meer specificiteit, meer gewicht, dan een selector met alleen een element. De eerdere selector die een lettergrootte van 0,95 opgeeft ’wint’ daardoor van de selector hier, ook al staat deze selector lager in de CSS.

Door aan deze selector span toe te voegen heeft ook deze selector een element én een id en daardoor dezelfde specificiteit. Omdat deze selector lager in de CSS staat, ’wint’ de selector hier nu wel van de eerdere selector.

Dit is van belang voor de hieronder opgegeven lettergrootte, want deze is bij deze <span> anders dan bij de eerste acht <span>’s met tekst.

width: 31ch;

Breedte. Omdat de het lichtgrijze wolkje onregelmatig is gevormd, moet de breedte van elke regel worden aangepast. De eenheid ch is gebaseerd op de breedte van de ’0’ van de gebruikte lettersoort. Hierdoor verandert de breedte mee met de lettergrootte.

Omdat rekening moet worden gehouden met allerlei maten van het browservensters en andere lettergroottes, is het vrij moeilijk deze tekst op de juiste plaats neer te zetten. De weinig gebruikte eenheid ch blijkt hier het meest geschikt voor te zijn.

font-size: 0.75em;

Deze laatste regel krijgt een nog iets kleinere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

margin: 5px 0 0 30px;

Aan de bovenkant een marge van 5px, rechts en onder geen marge, en links een marge van 30 px zodat de tekst boven het lichtgrijze wolkje staat.

#een ~ div a

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#een: het element met id="een". De <input> bij het eerste puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#een voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het eerste puzzelstukje in zitten.

a: alle <a>’s in die <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het eerste puzzelstukje.

margin-top: 5px;

Kleine marge aan de bovenkant.

padding: 5px;

Kleine afstand tussen tekst in en buitenkant van de link.

position: absolute;

Om de <a> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div>, waar de link in zit, die bij div absoluut is gepositioneerd.

Een <a> is van zichzelf een inline-element, waardoor eigenschappen als margin-top niet gebruikt kunnen worden. Door de <a> absoluut te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.

left: 50px;

50 px naar rechts verplaatsen, zodat de link boven het lichtgrijze wolkje komt te staan.

#twee ~ div p

#twee: het element met id="twee". De <input> bij het tweede puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#twee voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeeldingen en de link bij het tweede puzzelstukje in zitten.

p: alle <p>’s in die <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de <p> in de <div> die volgt op de <input> bij het tweede puzzelstukje.

De tekst in de <div> bestaat uit 64 afbeeldingen: voor elke letter eentje. Schermlezers lezen dit letter voor letter voor. Om dit te voorkomen worden de afbeeldingen met de letters voor schermlezers verborgen met de ARIA-code aria-hidden. In deze <p> staat voor schermlezers een ’gewone’ tekst. Die is op het scherm niet te zien, maar wordt gewoon voorgelezen.

position: absolute;

Om de <p> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <p> in zit, deze is bij div absoluut gepositioneerd.

left: -20000px;

Ver links buiten het scherm neerzetten. Nu is de tekst in de <p> niet zichtbaar op het scherm, maar deze wordt gewoon voorgelezen door schermlezers.

#twee ~ div a

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#twee: het element met id="twee". De <input> bij het tweede puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#twee voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeeldingen en de link bij het tweede puzzelstukje in zitten.

a: alle <a>’s in die <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het tweede puzzelstukje.

text-wrap: nowrap; Tekst in de link staat op twee regels.

Vooral in kleinere browservensters en bij een (hele) grote letter, kan het woord ’Letterdans’ worden afgebroken. Dat is wat op de afbeelding gebeurt. Dat ziet er wat vreemd uit. Met deze regel wordt dat afbreken voorkomen.

In Safari wordt dit pas in versie 17.4 ondersteund, dus in oudere

versies daarvan kan dit woord wel worden afgebroken. Omdat op iOS en iPadOS alle browsers gebruik maken van de weergave-machine van Safari, geldt dit op iOS en iPadOS voor alle browsers ouder dan versie 17.4 van die besturingssystemen.

padding: 5px 10px;

Boven en onder een padding van 5 px, rechts en links 10 px voor wat afstand tussen tekst in en omtrek van de <a>. Die padding is vrij groot, dat heeft te maken met de bij div a opgegeven ronde hoeken.

position: absolute;

Om de <a> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <a> in zit, deze is bij div absoluut gepositioneerd.

top: calc(140px + 1em); De tweede link staat te hoog.

In de HTML staat de <a> vrijwel bovenin de <div>, nog boven de <span> met de bewegende letters. Hierdoor komt de <a> helemaal bovenin en helemaal links in de <div> te staan, zoals op de afbeelding is te zien. (Alleen de <p> met tekst voor schermlezers staat boven de <a>, maar die is bij #twee ~ div p absoluut gepositioneerd en heeft daardoor geen invloed op de positie van de

<a>.) Daarom wordt de positie van de <a> gecorrigeerd.

Met behulp van calc() kunnen berekeningen worden gemaakt. In dit geval wordt de afstand tot de bovenkant van de <div> berekend.

140px: gewoon een vaste grootte: altijd 140 px.

1em: de lettergrootte. Hoe groter de letter, hoe groter deze waarde.

De berekening wordt hier gemaakt met twee verschillende eenheden: px en em. Dat kan niet, eerst moeten alle eenheden worden omgerekend naar dezelfde eenheid. Daarom rekent de browser de eenheid em om naar de eenheid px.

Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet hoe groot de lettergrootte is. Je kunt wel een lettergrootte opgeven, maar de bezoeker kan dat aanpassen. Maar op het moment van weergave weet de browser de lettergrootte wel.

De berekening wordt dan 140 px plus de lettergrootte in px.

Bij een standaard lettergrootte van 16 px is 1 em 16 px. 140 px + 16 px = 156 px: de afstand tot de bovenkant van de <div> wordt dan 156 px.

Bij een lettergrootte van twee keer de standaard lettergrootte is 2 em 32 px . 140 px + 32 px = 172 px. De afstand tot de bovenkant van de <div> wordt dan 172 px.

Door niet alleen een vaste afstand van 140 px te nemen, maar ook een afstand die mee verandert met de lettergrootte, blijft de <a> beter op de juiste plaats staan bij een andere lettergrootte.

left: 50px;

50 px vanaf de linkerkant van de <div> neerzetten. Een uitgebreider verhaal staat hier gelijk boven bij top: calc(140px + 1em); alleen is hier een simpele vaste afstand van 50 px voldoende.

z-index: 10;

Normaal genomen worden elementen in de volgorde van de HTML op het scherm gezet. Wat later in de HTML staat, wordt over eerdere elementen gezet. Daardoor zou de link verdwijnen onder de afbeeldingen met de letters. Om te zorgen dat de link altijd bovenaan staat, krijgt deze een hogere z-index.

Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een absolute positie. Die is iets hierboven aan de <a> gegeven, dus dat is geregeld.

#twee-tekst

Het element met id="twee-tekst". De <span> met de afbeeldingen met de bewegende letters bij het tweede puzzelstukje.

background: #0f9;

Lichtgroene achtergrondkleur.

width: 330px;

Breedte.

border: 10px outset #0ff;

Lichtblauwe rand van 10 px dik. outset zorgt ervoor dat de rand wat diepte lijkt te krijgen.

padding: 10px;

Wat afstand tussen inhoud en buitenkant van de <span>.

position: absolute;

Om de <span> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <span> in zit, deze is bij div absoluut gepositioneerd.

Een <span> is van zichzelf een inline-element, waardoor eigenschappen als breedte niet gebruikt kunnen worden. Door de <span> absoluut te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.

top: -80px; left: -150px;

80 px naar boven en 150 px naar links verplaatsen. De reden hiervan wordt gelijk hieronder beschreven.

transform: scale(0.6); De tweede tekst staat gedeeltelijk buiten het browservenster.

De <span> 0,6 keer zo klein maken, zodat deze binnen het browservenster past. Ook de inhoud van de <span> wordt verkleind, dus ook de 64 afbeeldingen met letters. Je zou ook in een grafisch programma die afbeeldingen alle 64 afzonderlijk kunnen verkleinen, maar dat is nogal ’n klus. Bovendien zijn het bewegende gif-afbeeldingen, waardoor het nog meer werk wordt. Het is veel eenvoudiger om dat met deze simpele regel voor de hele handel gewoon hier te doen met behulp van CSS.

Zonder correctie wordt de <span> in de linkerbovenhoek van de <div> bij het tweede puzzelstukje neergezet (op de afbeelding te herkennen aan de blauwe rand). (Voor de <span> staan in de HTML nog een <p> en een <a>, maar die zijn bij #twee ~ div p en #twee ~ div a absoluut gepositioneerd en hebben daardoor geen invloed op de positie van de <span>.)

Op de afbeelding staat de <span> met de letters echter niet in de linkerbovenhoek. Dat komt door de verkleining.

Een verkleining met scale() gebeurt vanuit het midden van het element: alle vier de kanten van de <span> worden naar binnen verplaatst. Zonder deze verkleining zou de <span> inderdaad netjes in de linkerbovenhoek van de <div> staan. (De link met ’Letterdans’ staat trouwens wel op de juiste plaats, want die is bij #twee ~ div a op die positie neergezet.)

Hier gelijk boven is de <span> 80 px naar boven en 150 px naar links verplaatst. Daardoor staat deze nu op de juiste plaats. (En de tekst staat dan ook weer op de juiste plaats ten opzichte van de link met ’Letterdans’.)

#twee-tekst span

De <span>’s binnen het element met id="twee-tekst". Elke regel tekst bij puzzelstukje het tweede puzzelstukje staat in een eigen <span>. Omdat de tekst is opgebouwd uit 64 afbeeldingen met elke één letter, kan deze niet op de normale manier worden weergegeven. De letters zouden dan op willekeurige plaatsen op de volgende regel komen te staan en niet alleen tussen twee woorden.

display: inline-block;

Een <span> is van zichzelf een inline-element. Hierdoor zijn eigenschappen als breedte en hoogte niet te gebruiken. Een inline-block is een soort kruising tussen een inline- en een blok-element. Het komt niet op een nieuwe regel te staan, maar eigenschappen als hoogte en breedte zijn wel te gebruiken.

height: 50px;

Hoogte. Zonder deze hoogte komen de afbeeldingen met de letters heel dicht op elkaar te staan.

#twee-tekst span img

#twee-tekst: het element met id="twee-tekst". De <span> waarbinnen de afbeeldingen met de letters bij het tweede puzzelstukje zitten.

span: alle <span>’s binnen die <span>. In elke <span> zit een groep afbeeldingen met letters, die samen op één regel komen te staan en daar een of meer woorden vormen.

img: de afbeeldingen binnen die <span>’s. Elke afbeelding bevat één bewegende letter.

background: #0f9;

Zelfde lichtgroene achterkleur als de <span> met alle tekst bij #twee-tekst heeft gekregen.

De achtergrond van de afbeeldingenmet letters is doorzichtig. Als om een of andere reden een afbeelding buiten span#twee-tekst zou komen te staan, is de achtergrondkleur van de letter in ieder geval nog steeds lichtgroen. Daardoor is de rode letter ook dan nog leesbaar.

margin-left: -3px;

Elke afbeelding 3 px naar links verplaatsen. Hierdoor passen ook de iets langere regels nog binnen span#twee-tekst. Omdat de tekst uit afbeeldingen bestaat, is deze nog steeds goed leesbaar.

#twee-tekst > img

>: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <img> moet een direct kind van span#twee-tekst zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij nav > img.

float: left; De tekst staat niet goed naast de afbeelding.

Een <img> is een inline-element en komt daardoor niet op een nieuwe regel te staan. De in de HTML op de afbeelding volgende <span> met ’VINDEN’ is bij #twee-tekst span met display: inline-block; veranderd in een soort kruising tussen een blok- en een inline-element, maar komt ook nog steeds niet op een nieuwe regel te staan.

De afbeelding met de olifant en ’VINDEN’ komen dus op dezelfde regel te staan. Omdat de afbeelding het hoogste is, bepaalt de afbeelding de hoogte van de regel. De onderkant van de olifant en de <span> met ’VINDEN’ komen op gelijke hoogte te staan, waardoor er boven ’VINDEN’ en grote lege ruimte ontstaat. Op de afbeelding is de lege ruimte boven ’VINDEN’ duidelijk te zien.

Door de afbeelding naar links te floaten, wordt de ruimte links van de afbeelding opgevuld, waardoor er links van de afbeelding genoeg ruimte is om ook de <span> met ’ZOIETS’ daar neer te zetten.

margin: 5px 20px 0 0;

5 px marge aan de bovenkant en 20 px rechts, zodat de afbeelding iets beter ten opzichte van de letters komt te staan.

#zes ~ div img

#zes: het element met id="zes". De <input> bij het zesde puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#zes voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de afbeelding en de link bij het zesde puzzelstukje in zitten.

img: de afbeeldingen binnen deze <div>. Dat is hier alleen de afbeelding met de nooduitgang.

De hele selector in gewone taal: doe iets met de afbeelding in de <div> die volgt op de <input> bij het zesde puzzelstukje.

position: absolute;

Om de afbeelding op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Hier is dat de bij div absoluut gepositioneerde <div>, waar de afbeelding in zit.

top: 0;

Helemaal bovenin de <div> neerzetten.

Bij div heeft de <div> een padding gekregen aan alle kanten. Daardoor komt de afbeelding 5 px vanaf de bovenkant te staan. Prima voor tekst, maar niet voor de afbeelding hier. Nu komt de afbeelding helemaal bovenin de <div> te staan. (Je zou natuurlijk ook de padding bij de <div> weg kunnen halen, dat heeft hetzelfde effect.)

right: 0;

De afbeelding zit in een <div>. In de HTML volgt die <div> op een <label>, dus de <div> wordt normaal genomen achter die <label> neergezet. Omdat de <label> bij label even breed is gemaakt als de <li>, waar deze in zit, komt de <div> helemaal rechts in die <li> te staan, helemaal rechts in het het puzzelstukje. Daardoor komt ook de afbeelding helemaal rechts in het puzzelstukje te staan, en daarmee ook buiten de puzzel. In smallere browservensters verdwijnt de afbeelding hierdoor voor een (groot) deel of zelfs volledig.

Door de afbeelding helemaal rechts te positioneren, komt de rechterkant van de afbeelding rechts in de <div> te staan. Hierdoor komt de hele afbeelding binnen de puzzel te staan en is dus altijd helemaal zichtbaar.

#zes ~ div a

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#zes: het element met id="zes". De <input> bij het zesde puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#zes voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de afbeelding en de link bij het zesde puzzelstukje in zitten.

a: de links binnen deze <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het zesde puzzelstukje.

text-wrap: nowrap;

Vooral in kleinere browservensters en bij een (hele) grote letter, kan het woord ’Wegwezen’ worden afgebroken. Dat ziet er wat vreemd uit. Met deze regel wordt dat afbreken voorkomen.

In Safari wordt dit pas in versie 17.4 ondersteund, dus in oudere versies daarvan kan dit woord wel worden afgebroken. Omdat op iOS en iPadOS alle browsers gebruik maken van de weergave-machine van Safari, geldt dit op iOS en iPadOS voor alle browsers ouder dan versie 17.4 van die besturingssystemen.

padding: 3px 8px;

Boven en onder een padding van 3 px, rechts en links 8 px voor wat afstand tussen tekst in en omtrek van de <a>. Die padding is vrij groot, dat heeft te maken met de bij div a opgegeven ronde hoeken.

position: absolute;

Om de <a> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <a> in zit, deze is bij div absoluut gepositioneerd.

right: 90px;

De <a> 90 px van de rechterkant van de <div> neerzetten. Op die plaats staat de link boven een leeg stukje van de afbeelding.

#negen ~ div

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(3n) div {right: 0;}

li:nth-of-type(n + 7) div {top: auto; bottom: 0;

#negen: het element met id=" negen". De <input> bij het negende puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#negen voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het negende puzzelstukje in zitten.

De hele selector in gewone taal: doe iets met de de <div> die volgt op de <input> bij het negende puzzelstukje.

top: 0; Tekst negen staat te hoog.

Bij li:nth-of-type(n + 7) div zijn de onderste zes <div>’s aan de onderkant van de bijbehorende <li> neergezet, zodat ze altijd binnen de puzzel staan. Dat ziet er bij de <div> bij het zevende puzzelstukje uit zoals op de foto: de onderkant van de tekst staat op gelijke hoogte als de onderkant van de <div>. Hier komt dat slecht uit, omdat de lettergrootte naar onderen toe steeds kleiner wordt en uiteindelijk aan de onderkant van het browservenster moet verdwijnen.

Daarom wordt de bovenkant van de <div>, en daarmee ook van de tekst die erin zit, op gelijke hoogte gezet als de bovenkant van de <div>.

(De twee tekstjes in de kleine blokjes worden apart gepositioneerd, die staan wel op de goede plaats.)

#negen ~ div span

#negen: het element met id="negen". De <input> bij het negende puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#negen voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het negende puzzelstukje in zitten.

span: de <span>’s binnen deze <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de <span>’s in de <div> die volgt op de <input> bij het negende puzzelstukje. Dat zijn er hier drie: twee voor de kleine blokjes met tekst op de witte en gele achtergrond, en een voor de langere tekst op de roze achtergrond.

background: #fc9;

Roze achtergrond.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.

Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.

Door beide op te geven, is redelijk zeker dat achtergrond‑ en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important heeft gebruikt in een eigen stijlbestand, is er nog niets aan de hand, want dan veranderen achtergrond‑ en voorgrondkleur geen van beide.

#negen ~ div #idee

Voor dit element. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#negen ~ div span {background: #fc9; color: black;}

#negen: het element met id="negen". De <input> bij het negende puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#negen voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het negende puzzelstukje in zitten.

#idee: het element met id="idee".

De hele selector in gewone taal: doe iets met het element met id="idee" in de <div> die volgt op de <input> bij het negende puzzelstukje. Dat is de <span> met de tekst met de gele achtergrond.

background: yellow;

Achtergrond geel.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.

Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.

Door beide op te geven, is redelijk zeker dat achtergrond‑ en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important heeft gebruikt in een eigen stijlbestand, is er nog niets aan de hand, want dan veranderen achtergrond‑ en voorgrondkleur geen van beide.

width: 40%;

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier de <div>, waar de <span> in zit.

Omdat die <div> een blok-element is, wordt die normaal genomen even breed als de ouder van de <div>. Dat is hier de <li> bij het negende puzzelstukje. Die <li> is bij grid-template-columns: repeat(3, 1fr); even breed gemaakt als het puzzelstukje, dus de <div> zou ook even breed als het puzzelstukje worden.

Verderop bij #negen-tekst wordt echter aan de <span> met de tekst op de roze achtergrond een breedte van 220 px gegeven. Omdat die <span> ook binnen de <div> staat, wordt hierdoor de <div> 220 px breed.

Iets verderop wordt de <span> absoluut gepositioneerd. Bij een absoluut gepositioneerd wordt een breedte in procenten genomen ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div>, die bij div absoluut is gepositioneerd. Hierdoor krijgt de <span> uiteindelijk een breedte van 40% van 220 px.

font-size: 1.1em;

Iets grotere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

border: black solid 1px;

Zwart randje.

border-radius: 8px;

Ronde hoeken. Omdat maar één maat is opgegeven, worden de hoeken overal 8 px breed en 8 px hoog: symmetrische hoeken die elk een kwart van een cirkel groot zijn.

padding: 3px;

Kleine afstand tussen tekst in en omtrek van de <span>.

position: absolute;

Om de <span> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <span> in zit, die bij div absoluut is gepositioneerd.

Een <span> is van zichzelf een inline-element, waardoor eigenschappen als breedte niet gebruikt kunnen worden. Door de <span> absoluut te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is e gebruiken.

right: 5px;

Vijf px vanaf de rechterkant van de <div> neerzetten. Omdat de <div> bij div een padding van 5 px heeft gekregen, komt de <span> precies tegen de buitenkant van de <div> aan te staan.

bottom: -140px;

De onderkant van de <span> 140 px onder de onderkant van de <div> neerzetten. Dat de <span> boven de tekst met de roze achtergrond komt te staan is geen probleem, want dat is flauwekultekst.

#negen ~ div #vraag-eerst

{background: #ddd; color: black; border: solid blue 2px; padding: 2px; position: absolute; bottom: -170px;}

De <span> met ’Vraag eerst toestemming!’. De CSS bij deze <span> is in grote lijnen hetzelfde als bij #negen ~ div #idee, de <span> met ’Fantastisch idee? Vraag? Mail ons!’. Bij de meeste eigenschappen verschillen alleen de waarden. Er is eigenlijk maar één echt verschil: bij deze <span> wordt geen breedte opgegeven. Achter ’Vraag eerst’ staat een <br>, waardoor ’toestemming!’ altijd op een nieuwe regel komt te staan. Daarom hoeft geen breedte opgegeven te worden.

#negen ~ div img

#negen: het element met id="negen". De <input> bij het negende puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#negen voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de afbeeldingen, de tekst en de link bij het negende puzzelstukje in zitten.

img: de afbeeldingen binnen deze <div>. Dat is hier alleen het bordje met ’E-MAIL’.

De hele selector in gewone taal: doe iets met de afbeelding in de <div> die volgt op de <input> bij het negende puzzelstukje.

float: right; Tekst negen staat te laag.

Een <img> is een inline-element en komt daardoor niet op een eigen regel te staan. In de HTML volgt op de <img> de <a> met ’E-mail’. Een <a> is ook een inline-element en zou op dezelfde regel als de afbeelding met ’E-MAIL’ komen te staan.

De tekst met de roze achtergrond staat ook in een <span>, maar deze komt op een nieuwe regel onder de afbeelding (en onder de link) te staan, omdat die <span> met display: block; in een blok-element is veranderd.

Dit is wat op de afbeelding gebeurt: de link staat op dezelfde regel als de afbeelding, en de tekst op de roze achtergrond staat op een volgende regel.

Door de afbeelding naar rechts te floaten, komt de tekst op de roze achtergrond links van de afbeelding te staan. De <link> wordt gelijk hieronder absoluut gepositioneerd, waardoor deze op de afbeelding in de lege ruimte onder het bordje komt te staan.

#negen ~ div a

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#negen: het element met id="negen". De <input> bij het negende puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#negen voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de afbeelding, de tekst en de link bij het negende puzzelstukje in zitten.

a: de links binnen deze <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het negende puzzelstukje.

font-size: 1.1em;

Iets grotere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

padding: 8px;

Kleine afstand tussen tekst in en buitenkant van de <a>.

position: absolute;

Om de <a> op een bepaalde plaats neer te kunnen zetten.

Er wordt gepositioneerd ten opzichte van het ’containing block’. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier de <div> waar de <a> in zit, deze is bij div absoluut gepositioneerd.

top: 70px; right: 30px;

70 px vanaf de bovenkant en 30 px vanaf de rechterkant van de <div> neerzetten. Op deze plaats staat de <a> precies boven het lege stukje in de afbeelding met het bordje met ’E-MAIL’.

#negen-tekst

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#negen ~ div span {background: #fc9; color: black;}

Het element met id="negen-tekst". De <span> waar de tekst met de roze achtergrond in zit.

display: block; Tekst is verkeerd omlijnd en verdwijnt gedeeltelijk.

Een <span> is een inline-element. Daardoor kan een eigenschap als breedte niet worden gebruikt. padding komt niet alleen aan de boven- en onderkant van de <span> te staan, maar aan de boven- en onderkant van elke regel. En botst met de regelhoogte. Links en rechts ontbreekt de padding. Ten slotte wordt een border aan boven- en onderkant van elke regel weergegeven, daarentegen wordt de border links alleen bij de eerste regel toegepast, en rechts alleen bij de laatste regel. (Die laatste regel valt buiten het browservenster, dus die zie je niet.)

Het fraaie geheel is op de afbeelding te zien: niet echt lekker leesbaar.

Door het inline-element te veranderen in een blok-element, werken deze eigenschappen wel op de bedoelde manier.

width: 220px;

Breedte.

font-size: 1.1em;

Iets grotere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

border: red solid 1px;

Rode rand.

padding: 5px;

Wat ruimte tussen tekst in en buitenkant van de <span>.

#negen-tekst span:nth-of-type(2)

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#negen ~ div span {background: #fc9; color: black;}

Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen ’n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type(), onderdeel van deze selector, worden alleen elementen van een bepaald soort geteld.

#negen-tekst: het element met id="negen-tekst". De <span> met de tekst op de roze achtergrond.

span: alle <spans>’s binnen span#negen-tekst.

:nth-of-type(2): het element met een bepaald volgnummer. Het volgnummer staat tussen de haakjes. In dit geval is het volgnummer twee. Het gaat hier dus om de tweede <span> binnen de span#negen-tekst.

Omdat voor :nth-of-type(2) een span staat, worden alleen <span>’s geteld. Als binnen span#negen-tekst 327 <a>’s zitten, tellen die niet mee. Hadden ze maar ’n <span> moeten zijn.

De hele selector in gewone taal: de tweede <span> binnen span#negen-tekst.

De tekst met de roze achtergrond is te hoog voor het browservenster (tenzij dit echt onwijs hoog is). Door de tekst in een aantal <span>’s te zetten en bij elke <span> de lettergrootte wat te verkleinen, wordt de tekst naar beneden toe steeds kleiner. Voor de leesbaarheid maakt dat niets uit, want het is zich steeds herhalende flauwekultekst.

font-size: 0.9em;

Iets kleinere letter.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

#negen-tekst span:nth-of-type(3)

{font-size: 0.85em;} #negen-tekst span:nth-of-type(4) {font-size: 0.8em;} #negen-tekst span:nth-of-type(5) {font-size: 0.75em;}

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#negen ~ div span {background: #fc9; color: black;}

De derde, vierde en vijfde <span> binnen span#negen-tekst. In elke <span> wordt de tekst weer iets kleiner. Een uitgebreidere beschrijving staat gelijk hierboven bij negen-tekst span:nth-of-type(2).

CSS voor vensters in landschapsstand en maximaal 360 px hoog

@media (orientation: landscape) and (max-height: 360px)

De CSS die hier tot nu toe staat, geldt voor alle browservensters. De CSS die binnen deze ’media query’ staat, geldt alleen voor vensters in landschapsstand maximaal 360 px hoog. In deze vensters wordt de puzzel verkleind en worden de <div>’s met teksten, afbeeldingen en links volledig binnen de puzzel getoond.

@media: geeft aan dat het om CSS gaat, die alleen van toepassing is, als aan bepaalde voorwaarden wordt voldaan. Al langer bestond de mogelijkheid om met behulp van zo’n @media-regel CSS voor bijvoorbeeld printers op te geven. CSS3 heeft dat uitgebreid tot veel meer eigenschappen, zoals de breedte en hoogte van het venster van de browser.

(orientation: landscape): het browservenster moet in landschapsstand zijn.

Een venster kent twee standen: landscape (landschap, liggend) en portrait (portret, staand). (Allerlei andere standen zoals bijvoorbeeld lezend in bed tellen hier niet mee. Liggend of staand, dat is het, al hang je met je tablet ondersteboven heen en weer te zwaaien aan de trapeze.)

and: er komt nog een voorwaarde, waaraan moet worden voldaan.

(max-height: 360px): het browservenster mag niet hoger zijn dan 360 px. Is het venster hoger, dan wordt de CSS die binnen deze media query staat genegeerd.

Gelijk na deze regel komt een { te staan, en aan het einde van de CSS die binnen deze query valt een bijbehorende afsluitende }. Die zijn in de regel hierboven weggevallen, maar het geheel ziet er zo uit:

@media (orientation: landscape) and (max-height: 360px) { body {color: silver;} (...) rest van de CSS voor deze media query (...) footer {color: gold;} }

Voor de eerste CSS binnen deze media query staat dus een extra {, en aan het eind staat een extra }.

Als je nou ’n mobieltje hebt met een resolutie van – om maar wat te noemen – 1280 x 720 px, dan geldt deze media query mogelijk toch voor dat mobieltje. Terwijl dat toch echt meer dan 360 px hoog is.

Mobiele apparaten, maar ook steeds meer gewone beeldschermen, hebben een hogere resolutiedichtheid. Dat wil zeggen dat ze kleinere pixels hebben, die dichter bij elkaar staan. Daardoor zijn foto’s, tekst, en dergelijke. veel scherper weer te geven. Hoe kleiner de

puntjes (de pixels) zijn, waaruit een afbeelding is opgebouwd, hoe duidelijker het wordt.

Er ontstaat alleen één probleem: als je de pixels twee keer zo klein maakt, wordt ook wat je ziet twee keer zo klein. En inmiddels zijn er al apparaten met pixels die meer dan vier keer zo klein zijn. Een lijntje van 1 px breed zou op die apparaten minder dan ’n kwart van de oorspronkelijke breedte krijgen en vrijwel onzichtbaar zijn. Een normale foto zou in een thumbnail veranderen. Kolommen zouden heel smal worden. Tekst zou onleesbaar klein worden. Allemaal fantastisch scherp, maar je hebt ’n vergrootglas nodig om ’t te kunnen zien.

Om dit te voorkomen wordt een verschil gemaakt tussen CSS-pixels en schermpixels (in het Engels ’device-pixels’). De CSS-pixels zijn gebaseerd op de – tot voor kort – normale beeldschermen van de desktop. 1 CSS-pixel is op zo’n beeldscherm 1 pixel. Het aantal schermpixels is het werkelijk op het apparaat aanwezige aantal pixels (dat is het aantal pixels, waarvoor je hebt betaald).

Dat eerder genoemde mobieltje van 1280 x 720 px heeft wel degelijk het aantal pixels, waarvoor je hebt betaald. Maar die zitten dichter bij elkaar. Op een gewoon beeldscherm zitten 96 pixels per inch, wat wordt uitgedrukt met de eenheid ppi (’pixels per inch’). Als dat mobieltje een resolutie van 192 ppi heeft, 192 pixels per inch, zijn de pixels ervan twee keer zo klein als op een origineel beeldscherm. Er zijn per inch twee keer zoveel schermpixels aanwezig.

Om nu te voorkomen dat alles op dat mobieltje twee keer zo klein wordt, geeft het mobieltje niet het echte aantal schermpixels (1280 x 720), maar een lager aantal CSS-pixels door bij een media query. De 192 ppi van het mobieltje is twee keer zo veel als de 96 ppi van een normaal beeldscherm. Het aantal CSS-pixels is dan het aantal schermpixels gedeeld door 2. 1280 x 720 gedeeld door 2 is 640 x 360 px. Het aantal CSS-pixels is 640 x 360 px en zit daarmee binnen de grens van deze media query.

Je bent dus niet opgelicht, of in ieder geval niet wat betreft het aantal pixel.

Door deze truc is een lijn van 1 px breed op een normaal beeldscherm ook op het mobieltje nog steeds 1 px breed, alleen wordt die ene (CSS‑)pixel opgebouwd uit twee schermpixels (feitelijk vier, want het verhaal geldt voor breedte én hoogte). De dikte van het lijntje is hetzelfde, maar het is veel fijner opgebouwd. Bij lijntjes is dat verschil bijvoorbeeld in bochten goed te zien.

Hetzelfde verhaal geldt voor hogere resoluties, Een tablet met een breedte van 4096 schermpixels en een ppi van 384 (vier keer de originele dichtheid) geeft 4096 gedeeld door 4 = 1024 CSS-pixel door. Het lijntje van 1 px breedte op de originele monitor is nog steeds 1 CSS-pixel breed op de tablet, maar die ene CSS-pixel is nu opgebouwd uit zestien schermpixels.

Kort samengevat: omdat niet het aantal schermpixels (waarvoor je hebt betaald), maar het aantal CSS-pixels (de door de ontwerper bedoelde afmeting) wordt doorgegeven, wordt voorkomen dat een hogeresolutiescherm onleesbaar klein wordt.

nav

Deze selector werkt alleen in browservensters in landschapsstand en maximaal 360 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

nav {height: 480px; max-height: 100%; align-self: center; position: relative;}

Alle <nav>’s. Dat is er hier maar één: de <nav> met het menu.

transform: scaleY(0.75);

Bij nav is een hoogte van 480 px opgegeven. Dat past niet in een browservenster met een maximale hoogte van 360 px. Door de <nav> (en alles daarin) te verkleinen tot driekwart, past de <nav> binnen het venster.

#negen ~ div #vraag-eerst

Deze selector werkt alleen in browservensters in landschapsstand en maximaal 360 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#negen ~ div #vraag-eerst {background: #ddd; color: black; border: solid blue 2px; padding: 2px; position: absolute; bottom: -170px;}

De <span> met ’Vraag eerst toestemming!’.

bottom: -120px;

Deze <span> is bij #negen ~ div #vraag-eerst onder de onderkant van de <div>, waar de <span> in zit, neergezet. In lagere browservensters verdwijnt de <span> hierdoor onder het venster. Daarom wordt de <span> iets hoger neergezet in deze lagere vensters.

CSS voor vensters minimaal 750 px breed

@media (min-width: 750px)

De opbouw van deze regel staat beschreven bij @media (orientation: landscape) and (max-height: 360px). Er zijn drie verschillen: landschaps- of portretstand en hoogte van het browservenster zijn onbelangrijk, en de breedte van het venster moet minimaal 750 px zijn.

#een ~ div

Deze selector werkt alleen in browservensters minimaal 750 px breed. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

#een: het element met id="een". De <input> bij het eerste puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#een voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het eerste puzzelstukje in zitten.

De hele selector in gewone taal: doe iets met de de <div> die volgt op de <input> bij het eerste puzzelstukje.

right: calc(-200px + 30vw - 6em);

In deze bredere browservensters is er ruimte om de <div> iets links buiten de puzzel neer te zetten. Daardoor blijven meer puzzelstukjes van de puzzel zichtbaar. De <div> een vaste afstand naar links verplaatsen kan niet, want die afstand hangt af van de precieze breedte van het venster. Bovendien moet ook nog rekening worden gehouden met een mogelijk door de bezoeker vergrote letter. Daarom wordt bij het berekenen van die afstand rekening gehouden met vensterbreedte en lettergrootte.

Met behulp van calc() kunnen berekeningen worden gemaakt. In dit geval wordt de afstand tussen de <div> en de rechterkant van de <li> berekend. (De <li> is de ouder van de <div> en is bij li relatief gepositioneerd.)

-200px: gewoon een vaste grootte: altijd 200 px, maar dan negatief.

+ 30vw: 30 vw bij de -200px optellen.

De eenheid vw is gebaseerd op de breedte van het venster van de browser. 1 vw is 1% van de breedte van het venster, en 30 vw is 30% van de breedte. De afstand die de <div> naar links wordt verplaatst, is iermee afhankelijk van de breedte van het venster: hoe breder het venster, hoe meer bij de -200px wordt opgeteld.

– 6em: 6 keer de lettergrootte van het voorgaande aftrekken.

Omdat de <div> vanaf rechts wordt gepositioneerd, zou de <div> bij een grotere lettergrootte toch weer voor een (groot) deel buiten het browservenster komen te staan en daardoor gedeeltelijk verdwijnen.

Door de verplaatsing bij een grotere letter weer kleiner te maken, blijft de <div> altijd binnen het venster van de browser staan.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

De berekening wordt hier gemaakt met drie verschillende eenheden: px, vw en em. Dat kan niet, eerst moeten alle eenheden worden omgerekend naar dezelfde eenheid. Daarom rekent de browser de eenheden vw en em om naar de eenheid px.

Als het browservenster 900 px breed is en de lettergrootte is 2 em, wordt de berekening -200 px + 270 px – 32 px = 38 px: de <div> wordt 38 px vanaf de rechterkant van de <li> neergezet.

Bij een browservenster met een breedte van 750 px en een lettergrootte van 1em wordt de berekening -200px + 225 px – 16 px = 9 px: de <div> wordt 9 px vanaf de rechterkant van de <li> neergezet.

#vier ~ div, #zeven ~ div, #tien ~ div

Deze selector werkt alleen in browservensters minimaal 750 px breed. Voor andere vensters is de uitleg hieronder niet van belang.

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(n + 7) div {top: auto; bottom: 0;}

#vier: het element met id=" vier". De <input> bij het vierde puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#vier voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst bij het vierde puzzelstukje in zit.

#zeven ~ div en #tien ~ div zijn precies dezelfde selectors, maar dan bij het zevende en tiende puzzelstukje.

De drie selectors in gewone taal: doe iets met de de <div> die volgt op de <input> bij het vierde, zevende en tiende puzzelstukje.

left: -100%; Links staat tekst vier volledig binnen de puzzel, rechts deels erbuiten.

In het vierde, zevende en tiende puzzelstukje staat alleen een korte tekst. Als de letters worden vergroot, past die tekst niet meer binnen de <div>. Omdat de <div> even groot is als het bijbehorende puzzelstukje, komt de tekst daardoor over de puzzelstukjes eronder te staan, zoals op de afbeelding links is te zien.

In deze bredere browservensters is voldoende ruimte om de tekst niet boven de puzzelstukjes eronder neer te zetten, maar links van de puzzel, zoals op de rechterafbeelding is te zien. Nu blijven de andere puzzelstukjes volledig zichtbaar.

Voor rechts wordt niets opgegeven, dus de rechterkant van de <div> blijft tegen de rechterkant van de <li> aan staan, en daarmee ook tegen de rechterkant van het puzzelstukje.

De linkerkant wordt 100% naar links verplaatst. Omdat de <div> even breed is als de <li> (en het puzzelstukje), is 100% de breedte van de <li> (wat ook de breedte van het puzzelstukje is). De <div> wordt hierdoor twee keer zo breed als de <li>, waarbij de linkerhelft buiten de puzzel komt te staan.

#zes ~ div, #twaalf ~ div

{right: -100%;} #negen ~ div {right: -110%;}

Deze selector werkt alleen in browservensters minimaal 750 px breed. Voor andere vensters is de uitleg hieronder niet van belang.

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(3n) div {right: 0;}

#negen ~ div {top: 0;}

Deze regel werkt precies hetzelfde als die hierboven bij #vier ~ div, #zeven ~ div, #tien ~ div, maar nu voor het zesde, negende en twaalfde puzzelstukje aan de rechterkant. Die worden hier voor de helft rechts buiten de puzzel gezet.

Omdat de <div> bij het negende puzzelstukje een langere tekst, afbeeldingen en een link bevat, wordt die iets meer naar rechts gezet.

CSS voor vensters minimaal 750 px hoog

@media (min-height: 750px)

De opbouw van deze regel staat beschreven bij @media (orientation: landscape) and (max-height: 360px). Er zijn twee verschillen: landschaps- of portretstand is onbelangrijk, en de hoogte van het browservenster moet minimaal 750 px zijn.

#een ~ div

Deze selector werkt alleen in browservensters minimaal 750 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}


Alleen in browservensters minimaal 750 px breed:

#een ~ div {right: calc(-200px + 30vw - 6em);}

#een: het element met id="een". De <input> bij het eerste puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#een voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het eerste puzzelstukje in zitten.

De hele selector in gewone taal: doe iets met de de <div> die volgt op de <input> bij het eerste puzzelstukje.

top: auto;

Eerder is de <div> bij div met top: 0; vanaf de bovenkant gepositioneerd. Hier gelijk onder wordt de <div> met bottom een stukje naar boven verplaatst, omdat daar in deze hogere browservensters ruimte voor is. Gelijktijdig vanaf de bovenkant en vanaf de onderkant neerzetten kan niet. In zo’n geval wordt bottom genegeerd en zou top: 0; winnen. Door top de standaardwaarde auto te geven, werkt bottom wel.

bottom: calc(30px - 2.4em);

De <div> vanaf de onderkant van ouder <li> neerzetten.

Met behulp van calc() kunnen berekeningen worden gemaakt. In dit geval wordt de afstand van de onderkant van de <div> tot de onderkant van ouder <li> berekend.

30px: 30 px vanaf de onderkant van de <li> neerzetten.

– 2.4em: daarvan 2,4 keer de lettergrootte aftrekken.

Bij een grotere letter zou een (groot) deel van de <div> boven het venster van de browser verdwijnen. Daarom wordt hier 2,4 keer de lettergrootte van de 30 px afgetrokken. Die 2,4 keer is gevonden door uitproberen. Ongetwijfeld bestaan daar ook briljante formules voor, maar gewoon even uitproberen werkt ook prima.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij gebruik van een absolute eenheid zoals px niet alle browsers de lettergrootte kunnen veranderen. Hierdoor wordt bij een grotere letter de <div> minder ver naar beneden verplaatst.

De berekening wordt hier gemaakt met twee verschillende eenheden: px en em. Dat kan niet, eerst moeten alle eenheden worden omgerekend naar dezelfde eenheid. Daarom rekent de browser de eenheid em om naar de eenheid px.

Bij een standaard lettergrootte van 16 px is 2,4 em 2,4 keer 16 = 38,4 px. De uitkomst is dan 30 px min 38,4 px = -8,4 px: de onderkant van de <div> komt 8,4 px onder de onderkant van de <li> te staan (wat afhankelijk van de pixeldichtheid van het scherm en de betreffende browser wordt afgerond).

Bij twee keer de standaard lettergrootte is 2,4 em 2,4 keer 32 = 76,8 px. De uitkomst is dan 30 px min 76,8 px = -46,8 px: de onderkant van de <div> komt 46,8 px onder de onderkant van de <li> te staan (wat afhankelijk van de pixeldichtheid van het scherm en de betreffende browser wordt afgerond).

Bij een grotere letter komt de <div> dus lager te staan, waardoor ook bij een grotere letter niets aan de bovenkant van het browservenster verdwijnt.

#twee ~ div a

Deze selector werkt alleen in browservensters minimaal 750 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#twee ~ div a {text-wrap: nowrap; padding: 5px 10px; position: absolute; top: calc(140px + 1em); left: 50px; z-index: 10;}

#twee: het element met id="twee". De <input> bij het tweede puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#twee voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeeldingen en de link bij het tweede puzzelstukje in zitten.

a: alle <a>’s in die <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het tweede puzzelstukje.

top: 20px; left: 60px;

Hier gelijk onder wordt de <span> met de bewegende letters hoger neergezet, omdat daar in deze hogere browservensters ruimte voor is. Om te zorgen dat de link op de juiste plaats ten opzichte van de letters blijft staan, moet ook deze hoger komen te staan. Ook wordt de link iets meer naar links verplaatst, omdat dat hier beter uitkomt.

#twee-tekst

Deze selector werkt alleen in browservensters minimaal 750 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#twee-tekst {background: #0f9; width: 330px; border: 10px outset #0ff; padding: 10px; position: absolute; top: -80px; left: -150px; transform: scale(0.6);}

Het element met id="twee-tekst". De <span> met de afbeeldingen met de bewegende letters bij het tweede puzzelstukje.

top: auto;

Eerder is de <span> bij #twee-tekst met top: -80px; vanaf de bovenkant gepositioneerd. Hier gelijk onder wordt de <span> met bottom een stukje naar boven verplaatst, omdat daar in deze hogere browservensters ruimte voor is. Gelijktijdig vanaf de bovenkant en vanaf de onderkant neerzetten kan niet. In zo’n geval wordt bottom genegeerd en zou top: 0; winnen. Door top de standaardwaarde auto te geven, werkt bottom wel.

bottom: -160px;

De onderkant van de <span> 160 px onder de onderkant van de <div>, de ouder van de <span>, neerzetten.

In deze hogere browservensters kan de <span> met de bewegende letters iets hoger worden neergezet, omdat daar genoeg ruimte voor is. (Dat de verplaatsing minder dan 160 px lijkt, komt doordat de <span> bij transform: scale(0.6); 0,6 keer zo klein is gemaakt.)

Bij #een ~ div is de <div> met de tekst, de afbeelding en de link bij het eerste puzzelstukje ook hoger neergezet. Daar was de verticale verplaatsing met bottom: calc(30px - 2.4em); mede afhankelijk van de lettergrootte. Dat is hier niet nodig, want de tekst bestaat hier niet uit gewone letters, maar uit afbeeldingen met letters. En die veranderen niet van grootte bij een andere lettergrootte. Daarom volstaat hier een simpele vaste waarde van 160 px.

#drie ~ div

Deze selector werkt alleen in browservensters minimaal 750 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

li:nth-of-type(3n) div div {right: 0;}

#drie: het element met id="drie". De <input> bij het eerste puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#drie voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de korte tekst bij het derde puzzelstukje in zit.

De hele selector in gewone taal: doe iets met de de <div> die volgt op de <input> bij het derde puzzelstukje.

top: -100%;

In deze <div> staat alleen hele korte tekst. Bij een grotere letter zou die tekst gedeeltelijk over de puzzelstukjes eronder komen te staan. Door de bovenkant van de <div> naar boven te verplaatsen, wordt dit voorkomen.

Een uitgebreider verhaal staat bij #vier ~ div, #zeven ~ div, #tien ~ div. Alleen gaat het daar om een verplaatsing naar links, en hier om een verplaatsing naar boven.

#negen ~ div #idee

{bottom: -200px;} #negen ~ div #vraag-eerst {bottom: -240px;}

Deze selectors werken alleen in browservensters minimaal 750 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#negen ~ div #idee {background: yellow; color: black; width: 40%; font-size: 1.1em; border: black solid 1px; border-radius: 8px; padding: 3px; position: absolute; right: 5px; bottom: -140px;}

#negen ~ div #vraag-eerst {background: #ddd; color: black; border: solid blue 2px; padding: 2px; position: absolute; bottom: -170px;

In #negen ~ div span#idee zit de tekst op de gele achtergrond, in #negen ~ div span#vraag-eerst zit de tekst op de witte achtergrond. In deze hogere browservensters is er ruimte om deze twee <span>’s iets lager neer te zetten.

Een uitgebreidere beschrijving van deze twee <span>’s en de CSS staat bij #negen ~ div #idee en #negen ~ div #vraag-eerst.

CSS voor vensters minimaal 1000 px breed

@media (min-width: 1000px)

De opbouw van deze regel staat beschreven bij @media (orientation: landscape) and (max-height: 360px). Er zijn drie verschillen: landschaps- of portretstand en hoogte van het browservenster zijn onbelangrijk, en de breedte van het venster moet minimaal 1000 px zijn.

#een ~ div

Deze selector werkt alleen in browservensters minimaal 1000 px breed. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

#een ~ div {right: calc(-200px + 30vw - 6em);}


Alleen in browservensters minimaal 750 px hoog:

#een ~ div {top: auto; bottom: calc(30px - 2.4em);}

#een: het element met id="een". De <input> bij het eerste puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#een voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeelding en de link bij het eerste puzzelstukje in zitten.

De hele selector in gewone taal: doe iets met de de <div> die volgt op de <input> bij het eerste puzzelstukje.

right: 0;

Bij #een ~ div is de <div> meer naar links gezet, omdat er in browservensters met een minimale breedte van 750 px meer ruimte is. Hierdoor komt de <div> minder over andere puzzelstukjes te staan.

In deze browservensters van minimaal 1000 px breed is nog meer ruimte. Daardoor kan de rechterkant van de <div> tegen de rechterkant van ouder <li> worden neergezet. Nu blijven de puzzelstukjes rechts van deze <div> volledig vrij als de <div> wordt getoond.

CSS voor vensters minimaal 1000 px hoog

@media (min-height: 1000px)

De opbouw van deze regel staat beschreven bij @media (orientation: landscape) and (max-height: 360px). Er zijn twee verschillen: landschaps- of portretstand is onbelangrijk, en de hoogte van het browservenster moet minimaal 1000 px zijn.

#twee ~ div a

Deze selector werkt alleen in browservensters minimaal 1000 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

#twee ~ div a {text-wrap: nowrap; padding: 5px 10px; position: absolute; top: calc(140px + 1em); left: 50px; z-index: 10;}

#twee ~ div a {top: 20px; left: 60px;}

#twee: het element met id="twee". De <input> bij het tweede puzzelstukje.

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#twee voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: er volgt maar één <div> op de <input>: de <div> waar de tekst, de afbeeldingen en de link bij het tweede puzzelstukje in zitten.

a: alle <a>’s in die <div>. Dat is er hier maar één.

De hele selector in gewone taal: doe iets met de link in de <div> die volgt op de <input> bij het tweede puzzelstukje.

top: auto;

Eerder is de <a> bij #twee ~ div a met top: 20px; vanaf de bovenkant gepositioneerd. Hier gelijk onder wordt de <a> met bottom een stuk naar boven verplaatst, omdat daar in deze hogere browservensters ruimte voor is. Gelijktijdig vanaf de bovenkant en vanaf de onderkant neerzetten kan niet. In zo’n geval wordt bottom genegeerd en zou top: 20px; winnen. Door top de standaardwaarde auto te geven, werkt bottom wel.

bottom: 235px;

Hier gelijk onder wordt span#twee-tekst, de <span> met de bewegende letters, hoger neergezet. Om de <a> ten opzichte van de letters op de juiste plaats te houden, wordt de onderkant van de <a> 235 px boven de onderkant van ouder <div> neergezet.

#twee-tekst

Deze selector werkt alleen in browservensters minimaal 1000 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor dit element is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#twee-tekst {background: #0f9; width: 330px; border: 10px outset #0ff; padding: 10px; position: absolute; top: -80px; left: -150px; transform: scale(0.6);}

#twee-tekst {top: auto; bottom: -160px;}

Het element met id="twee-tekst". De <span> met de afbeeldingen met de bewegende letters bij het tweede puzzelstukje.

bottom: 0;

Bij #twee-tekst is de onderkant van de <span> 160 px onder de onderkant van ouder <div> neergezet. In deze hogere ruimtes is meer ruimte en kan de onderkant van de <a> tegen de onderkant van ouder <div> worden neergezet. Hierdoor blijven de puzzelstukjes onder de <div> volledig zichtbaar. (Dat de onderkant van de <span> een eind boven de onderkant van de <div> komt te staan, komt doordat de <span> bij transform: scale(0.6); 0,6 keer zo klein is gemaakt.)

#negen ~ div #idee

{bottom: -140px;} #negen ~ div #vraag-eerst {bottom: -180px;}

Deze selectors werken alleen in browservensters minimaal 1000 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#negen ~ div #idee {background: yellow; color: black; width: 40%; font-size: 1.1em; border: black solid 1px; border-radius: 8px; padding: 3px; position: absolute; right: 5px; bottom: -140px;}

#negen ~ div #idee {bottom: -200px}

#negen ~ div #vraag-eerst {background: #ddd; color: black; border: solid blue 2px; padding: 2px; position: absolute; bottom: -170px;}

#negen ~ div #vraag-eerst {bottom: -240px;}

In #negen ~ div span#idee zit de tekst op de gele achtergrond, in #negen ~ div span#vraag-eerst zit de tekst op de witte achtergrond.

In browservensters met een minimale hoogte van 750 px zijn deze twee <span>’s iets hoger neergezet. In deze nog hogere browservensters worden ze nog iets hoger neergezet.

Een uitgebreidere beschrijving staat bij #negen ~ div #idee.

CSS weer voor alle vensters

input:checked ~ div

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald. Hier staat alleen de CSS die voor álle <div>’s geldt. Verschillende <div>’s worden anders gepositioneerd dan hieronder staat, mede afhankelijk van de grootte van het browservenster. Dat is bij elkaar heel veel CSS en heeft geen invloed op de werking van deze regel. Daarom wordt die CSS voor afzonderlijke <div>’s hier weggelaten.

div {opacity: 0.85; display: none; box-sizing: border-box; min-height: 100%; font-size: 0.85em; -webkit-hyphens: auto; hyphens: auto; padding: 5px; pointer-events: none; position: absolute; top: 0; z-index: 10;}

input: alle <input>’s. Elk puzzelstukje heeft een eigen <input>.

:checked: maar alleen als de <input> is aangevinkt (de <input>’s hebben het attribuut type="checkbox" en zijn dus een aankruisvakje).

~: het hierachter staande element moet ergens in de HTML staan, na het hiervoor staande element. Het hoeft er, anders dan bij de +, niet gelijk op te volgen, als het maar ergens na het eerste element in de HTML staat.

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input voor de ~ en div na de ~ hebben beide als ouder dezelfde <li>.

div: de <div>’s die aan bovenstaande voorwaarde voldoen. Dat is er bij elke <input> maar één: de <div> met de tekst, afbeelding en link die bij hetzelfde puzzelstukje als de <input> hoort.

De hele selector in gewone taal: die iets met de <div> die in de HTML op een <input> volgt, maar alleen als die <input> is aangevinkt.

display: block;

Bij div zijn de <div>’s met display: none; verborgen. Zodra de <input> die bij de <div> hoort is aangevinkt, wordt die <div> hier zichtbaar gemaakt.

input:focus + label, label:hover

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

label {width: 100%; height: 100%; position: absolute; left: 0;}

De selector voor de komma:

input: alle <input>’s. Elk puzzelstukje heeft een eigen <input>.

:focus: maar alleen als die <input> de focus heeft.

+: het element achter de + moet in de HTML direct volgen op het element voor de +. In dit geval gaat het om de <label> die gelijk op de <input> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder dezelfde <li>.

label: de <label> die gelijk op de <input> volgt.

De hele selector in gewone taal: doe iets met de <label> die gelijk op een <input> volgt, maar alleen als die <input> de focus heeft.

De selector na de komma:

label: alle <label>’s.

hover: maar alleen als over die <label> wordt gehoverd.

:focus en :hover zijn vaak problematisch op touchscreens.

Waar hieronder wordt gesproken over een muis, geldt dit ook voor een touchpad.

Als een element de focus heeft, wordt dat element op een of andere manier door de browser gemarkeerd. Hier gelijk onder wordt het uiterlijk van die markering aangepast, maar dat maakt voor de werking verder niets uit.

Een element krijgt de focus als erop wordt geklikt, als het wordt aangeraakt, of als er met de Tab-toets is aangekomen. Bij gebruik van de muis en op een aanraakscherm weet je, waar je hebt geklikt of aangeraakt. Daarom is daar is een markering van het element met focus normaal genomen overbodig. Bij gebruik van de Tab-toets is die markering wel heel belangrijk, want anders zou je niet weten welk element op bijvoorbeeld een Enter reageert.

Tegenwoordig wordt daarom meestal :focus-visible gebruikt in plaats van :focus. :focus-visible markeert alleen bij gebruik van de Tab-toets het element met de focus, niet bij gebruik van een muis of aanraakscherm. Omdat die markering in de regel foeilelijk is, is dat een prima oplossing.

Standaard wordt tegenwoordig een element met focus alleen gemarkeerd bij gebruik van de Tab-toets, dus als je :focus en :focus-visible geen van tweeën gebruikt, wordt toch alleen gemarkeerd bij gebruik van de Tab-toets.

Hier wordt :focus gebruikt, waardoor ook bij gebruik van een aanraakscherm of een muis het element met focus wordt gemarkeerd. Hier is dat echter nuttig, omdat de <div> die wordt getoond vaak niet (helemaal) boven het bijbehorende puzzelstukje staat. Daardoor is niet altijd duidelijk, bij welk puzzelstukje een <div> hoort. Omdat de <label> precies even groot is als het bijbehorende puzzelstukje, is dit door het markeren van de <label> altijd duidelijk.

:hover is ook vaak een probleem op aanraakschermen. Je hovert over een element als je er met de muis boven hangt. Voordat aanraakschermen bestonden, werd :hover voor van alles en nog wat gebruikt. Zonder meer weglaten op aanraakschermen kon daardoor niet: heel veel sites zouden dan niet goed meer werken.

Daarom wordt op veel aanraakschermen :hover min of meer hetzelfde behandeld als een aanraking. Helaas gebeurt dit niet op alle besturingssystemen en in alle browser op dezelfde manier. Soms werkt :hover hetzelfde als een aanraking, soms markeert het een element alleen maar, soms verdwijnt de markering weer als je het scherm ergens anders aanraakt.

Er zijn trucs om :hover op een aanraakscherm uit te schakelen, maar dat hoeft hier niet. :hover werkt hier op precies dezelfde manier als :focus: er wordt een <div> getoond. Daarom maakt het hier niets uit of :hover helemaal niet werkt, werkt als een klik, of wat dan ook.

outline: blue solid 4px;

Dikke blauwe rand geven, zodat duidelijk is bij welk <label> (en puzzelstukje) de getoonde <div> hoort.

a:focus

{outline: blue solid 3px;}

Voor deze elementen is eerder CSS opgegeven. Deze wordt binnen dit blokje herhaald. Hier staat alleen de CSS die voor álle <a>’s geldt. Verschillende <a>’s worden anders gepositioneerd. Dat is bij elkaar heel veel CSS en heeft geen invloed op de werking van deze regel. Daarom wordt die CSS voor afzonderlijke <a>’s hier weggelaten.

div a {background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; box-shadow: 4px 6px 4px; border: black solid 1px; border-radius: 10em / 6em; pointer-events: auto;}

Alle <a>’s, maar alleen als deze de focus hebben.

Als een link vanuit het menu de focus heeft, wordt dat op dezelfde manier gemarkeerd als een <label>, waarvan de bijbehorende <input> de focus heeft. Een uitgebreid verhaal waarom hier :focus wordt gebruikt, staat hier iets boven bij input:focus + label, label:hover.

JavaScript

De code is geschreven in een afwijkende lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code) is in de hele uitleg onderstippeld blauw. (In dit voorbeeld is dat alleen JavaScript die nodig is voor toegankelijkheid, hoewel ook andere gebruikers daar voordeel van kunnen hebben.) Alle niet-essentiële code is bruin.

Als je onderstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

Als je bovenstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

Hieronder wordt de werking van dit script uitgelegd, maar dit is zeker geen cursus JavaScript. Daar is deze site niet voor bedoeld.

Als je deze uitleg gaat lezen, wordt dat heel lastig als je het script niet ernaast legt. Het is dan haast onmogelijk om te zien, waar een bepaald stuk code begint en eindigt. Het script staat hier gelijk boven, maar het is beter het als apart bestand te openen, zodat je niet steeds tussen uitleg en script heen en weer hoeft te vliegen. Het script is te vinden in de map 014-js.

// menu-014.js

Om vergissingen te voorkomen is het een goede gewoonte bovenaan het script even de naam neer te zetten. Voor je het weet, zit je anders in het verkeerde bestand te werken.

(function () {

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven. Een functie is een stukje bij elkaar horende code. (Het haakje helemaal aan het begin wordt iets verderop beschreven.)

(): achter een functie moeten twee haakjes staan. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie, maar bij deze functie gebeurt dat niet.

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie, dat is in dit geval helemaal onderaan in de laatste regel van het script, staat een bijbehorende }.

In die laatste regel staat in dit geval nog meer dan alleen de } die het einde van de code aangeeft: }) ();

}: dit is de eerder genoemde afsluitende }, waarmee het einde van de code in de functie wordt aangegeven.

): dit afsluitende haakje is de tegenhanger van het haakje dat voor de functie staat. Samen zorgen ze ervoor dat de hele functie, met alles erop en eraan, tussen haakjes staat. De reden van deze haakjes wordt iets hieronder besproken.

(): dit is weer gewoon een taalregel van JavaScript. In dit geval moeten er achter de }) nog twee haakjes staan. Een computer is gewoon niet zo slim, anders weet de ziel niet wat er moet gebeuren.

;: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.

We hebben hier een script, waarin alle code binnen een functie is geplaatst. Alle code staat tussen { en }, zodat de computer het begin en einde van de functie kan herkennen.

Een functie is een stukje bij elkaar horende code dat je, zo vaak als je wilt, kunt uitvoeren. Als je de tafel van twaalf op het scherm wilt zetten, hoef je niet twaalf vermenigvuldigingen in JavaScript te schrijven, maar schrijf je ’n functie voor één vermenigvuldiging, die je vervolgens tien keer (op steeds iets andere wijze) uitvoert.

Een functie heeft echter één probleem: de code in de functie wordt pas uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen kan op allerlei manieren gebeuren, bijvoorbeeld als de gebruiker ’n toets indrukt, als de pagina is geladen, als het 13:13 is op vrijdag de dertiende, noem maar op. Maar zonder te worden aangeroepen doet een functie helemaal niets.

In dit script is dat ook zo. Er zit bijvoorbeeld een functie in die reageert op het indrukken van een toets. Om die functie goed te laten werken, moet de computer eerst wat voorbereidend werk verrichten. Daarvoor moet het script worden gelezen. Maar dat hele script zit in een functie, en die functie doet dus pas wat, als die wordt aangeroepen.

Om te zorgen dat de buitenste functie, die waar alle code in zit, toch vanzelf wordt uitgevoerd, zet je er een ( voor. En helemaal aan het eind, achter de afsluitende }, zet je de tegenhanger ). Om te zorgen dat de functie echt vanzelf wordt uitgevoerd, moeten hierachter nog twee haakjes () worden gezet. Nu wordt de functie automatisch uitgevoerd, zonder dat deze hoeft te worden aangeroepen. En kan de code in het script worden gelezen, waardoor het benodigde voorbereidende werk kan worden uitgevoerd.

(Je kunt de code ook buiten zo’n alles omvattende functie zetten. Dan wordt de code ook gelijk uitgevoerd. Maar dat brengt belangrijke risico’s met zich mee. In dit script worden namen voor variabelen gebruikt zoals ’INPUTS’. Als nou een ander JavaScript toevallig dezelfde namen zou gebruiken, gaat het gruwelijk mis. Door het hele script in een functie te stoppen, voorkom je dat. Als je hier meer over wilt weten, kun je op internet zoeken naar ’JavaScript name conflict’ of ’JavaScript name clash’.)

"use strict";

Deze regel zorgt ervoor dat bepaalde slordigheden in de code, die makkelijk tot grote problemen kunnen leiden, niet meer mogen. Een validator (die controleert op fouten in de code) is nu strenger en keurt meer dingen af.

const INPUTS = document.querySelectorAll("main input");

const INPUTS: met het sleutelwoord const wordt aangegeven dat het erop volgende woord de naam van een ’variabele’ is: de variabele wordt hier ’aangemaakt’ of ’gedeclareerd’.

Gelijk na const volgt de naam van één of meer variabelen. Hier staat maar één variabele achter const: ’INPUTS’. Een variabele is een soort portemonnee: er kan van alles in zitten.

In INPUTS wordt dus iets opgeborgen. Omdat de variabele een naam heeft, kan de variabele worden aangeroepen bij deze naam. Net zoals je iemand die ’Marie’ heet kunt aanroepen met ’Marie’, ongeacht of Marie aardig, onaardig, muzikaal of arrogant is, ongeacht de ’inhoud’ van Marie.

Tot een aantal jaren geleden kon je maar op één manier variabelen aanmaken: met var. Die manier wordt vrijwel niet meer gebruikt, maar je komt var nog heel vaak tegen in iets oudere code. Het grootste nadeel van var: het was uitermate makkelijk om per ongeluk de inhoud van een met var aangemaakte variabele te veranderen. En die fout was vaak bijzonder moeilijk op te sporen.

In dit geval mag de inhoud van INPUTS niet veranderen: het aantal <input>’s in het menu onder de puzzel verandert ook niet, dus de variabele hoeft ook niet te veranderen. Daarom wordt voor het declareren het sleutelwoord const gebruikt. (Als de variabele wel moet kunnen veranderen, maak je de variabele aan met let in plaats van const.)

Om duidelijk te maken dat de variabele niet veranderd kan worden, is het gebruikelijk de naam in hoofdletter te schrijven: ’INPUTS’, en geen ’inputs’. Zo is overal in het script gelijk duidelijk, dat het hier om een variabele met een vaste waarde gaat.

Als je const gebruikt, moet je gelijk bij het aanmaken van de variabele een waarde aan de variabele geven, want later kan die waarde niet meer worden veranderd. Dat opgeven van de waarde gebeurt hieronder.

Met const INPUTS wordt de variabele hier aangemaakt: er wordt een naam aan gegeven. Dat aanmaken gebeurt in de volgorde, waarin ze in het script worden gebruikt. Die volgorde hoeft niet, dat is een kwestie van voorkeur. Je hoeft ook niet alle variabelen aan het begin van hun scope aan te maken, maar dat is wel overzichtelijker.

Pardon? Scope?

Een met const (of, als de inhoud van de variabele nog moet kunnen veranderen, let) aangemaakte variabele kan niet overal worden aangeroepen, kan niet overal worden gebruikt: de variabele heeft een ’scope’ en kan alleen binnen die scope worden aangeroepen.

Als een variabele binnen een functie wordt aangemaakt, kan de variabele alleen binnen die functie worden aangeroepen. Hier wordt INPUTS binnen de buitenste functie (function () aangemaakt. Binnen die buitenste functie staat het hele script. Daardoor kan INPUTS binnen het hele script worden gebruikt: de scope van INPUTS is het hele script.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

document.querySelectorAll("main input"): het opzoeken van de <input>’s.

Het middelste stukje querySelectorAll is een zogenaamde ’functie’. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam ’document’. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode querySelectorAll() uit document kan JavaScript alle elementen opzoeken, die aan een bepaalde voorwaarde voldoen, zoals alle <div>’s, alle elementen met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:

querySelectorAll("main input")

Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in CSS. In bovenstaande regel wordt naar alle <inputs>’s binnen <main> gezocht. Net zoals je in CSS in een selector main input {...} zou gebruiken.

Zou je naar alle <span>’s zoeken die een eerste kind zijn, dan zou je de volgende regel gebruiken:

querySelectorAll("span:first-child")

Precies zoals selectors in CSS werken.

Er zijn twaalf <input>’s die door deze selector worden gevonden. De gevonden <input>’s worden in een zogenaamde ’nodelist’ gestopt: een soort adressenlijst met twaalf ingangen, voor elke <input> één. Met de <input>’s in zo’n nodelist kun je van alles doen: CSS toevoegen, het uiterlijk veranderen, noem maar op.

;: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele declaratie const INPUTS = document.querySelectorAll("main input"): sla de <input>’s binnen <main> op in INPUTS. Dat wil zeggen dat in variabele INPUTS een ongelooflijke hoeveelheid informatie over de twaalf <input>’s wordt gestopt, waar het script later gebruik van kan maken. Zo zit bijvoorbeeld alle CSS, die aan de <input>’s is gegeven, ook in INPUTS. Maar niet alleen de in het stijlbestand opgegeven CSS, ook alle standaardwaarden, attributen, alle CSS die van voorouders wordt geërfd, enzovoort zitten in INPUTS.

Van al deze informatie in INPUTS kan het script gebruik maken. En ook kunnen veel van deze dingen worden veranderd door het script, zoals een kleur veranderen, of een element verwijderen, of juist toevoegen.

Feitelijk zijn de twaalf <inputs>’s elk in de vorm van een object in INPUTS opgeslagen. Naast allerlei informatie die in die objecten zelf in INPUTS wordt opgeslagen, kun je daardoor ook gebruik maken van allerlei methoden, die JavaScript gratis en voor niets toevoegt aan de objecten in INPUTS.

Eerder werd beweerd dat een met const aangemaakte variabele niet veranderd kan worden. Dat is ook zo voor wat er in INPUTS zélf is opgeslagen: je kunt geen <input> meer toevoegen of verwijderen of zoiets. De twaalf <input>’s zijn echter in de vorm van een object opgeslagen, en bínnen dat object kun je wel van alles wijzigen. Het script gebruikt de objecten in variabele INPUTS onder andere om bij de <input>’s de ARIA-code aria-expanded te veranderen.

In plaats van het gebruik van variabele INPUTS zou je in de rest van het script ook elke keer document.querySelectorAll("main input") kunnen gebruiken, maar dat maakt code al snel onleesbaar. Bovendien hoeft het nu maar één keer opgezocht te worden. Maar dat laatste speelt hier eigenlijk geen rol, omdat het opzoeken snel gaat en het maar om twaalf <inputs>’s gaat.

(Als je je afvraagt, waarom er naar ’main input’ wordt gezocht en niet gewoon naar ’input’: dat zou hier inderdaad kunnen. Maar vaak zullen er meerdere <input>’s op een pagina zijn, die niet allemaal in de variabele moeten worden opgenomen. Daarom worden de <input>’s beperkt tot de <input>’s in een ander element. Hier is dat andere element <main>. Op de site zijn inderdaad andere <input>’s aanwezig, die buiten <main> staan en die absoluut niet in dit script terecht mogen komen.)

document.querySelector("body").addEventListener("keydown", toonVerbergToets);

document.querySelector("body"): eerst het eerste deel van deze regel. Het middelste stukje querySelector is een zogenaamde ’functie’. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam ’document’. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode querySelector() uit document kan JavaScript het eerste element van ’n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:

querySelector("body")

Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in CSS. In bovenstaande regel wordt naar het element <body> gezocht, net zoals je in CSS in een selector body {...} zou gebruiken.

Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:

querySelector("span:first-child")

Precies zoals selectors in CSS werken.

Het hele deel document.querySelector("body"): zoek het element <body> op in de huidige pagina. Je zou kunnen zeggen dat dit eerste deel dus eigenlijk gewoon <body> betekent: er moet iets gebeuren met het <body>-element van de pagina.

addEventListener: er wordt een zogenaamde ’eventlistener’ gekoppeld aan het voor de punt staande document.querySelector("body"), dus eigenlijk aan <body>. Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de ’event’, kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener() staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar ’n soort rampenplan: áls gebeurtenis is ’doodsmak’, dán handeling is ’bel 112’.

Omdat deze eventlistener aan <body> is gekoppeld, werkt deze eventlistener overal binnen <body> en de nakomelingen van <body>, dus binnen de hele pagina.

"keydown": tussen aanhalingstekens, zodat het script weet dat dit een letterlijke naam is, een ’string’ (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: ’keydown’: als een toets wordt ingedrukt.

Deze regel roept de functie aan, die het eigenlijke werk gaat doen.

toonVerbergToets: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo, een ’string’ gaat. De naam verwijst naar een ’functie’, iets wat moet gebeuren. Die functie staat iets hieronder bij function toonVerbergToets(e) en regelt het tonen en verbergen van de <div>’s met tekst, afbeelding en link door gebruik van de toetsen Enter en Escape. Ook wordt de ARIA-code aria-expanded aangepast.

;: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: als ergens binnen <body> een toets wordt ingedrukt, voer dan de functie ’toonVerbergToets’ uit.

In plaats van het koppelen van de eventlistener gelijk hier helemaal af te handelen, zou je <body> ook eerst in een variabele kunnen opslaan, en die variabele vervolgens in een functie of zo kunnen gebruiken om de eventlistener te koppelen. Maar omdat <body> alleen hier wordt gebruikt, en omdat het om iets eenvoudigs gaat, is het makkelijker om het hele gebeuren hier in één regel af te handelen.

Meestal zal niet ’keydown’, maar ’keyup’ worden gebruikt als gebeurtenis: als de toets weer wordt losgelaten. Het maakt niet zoveel uit, maar dat is gebruikelijker. In dit geval kan dat echter niet.

In function toonVerbergToets(e) wordt onder andere getest of de ingedrukte toets Escape is. Bij het indrukken van Escape wordt standaard het laden van een pagina gestopt. In dit voorbeeld wordt Escape ook gebruikt om een getoonde <div> weer te verbergen. Voor de meeste geteste browsers is dat geen probleem. Vivaldi echter stopt het laden van de pagina en blokkeert vervolgens elk verder gebruik van Escape bij gebruik van ’keydown’. Als ’keyup’ wordt gebruikt, werkt alles wel zoals bedoeld.

(Je zou ook een of andere min of meer ingewikkelde test kunnen bedenken op een goede werking, maar daar is dit niet belangrijk genoeg voor.)

document.querySelector("main").addEventListener("click", toonVerbergMain);

Deze regel is vrijwel hetzelfde als die gelijk hierboven bij document.querySelector("body").addEventListener("keydown", toonVerbergToets);. Er zijn drie verschillen.

* De eventlistener wordt hier niet aan <body>, maar aan <main> gekoppeld.

* Hier wordt niet geluisterd naar ’keydown’, maar naar ’click’: als de muis wordt ingedrukt, of als een touchscreen wordt aangeraakt (toen ’click’ werd bedacht, bestonden er nog geen touchscreens. Je kon alleen maar ’click’ doen met de muis, vandaar de wat achterhaalde naam.)

* De functie die hier wordt aangeroepen is niet ’toonVerbergToets’, maar function toonVerbergMain(e). Deze functie verbergt een eventueel getoonde <div>, als buiten de puzzel binnen <main> wordt geklikt, of als het scherm daar wordt aangeraakt. Ook wordt de ARIA-code aria-expanded aangepast.

INPUTS.forEach(item => {

INPUTS: in de variabele INPUTS zijn bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s van de puzzel opgeslagen in de vorm van een object. Een object is een bij elkaar horende verzameling van functies en andere code, waarin veel informatie over het opgeslagen element, de kinderen daarvan, enzovoort zit. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

forEach(item =>: doe iets met elk van de <input>’s in variabele INPUTS.

forEach(): dit is zo’n hierboven genoemde methode. Omdat voor de punt INPUTS staat, wordt de methode uitgevoerd op de <inputs>’s die in de vorm van een object in INPUTS zitten.

forEach zorgt ervoor dat elk van die <inputs>’s wordt verwerkt. Van de eerste tot en met de laatste. Als een <input> in variabele INPUTS is verwerkt, wordt met de volgende <input> verder gegaan, tot de hele lijst met <inputs>’s is verwerkt.

item: om de <input> die aan de beurt is te kunnen verwerken, moet het beestje ’n naam hebben: item. Dit is een veel gebruikte naam in dit soort bewerkingen. Ook element wordt vaak gebruikt. Maar je kunt desnoods ook rottigeKerstboom gebruiken als naam, alleen maak je je dan niet populair bij degene die ooit je code moet bekijken (en bij je toekomstige zelf van over twintig jaar, want zelf begrijp je dit dan ook niet meer).

=>: een vereenvoudigde manier om te zeggen dat hierop een functie volgt. De functie wordt hier gelijk uitgevoerd en kan, omdat de functie geen naam heeft, niet worden aangeroepen: het is een ’anonieme’ functie. Of eigenlijk: het is een speciaal soort anonieme functie, een ’arrow function’. De code die de functie moet uitvoeren staat tussen de {}. De { staat op deze regel, de bijbehorende } staat aan het eind van de functie.

De hele regel in gewone taal: voer bij elk van de <input>’s in variabele INPUTS de code uit die tussen de { en de } achter => staat.

item.addEventListener("click", toonVerbergKlik);

Deze regel is onderdeel van de anonieme functie die begint bij INPUTS.forEach(item => {

Deze regel wordt voor elk item één keer uitgevoerd.

item: bij const const INPUTS = document.querySelectorAll("main input"); zijn de twaalf <input>’s in de vorm van een nodelist opgeslagen in variabele INPUTS, elk in de vorm van een object. Er zitten dus twaalf objecten in INPUTS.

De regel hier is onderdeel van de anonieme functie die begint bij INPUTS.forEach(item => {. Die functie regelt dat achtereenvolgens voor elk object in INPUTS iets wordt gedaan, totdat alle twaalf objecten zijn afgewerkt. Omdat de objecten bij een <input> horen, worden dus eigenlijk de twaalf <input>’s een voor een afgewerkt.

De regel INPUTS.forEach(item => { moet een naam aan de regel hier doorgeven, want anders weet de regel hier niet waar wat mee moet gebeuren. Als naam is item gebruikt. Achtereenvolgens worden dus alle twaalf objecten binnen INPUTS afgewerkt.

In elk object in variabele INPUTS zit een enorme hoeveelheid informatie over de <input>, waar het object bij hoort. Naast die in INPUTS opgeslagen informatie kun je ook gebruik maken van allerlei functies, die JavaScript gratis en voor niets toevoegt aan het object INPUTS.

Een functie bij een object werkt iets anders dan een gewone functie, daarom heet een functie bij een object een ’methode’.

addEventListener: dit is zo’n methode. Er wordt een zogenaamde ’eventlistener’ gekoppeld aan het voor de punt staande object item, dus eigenlijk aan de <input> die aan de beurt is. Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de ’event’, kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener() staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar ’n soort rampenplan: áls gebeurtenis is ’doodsmak’, dán handeling is ’bel 112’.

Omdat deze eventlistener aan elk item binnen INPUTS is gekoppeld, werkt deze eventlistener bij elke <input>.

"click": tussen aanhalingstekens, zodat het script weet dat dit een letterlijke naam is (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: ’click’: als de muis wordt ingedrukt, of als een touchscreen wordt aangeraakt (toen ’click’ werd bedacht, bestonden er nog geen touchscreens. Je kon alleen maar ’click’ doen met de muis, vandaar de wat achterhaalde naam.)

Deze regel roept de functie aan, die het eigenlijke werk gaat doen.

toonVerbergKlik: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. De naam verwijst naar een ’functie’, iets wat moet gebeuren. Die functie staat iets hieronder bij function toonVerbergKlik(e) en toont of verbergt de <div> die bij de aangeklikte of aangeraakte <input> hoort. Bovendien worden eventueel andere getoonde <div>’s verborgen. Ook wordt de ARIA-code aria-expanded aangepast.

;: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: voeg aan elke <input> een eventlistener toe die bij een klik (of een aanraking) functie toonVerbergKlik() uitvoert.

function toonVerbergToets(e) {

Ook deze functie is, zoals elke functie, weer een stukje bij elkaar horende code. Maar anders dan de buitenste functie wordt deze niet automatisch uitgevoerd. (Waarom de buitenste functie wel automatisch wordt uitgevoerd, is te vinden bij (function ().)

De code in deze functie wordt alleen uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen gebeurt, als binnen <main> een toets wordt ingedrukt. Het luisteren naar dat indrukken wordt geregeld bij document.querySelector("body").addEventListener("keydown", toonVerbergToets);.

De code in deze functie regelt, wat er gebeurt, als de ingedrukte toets Enter of Escape is.

Als Enter is ingedrukt:

– vink de <input> die de focus heeft uit als die is aangevinkt, en omgekeerd;

– pas de ARIA-code aria-expanded aan;

– Als de <input> die de focus heeft is aangevinkt, vink dan alle andere <input>’s uit.

Als Escape is ingedrukt:

– vink alle <input>’s uit;

– pas de ARIA-code aria-expanded aan.

Een <input> kan standaard worden aan- en uitgevinkt met de Spatiebalk. In dit voorbeeld is echter niet te zien dat het om <input>’s gaat. Daarom kun je met behulp van JavaScript ook met Enter de <input>’s aan- en uitvinken. Daarbij is het belangrijk dat echt op de Enter-toets wordt gecontroleerd en dat deze functie niet ook reageert op de Spatiebalk.

Standaard vinkt de browser een <input> aan of uit met de Spatiebalk, onafhankelijk van deze functie. Als dat in deze functie ook nog ’ns zou gebeuren, zou de <input> snel na elkaar worden aan- en weer uitgevinkt, of omgekeerd. ’n Leuke manier om mensen dol te maken, maar toch niet helemaal de bedoeling.

(Je kunt het aan- en uitvinken van de <input>’s met de Spatiebalk uitschakelen met e.preventDefault(), maar dat is geen goed idee. Ook in schermlezers werkt de Spatiebalk dan niet meer, waardoor voor gebruikers van schermlezers onduidelijk is, hoe de <input> kan worden aan- of uitgevinkt.)

Als een <div> wordt getoond, ziet dat er enigszins uit als een pop-up. Een pop-up kan vaak worden gesloten met Escape. Deze functie zorgt ervoor dat de <div>’s ook met Escape kunnen worden verborgen.

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven.

toonVerbergToets: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van ’hé!’, ’hé, jij daar!’, ’hé, jij daar in die zwarte jas’, en dergelijke., maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke. niet gebruikt mogen worden in een naam. In CSS zou je hier bijvoorbeeld toon-verberg-toets kunnen gebruiken in plaats van toonVerbergToets.

(e): die haakjes horen nou eenmaal zo na het sleutelwoord function. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie. Zoals de plaats waar het scherm is aangeraakt of -geklikt. In dit geval wordt e doorgegeven.

e is een zogenaamd object. In een object zitten allerlei gegevens over hoe de functie is aangeroepen (over dat aanroepen later meer). In dit geval wordt deze functie aangeroepen door het indrukken van een toets. In e zit bijvoorbeeld, welke toets is ingedrukt, of de toets blíjft ingedrukt (repeteert), en de taal waarvoor het toetsenbord is geconfigureerd.

Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

Heel formeel is e eigenlijk geen object, maar is e een parameter, iets dat wordt doorgegeven aan de functie, zodat het binnen die functie gebruikt kan worden. e is de naam van het object, en de inhoud van e is een object. Om het object iets te kunnen vragen, of het iets te laten doen, moet het beestje ’n naam hebben: e.

De naam e voor het object is niet verplicht, maar ’n soort afspraak, zodat code makkelijker door anderen is te begrijpen. Maar als je het object niet e, maar ’hetIsStervenskoud’ wilt noemen, is daar technisch geen enkel bezwaar tegen. Het is dan wel verstandig een cursus zelfverdediging te volgen, voor het geval iemand anders ooit je code moet bekijken.

(e is een afkorting van ’event’, gebeurtenis. De functie reageert op een gebeurtenis, in dit geval het indrukken van een toets. In e zit het object dat bij díé gebeurtenis hoort. Bij bijvoorbeeld een muisklik krijg je een ander object met andere informatie dan bij het indrukken van een toets.)

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }.

if (e.key === "Enter") {

Deze regel is onderdeel van function toonVerbergToets(e)

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Als de Enter-toets wordt ingedrukt, wordt de <input> die de focus heeft aangevinkt als die niet is aangevinkt, en omgekeerd.

if: dat betekent gewoon ’als’: als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function toonVerbergToets(e), de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.

key: dit is zo’n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

"Enter": dit is in JavaScript de naam van de Enter-toets. De inhoud van key, oftewel de naam van de ingedrukte toets, moet ’Enter’ zijn: de Enter-toets moet zijn ingedrukt.

{: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel in gewone taal: als de Enter-toets is ingedrukt, voer dan de code tussen de {} achter de if uit.

for (let i = 0; i < INPUTS.length; i++) {

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde voldaan:

– De Enter-toets is ingedrukt.

for: doe iets met iets, zolang iets waar is. Nou, is dat lekker vaag of niet? Wat er waarmee moet gebeuren, komt later. Dat staat tussen de {} aan het eind van de regel. (De } staat in werkelijkheid niet aan het eind van de regel, maar lager, achter de code die bepaalt, wat er waarmee moet gebeuren.)

Hier eerst het deel dat ervoor zorgt dat elk van de twaalf in de vorm van een object in variabele INPUTS pgeslagen <inputs>’s netjes aan de beurt komt.

(: met dit haakje opent het deel dat ervoor zorgt dat elke <input> aan de beurt komt. Aan het eind van de regel staat de bijbehorende ), waardoor het script weet dat dit het einde van dit deel van de code is.

let i = 0;: met het sleutelwoord let wordt aangegeven dat het erop volgende woord de naam van een ’variabele’ is: de variabele wordt hier ’aangemaakt’ of ’gedeclareerd’.

Gelijk na let volgt de naam van de variabele: ’i’. Een variabele is een soort portemonnee: er kan van alles in zitten, en de inhoud kan veranderen.

In i wordt dus iets opgeborgen. Omdat de variabele een naam heeft, kan de variabele worden aangeroepen bij deze naam. Net zoals je iemand die ’Marie’ heet kunt aanroepen met ’Marie’, ongeacht of Marie aardig, onaardig, muzikaal of arrogant is, ongeacht de ’inhoud’ van Marie.

Omdat variabele i met het sleutelwoord let is aangemaakt, kan de variabele alleen worden gebruikt binnen de code bij deze for, binnen de { op deze regel en de } die het eind van de code bij deze for aangeeft.

Meer over variabelen is te vinden bij const INPUTS = document.querySelectorAll("main input");

Met het isgelijkteken geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat, moet worden opgeslagen. In dit geval is dat resultaat heel simpel: het is het getal 0.

De variabele heet hier ’i’. Deze variabele wordt als teller gebruikt (waarover later meer), en bij dit soort tellers is het gebruikelijk ze de korte naam ’i’ (van ’index’) te geven. Dat soort gewoontes maakt het voor mensen een stuk makkelijker elkaars code te lezen en te begrijpen.

Omdat i een variabele is, kan de waarde ervan veranderen, variëren. Wat later in de regel ook gaat gebeuren: elke keer als er een <input>-object is afgehandeld, wordt i met 1 verhoogd. Dat gebeurt in het laatste deel van de regel.

De ; geeft aan dat dit stukje code, waarin de variabele i een beginwaarde krijgt, hier eindigt.

i++: eerst het laatste deel van de regel, het middelste deel komt gelijk hieronder.

Elke keer als een in INPUTS zittend <input>-object is afgehandeld, 1 optellen bij i. Omdat programmeurs liederlijk lui zijn, wordt ’er 1 bij optellen’ ingekort tot ++.

Als i 0 is, betekent i++: i wordt 0 + 1 (’1’ dus).

Als i 1 is, betekent i++: i wordt 1 + 1 (’2’ dus).

Als i 2 is, betekent i++: i wordt 2 + 1 (’3’ dus).

Als i 11 is, betekent i++: i wordt 11 + 1 (’12’ dus).

En meer dan twaalf wordt het niet, vanwege redenen die gelijk hieronder staan.

(Op een pagina met een ander aantal <input>’s dan twaalf zal eerder of later worden gestopt met het verhogen van i.)

i < INPUTS.length;: het middelste deel van de regel. In variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

In het sleutelwoord length zit het aantal in INPUTS opgeslagen elementen. Dat zijn er hier twaalf. INPUTS.length betekent hier dus eigenlijk gewoon het getal 12.

i bevat een getal. Voordat het eerste <input>-object is verwerkt, is dat getal 0, want dat is hierboven opgegeven.

Het teken < betekent: kleiner dan.

De ; aan het einde geeft aan dat dit stukje code, de voorwaarde waaraan moet worden voldaan, hier eindigt.

In gewone taal staat hier: zolang teller i kleiner is dan INPUTS.length, kleiner dan twaalf.

Als niet meer aan deze voorwaarde wordt voldaan, als teller i niet meer kleiner is dan twaalf, stop dan met het uitvoeren van de code die tussen de {} van de for-lus staat. Omdat length even groot is als het aantal <input>-objecten in INPUTS, geeft dit een mogelijkheid om elk <input>-object precies één keer te verwerken. En er, als ze allemaal verwerkt zijn, mee te stoppen.

): dit haakje hoort bij de ( gelijk achter for. Tussen deze twee haakjes staat de code die ervoor zorgt dat elk <input>-object aan de beurt komt.

{: het laatste teken op de regel. Hiermee geef je aan dat hierna de code volgt die uitgevoerd moet worden. Tot nu toe is alleen gezorgd dat er bij elk <input>-object íéts moet gebeuren, maar nog niet wát. Dat wát volgt na deze {. Na de code die moet worden uitgevoerd staat nog een afsluitende }. Hiermee geef je aan dat het uitvoerende deel van de code bij de for-lus hier stopt.

Dat uitvoerende deel is op een nieuwe regel gezet, net zoals de afsluitende }. Dat soort dingen zijn informele afspraken, omdat het de code voor mensen leesbaarder maakt. Wat de computer betreft zou je alles ook achter elkaar kunnen zetten op één onwijs lange regel. Alleen is niet alleen die regel dan onwijs, ook de gemiddelde programmeur zou heel snel bijzonder onwijs worden, als alles op één lange regel wordt gezet.

Elke keer als een <input>-object is behandeld (met het stukje code tussen de {}, wat hieronder aan de beurt komt), wordt i door middel van i++ met 1 verhoogd.

Aan het begin heeft i de waarde 0. Na de eerste ronde heeft i de waarde 1. Na de tweede ronde heeft i de waarde 2. Na de derde ronde heeft i de waarde 3. Na de twaalfde ronde heeft i de waarde 12. Waarmee i niet meer kleiner is dan INPUTS.length, dan het aantal <input>-objecten.

Omdat niet meer aan de voorwaarde i < INPUTS.length wordt voldaan, wordt gestopt met het behandelen van de <input>-objecten. En dat komt goed uit, want alle <input>-objecten zijn allemaal precies één keer aan de beurt geweest. Niet meer, niet minder. Bij allemaal is de tussen de {} staande code één keer toegepast.

De code tussen de haakjes in gewone taal: zet teller i op 0 als de for-lus de eerste keer wordt uitgevoerd. Herhaal de for-lus zolang teller i lager is dan het aantal <input>-objecten. Verhoog teller i elke keer als de for- lus wordt uitgevoerd met 1.

if (INPUTS[i].id === e.target.id) {

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

Als de id van de <input> die wordt verwerkt hetzelfde is als de id van de <input> die de focus had toen Enter werd ingedrukt, wordt de code tussen de {} bij de if uitgevoerd.

if: dat betekent gewoon ’als’: als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if.

INPUTS: In variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus.

id: de <input>’s zijn in variabele INPUTS opgeslagen in de vorm van objecten. In die objecten zit allerlei informatie over de <input>’s. Dit is zo’n stukje informatie: de id van de <input> die wordt verwerkt.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

e: bij function toonVerbergToets(e) is in e een object doorgegeven, waarin heel veel informatie zit over de <input> die wordt verwerkt. Meer over e is te vinden bij (e).

target: dit is zo’n stukje informatie uit het hierboven genoemde object. Hierin zit, ook weer in de vorm van een object met veel informatie, de <input> die de focus had op het moment dat Enter werd ingedrukt.

id: dit is zo’n stukje informatie uit target: de id van <input> die de focus had op het moment dat Enter werd ingedrukt.

{: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel in gewone taal: als de id van de <input> die wordt verwerkt hetzelfde is als de id van de <input> die de focus had toen Enter werd ingedrukt.

INPUTS[i].checked = !INPUTS[i].checked;

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

Vink de <input> die wordt verwerkt uit als die is aangevinkt, en omgekeerd.

INPUTS: in variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus. In dit object is onder andere allerlei informatie over de <input> opgeslagen.

checked: dit is zo’n stukje informatie. In checked is opgeslagen, of de <input> is aangevinkt of niet. Maar in dit geval is dat eigenlijk niet van belang, omdat checked voor het isgelijkteken staat. Dat wil zeggen dat het resultaat van wat achter het isgelijkteken staat, wordt opgeslagen in het deel voor het isgelijkteken.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande deel het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

!: een uitroepteken betekent in JavaScript ’niet’. Wat achter het uitroepteken staat wordt hierdoor omgedraaid.

INPUTS[i]: dit is weer het object met de <input> die aan de beurt is.

checked: hierin is opgeslagen, of de <input> is aangevinkt of niet. Als de <input> is aangevinkt, is checked ’true’, als de <input> niet is aangevinkt is checked ’false’.

!INPUTS[i].checked bij elkaar: als checked ’true’ is, als de <input> is aangevinkt, levert dit ’false’ op, omdat het uitroepteken de waarde van checked omkeert. Als checked ’false’ is, levert dit juist ’true’ op.

De hele regel in gewone taal: stop in INPUTS[i].checked voor het isgelijkteken het resultaat van !INPUTS[i}.checked achter het isgelijkteken.

Als INPUTS[i] is aangevinkt, is checked achter het isgelijkteken ’true’, maar dat wordt door het uitroepteken veranderd in ’false’. Stop die waarde in checked voor het isgelijkteken. Hierdoor wordt INPUTS[i] uitgevinkt.

Als INPUTS[i] niet is aangevinkt, gebeurt het omgekeerde: de <input> wordt aangevinkt.

Omdat hierdoor ook de selector input:checked in de CSS wordt beïnvloed, wordt de <div> met tekst, afbeelding en link die bij de <input> hoort getoond als deze was verborgen, en verborgen als deze werd getoond.

INPUTS[i].setAttribute("aria-expanded", INPUTS[i].checked);

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

Geef ARIA-code aria-expanded de waarde ’true’ als de <input> die aan de beurt is is aangevinkt, en de waarde ’false’ als de <input> niet is aangevinkt.

INPUTS: in variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus. In dit object is onder andere allerlei informatie over de <input> opgeslagen. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

setAttribute(): dit is zo’n hierboven genoemde methode. Omdat voor de punt INPUTS[i] staat, wordt de methode uitgevoerd op de <input> die aan de beurt is en in de vorm van een object in INPUTS zit.

Met deze methode kan een attribuut bij een element worden aangebracht of gewijzigd. Omdat het attribuut hier al in de HTML aanwezig is (het gaat om aria-expanded="false"), wordt het niet nieuw aangebracht, maar wordt eventueel de waarde gewijzigd.

Tussen de haakjes staat voor de komma de naam van het attribuut, achter de komma staat de waarde die het attribuut moet krijgen.

"aria-expanded": de naam van het attribuut. Hiermee wordt voor schermlezers aangegeven of de bij de <input> horende <div> wel of niet wordt getoond.

INPUTS[i]: verwijst weer naar het object met de <input> die aan de beurt is.

checked: hierin is opgeslagen, of die <input> is aangevinkt of niet. Als de <input> is aangevinkt, heeft checked de waarde true. Als de <input> niet is aangevinkt, heeft checked de waarde false.

INPUTS[i].checked samen is dus altijd true of false.

;: De puntkomma geeft het eind van dit deel van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel INPUTS[i]("aria-expanded", INPUTS[i].checked); zorgt ervoor dat aria-expanded de waarde true of false krijgt, waardoor schermlezers kunnen melden of het menu zichtbaar is of niet.

Als de <input> niet is aangevinkt, is this.checked ’false’ en komt bij de <input> die aan de beurt is in de HTML aria-expanded="false" te staan. Als de <input> is aangevinkt, is this.checked ’true’ en komt bij de <input> die aan de beurt is in de HTML aria-expanded="true" te staan.

Een door JavaScript in de code aangebrachte wijziging is niet in de broncode van de site te zien. Om de door JavaScript aangebrachte wijziging te zien, moet je de code bekijken in het ontwikkelgereedschap van de browser.

} else {

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze else hoort bij een eerdere if. De code bij deze else wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is niet hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

}: dit is de afsluitende accolade van de code die bij de tweede hierboven genoemde if hoort. Die heeft dus eigenlijk niets met deze else te maken.

else: als aan de voorwaarde bij de eerdere if niet wordt voldaan, voer dan de code tussen de {} bij deze else uit. Er staat hier verder geen voorwaarde of zo: deze code wordt gewoon altijd uitgevoerd, als aan de voorwaarde bij de eerdere if niet wordt voldaan, dus als de id van de <input> die wordt verwerkt niet hetzelfde is als de id van de <input> die de focus had toen Enter werd ingedrukt.

{: de code die wordt uitgevoerd, als aan deze else wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze else hoort. Aan het eind van de code bij deze else staat de afsluitende }.

INPUTS[i].setAttribute("aria-expanded", false);

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regel hoort bij de iets hierboven staande else. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is niet hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

Deze regel is vrijwel hetzelfde als die bij INPUTS[i].setAttribute("aria-expanded", INPUTS[i].checked);. Alleen staat hier achter de komma niet INPUTS[i].checked, maar false.

Als een <input> de focus heeft, kan die met Enter worden aan- of uitgevinkt. Alleen bij die <input> moet de bij die <input> horende <div> met tekst, afbeelding en link eventueel worden getoond. Die code is al eerder afgehandeld.

Deze regel wordt alleen uitgevoerd, als de <input> niet de focus had. In dat geval moet de <div> bij die <input> nooit worden getoond. Als die <div> eventueel al werd getoond, moet die worden verborgen, om te voorkomen dat twee (of meer) <div>’s gelijktijdig worden getoond.

Daarom is hier het deel na de komma false de ARIA-code aria-expanded moet altijd de waarde ’false’ krijgen, omdat de <div> altijd verborgen is.

INPUTS[i].checked = false;

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De Enter-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regel hoort bij de iets hierboven staande else. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is niet hetzelfde als de id van de <input> die de focus had toen Enter werd ingedrukt.

Deze regel is vrijwel hetzelfde als die bij INPUTS[i].checked = !INPUTS[i].checked; . Alleen staat hier achter de komma niet !INPUTS[i].checked, maar false.

Als een <input> de focus heeft, kan die met Enter worden aan- of uitgevinkt. Alleen bij die <input> moet de bij die <input> horende <div> met tekst, afbeelding en link eventueel worden getoond. Die code is al eerder afgehandeld.

Deze regel wordt alleen uitgevoerd, als de <input> niet de focus had. In dat geval moet de <div> bij die <input> nooit worden getoond. Als die <div> eventueel al werd getoond, moet die worden verborgen, om te voorkomen dat twee (of meer) <div>’s gelijktijdig worden getoond.

Daarom is hier het deel na de komma false: de <input> moet altijd worden uitgevinkt, zodat de bij de <input> horende <div> wordt verborgen.

} else if (e.key === "Escape") {

Deze regel is onderdeel van function toonVerbergToets(e)

Deze else if hoort bij een eerdere if. De code bij deze else if wordt alleen uitgevoerd, als aan onderstaande voorwaarde:

– De Escape-toets is ingedrukt.

Als de Escape-toets wordt ingedrukt, worden alle eventueel getoonde <div>’s verborgen.

Als aan de voorwaarde bij de if, waar deze } else if { bij hoort, niet is voldaan, volgt hier een herkansing. In stukjes gehakt wordt ook deze regel weer veel overzichtelijker. (Die voorwaarde bij de eerdere if is dat Enter is ingedrukt.)

} else if: de } aan het begin is de afsluitende } van de code, die wordt uitgevoerd als aan de voorwaarde bij de if is voldaan. Die heeft dus eigenlijk niets met deze else if te maken.

else if letterlijk vertaald betekent ’anders als’. Oftewel: als aan de eerder bij if gestelde voorwaarde niet is voldaan, kijk dan of aan deze andere voorwaarde misschien wel wordt voldaan.

Tussen de haakjes staat de voorwaarde, waaraan moet worden voldaan om de code bij deze else if uit te voeren: e.key === "Escape".

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function toonVerbergToets(e), de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.

key: dit is zo’n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

"Escape": dit is in JavaScript de naam van de Escape-toets. De inhoud van key, oftewel de naam van de ingedrukte toets moet ’Escape’ zijn: de Escape-toets moet zijn ingedrukt.

{: het begin van de eventueel bij deze else if uit te voeren code wordt aangegeven met de { aan het eind van de regel. Het eind van de eventueel bij deze else if uit te voeren code wordt aangegeven met een }.

De hele regel in gewone taal: als de ingedrukte toets Escape is. De code tussen de {} achter de else if wordt alleen uitgevoerd, als aan deze voorwaarde is voldaan: Escape moet zijn ingedrukt. Als aan deze voorwaarde niet wordt voldaan, wordt de code tussen de {} niet uitgevoerd.

for (let i = 0; i < INPUTS.length; i++) {

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel hoort bij de iets hierboven staande else if. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De Escape-toets is ingedrukt.

Deze regel zorgt ervoor dat de code die tussen de { op deze regel en de } iets verderop voor elke in de vorm van een object in INPUTS opgeslagen <input> één keer wordt uitgevoerd.

De regel is precies hetzelfde als die bij for (let i = 0; i < INPUTS.length; i++) {. Een uitgebreide beschrijving is daar te vinden.

INPUTS[i].setAttribute("aria-expanded", false);

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarde is voldaan:

– De Escape-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Geef ARIA-code aria-expanded de waarde ’false’.

INPUTS: in variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus. In dit object is onder andere allerlei informatie over de <input> opgeslagen. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

setAttribute(): dit is zo’n hierboven genoemde methode. Omdat voor de punt INPUTS[i] staat, wordt de methode uitgevoerd op de <input> die aan de beurt is en in de vorm van een object in INPUTS zit.

Met deze methode kan een attribuut bij een element worden aangebracht of gewijzigd. Omdat het attribuut hier al in de HTML aanwezig is (het gaat om aria-expanded="false"), wordt het niet nieuw aangebracht, maar wordt eventueel de waarde gewijzigd.

Tussen de haakjes staat voor de komma de naam van het attribuut, achter de komma staat de waarde die het attribuut moet krijgen.

"aria-expanded": de naam van het attribuut. Hiermee wordt voor schermlezers aangegeven of de bij de <input> horende <div> wel of niet wordt getoond.

false: de waarde die het attribuut moet krijgen.

;: De puntkomma geeft het eind van dit deel van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De regel in gewone taal: geef aan de ARIA-code aria-expanded bij de <input> die aan de beurt is de waarde ’false’. Hierdoor is voor schermlezers duidelijk dat de bij de <input> horende <div> met tekst, afbeelding en link niet wordt getoond.

Als de Escape-toets is ingedrukt, moeten alle eventueel getoonde <div>’s worden verborgen. Daarom is hier geen verdere controle of zoiets nodig om te zien, bij welke <input> Escape is ingedrukt: gewoon alle <div>’s verbergen, dus bij elke <input> aria-expanded als waarde ’false’ geven.

Een door JavaScript in de code aangebrachte wijziging is niet in de broncode van de site te zien. Om de door JavaScript aangebrachte wijziging te zien, moet je de code bekijken in het ontwikkelgereedschap van de browser.

INPUTS[i].checked = false;

Deze regel is onderdeel van function toonVerbergToets(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarde is voldaan:

– De Escape-toets is ingedrukt.

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Vink de <input> die aan de beurt is uit.

INPUTS: in variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus. In dit object is onder andere allerlei informatie over de <input> opgeslagen. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object ’methode’.

checked: dit is zo’n stukje informatie. In checked is opgeslagen, of de <input> is aangevinkt of niet. Maar in dit geval is dat eigenlijk niet van belang, omdat checked voor het isgelijkteken staat. Dat wil zeggen dat het resultaat van wat achter het isgelijkteken staat, wordt opgeslagen in het deel voor het isgelijkteken.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande deel het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

false: vink de <input> uit.

De hele regel in gewone taal: vink de <input> die aan de beurt is uit.

Als de Escape-toets is ingedrukt, moeten alle eventueel getoonde <div>’s worden verborgen. Daarom is hier geen verdere controle of zoiets nodig om te zien, bij welke <input> Escape is ingedrukt: gewoon alle <div>’s verbergen, dus elke <input> uitvinken.

function toonVerbergKlik(e) {

Ook deze functie is, zoals elke functie, weer een stukje bij elkaar horende code. Maar anders dan de buitenste functie wordt deze niet automatisch uitgevoerd. (Waarom de buitenste functie wel automatisch wordt uitgevoerd, is te vinden bij (function ().)

De code in deze functie wordt alleen uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen gebeurt, als een <input> wordt aangeraakt of -geklikt. Het luisteren naar die aanraking of klik wordt geregeld bij INPUTS.forEach(item => { en de daaropvolgende regel.

De code in deze functie regelt, wat er gebeurt, als een <input> wordt aangeraakt of -geklikt. De browser regelt zelf al, zonder dat deze functie daarvoor nodig is, dat de <input> bij een aanraking of klik wordt aangevinkt als deze was uitgevinkt, en omgekeerd. Daardoor wordt de bijbehorende <div> verborgen of getoond. Deze functie zorgt dat de ARIA-code aria-expanded de juiste bijbehorende waarde krijgt.

Daarnaast worden eventueel aangevinkte <input>’s uitgevinkt, als het browservenster buiten de puzzel wordt aangeraakt of -geklikt. Daardoor worden eventueel getoonde <div>’s verborgen. De ARIA-code aria-expanded krijgt bij elke <input> de waarde ’false’. De getoonde <div>’s zien eruit als een pop-up. Vaak kan zo’n pop-up worden gesloten door een aanraking of klik buiten de pop-up. Hier kan dat nu met behulp van deze functie ook door een aanraking of klik buiten de puzzel (een aanraking of klik bínnen de puzzel zal een andere <div> tonen, want de puzzel wordt volledig afgedekt door <input>’s).

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven.

toonVerbergKlik: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van ’hé!’, ’hé, jij daar!’, ’hé, jij daar in die zwarte jas’, en dergelijke., maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke. niet gebruikt mogen worden in een naam. In CSS zou je hier bijvoorbeeld toon-verberg-klik kunnen gebruiken in plaats van toonVerbergKlik

(e): die haakjes horen nou eenmaal zo na het sleutelwoord function. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie. Zoals de plaats waar het scherm is aangeraakt of -geklikt. In dit geval wordt e doorgegeven. Een uitgebreidere beschrijving van e staat bij (e).

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }.

for (let i = 0; i < INPUTS.length; i++) {

Deze regel is onderdeel van function toonVerbergKlik(e)

Deze regel zorgt ervoor dat de code die tussen de { op deze regel en de } iets verderop voor elke in de vorm van een object in INPUTS opgeslagen <input> één keer wordt uitgevoerd.

De regel is precies hetzelfde als die bij for (let i= 0; i < INPUTS.length; i++) {. Een uitgebreide beschrijving is daar te vinden.

if (INPUTS[i].id === e.target.id) {

Deze regel is onderdeel van function toonVerbergKlik(e)

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is hetzelfde als de id van de aangeraakte of -geklikte <input>.

Als de id van de <input> die wordt verwerkt hetzelfde is als de id van de aangeraakte of -geklikte <input>, wordt de code tussen de {} bij de if uitgevoerd.

if: dat betekent gewoon ’als’: als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if.

INPUTS: In variabele INPUTS zijn eerder bij const INPUTS = document.querySelectorAll("main input"); de twaalf <input>’s binnen <main> in de vorm van objecten opgeslagen.

[i]: in i zit het volgnummer van het object met de <input> dat aan de beurt is. Dit volgnummer wordt opgegeven bij de for-lus.

id: de <input>’s zijn in variabele INPUTS opgeslagen in de vorm van objecten. In die objecten zit allerlei informatie over de <input>’s. Dit is zo’n stukje informatie: de id van de <input> die wordt verwerkt.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

e: bij function toonVerbergKlik(e) is in e een object doorgegeven, waarin heel veel informatie zit over de <input> die wordt verwerkt. Meer over e is te vinden bij (e).

target: dit is zo’n stukje informatie uit het hierboven genoemde object. Hierin zit, ook weer in de vorm van een object met veel informatie, de aangeraakte of -geklikte <input>.

id: dit is zo’n stukje informatie uit target de id van de aangeraakte of -geklikte <input>.

{: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel in gewone taal: als de id van de <input> die wordt verwerkt hetzelfde is als de id van de aangeraakte of -geklikte <input>.

INPUTS[i].setAttribute("aria-expanded", INPUTS[i].checked);

Deze regel is onderdeel van function toonVerbergKlik(e)

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is hetzelfde als de id van de aangeraakte of -geklikte <input>.

Geef ARIA-code aria-expanded de waarde ’true’ als de <input> is aangevinkt en de waarde ’false’ als de <input> niet is aangevinkt.

Deze regel is precies hetzelfde als die bij INPUTS[i].setAttribute("aria-expanded", INPUTS[i].checked);. Een beschrijving staat daar.

} else {

Deze regel is onderdeel van function toonVerbergKlik(e)

Deze regel zit binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze else hoort bij een eerdere if. De code bij deze else wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is niet hetzelfde als de id van de aangeraakte of -geklikte <input>.

}: dit is de afsluitende accolade van de code die bij de hierboven genoemde if hoort. Die heeft dus eigenlijk niets met deze else te maken.

else: als aan de voorwaarde bij die if niet wordt voldaan, voer dan de code tussen de {} bij deze else uit. Er staat hier verder geen voorwaarde of zo: deze code wordt gewoon altijd uitgevoerd, als aan de voorwaarde bij de if niet wordt voldaan, dus als de id van de <input> die wordt verwerkt niet hetzelfde is als de id van de <input> die werd aangeraakt of -geklikt.

{: de code die wordt uitgevoerd, als aan deze else wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze else hoort. Aan het eind van de code bij deze else staat de afsluitende }.

INPUTS[i].setAttribute("aria-expanded", false);

INPUTS[i].checked = false;

Deze twee regels zijn onderdeel function toonVerbergKlik(e)

Deze twee regels zitten binnen een for-lus en wordt voor elke <input> in <main> één keer uitgevoerd.

Deze regels horen bij de iets hierboven staande else. De regels worden alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de <input> die wordt verwerkt is niet hetzelfde als de id van de aangeraakte of -geklikte <input>.

De eerste regel: geef de ARIA-code aria-expanded bij de <input> die wordt verwerkt de waarde ’false’. Hierdoor is voor schermlezers duidelijk dat de <div> met tekst, afbeelding en link wordt verborgen.

De tweede regel: vink de <input> die wordt verwerkt uit. Als Escape wordt ingedrukt, moeten alle eventueel aangevinkte <input>’s worden uitgevinkt. Hierdoor worden eventueel getoonde <div>’s met tekst, afbeelding en link verborgen.

Omdat bij het indrukken van Escape álle eventueel getoonde <div>’s moeten worden verborgen, hoeft nergens op gecontroleerd te worden.

Een uitgebreide beschrijving van deze twee regels is te vinden bij INPUTS[i].setAttribute("aria-expanded", false); en INPUTS[i].checked = false;.

function toonVerbergMain(e) {

Ook deze functie is, zoals elke functie, weer een stukje bij elkaar horende code. Maar anders dan de buitenste functie wordt deze niet automatisch uitgevoerd. (Waarom de buitenste functie wel automatisch wordt uitgevoerd, is te vinden bij (function ().)

De code in deze functie wordt alleen uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen gebeurt, als <main> ergens buiten de puzzel wordt aangeraakt of -geklikt. Het luisteren naar die aanraking of klik wordt geregeld bij document.querySelector("main").addEventListener("click", toonVerbergMain);

De code in deze functie regelt wat er gebeurt, als <main> ergens wordt aangeraakt of -geklikt: alle <input>’s worden uitgevinkt en bij elke <input> krijgt de ARIA-code aria-expanded de waarde ’false’.

<main> is een blok-element en wordt normaal genomen niet hoger dan nodig is om de inhoud ervan weer te geven. Die inhoud is in dit geval de puzzel. Daardoor zou een aanraking of klik onder de puzzel geen effect hebben, omdat <main> even hoog is als de puzzel. Maar omdat <main> bij main met height: 100vh; even hoog is gemaakt als het venster van de browser, werkt een aanraking of klik onder de puzzel ook.

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven.

toonVerbergMain: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van ’hé!’, ’hé, jij daar!’, ’hé, jij daar in die zwarte jas’, en dergelijke., maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke. niet gebruikt mogen worden in een naam. In CSS zou je hier bijvoorbeeld toon-verberg-main kunnen gebruiken in plaats van toonVerbergMain.

(e): die haakjes horen nou eenmaal zo na het sleutelwoord function. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie. Zoals de plaats waar het scherm is aangeraakt of -geklikt. In dit geval wordt e doorgegeven. Een uitgebreidere beschrijving van e staat bij (e).

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }.

if (e.target.tagName === "MAIN") {

Deze regel is onderdeel van function toonVerbergMain(e)

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– Het element dat werd aangeraakt of -geklikt is <main>.

Als het element dat werd aangeraakt of -geklikt <main> is.

if: dat betekent gewoon ’als’: als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function toonVerbergMain(e), de functie waar deze regel een onderdeel van is, is it object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.

target: dit is zo’n stukje informatie uit het hierboven genoemde object. Hierin zit, ook weer in de vorm van een object met veel informatie, het element dat werd aangeraakt of -geklikt.

tagName: dit is zo’n stukje informatie uit target: de naam van het aangeraakte of -geklikte element.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

"MAIN": dit is de naam van het element: <main>. In JavaScript wordt die naam in hoofdletters en tussen aanhalingstekens geschreven.

{: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel in gewone taal: als de naam van het aangeraakte of -geklikte element <main> is, voer dan de code tussen de {} achter de if uit.

Aan deze voorwaarde kan alleen worden voldaan, als het scherm búíten de puzzel is aangeraakt. Alleen op die plaats is de naam van het aangeraakte of -geklikte element <main>. Overal bínnen de puzzel is de naam van het aangeraakte of -geklikte element <input>.

for (let i = 0; i < INPUTS.length; i++) {

Deze regel is onderdeel van function toonVerbergMain(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde voldaan:

– Het element dat werd aangeraakt of -geklikt is <main>.

Deze regel zorgt ervoor dat de code die tussen de { op deze regel en de } iets verderop voor elke in de vorm van een object in INPUTS opgeslagen <input> één keer wordt uitgevoerd.

De regel is precies hetzelfde als die bij for (let i = 0; i < INPUTS.length; i++) {. Een uitgebreide beschrijving is daar te vinden.

INPUTS[i].setAttribute("aria-expanded", false);

INPUTS[i].checked = false;

Deze regels zijn onderdeel van function toonVerbergMain(e)

Deze regels worden alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde voldaan:

– Het element dat werd aangeraakt of -geklikt is <main>.

Deze regels zitten binnen een for-lus en worden voor elke <input> in <main> één keer uitgevoerd.

De eerste regel: geef de ARIA-code aria-expanded bij de <input> die wordt verwerkt de waarde ’false’. Hierdoor is voor schermlezers duidelijk dat de bij de <input> horende <div> met tekst, afbeelding en link wordt verborgen.

De tweede regel: vink de <input> die wordt verwerkt uit. Als de naam van het element dat werd aangeraakt of -geklikt <main> is, is de aanraking of klik altijd buiten de puzzel. Als de aanraking of klik binnen de puzzel zou zijn, zou de naam van het aangeraakte of -geklikte element <input> zijn.

Omdat bij een aanraking of klik buiten de puzzel álle eventueel getoonde <div>’s moeten worden verborgen, hoeft nergens op gecontroleerd te worden.

Een uitgebreide beschrijving van deze twee regels is te vinden bij INPUTS[i].setAttribute("aria-expanded", false); en INPUTS[i].checked = false;.