Skip links en inhoudsopgave

Menu met links met toelichting en indicatie van huidige, bezochte en onbezochte pagina's - uitleg

Laatst aangepast: .

Menu met acht links en extra informatie bij de links.

Korte omschrijving

Menu met acht links. In een smal browservenster moet het menu eerst worden geopend. Bij gebruik van muis of toetsenbord verschijnt een klein venstertje met informatie. De link kan gewoon worden gevolgd. Op een aanraakscherm verschijnt bij de eerste eerste aanraking een venstertje met informatie, bij de tweede aanraking wordt de link gevolgd.

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

In browservensters minimaal 760 px breed staat een horizontale balk met acht knoppen. In smallere vensters zijn de knoppen verborgen en moet je ze eerst zichtbaar maken. In die smallere vensters staan de knoppen niet naast elkaar, maar onder elkaar.

De werking is in smallere en bredere browservensters precies hetzelfde. Alleen staan in smallere vensters de venstertjes met informatie naast de knoppen en in bredere vensters eronder.

De knoppen zijn blauw met witte tekst, maar als erover wordt gehoverd of als ze de focus hebben, worden ze wit met zwarte tekst. Vooral in smallere browservensters is dat nodig om te laten zien, bij welke knop het venstertje met informatie hoort. De knop voor de huidige pagina heeft een afwijkende kleur.

In het verleden konden websites aan de hand van gewijzigde eigenschappen bij bezochte links zien, welke sites iemand had bezocht. Om deze schending van de privacy te voorkomen, kunnen nog slechts enkele eigenschappen van bezochte links met behulp van css worden aangepast. Eén van die eigenschappen is border-color.

In elke link staat een <span> die even breed is als de link. Die <span> heeft aan de onderkant een border. Als een link nog niet is bezocht, is de border rood. Zodra een link is bezocht, is de border groen.

Tot zover is het allemaal nog niet al te ingewikkeld. Het tonen en verbergen van de venstertjes met informatie is echter 'n stuk lastiger, omdat dat op verschillende manieren kan gebeuren. De eerste fase is voor al die manieren hetzelfde.

Bij openen van de pagina reageren de <a>'s met de links nergens op door het gebruik van pointer-events: none;. Hierdoor wordt een aanraking, hoveren, en dergelijke doorgegeven aan de ouder van de <a>: de <li> waar de <a> in zit. Omdat de <a>'s eigenlijk (nog) geen echte link zijn, is de tekst in de <a>'s niet onderstreept.

Bij de link die bij de huidige pagina hoort gebeurt er verder niets: deze krijgt alleen een rode streep door de tekst, als over de link wordt gehoverd of als deze de focus heeft. De werking van de andere zeven links hangt af van de gebruikte bedieningsmethode.

Als met een muis over een knop wordt gehoverd, wordt bij die knop een venstertje met informatie geopend. Gelijktijdig wordt bij de in de <li> zittende <a> pointer-events: none; veranderd in pointer-events: auto;. Hierdoor gaat de <a> werken als een gewone link: bij klikken erop wordt de link gevolgd. De tekst in de <a> wordt onderstreept, zoals min of meer gebruikelijk is bij een link. Omdat de link nu werkt als een gewone link, wordt nu bij klikken op de link de link gevolgd.

Bij hoveren over een knop gaat de verandering van een niet-werkende link in een werkende link zo snel, dat het onmogelijk is om op de link te klikken, voordat deze werkt. De bezoeker merkt dus helemaal niets van de niet-werkende link: alles lijkt gewoon normaal te werken.

Voor gebruikers van de Tab-toets werkt het, zoals het altijd werkt: pointer-events heeft geen invloed op de werking van het toetsenbord. Met behulp van de Tab-toets wordt van link naar link gesprongen, en bij indrukken van Enter wordt de link gevolgd. Zodra een link de focus heeft, wordt de tekst in de link onderstreept en wordt het bij de link horende venstertje met informatie getoond.

In browservensters minimaal 760 px breed zijn de acht knoppen met links altijd zichtbaar. Het kan knap irritant zijn, als op elke pagina acht keer de Tab-toets moet worden ingedrukt, voordat de eigenlijke inhoud van de pagina wordt bereikt. Daarom is bovenaan de pagina een skip-link aangebracht, waarmee de acht knoppen in een keer kunnen worden gepasseerd.

Op een aanraakscherm werkt bovenstaande allemaal niet. Hoveren wordt daar hetzelfde behandeld als een aanraking. Zodra je een knop aanraakt, gebeurt er bij die knop hetzelfde als iets hierboven bij Als met een muis over een knop wordt gehoverd wordt beschreven: de link gaat gelijk werken. Dat betekent op een aanraakscherm dat de link gelijk bij de eerste aanraking zou worden gevolgd, waarbij – afhankelijk van besturingssysteem en browser – soms in een flits het venstertje met informatie nog heel even is te zien.

Door aan elke <li> een tabindex="-1" te geven, kan de <li> door deze aan te raken de focus krijgen. Omdat de <a> in de <li> met behulp van pointer-events: none; niet op een aanraking reageert, wordt de link niet gevolgd, maar krijgt de <li> de focus. Hierdoor wordt het bij de link horende venstertje met informatie getoond en wordt de tekst in de link onderstreept.

Zodra de <li> de focus heeft, wordt bij de <a> pointer-events: none; veranderd in pointer-events: auto;. Hierdoor wordt bij een tweede aanraking van de knop de link gevolgd.

Schermlezers volgen gelijk de link. Met behulp van WAI-ARIA wordt geregeld dat de informatie uit het venstertje gelijk na de tekst in de link wordt voorgelezen, afhankelijk van de schermlezer al dan niet na een kleine vertraging.

Dan is er nog een hele reeks css om te voorkomen dat meldingen te breed worden, soepel tonen en verbergen, en dergelijke, maar deze zijn voornamelijk voor het uiterlijk van belang.

Semantische elementen en WAI-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 <nav>, <main> en <section> gebruikt.

<nav>

<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.

Om wat voor soort links het gaat, is echter niet duidelijk. Daarom is gelijk onder <nav> een <h2> aangebracht met de tekst 'Menu voorbeeld'. Deze <h2> is onzichtbaar buiten het scherm geplaatst, maar wordt gewoon voorgelezen door schermlezers. Hierdoor is ook voor schermlezers gelijk aan het begin van de <nav> duidelijk, wat voor soort links in de <nav> zitten.

Zeker als er meerdere <nav>'s aanwezig zijn, zoals in het voorbeeld op de site het geval is, is zo'n <h> aan het begin van de <nav> heel belangrijk.

<main>

Hierbinnen staat de belangrijkste inhoud van de pagina (in dit voorbeeld is dat de tekst onder de knoppen. Eigenlijk is dat hier niet helemaal juist, omdat het hier juist om de knoppen en niet om die tekst gaat. Maar normaal genomen zal dat niet zo zijn.)

<section>

Min of meer samenhangend deel van een pagina, deel van een tekst, en dergelijke.

In dit voorbeeld is de tekst onder de knoppen onderverdeeld in een aantal <section>'s. Gelijk onder elke <section> staat een <h>, zodat schermlezers makkelijk van <section> naar <section> kunnen springen.

WAI-ARIA-codes

WAI-ARIA wordt vaak ingekort tot ARIA. Voluit betekent het Web Accessibility Initiative - Accessible Rich Internet Applications.

Er worden in dit voorbeeld zeven WAI-ARIA-codes gebruikt: aria-labelledby, aria-controls, aria-expanded, aria-current, aria-describedby, role="tooltip" en aria-hidden.

aria-labelledby

Gelijk onder de <nav> met de links staat een <h2> met 'Menu voorbeeld'. Veel gebruikers van schermlezers 'springen' van kopregel naar kopregel, zodat ze snel de inhoud van de pagina kunnen scannen.

Door in de <nav> met aria-labelledby een verwijzing naar de id van de <h2> aan te brengen, worden <nav> en <h2> aan elkaar gekoppeld:

<nav id="menu-vb" aria-labelledby="h2-vb">

<h2 id="h2-vb">Menu voorbeeld</h2>

Nu is voor schermlezers duidelijk om wat voor soort links het binnen de <nav> gaat. Zeker als er meerdere <nav>'s aanwezig zijn, zoals bijvoorbeeld op de site het geval is, is dit heel belangrijk.

(Je zou de <nav> ook voor schermlezers herkenbaar kunnen maken met <nav aria-label="Menu voorbeeld">, maar dan missen gebruikers van een schermlezer die van <h> naar <h> springen de hele <nav>.)

aria-controls

Met behulp van een aankruisvakje kan in browservensters smaller dan 760 px het menu worden verborgen of getoond. Met behulp van aria-controls wordt het aankruisvakje gekoppeld aan het bijbehorende menu in ul#menu-ul-vb:

<input id="toon-menu-vb" type="checkbox" aria-controls="menu-ul-vb" aria-expanded="false">

<ul id="menu-ul-vb">

Op dit moment wordt aria-controls door bijna geen enkele schermlezer ondersteund, maar mogelijk verandert dat in de toekomst en wordt dan bijvoorbeeld gezegd, waar het aankruisvakje voor dient.

aria-expanded

In browservensters smaller dan 760 px kan het menu worden getoond en verborgen door het aan- of uitvinken van een aankruisvakje. Op het scherm is dat duidelijk te zien, maar voor schermlezers is onduidelijk, of het menu wel of niet wordt getoond.

Met behulp van aria-expanded kan aan een schermlezer worden doorgegeven, of het menu wordt getoond of niet. De schermlezer zal dit dan melden. (Hoe dat precies wordt gemeld, verschilt per schermlezer.)

aria-expanded="false" geeft aan dat het menu is verborgen, aria-expanded="true" geeft aan dat het menu wordt getoond. Bij openen van de pagina wordt het menu niet getoond en ziet dat er zo uit:

<input id="toon-menu-vb" type="checkbox" aria-controls="menu-ul-vb" 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 het menu niet wordt getoond, omdat de waarde bij aria-expanded altijd false blijft.

aria-current

Alle acht knoppen met links worden op elke pagina herhaald. Maar op elke pagina is één knop eigenlijk overbodig, omdat die bij de pagina zelf hoort. Je zou die knop kunnen weglaten, maar dat wordt 'n rommeltje. Op de ene pagina linkt de vierde knop dan naar pagina drie, op een andere pagina linkt de vierde knop naar pagina twee.

Daarom worden steeds alle acht knoppen getoond. De knop die bij de huidige pagina hoort heeft een ander uiterlijk, waardoor gelijk duidelijk is dat die knop 'n apart geval is.

Schermlezers hebben aan dat andere uiterlijk niets. Daarom wordt bij de knop die bij de huidige pagina hoort aria-current="page" toegevoegd:

<a href="#" aria-current="page">Home<span></span></a>

Een schermlezer zal nu iets zeggen als 'Home, huidige pagina' als deze link wordt voorgelezen.

aria-describedby

aria-describedby verwijst naar de id van een element, waarin een uitgebreidere beschrijving of iets soortgelijks staat. In dit voorbeeld verwijst het van een <a> naar de bijbehorende <span> met extra informatie (behalve bij de link die bij de huidige pagina hoort):

<a href="012-files-dl/menu-012-1-dl.html" aria-describedby="tt-1">Pagina 1<span></span></a><span id="tt-1" role="tooltip" aria-hidden="true">Ruimte voor informatie over de inhoud van pagina 1</span>

Door deze koppeling leest een schermlezer de tekst in de <a> voor, gelijk gevolgd door de tekst in de <span>. Afhankelijk van de schermlezer kan er een kleine pauze zijn tussen voorlezen van de <a> en de <span>

role="tooltip"

Met een role wordt aangegeven, wat voor soort rol dit element speelt. Een algemeen aanvaarde Nederlandse vertaling voor 'tooltip' is er niet, maar het gaat hier om het venstertje met extra informatie:

<span id="tt-1" role="tooltip" aria-hidden="true">Ruimte voor informatie over de inhoud van pagina 1</span>

Als een schermlezer dit ondersteunt, kan deze iets toevoegen als 'Beschrijving' aan het begin van de tekst.

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.

De extra informatie zit in een <span> met role="tooltip", waardoor deze met behulp van aria-describedby in de <a> gelijk na de tekst in de link worden voorgelezen. Je zou denken dat een schermlezer dan begrijpt dat dat voldoende is, maar schermlezers blijken de tekst in de <span> nogmaals voor te lezen. Door aan de <span> aria-hidden="true" toe te voegen, wordt dat voorkomen:

<span id="tt-1" role="tooltip" aria-hidden="true">Ruimte voor informatie over de inhoud van pagina 1</span>

Toetsenbordnavigatie en tabindex

Op een desktopcomputer gebruiken de meeste mensen een muis om over een pagina te navigeren, links te volgen, en dergelijke. Soms gebruiken mensen hier echter een toetsenbord voor, omdat ze geen muis kunnen of willen gebruiken. Navigeren, links volgen, en dergelijke gebeurt dan met behulp van de Tab-toets, Enter, Spatiebalk, pijltjestoetsen, en dergelijke. (Overigens wordt ook bij aanraakschermen soms een toetsenbord en/of muis gebruikt.)

Als je alleen simpele html zou gebruiken, werkt deze toetsenbordnavigatie fantastisch. Maar soms kan een bepaalde constructie in de html of in de css de werking ervan verstoren. Bij het maken van een website moet je er vooral op letten dat alles bereikbaar is met de Tab-toets. Voor andere toetsen zijn dan meestal geen aanpassingen nodig.

Links, invoervelden in formulieren, en dergelijke kunnen met behulp van de Tab-toets één voor één worden bezocht, in de volgorde waarin ze in de html voorkomen. Shift+Tab-toets keert de volgorde van de Tab-toets om. Dit is een belangrijk hulpmiddel voor mensen die om een of andere reden de muis niet kunnen of willen gebruiken. (En het is vaak ook veel sneller dan de muis, vooral in formulieren.)

In sommige browsers en/of besturingssystemen is dit vreemd genoeg standaard uitgeschakeld en is een zoektocht in de instellingen nodig om dit aan te zetten. Maar gebruikers van de Tab-toets zullen dit al hebben gedaan.

Als je met behulp van de Tab-toets een element hebt bereikt, heeft dit focus: als het een link is en je drukt op Enter, wordt de link gevolgd. Bij een tekstveld kun je tekst gaan invoeren. Enzovoort.

De Tab-toets volgt normaal genomen de volgorde van de elementen in de html. Het maakt niet uit, in welke volgorde ze op het scherm staan. Als je met behulp van css de elementen van plaats verwisselt op het scherm, wordt toch gewoon de volgorde in de html gevolgd.

De volgorde van de Tab-toets kan worden veranderd met behulp van het tabindex-attribuut: <div tabindex="3">. Deze <div> zal nu als derde worden bezocht, ook al krijgt een simpele <div> normaal genomen nooit bezoek van de Tab-toets.

Normaal genomen is het gebruik van een tabindex niet nodig. Het is zeker niet bedoeld om de bezoeker als een kangoeroe op een hindernisbaan van onder via links over rechts naar boven te laten springen. Maar soms kan het handig zijn voor kleinere correcties, als de normale volgorde in de html niet optimaal is. Of om een element bereikbaar te maken voor de Tab-toets, zoals de hierboven genoemde <div>.

Schermlezers blijven altijd de volgorde van de html volgen, dus als de tabindex sterk afwijkt van de volgorde in de html, kan dat behoorlijk verwarrend zijn.

De tabindex kan drie verschillende waarden hebben: -1, 0 of een positief getal.

In principe is de volgorde bij gebruik van de Tab-toets als volgt: eerst worden alle positieve getallen in volgorde afgewerkt. Als twee tabindexen dezelfde waarde hebben, wordt de volgorde in de html aangehouden. Daarna worden elementen die automatisch al de focus kunnen krijgen (zoals een link en een knop) en elementen met een tabindex="0" afgewerkt in de volgorde, waarin ze in de html staan.

tabindex="-1"

Een negatieve waarde van -1 zorgt ervoor dat een element, dat normaal genomen door het gebruik van de Tab-toets de focus kan krijgen, volledig wordt genegeerd door de Tab-toets. Zelfs een link met een negatieve tabindex wordt volledig genegeerd.

Ook kan aan een element met een negatieve tabindex dat normaal genomen geen focus kan krijgen, toch de focus worden gegeven met behulp van JavaScript, een klik of een aanraking. Waarbij gebruikers van de Tab-toets daar geen last van hebben, omdat tabindex="-1" wordt genegeerd door de Tab-toets.

In dit voorbeeld wordt tabindex="-1" op drie plaatsen gebruikt.

<html lang="nl" tabindex="-1">

Op Android en Windows kan het venstertje met informatie weer worden gesloten door ergens buiten het menu het scherm aan te raken (op Windows uiteraard alleen als het apparaat een aanraakscherm heeft).

Op iOS en iPadOS is dat niet het geval. Door aan <html> een negatieve tabindex te geven, kan ook op iOS en iPadOS het venstertje worden gesloten door ergens buiten het menu het scherm aan te raken.

(Zoals iets hieronder beschreven heeft ook <main> een tabindex="-1", waardoor het venstertje ook gesloten wordt door ergens buiten het menu <main> aan te raken. Maar dan moet je het scherm binnen <main> aanraken, en <main> bestrijkt mogelijk niet het hele scherm, waardoor dit mogelijk niet overal op het scherm werkt.)

<li tabindex="-1">

Een <li> kan normaal genomen niet de focus krijgen. Maar dat is hier wel nodig, omdat op aanraakschermen een aantal dingen wordt geregeld met behulp van li:focus-within. Als op een aanraakscherm de <li> nu wordt aangeraakt, kan deze toch de focus krijgen.

(Met een muis speelt dit niet, die worden dingen geregeld met li:hover, en dat werkt gewoon standaard bij een <li>.)

<main id="inhoud" tabindex="-1">

Als in een browservenster smaller dan 760 px Escape wordt ingedrukt, terwijl geen van de knoppen de focus heeft, wordt het menu gesloten en de focus op <main> gezet. Daardoor kan het menu snel worden gesloten en kan met scrollen en dergelijke gelijk worden verder gegaan met de tekst en dergelijke onder de knoppen. Dit wordt geregeld met behulp van JavaScript. Maar om <main> de focus te kunnen geven, is het attribuut tabindex="-1" nodig.

tabindex="0"

Als je tabindex="0" gebruikt, kan een element focus krijgen met behulp van de Tab-toets, door een aanraking of door een klik. Ook als dat element normaal genomen geen focus kan krijgen.

tabindex="0" wordt in dit voorbeeld niet gebruikt.

tabindex="..."

Op de plaats van de puntjes moet een positief getal worden ingevuld: het volgnummer. Een element met een positieve tabindex wordt altijd bezocht bij gebruik van de Tab-toets, ook als dit element normaal genomen zou worden genegeerd. Elementen met een tabindex met een positief volgnummer worden altijd als eerste bezocht, voor elementen als een link of tekstveld zonder tabindex, en ook voor elementen met tabindex="0".

In dit voorbeeld wordt een tabindex met een positief getal niet gebruikt.

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.

Op een aanraakscherm wordt hoveren vaak hetzelfde afgehandeld als een aanraking.

Bij gebruik van een muis is er een verschil tussen hoveren en klikken, maar op een aanraakscherm is dat verschil er niet: je raakt een aanraakscherm aan of niet. Dat levert vooral soms problemen op, als bij een element :hover én klikken worden gebruikt, zoals bij een link die bij hoveren erover verkleurt.

Op een laptop met een aanraakscherm zal een aanraking in de regel hetzelfde werken als een klik met een muis: de link wordt gelijk gevolgd en het venstertje met informatie wordt niet of alleen in een flits getoond. Het tonen van het venstertje met behulp van :hover werkt daardoor niet, als op een laptop met een aanraakscherm de link wordt aangeraakt. Daarom is deze constructie niet geschikt, als in het venstertje met informatie belangrijke informatie staat. (Bij gebruik van een muis werkt het wel, omdat dan hoveren en klikken twee verschillende handelingen zijn.)

In browservensters smaller dan 760 px is er weinig ruimte. Daarom is het menu daar verborgen, tot een aankruisvakje wordt aangevinkt. Omdat :hover daar niet wordt gebruikt, levert dit geen problemen op.

Anders is het bij het menu zelf, want daar wordt :hover wel gebruikt. Bij gebruik van een muis wordt bij hoveren over een <li> het venstertje met informatie geopend, en bij een klik op de in de <li> zittende <a> wordt de link gevolgd.

Als het venstertje met informatie wordt geopend door hoveren over de link, wordt op een aanraakscherm ook gelijk de link gevolgd, want die wordt ook aangeraakt. Afhankelijk van besturingssysteem en browser zie je soms in een flits even het venstertje, maar soms zie je het helemaal niet.

Daarom wordt op een aanraakscherm de link met behulp van pointer-events: none; ongevoelig gemaakt voor een aanraking. Omdat de <li>, waar de link in zit, een tabindex="-1" heeft gekregen, is deze op een aanraakscherm juist gevoelig gemaakt voor een aanraking: de <li> kan de focus krijgen.

Hierdoor kan bij de eerste aanraking het venstertje met informatie worden geopend. Gelijktijdig wordt bij de in de <li> zittende link pointer-events: none; veranderd in pointer-events: auto;. Hierdoor zal bij een tweede aanraking de link worden gevolgd.

Op apparaten waar een muis (of een touchpad) de belangrijkste aanwijzer is, mag hoveren gewoon werken. Bij hoveren over de <li> opent het venstertje met informatie en bij klikken op de link wordt de link gevolgd. Omdat je altijd over de <li> hovert als je op de link wilt klikken, werkt dit prima. Met behulp van een @media-regel wordt het hoveren beperkt tot apparaten, waarbij de belangrijkste aanwijzer een muis of een touchpad is.

: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'. (Over het gebruik van de Tab-toets staat meer bij Toetsenbordnavigatie en tabindex.)

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-within: 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-within in alle iets nieuwere browsers de standaard, dus eigenlijk hoef je :focus en/of :focus-within alleen nog te gebruiken, als je bijvoorbeeld het uiterlijk van het kadertje wilt aanpassen.

Voor de Tab-toets zijn hier geen aanpassingen nodig: die gaat gewoon van <a> naar <a>, waarbij bij de <p> met de focus het venstertje met informatie wordt getoond. Ook wordt de achtergrond van de <a> wit en de tekst wordt onderstreept zwart.

Op een aanraakscherm doen zich hier echter wel problemen voor. Als de <a> wordt aangeraakt, wordt de link gelijk gevolgd. Het venstertje met informatie wordt, afhankelijk van browser en besturingssysteem, helemaal niet of heel kort in een flits getoond.

Daarom wordt aan de <li>'s een tabindex="-1" gegeven. Nu kunnen de <li>'s bij aanraking de focus krijgen, waardoor het venstertje met informatie wordt getoond. De achtergrond van de <a> in de <li> wordt wit en de tekst in de <a> onderstreept zwart.

De <a>'s in de links zijn met behulp van pointer-events="none" ongevoelig voor een aanraking gemaakt. Als de <li> de focus krijgt, wordt dit veranderd in pointer-events="auto". Bij de tweede aanraking wordt de link hierdoor gewoon gevolgd.

In browservensters smaller dan 760 px kan de bezoeker het menu tonen of verbergen. Dat verbergen kan ook door het venster ergens buiten het menu aan te raken. In dat geval wordt de focus op <main> gezet, waarin de eigenlijke tekst van de pagina zit. Dit zorgt ervoor dat het geopende menu makkelijk gesloten kan worden, als het een deel van de pagina bedekt.

<main> krijgt de focus met behulp van JavaScript. Omdat <main> normaal genomen geen focus kan krijgen, heeft <main> een tabindex="-1" gekregen.

: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.

Gegenereerde code

Het onderstaande geldt alleen voor desktopbrowsers. In browsers op mobiele systemen is het vaak ook mogelijk gegenereerde code te bekijken, maar dat is veel ingewikkelder. Bovendien verandert de manier, waarop dat kan, nogal snel.

Als je html schrijft, kan dat (hopelijk) in de browser worden weergegeven. Vanuit de browser kun je die html bekijken, precies zoals je de code hebt ingevoerd. Alsof je het in een editor ziet. In Firefox bijvoorbeeld kan dat door in het menu te kiezen voor Extra → Browserhulpmiddelen → Paginabron. (Of door de veel snellere sneltoets Ctrl+U.) Elke browser heeft dit soort mogelijkheden.

Wat je dan te zien krijgt, is exact de code, zoals jij die hebt ingetypt. Inclusief alle fouten, hoofd- en kleine letters, noem maar op. Als je zo slordig bent om een <p> niet af te sluiten, zit er niet plotsklaps een afsluitende </p> in de code. Als er css wordt gebruikt, html wordt ingevoegd via JavaScript, noem maar op, zie je daar niets van, want dat heb jij niet ingetypt.

Daar heb je dus eigenlijk vrij weinig aan, want die code kende je al. Die heb je zelf fluitend of met bloed, zweet en tranen zitten intypen.

Wat de browser daadwerkelijk gebruikt, is iets totaal anders: de gegenereerde code. En die is veel interessanter, want die code blijkt (fors) af te wijken, van wat jij heb ingetypt. De browser gebruikt een tijdelijke kopie van de door jou geschreven code, die zo is aangepast dat er voor de browser mee is te werken.

Elke browser heeft inmiddels mogelijkheden om de gegenereerde code te bekijken. In Firefox bijvoorbeeld in het menu Extra → Browserhulpmiddelen → Webontwikkelaarshulpmiddelen. In Google Chrome in het menu onder Meer hulpprogramma's → Hulpprogramma's voor ontwikkelaars. In Edge open je dit door op F12 te drukken, en het kan vast ook via het menu.

Houdt er wel rekening mee dat elke browser de door jou ingetypte code iets zal aanpassen. In Firefox bijvoorbeeld wordt een <P> veranderd in een <p>. Als er 'n </p> mist, is die opeens wel aanwezig in de gegenereerde code. Wat elke browser precies aanpast, zal iets verschillen en kan ook nog veranderen. Oudere versies van Internet Explorer bijvoorbeeld veranderden een <p> juist in een <P>, nieuwere versies deden dat niet meer.

Als je met behulp van JavaScript elementen invoegt of verwijdert in de HTML (zoals in dit voorbeeld gebeurt), zie je dat alleen in deze gegenereerde code.

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 26 september 2025.

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 6.4.4) (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 10 (1600 x 900 px, resolutie: 106 ppi):
Firefox, Google Chrome en Edge, in grotere en kleinere browservensters.

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

macOS 26 ('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.7 (2048 x 1536 px, device-pixel-ratio: 2:
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

iPad met iPadOS 18.6.2 (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 ('Tiramisu') (2000 x 1200 px, resolutie: 225 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Smartphones

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

iPhone met iOS 18.6.2 (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 9.0 ('Pie') (1920 x 1080 px, resolutie: 424 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Android 14 ('Upside Down Cake') (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 10 is getest met aanraakscherm, touchpad, toetsenbord, muis, en - waar dat zinvol was - op een combinatie daarvan. Op macOS 11.7.10 en 15.6 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 10.

NVDA is een schermlezer, zoals die door blinden wordt gebruikt. Er is getest op Windows 10 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, 9, 13 en 14

VoiceOver is een in iOS en macOS ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS 12.5.7, 15.8.4 en 18.6.2, iPadOS 18.6.2, macOS 11.7.10 en 26.

Orca is een schermlezer voor Linux. Er is getest in combinatie met Firefox op KDE Neon 6.4.4.

Verteller (Narrator) is een in Windows 10 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: zonder JavaScript is de toegankelijkheid slechter.

Zonder JavaScript werkt alles nog steeds, maar voor gebruikers van schermlezers en Tab-toets werkt het minder handig:

  • In browservensters smaller dan 760 px kan het menu alleen worden geopend door een aanraking, een klik of de Spatiebalk. De Enter-toets werkt niet.
  • In browservensters smaller dan 760 px kan het menu niet worden gesloten met Escape.
  • In browservensters smaller dan 760 px wordt de focus niet op <main> gezet, als het scherm ergens buiten het menu wordt aangeraakt.
  • De WAI-ARIA-code aria-expanded houdt altijd de waarde false, waardoor schermlezers altijd melden dat het menu niet wordt getoond, ook als het wel wordt getoond.

Zonder css

Geen problemen.

Alle links werken gewoon. De venstertjes met informatie staan gelijk achter de links op het scherm. Het geheel ziet er niet uit, maar dat is logisch zonder css.

Toetsenbordnavigatie

Geen problemen.

Bij gebruik van de Tab-toets wordt van link naar link gesprongen, waarbij het bij de link horende venstertje met informatie wordt getoond.

Met behulp van JavaScript kan in browservensters smaller dan 760 px het menu ook worden geopend en gesloten met de Enter-toets, en het kan ook worden gesloten met Escape.

Tekstbrowsers

Probleem: webbIE toont de extra informatie niet.

Lynx toont de links met gelijk daarachter de extra informatie.

In webbIE werken de links gewoon, maar de extra informatie wordt niet getoond.

Schermlezers

Probleem: in TalkBack op Android 6 moet de link twee keer worden geactiveerd, voordat deze wordt gevolgd.

Als de link wordt voorgelezen, opent een eerste aanraking het venstertje met informatie. De volgende aanraking zou de link moeten volgen, maar in TalkBack op Android 6 zijn daar twee aanrakingen voor nodig. In TalkBack op Android 7 speelt dit niet meer.

Probleem: in VoiceOver op iPadOS 18.5 en 18.6 (en ook in 18.6.2) wordt het aankruisvakje voor browservensters smaller dan 760 px voorgelezen, alsof het zou werken.

In browservensters smaller dan 760 px kan het menu worden getoond en verborgen door het aan- of uitvinken van een aankruisvakje. Dit aankruisvakje wordt in vensters met een minimale breedte van 760 px verborgen met behulp van display: none;, Schermlezers zouden het hierdoor volledig moeten negeren.

VoiceOver op iPadOS 18.5 en 18.6 (en ook in 18.6.2) leest het aankruisvakje echter toch voor, en ook aan- en uitvinken kan, ondanks dat de geteste tablets breder dan 760 px zijn. Daardoor lijkt het aan- of uitvinken een functie te hebben, terwijl het in deze bredere browservensters geen enkel effect heeft.

In VoiceOver op een tablet met iOS 15.8.4 speelt dit probleem niet, dus het gaat vermoedelijk om een bug die vroeger of later (hopelijk) zal worden opgelost.

Of dit probleem ook speelt in eerdere versies dan iPadOS, is onduidelijk, want daar wordt niet op getest.

Overigens is dit ook geen wereldprobleem: er wordt alleen gemeld dat het aankruisvakje is aangevinkt of niet, en verder gebeurt er niets.

Probleem: in TalkBack op Android voor versie 7 en in iOS voor versie 13 werkt de skip-link niet.

Als een schermlezer de pagina voorleest, wordt eerst het menu voorgelezen. Dat kan behoorlijk irritant zijn, zeker als het menu wat langer is en op 'n (groot) aantal pagina's bovenaan de pagina staat. Daarom is voor het menu een skip-link neergezet, waarmee het menu in één keer gepasseerd kan worden.

Deze skip-link is normaal genomen onzichtbaar, maar dat zou voor schermlezers geen probleem horen te zijn.

In TalkBack, de schermlezer van Android, werkt zo'n link echter pas in Android versie 7 en later.

In VoiceOver op iOS voor versie 13 werkt een link binnen de pagina gewoon helemaal nooit. De link wordt wel netjes voorgelezen door VoiceOver, maar vervolgens gaat het voorlezen gewoon op dezelfde plaats verder. In iOS 13 en iPadOS 13 en later is dit eindelijk opgelost.

Zoomen en andere lettergrootte

Geen problemen.

Overige problemen

Probleem: of een link is bezocht of niet, wordt alleen met een andere kleur aangegeven.

Als een link is bezocht, krijgt deze aan de onderkant een groene balk. Nog niet bezochte links hebben een rode balk. Omdat bezocht of niet bezocht alleen met een andere kleur wordt aangegeven, kan dit problemen opleveren voor mensen die moeite hebben met het onderscheiden van kleuren.

Hier is helaas niets aan te doen. Omdat websites aan de hand van css-eigenschappen konden zien, welke sites iemand had bezocht, kunnen nog maar heel weinig eigenschappen worden veranderd bij een bezochte link. Daardoor kan alleen de kleur van het balkje worden veranderd, maar niet de stijl of de dikte. Ook andere eigenschappen die naast het verschil in kleur ook een verschil in vorm zouden weergeven, kunnen helaas niet worden gebruikt.

Ook de doorzichtigheid kan niet worden veranderd. Als dat wel zou kunnen, zou je de link als die nog niet is bezocht een doorzichtige balk kunnen geven en die balk pas kleuren, als de link is bezocht.

Probleem: de validator van w3c meldt een fout bij de <input>.

Als de css wordt gevalideerd, meldt de validator dat bij <input> het verplichte WAI-ARIA-attribuut role mist. Dit is echter niet meer nodig. De validator loopt soms wat achter, dus deze melding kan gewoon worden genegeerd.

Wijzigingen

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

:

Nieuw opgenomen.

10 augustus 2008:

Bij Bekende problemen (en oplossingen) stukje over verdwijnende letters bij vergroten lettergrootte toegevoegd.

4 oktober 2008:

Bij Bekende problemen (en oplossingen) tekst over Internet Explorer 7 en zoomen toegevoegd.

6 april 2009:

Tekst aangepast aan de nieuw verschenen Internet Explorer 8. De code is hetzelfde gebleven.

16 juli 2013:

De code en de tekst zijn volledig herschreven. Daarom is er geen beginnen aan alle wijzigingen hier te noteren. Een samenvatting:

26 september juni 2025:

Volledig herschreven. De belangrijkste wijzigingen:

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-012-dl.html: de pagina Home met het voorbeeld.

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

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

012-css-dl:

menu-012-dl.css: stijlbestand voor de acht html-pagina's met de menu's.

012-files-dl

de pagina's 1 tot en met 7 met het voorbeeld.

012-js:

menu-012.js: javascript om de toegankelijkheid te verbeteren.

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 tabindex="-1">

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.

tabindex="-1" is nodig om op tablets met iOS en iPadOS het venstertje met informatie weer te kunnen verbergen. Meer hierover bij tabindex="-1".

<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 wordt in vensters smaller dan 760 px het menu alleen op verzoek getoond.

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.

<nav id="menu-vb" aria-labelledby="h2-vb">

<h2 id="h2-vb">Menu voorbeeld</h2>

<nav> geeft aan dat hierbinnen een serie links staat. Voor schermlezers is niet duidelijk om wat voor soort links het gaat. Daarom wordt met behulp van aria-labelledby de <h2> gelijk onder de <nav> aan de <nav> gekoppeld. Meer hierover bij aria-labelledby.

De <h2> wordt buiten het scherm neergezet, zodat deze onzichtbaar is. Voor schermlezers maakt dat echter niets uit: die lezen het gewoon voor.

Veel gebruikers van schermlezers springen van <h> naar <h>, zodat ze snel een overzicht van de pagina krijgen. Ook daarom is die <h> gelijk onder de <nav> neergezet: zonder de <h> zouden schermlezers anders de hele <nav> passeren, als ze van <h> naar <h> springen.

<a id="skippy" href="#inhoud">Skip menu</a>

Sommige mensen gebruiken niet de muis, maar de Tab-toets om links, tekstvelden, en dergelijke af te lopen. Dat kan zijn vanwege een handicap, maar soms is het gewoon ook veel sneller dan de muis, bijvoorbeeld in formulieren.

In browservensters minimaal 760 px breed is het menu met de acht links altijd geopend. Als bovenaan elke pagina datzelfde menu staat, moet de gebruiker eerst door al die links heen. Voor elke link moet één keer de Tab-toets worden ingedrukt. Dat kan knap gaan irriteren, als je bovenaan elke pagina steeds dezelfde acht links tegenkomt. Daarom is bovenaan de pagina een zogenaamde skip-link geplaatst.

Omdat het een gewone link is, wordt deze bij gebruik van de Tab-toets bezocht, krijgt focus. Als de gebruiker dan op Enter drukt, wordt de link gevolgd. In dit geval is het een link naar het begin van de echte inhoud van de pagina bij <main>. Op deze manier kan in één keer een heel menu worden gepasseerd.

Normaal genomen is de skip-link onzichtbaar. Zodra de link focus heeft, wordt deze met behulp van css zichtbaar gemaakt. Als de Tab-toets nogmaals wordt ingedrukt, of als de link wordt gevolgd, verliest de skip-link weer focus en verdwijnt weer van het scherm. Hierdoor verpest de skip-link niet de hele lay-out.

Als mensen de Tab-toets niet gebruiken, krijgen ze de skip-link helemaal niet te zien.

In alle op iets nieuwere besturingssystemen werkt deze link ook. Maar schermlezers hebben ook andere manieren om een serie links te passeren. (Meer over waar de skip-link niet werkt is te vinden bij Probleem: in TalkBack ...)

<input id="toon-menu-vb" type="checkbox" aria-controls="menu-ul-vb" aria-expanded="false">

Met deze <input> kan in browservensters smaller dan 760 px het menu worden getoond of verborgen door het aankruisvakje aan- of uit te vinken.

Met aria-controls wordt de <input> voor schermlezers aan het bijbehorende menu gekoppeld. aria-expanded geeft voor schermlezers aan of het menu wordt getoond of niet.

<label id="label-toon-menu-vb" for="toon-menu-vb">Menu </label>

Deze <label> hoort bij de <input>, waarmee het menu getoond of verborgen kan worden. De <input> zelf heeft geen enkele tekst, waardoor voor schermlezers volstrekt onduidelijk is, waar de <input> voor dient.

Je zou dit kunnen opvangen door de <input> binnen de <label> te zetten:

<label>Menu <input type="checkbox"></label>

(id en dergelijke zijn even weggelaten.

In dit geval kan dat niet, omdat de <label> achter de <input> moet komen te staan. Hierdoor kan met behulp van ::after ' openen' of ' sluiten' achter het woordje 'Menu' worden gezet, afhankelijk van of het aankruisvakje is aangevinkt of niet.

Daarom wordt de <label> met behulp van for gekoppeld aan de id van de <input>. Nu werkt het in schermlezers hetzelfde, als wanneer de <input> binnen de <label> zou staan.

Achter het woordje 'Menu' staat een spatie, omdat hierachter nog het woordje 'openen' of 'sluiten' komt te staan. Zonder die spatie zou 'Menu' daaraan vast komen te staan.

<li><a href="#" aria-current="page">Home</a></li>

Deze link hoort bij de link naar de huidige pagina. De link hier is de link, zoals die er op de pagina Home uitziet. Met behulp van css wordt het uiterlijk van deze link aangepast. Schermlezers hebben niets aan zo'n aanpassing, daarom wordt voor schermlezers aan de link naar de huidige pagina aria-current toegevoegd.

<li tabindex="-1">

Een <li> kan normaal genomen niet de focus krijgen. Maar dat is hier wel nodig, omdat op aanraakschermen een aantal dingen wordt geregeld met behulp van li:focus-within. Door aan de <li> het attribuut tabindex="-1" te geven, kan de <li> op een aanraakscherm bij aanraking toch de focus krijgen.

<a href="012-files-dl/menu-012-1-dl.html" aria-describedby="tt-1">Pagina&nbsp;1<span></span></a>

Dit is een van de zeven links, die niet bij de huidige pagina hoort. Dit is de link naar Pagina 1. Deze zeven links zijn allemaal hetzelfde, alleen verschilt de href en de tekst in de link iets.

aria-describedby is een verwijzing voor schermlezers naar de bijbehorende span#tt-1 met informatie.

De <span> binnen de <a> wordt gebruikt om een groene of rode streep onder de link te zetten, afhankelijk van of deze al is bezocht of niet.

De &nbsp; tussen 'Pagina' en '1' is een harde spatie (in het Engels 'non-breaking space'). Deze voorkomt dat bij een grotere letter de '1' op de volgende regel komt te staan, onder 'Pagina'.

<span id="tt-1" role="tooltip" aria-hidden="true">Ruimte voor informatie over de inhoud van pagina 1</span>

Deze <span> zorgt voor het venstertje met informatie.

role="tooltip" maakt aan schermlezers duidelijk, dat het hier om aanvullende informatie gaat. Bij de <a> gelijk hierboven is met behulp van aria-describedby een koppeling naar deze <span> aangebracht.

aria-hidden voorkomt dat de tekst in de <span> door schermlezers twee keer wordt voorgelezen.

<main id="inhoud" tabindex="-1">

Een <main> kan normaal genomen niet de focus krijgen. Door aan de <main> het attribuut tabindex="-1" te geven, kan <main> bij een aanraking of met behulp van wat JavaScript toch de focus krijgen. Hierdoor kan in browservensters smaller dan 760 px gelijk naar de eigenlijke inhoud van de pagina worden gegaan, als het menu wordt gesloten.

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

Dit is een koppeling naar een klein beetje JavaScript. Ook zonder JavaScript werkt dit voorbeeld, maar het werkt dan (veel) lastiger voor gebruikers van schermlezers of Tab-toets.

Het script past wat elementen op de pagina aan. Om dat te kunnen doen, moeten die elementen eerst zijn geladen. Daarom staat het script helemaal onderaan de pagina, gelijk boven <body>.

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.

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 de stijlbestanden, 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 apparaten en vensters

/* menu-012-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.

background: #ff9;

Achtergrondkleurtje.

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.

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 weghalen.

#menu-vb

Het element met id="menu-vb". De <nav> waar het hele menu in zit. Deze <nav> is in browservensters smaller dan 760 px zichtbaar als de blauwe balk bovenin het venster.

background: blue;

Blauwe achtergrond.

color: white;

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

height: 2.5rem; Menubalk is veel te hoog.

Hoogte.

De inhoud van een <nav> wordt normaal genomen bepaald door wat erin zit. Hier is dat ook zo: het hele menu zit erin. In browservensters smaller dan 760 px staan de acht knoppen in dat menu onder elkaar. Je ziet die knoppen pas, als het menu wordt getoond. Maar ze zijn wel degelijk aanwezig, alleen staan ze onzichtbaar links buiten het venster. Daardoor wordt de <nav>, en daarmee de blauwe balk, even hoog als die acht knoppen onder elkaar.

Op de afbeelding is de hoogte bij de <nav> weggehaald, waardoor de <nav> even hoog wordt als die acht knoppen. Om dat te voorkomen wordt een hoogte aan de <nav> gegeven.

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

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

line-height: 2.5rem;

Regelhoogte.

Tekst staat automatisch in het midden van de regelhoogte. Door de regelhoogte even hoog te maken als de gelijk hierboven aan de <nav> met de menubalk opgegeven hoogte, komt de tekst verticaal in het midden van die balk te staan. Om dat bij elke lettergrootte zo te houden, wordt net als hierboven voor de hoogte de eenheid rem gebruikt.

padding-bottom: 4px;

De tekst in de menubalk 'Menu openen' of 'Menu sluiten' wordt onderstreept. Hoewel de tekst verticaal in het midden staat, lijkt de tekst door die onderstreping iets te hoog te staan. Door aan de onderkant wat ruimte toe te voegen, komt de tekst voor het oog in het midden van de balk te staan. (In werkelijkheid staat de tekst, als je gaat meten, nu iets te hoog.)

#h2-vb

Het element met id="h2-vb". Een voor schermlezers aangebrachte <h2>. Meer over het waarom van deze <h2> is te vinden bij <nav id="menu-vb" aria-labelledby="h2-vb">.

position: absolute;

Om de <h2> 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. Als zo'n voorouder er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.

top: -100px; left: -20000px;

100 px boven en 20000 px links buiten het browservenster zetten. De <h2> wordt ook iets boven het venster gezet, omdat in sommige gevallen de werking van knoppen, links, en dergelijke wordt verstoord, als een element alleen links buiten het venster wordt neergezet.

#skippy

Het element met id="skippy". Een zogenaamde skip-link, waarmee in browservensters minimaal 760 px breed in één keer de acht knoppen met links gepasseerd kunnen worden.

display: none;

In browservensters smaller dan 760 px is deze skip-link niet nodig, omdat het menu eerst geopend moet worden, voordat de knoppen met de links zichtbaar zijn.

#toon-menu-vb

Het element met id="toon-menu-vb". De <input> waarmee het menu in browservensters smaller dan 760 px kan worden getoond en verborgen.

width: 1px; height: 1px;

De input is van het type checkbox, een aankruisvakje. Het aankruisvakje kan worden aan- en uitgevinkt met behulp van de bijbehorende label#label-toon-menu-vb.

Input staat deels over label heen.

Standaard is het aankruisvakje zichtbaar als een vierkantje van ongeveer zeven bij zeven px (het precieze uiterlijk is in elke browser iets anders). Op de afbeelding is te zien, dat het aankruisvakje hier nou niet bepaald oogverblindend mooi staat te wezen. Bovendien is het niet nodig, want het aan- en uitvinken gebeurt met behulp van de <label>.

Daarom wordt de <input> zo klein mogelijk gemaakt: 1px breed en 1 px hoog. 0 px bij 0 px kan niet, want dan werkt het aankruisvakje niet in oudere schermlezers.

margin-left: 10px;

Links een marge van 10 px. Hierdoor wordt de <input> 10 px naar links geschoven. Hier gelijk onder wordt de bijbehorende <label> 15 px naar rechts verplaatst. Nu staat de <input> altijd onder de <label>, ongeacht de lettergrootte, waardoor de <input> altijd onzichtbaar is.

#label-toon-menu-vb

Het element met id="label-toon-menu-vb". De <label> bij de <input> waarmee het menu in browservensters smaller dan 760 px getoond en verborgen kan worden.

cursor: pointer; text-decoration: underline;

Het bij de <label> horende aankruisvakje is gelijk hierboven bij #toon-menu-vb verborgen achter de <label>. Hierdoor is niet echt duidelijk, hoe het menu kan worden geopend of gesloten: er staat bij openen van de pagina alleen de tekst 'Menu openen'. Een gewone tekst, die er niet uitziet alsof je erop kunt klikken of zoiets.

Daarom wordt de cursor veranderd in een cursor die bij een link hoort, en wordt de tekst onderstreept. Hierdoor komt het er meer uit te zien als iets, waar je op kun klikken.

display: inline-block;

Bij voorouder #menu-vb is een regelhoogte van 2,5 rem opgegeven, even hoog als de blauwe menubalk. Deze regelhoogte wordt door de nakomelingen geërfd, maar alleen als dat blok-elementen zijn. En een <label> is een inline-element. Je kunt de <label> met display: block; veranderen in een soort blok-element, maar een blok-element komt standaard op een nieuwe regel te staan. Daardoor zou de <label> niet op dezelfde regel als de bijbehorende <input>, maar op de regel daaronder komen te staan.

Dit combineert een blok‑ en een inline-element. De meeste eigenschappen van een blok-element kunnen worden gebruikt, maar de <label> komt niet op een nieuwe regel te staan.

padding: 0 3px;

Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 0 3px 0 3px in de volgorde boven – rechts – onder – links.

Onder en boven geen padding. Links en rechts een kleine padding. Als de bij de <label> horende <input> de focus krijgt, wordt de achtergrondkleur wit. Ook de padding krijgt deze achtergrondkleur, waardoor voor de eerste en na de laatste letter ook een klein beetje wit komt te staan.

position: relative;

Door de <label> een relatieve positie te geven, kan deze worden verplaatst. Die verplaatsing is ten opzichte van de <label> zelf en wordt gelijk hieronder bij left opgegeven.

left: -15px;

Door de <label> iets naar links te verplaatsen, bedekt deze de bijbehorende <input>. Nu is alleen de tekst 'Menu openen' of 'Menu sluiten' zichtbaar, wat veel mooier is.

#label-toon-menu-vb::after

Met behulp van ::after wordt bij het element met id="label-toon-menu-vb", de <label> met 'Menu', een pseudo-element gemaakt. Dit pseudo-element wordt gebruikt om 'openen' weer te geven.

content: "openen";

Tussen de aanhalingstekens komt de tekst te staan die achter 'Menu ' in de <label> komt te staan.

#menu-ul-vb

Het element met id="menu-ul-vb". De <ul> waar het menu in zit.

display: flex;

De waarde flex is onderdeel van een hele reeks eigenschappen en waarden, die bij elkaar 'flexbox' worden genoemd. Met display: flex; wordt de <ul> 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. De directe kinderen van de <ul> zijn hier de acht <li>'s, waarin de links zitten.

flex-direction: column;

Met deze eigenschap wordt de richting van de hoofdas (in het Engels: 'main axis') aangegeven. Standaard heeft flex-direction de waarde 'row': horizontaal, waardoor de flex items naast elkaar komen te staan: op één regel. Hier moeten de flex items niet naast, maar onder elkaar komen te staan, in een kolom.

width: 6.1rem;

Breedte.

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

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

max-width: 45%;

Maximumbreedte. In smalle browservensters is weinig ruimte. Naast de knoppen met de links moet nog genoeg ruimte overblijven voor de venstertjes met informatie. Daarom wordt de breedte hier beperkt tot maximaal 45%. Nu is er ook bij een grotere letter voldoende ruimte voor de venstertjes.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier het blok-element <nav>. Een blok-element wordt normaal genomen even breed als z'n ouder. De ouder van <nav> is <body>, ook weer een blok-element. <body> wordt daarom normaal genomen even breed als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk krijgt de <ul> hierdoor een maximale breedte van 45% van het venster, ongeacht de breedte van het venster.

list-style-type: none;

De standaardbolletjes en dergelijke bij de <li>'s in een <ul> weghalen.

margin: 5px 0 0;

Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 5px 0 0 0 in de volgorde boven – rechts – onder – links.

Met deze marge aan de bovenkant sluit de <ul> met de knoppen precies aan op de blauwe menubalk, als het menu wordt geopend. Alle andere marges worden weggehaald.

padding: 0;

Standaard padding weghalen.

#menu-ul-vb li

Alle <li>'s binnen het element met id="menu-ul-vb". De acht <li>'s met de knoppen.

height: 2.2rem;

Hoogte.

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

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

line-height: 2.2rem;

Tekst staat automatisch in het midden van de regelhoogte. Door de regelhoogte even hoog te maken als de gelijk hierboven aan de <li>'s opgegeven hoogte, komt de tekst verticaal in het midden van de <li>'s te staan. Om dat bij elke lettergrootte zo te houden, wordt net als hierboven voor de hoogte de eenheid rem gebruikt.

text-align: center;

Tekst horizontaal centreren.

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> zelf, omdat niets voor top en dergelijke wordt opgegeven.

#menu-ul-vb a

Alle links binnen het element met id="menu-ul-vb". De links binnen het menu.

background: blue;

Blauwe achtergrond.

color: white;

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

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.

Dit is eerder ook al bij #menu-vb opgegeven, maar sommige mensen hebben bij álle elementen de kleuren veranderd. Het heeft immers weinig zin, als ze dat alleen bij de body doen, terwijl de sitebouwer de kleuren ook bij bijvoorbeeld de paragrafen heeft aangepast.

display: block;

Een <a> is van zichzelf een inline-element. Daardoor kunnen eigenschappen als breedte niet worden gebruikt. Door de <a> in een blok-element te veranderen, kunnen dit soort eigenschappen wel worden gebruikt.

text-decoration: none;

Onderstreping weghalen.

In een menu met knoppen met links is een onderstreping niet gebruikelijk: het is ook zonder onderstreping al duidelijk dat het om links gaat.

Op een aanraakscherm speelt nog iets anders mee: bij de eerste aanraking wordt de link niet gevolgd, maar opent het venstertje met informatie. Ook wordt de link dan onderstreept, want bij een tweede aanraking wordt de link wel gevolgd.

border: white solid 1px;

Wit randje.

pointer-events: none;

Normaal genomen zullen een knop, een link, en dergelijke reageren op een aanraking of een klik. Nu wordt daar niet op gereageerd: een aanraking van of klik op een van de acht links wordt volledig genegeerd.

De werking van de link wordt hersteld bij hoveren over de link, of als deze de focus krijgt. Bij gebruik van een muis of de Tab-toets werkt de link daardoor op de gebruikelijke manier. (Of eigenlijk: de werking wordt zo snel hersteld, dat je niet merkt dat de link eerst niet werkte.)

Op een aanraakscherm werkt de link niet, maar wordt de aanraking 'doorgegeven' aan ouder <li>. Hierdoor wordt het venstertje met informatie getoond en gaat de link gaat werken. Bij een tweede aanraking wordt de link dan alsnog gevolgd. Het waarom hiervan staat uitgebreider beschreven bij :hover.

#menu-ul-vb a[aria-current]

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.

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb a: alle links binnen het element met id="menu-ul-vb". De links binnen het menu.

[aria-current]: maar alleen als ze een bepaald attribuut hebben. Dat attribuut staat tussen twee teksthaken. In dit geval moet de <a> het aria-current-attribuut hebben.

De hele selector in gewone taal: alle <a>'s binnen ul#menu-ul-vb, maar alleen als ze een aria-current-attribuut hebben.

De link die bij de huidige pagina hoort, ziet er anders uit. Schermlezers hebben niets aan dat andere uiterlijk, daarom wordt dit voor schermlezers met behulp van aria-current aangegeven. Bij elk van de acht pagina's hoort uiteraard een andere link bij de huidige pagina. Omdat bij die link toch al aria-current aanwezig is, kan dat attribuut gelijk worden gebruikt om de link te selecteren, waarvan het uiterlijk moet worden aangepast. Dan hoef je niet voor elke pagina iets anders als li:nth-of-type(3) a of zo te gebruiken.

background: #444;

Donkergrijze achtergrond.

color: white;

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

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.

Dit is eerder ook al bij #menu-vb opgegeven, maar sommige mensen hebben bij álle elementen de kleuren veranderd. Het heeft immers weinig zin, als ze dat alleen bij de body doen, terwijl de sitebouwer de kleuren ook bij bijvoorbeeld de paragrafen heeft aangepast.

cursor: default;

Boven een link verandert de cursor standaard van vorm om aan te geven dat het om een link gaat. Boven de link naar de huidige pagina gebeurt dat nu niet, want dat is geen echte link.

#menu-ul-vb a span

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar de links in zitten.

a: alle <a>'s binnen #menu-ul-vb.

span: de <span>'s binnen die <a>'s.

De hele selector in gewone taal: alle <span>'s binnen een <a> die weer binnen ul#menu-ul-vb zit. In elke <a> zit één zo'n <span>, die voor de rode of groene onderstreping onderin de <a> zorgt.

width: 100%;

In deze <span>'s zit helemaal geen tekst, afbeelding, of wat dan ook. Daardoor hebben ze geen breedte. De groene of rode rand is eigenlijk een border aan de onderkant van de <span>. Als de <span> helemaal geen breedte heeft, is er ook geen border. Daarom krijgt de <span> hier een breedte.

Een <span> is een inline-element en kan daarom geen breedte krijgen. Maar iets hieronder wordt de <span> absoluut gepositioneerd, waardoor deze in een soort blok-element verandert en toch een breedte kan krijgen.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat zou hier de <a> zijn. Maar omdat de <span> iets hieronder absoluut wordt gepositioneerd, wordt die breedte hier genomen 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 <span> in zit, want deze heeft bij #menu-ul-vb li een relatieve positie gekregen.

border-bottom: red solid 5px;

Rode border aan de onderkant. Zodra een link is bezocht, wordt dit veranderd in een groene border.

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. Hier is dat de <li>, waar de <span> in zit, want deze heeft bij #menu-ul-vb li een relatieve positie gekregen.

Een <span> is een inline-element. Daardoor kan er geen breedte aan worden gegeven. Door de <span> absoluut te positioneren verandert deze in een soort blok-element en kan de <span> wel een breedte krijgen.

bottom: -1px; left: -1px; Onderstreping van de links staat te ver naar rechts.

De <span> bestaat alleen uit de border aan de onderkant. Omdat de <span> absoluut is gepositioneerd, komt de <span> bovenin de <li> te staan, het 'containing block'. Daardoor komt de <span> op gelijke hoogte met de bovenkant van de <a> te staan.

De <span> zit in de <a>, gelijk achter de tekst in de <a>. Zonder correctie zou de <span> daardoor achter die tekst komen te staan.

Dit is precies wat er op de afbeelding gebeurt: de <span>, en dus de border, staat aan de bovenkant van de <a>, en achter de tekst in de <a>.

Door de <span> 1 px onder de <li> neer te zetten, naar, komt de border van de <span> gelijk te staan met de onderkant van de <a>. En door de <span> 1 px links van de <li> te laten beginnen, komt de linkerkant van de border gelijk te staan met de linkerkant van de <a>.

#menu-ul-vb a:visited span

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.

#menu-ul-vb a span {width: 100%; border-bottom: red solid 5px; position: absolute; bottom: -1px; left: -1px;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar de links in zitten.

a: alle <a>'s binnen #menu-ul-vb.

:visited: maar alleen als deze <a>'s zijn bezocht.

span: de <span>'s binnen die <a>'s.

De hele selector in gewone taal: alle <span>'s binnen een <a> die weer binnen ul#menu-ul-vb zit, maar alleen als die <a>'s zijn bezocht.

border-bottom-color: #32cd32;

Kleur van de border aan de onderkant veranderen in lichtgroen. Als de <a> nog niet is bezocht, is de kleur van de border rood.

Vanwege privacy kunnen maar heel weinig eigenschappen van een bezochte link worden veranderd. In het verleden konden sites aan de hand van veranderde eigenschappen zien, welke sites iemand had bezocht. Vandaar deze beperking. Een van de weinige eigenschappen die nog kan worden veranderd is de kleur van een border. Meer hierover is te vinden bij Overige problemen.

#toon-menu-vb:checked + #label-toon-menu-vb::after

Voor dit element 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-toon-menu-vb::after {content: "openen";}

#toon-menu-vb: het element met id="toon-menu-vb". De <input> (aankruisvakje) waarmee het menu in browservensters smaller dan 760 px getoond of verborgen kan worden.

:checked: maar alleen als de <input> is aangevinkt.

+: 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-toon-menu-vb: het element met id="label-toon-menu-vb". De <label> die bij de <input> hierboven hoort.

::after: met behulp van ::after wordt bij label#label-toon-menu-vb een pseudo-element gemaakt. Dit pseudo-element wordt gebruikt om 'sluiten' weer te geven.

De hele selector in gewone taal: als input#toon-menu-vb is aangevinkt, maak dan met behulp van ::after een pseudo-element bij de gelijk op die <input> volgende label#label-toon-menu-vb.

content: "sluiten";

Tussen de aanhalingstekens komt de tekst te staan die achter 'Menu ' in de <label> komt te staan.

Bij openen van de pagina staat hier 'openen', omdat het menu dan nog niet wordt getoond. Als het aankruisvakje is aangevinkt, wordt het menu getoond en wordt 'openen' in 'sluiten' veranderd.

#menu-ul-vb li:focus-within a:not([aria-current])

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.

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:focus-within: maar alleen als het element (hier de <li>) of een van de nakomelingen daarvan de focus heeft.

a: de <a>'s binnen die <li> met focus. Binnen elke <li> zit één <a>.

[aria-current]: van die <a>'s alleen de <a>'s die het attribuut aria-current hebben.

:not([aria-current]): maar dan omgekeerd. Omdat aria-current binnen :not() staat, worden juist alleen de <a>'s geselecteerd, die níét het attribuut aria-current hebben. De pseudo-class :not() geeft aan dat wat tussen de haakjes staat, juist níét geselecteerd moet worden.

De hele selector in gewone taal: de <a> binnen de <li> met focus, of waarvan een van de nakomelingen de focus heeft, binnen ul#menu-ul-vb, maar alleen als die <a> niet het attribuut aria-current heeft. Dat zijn alle <a>'s, behalve de <a> die bij de huidige pagina hoort (en alleen als de <li>, waar de <a> in zit, of de <a> zelf de focus heeft).

background: white;

Witte achtergrond.

color: black;

Zwarte voorgrondkleur. Dat is onder andere de kleur van de tekst.

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.

text-decoration: underline;

Onderstrepen.

Bij #menu-ul-vb a zijn de links met pointer-events: none; ongevoelig gemaakt voor een aanraking of klik: ze werken niet. Zodra de <li>, waar de <a> in zit, de focus heeft, werkt de link weer. Om de <a> er meer als een link uit te laten zien, wordt deze nu onderstreept.

outline: black solid 1px;

Zwart randje rondom de <a>.

Anders dan een border neemt een outline geen ruimte in. Hierdoor heeft een outline geen invloed op de plaats van het venstertje met informatie.

outline-offset: -1px;

De outline 1 px naar binnen zetten. Hierdoor komt die op precies op de goede plaats te staan.

#menu-ul-vb a:focus-within[aria-current]

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.

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb a[aria-current] {background: #444; color: white; cursor: default;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

a: de <a>'s binnen die <ul>.

:focus-within: maar alleen als de <a> of een van de nakomelingen daarvan de focus heeft.

[aria-current]: de <a> moet ook het attribuut aria-current hebben. Dit is de <a> die bij de huidige pagina hoort.

De hele selector in gewone taal: als de <a> binnen het menu die bij de huidige pagina hoort de focus heeft.

text-decoration: red line-through 3px;

Deze link hoort bij de huidige pagina en werkt niet. Om dat duidelijk te maken, wordt door de tekst in de <a> een rode streep gezet.

outline: black solid 1px;

Zwart randje rond de <a>.

#menu-ul-vb a + span

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

a: alle <a>'s in die <ul>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder dezelfde <li>.

span: de <span> die direct op de <a> volgt.

De hele selector in gewone taal: de <span> die gelijk op een <a> in het menu volgt. Dit is de <span>, waarin het venstertje met informatie over de link zit.

background: yellow;

Gele achtergrond.

color: black;

Zwarte voorgrondkleur. Dat is onder andere de kleur van de tekst.

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-sizing: border-box;

Hier iets onder wordt een breedte opgegeven. Standaard worden een border en een padding bij deze breedte opgeteld. Het element wordt daardoor dus breder dan de opgegeven breedte.

Hier komt dat slecht uit. Met de hier opgegeven waarde bij box-sizing komen border en padding bínnen de opgegeven breedte te staan, waardoor het element niet breder wordt dan de opgegeven breedte.

width: 45vw;

Breedte.

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 45 vw is 45% van de breedte. De <span> met het venstertje wordt hierdoor nooit breder dan 45% van de breedte van het venster, ongeacht de breedte van het venster.

line-height: normal;

Bij #menu-ul-vb li is aan ouder <li> een regelhoogte van 2,2 rem gegeven. Die grote regelhoogte wordt geërfd door de nakomelingen. Hier wordt de regelhoogte teruggezet naar de standaardwaarde.

border: black solid 1px;

Zwart randje.

padding: 5px;

Kleine afstand tussen buitenkant van en tekst in 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 <li>, waar de <span> in zit. Deze heeft bij #menu-ul-vb li een relatieve positie gekregen.

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: 0;

De <span> staat in de html gelijk achter de <a> in de <li>. Omdat de <span> hier gelijk boven absoluut is gepositioneerd, is deze in een soort blok-element verandert. Hierdoor zou de <span> niet naast, maar onder de <a> komen te staan. Hiermee komt de bovenkant van de <span> gelijk te staan met de bovenkant van de <a>.

z-index: -1; Zonder z-index zijn alle venstertjes altijd zichtbaar.

Omdat de <span>'s in de html achter de <a>'s staan, komen deze normaal genomen over de <a>'s heen te staan. Omdat de <span>'s niet helemaal buiten het scherm staan, zouden ze hierdoor de <a>'s voor een groot deel afdekken. Dat is precies wat op de afbeelding gebeurt.

Gelijk hieronder worden de <span>'s met transform 80% naar links verplaatst.

Je zou dat percentage zo hoog kunnen maken, dat de <span>'s buiten het browservenster komen te staan. Maar dan wordt soepel tonen en verbergen een stuk lastiger, omdat de <span>'s dan een veel grotere afstand af moeten leggen. Bovendien staan ze dan ook tijdens het zichtbaar worden, als de <span> vanaf links naar binnen schuift, over een aantal <a>'s heen, waardoor die <a>'s voortdurend zichtbaar en onzichtbaar zijn. Mensen zijn van minder zeeziek geworden.

Heel vaak zou de <span> ook gewoon verborgen kunnen worden met display: none; of visibility: hidden;, maar dan wordt de inhoud van de <span> niet voorgelezen door schermlezers.

De eenvoudigste oplossing is het geven van een negatieve z-index.

Elementen met een negatieve z-index komen onder vrijwel al het andere te staan, waardoor de <span>'s de knoppen met de <a>'s niet meer gedeeltelijk afdekken.

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

transform: translateX(-80%); Spans met informatie staan halverwege de knoppen.

Iets hierboven is de <span> met position: absolute; veranderd in een soort blok-element. Maar er zijn nog wat verschillen met een écht blok-element.

Bij #menu-ul-vb li is met text-align: center; opgegeven dat tekst in de <li> horizontaal gecentreerd moet worden. Een echt blok-element negeert dit. Maar de <span> is geen echt blok-element en luistert hier gewoon naar. Als dat niet wordt aangepast, komt de <span> halverwege de <li>, en daarmee ook halverwege de in de <li> zittende <a>, te staan. Op de afbeelding zijn de <span>'s even zichtbaar gemaakt en staan ze inderdaad keurig halverwege de <li>.

De bedoeling is dat de <span>'s bij het zichtbaar worden niet in één keer zichtbaar worden, maar onder de knoppen met de <a>'s vandaan lijken te schuiven. Om dat voor elkaar te krijgen, moet het beginpunt van die verschuiving naar links worden verplaatst.

Met de bij transform horende functie translateX() kun je 'n element ten opzichte van zichzelf verplaatsen. En als je die verplaatsing in procenten opgeeft, gelden die procenten ten opzichte van het element zelf. Omdat de browser bij het maken van de pagina alle maten weet, kan de browser altijd uitrekenen, hoeveel 80% is.

translateX() verplaatst in horizontale richting. Omdat de waarde negatief is, wordt naar links verplaatst. Met 80% in combinatie met de hieronder opgegeven transition-property en transition-duration lijkt het venstertje nu bij tonen onder de knop met de <a> vandaan te komen, en bij verbergen daar weer achter te verdwijnen.

transition-property: transform, z-index; transition-duration: 0.5s, 0s;

Als in browservensters smaller dan 760 px het menu is geopend, staan de acht knoppen met links onder elkaar. Als met een muis over een van die knoppen wordt gehoverd, of als op een aanraakscherm een van die knoppen wordt aangeraakt, wordt de bij die knop horende <span> het het venstertje met informatie getoond. Om die <span> zichtbaar te maken, worden daarbij de eigenschappen left, z-index en translateX() veranderd. (In bredere vensters gebeurt dit op een andere manier, die wordt later besproken.)

Als je bij :hover, :focus, :checked, :user-valid, en dergelijke een andere waarde bij een eigenschap opgeeft, kun je er met transition bij veel eigenschappen voor zorgen dat de oude waarde niet in één keer in de nieuwe waarde wordt veranderd, maar geleidelijk, gedurende een op te geven tijdsduur, of na een vertraging.

De twee eigenschappen hierboven die beginnen met transition horen bij elkaar. Je kunt ze ook combineren in een zogenaamde 'shorthand': transition. Daarachter komen dan in een bepaalde volgorde alle waarden die hierboven achter de twee aparte eigenschappen staan. Bij zo'n serie veranderingen zijn de aparte eigenschappen vaak duidelijker, maar dat is vooral een kwestie van persoonlijke voorkeur.

Het tonen van het venstertje met informatie door een aanraking gebeurt met behulp van :focus-within bij #menu-ul-vb li:focus-within a + span. Het tonen van het venstertje door hoveren met de muis gebeurt met behulp van :hover bij #menu-ul-vb li:hover a + span. Bij beide gebeurt dit op dezelfde manier.

Het tonen van het venstertje gebeurt op een iets andere manier dan het weer verbergen. Die andere manier van tonen en verbergen wordt bereikt door sommige eigenschappen bij :hover en :focus-within te veranderen.

transition-property: transform, z-index:: dit zijn de twee eigenschappen, waarvoor transition geldt, gescheiden door een komma: transform en z-index. De andere transition-eigenschappen hebben alleen invloed op deze twee eigenschappen.

Omdat deze eigenschap bij verderop bij de selectors met :hover of :focus-within niet wordt veranderd, gelden de transition-eigenschappen zowel bij tonen als bij verbergen voor transform en z-index.

transition-duration: 0.5s, 0s;: omdat verderop bij de selectors met :hover en :focus-within voor deze eigenschap geen andere waarden zijn opgegeven bij :hover en :focus-within, zijn de waarden bij tonen en verbergen van het venstertje hetzelfde: de browser gebruikt automatisch dezelfde waarden bij :hover en :focus-within.

Hiermee wordt de duur van de verandering opgegeven. Bij transition-property staan twee eigenschappen, gescheiden door een komma: transform en z-index. Hier staan twee tijdsduren: 0.5s en 0s, gescheiden door een komma. De eerste tijdsduur van 'n halve seconde hoort bij de eerste eigenschap transform, en de tweede eigenschap van nul seconden hoort bij z-index. De horizontale verplaatsing met behulp van transform: translateX() duurt 'n halve seconde. De verandering van de z-index vindt gedurende nul seconden plaats: pats-boem in één keer.

Als de verandering bij z-index in één keer plaatsvindt, waarom die dan opgeven? De reden daarvan staat gelijk hieronder bij transition-delay.

transition-delay: met deze eigenschap wordt een vertraging aan het begin opgegeven. Hier wordt niets opgegeven, dus worden de standaardwaarden weer door de browser ingevuld: 0s, nul seconden. Geen vertraging. (Dit kun je in het ontwikkelgereedschap van de browser zien, op dezelfde manier als iets hieronder bij left wordt beschreven.)

Verderop bij de selectors met :hover en :focus-within worden andere waarden opgegeven: transition-delay: 0s, 0.5s;. Twee waarden, gescheiden door een komma. De eerste waarde van nul seconden hoort weer bij de eerste bij transition-property opgegeven eigenschap transition, de tweede waarde van 'n halve seconde hoort bij de tweede eigenschap z-index. De z-index verandert dus pas na 'n halve seconde.

Waar ongetwijfeld door knappe koppen grondig over is nagedacht, maar wat ongelooflijk verwarrend is: de waarden hier (nul seconden voor beide eigenschappen) gelden voor het verbergen van de <span> met informatie, voor als :hover en :focus-within niet meer geldig zijn. Bij het verbergen is er dus voor beide eigenschappen geen vertraging.

De verderop bij de selectors met :hover en :focus-within opgegeven waarden van nul en 'n halve seconde gelden bij het tonen van de <span> met informatie.

Omdat verderop bij de selectors met :hover en :focus-within een andere waarde is opgegeven dan de browser hier automatisch invult, kun je het tonen en verbergen met een andere vertraging aan het begin laten verlopen.

De hier eerder opgegeven z-index: -1; zorgt ervoor dat de <span> met informatie áchter de knoppen met de links staat, onzichtbaar is. De met behulp van transform: translateX() uitgevoerde horizontale beweging van de <span> met informatie begint gelijk, na nul seconden. Als de z-index ook gelijk zou worden verhoogd, zou de <span> met informatie al zichtbaar zijn, terwijl die nog gedeeltelijk boven de knop met de link staat. Dat geeft een ongelooflijk onrustig beeld. Daarom wordt bij de selectors met :hover en :focus-within verderop transition-delay: 0s, 0.5s; opgegeven.

De combinatie van een gelijk beginnende horizontale beweging gedurende 'n halve seconde, en een z-index die pas na 'n halve seconde wordt verhoogd, laat het erop lijken dat de <span> met het venstertje met informatie achter de knoppen vandaan komt.

Hier is geen waarde opgegeven bij transition-delay, waardoor hier in feite transition-delay: 0s, 0s; staat. Deze waarden gelden voor het weer verbergen van de <span> met informatie. De <span> begint gelijk gedurende 'n halve seconde naar links te schuiven, en omdat de z-index gelijk wordt veranderd in z-index: -1; lijkt de <span> weer onder de knoppen met de links te verdwijnen.

transition brengt een geheel eigen risico met zich mee. In de specificatie staat, welke eigenschappen met behulp van transition kunnen veranderen. Je kunt eventueel bij transition opgeven, voor welke eigenschappen het geldt. Als je bijvoorbeeld top en left beide wilt veranderen bij :checked, kun je bijvoorbeeld aangeven dat de geleidelijke verandering met behulp van transition alleen voor top geldt. left verandert dan gewoon in één keer.

Als je niet opgeeft, voor welke eigenschappen transition geldt, geldt het voor álle eigenschappen die bij :hover, :focus, :checked, en dergelijke worden veranderd. Als daar 'n eigenschap bij zit die op dit moment niet door transition kan worden vertraagd, verandert die gewoon in één keer. Maar als de specificatie of een van de browsermakers de geest krijgt en plotsklaps die eigenschap ook onder transition laat vallen, kan dat tot heel onverwachte resultaten leiden.

Stel dat je een bepaalde eigenschap heel traag wilt veranderen bij :hover en een andere eigenschap flitsend snel. Mogelijk wordt dat flitsend snel dan opeens ook heel traag, als transition in de toekomst die eigenschap ook gaat ondersteunen. Daarom moet je absoluut zeker weten dat ook in de toekomst geen problemen kunnen ontstaan. Bij twijfel: geef aan welke eigenschap(pen) transition mag aansturen.

In dit voorbeeld speelt dit risico niet, omdat bij transition de eigenschappen zijn opgegeven, waarvoor transition geldt. Daardoor blijft transition, wat er eventueel ooit nog gaat veranderen, hoe dan ook alleen voor die eigenschappen gelden.

left

Voor left is hier geen waarde opgegeven, maar toch kan die waarde veranderen: als geen waarde wordt opgegeven, vult de browser automatisch een waarde in. Vaak is dat de standaardwaarde, die bij left 0 is. Hier is die waarde iets anders.

Zoals iets hierboven bij transform: translateX(-80%); beschreven, komt de <span> met het venstertje horizontaal halverwege de <li> te staan, vanwege de bij #menu-ul-vb li opgegeven text-align: center;. In een browservenster met een breedte van 320 px zijn de <li>'s 96 px breed. Als je in Firefox het ontwikkelgereedschap opent, kun je dat in de tab Inspector → Berekend zien onder 'width'. (Omdat de browser die breedte invult, moet je om dat te zien wel rechts bovenin 'Browserstijlen' aanvinken.) Andere browsers hebben soortgelijke hulpmiddelen.

Als je nu in diezelfde tab bij de <span> kijkt, zie je daar dat left niet de waarde 0, maar de waarde 48 px heeft. De <span> met het venstertje komt dus halverwege de <li> te staan. (Op de afbeelding bij transform: translateX(-80%); is dat te zien.)

Door die left:48px; bij het tonen van het venstertje te veranderen in left: 99%; komt de linkerkant van het venstertje precies links van de <li> te staan, en daarmee ook precies links van de in de <li> zittende <a>. Je moet de nieuwe waarde van left bij :hover en :focus-within opgeven in procenten, want bij een andere breedte van het browservenster, verandert de breedte van de <li> ook, en daarmee ook de door de browser ingevulde waarde bij left.

Omdat de eigenschap left hierboven bij transition-property niet wordt genoemd, verandert deze in één keer, zonder vertraging of wat dan ook. De nieuwe waarde bij :focus-within en :hover wordt in één keer 99%. Zou left wél worden genoemd bij transition-property, dan zouden de transition-eigenschappen ook voor left gelden.

#menu-ul-vb li:nth-of-type(n + 5) a + span

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.

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: alle <li>'s binnen ul#menu-ul-vb.

: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 + 5): tussen de haakjes staat aangegeven, om welke <input> of <input>'s het gaat.

Het getal '5' 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 + 5 = 5. Oftewel: de vijfde <li>.

De tweede keer is het resultaat 1 + 5 = 6. Oftewel: de zesde <li>.

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

Bij de laatste berekening is het resultaat 3 + 5 = 8. Hoger is niet zinvol, want bij de volgende berekening zou de uitkomst 4 + 5 = 9 zijn, en er zijn maar acht <li>'s.

In dit geval gaat het om de vijfde en latere <li>. Dit zijn de <li>'s met de onderste vier knoppen met links.

Deze selector telt alleen de <li>'s die dezelfde ouder hebben. Alle acht <li>'s hebben hier als ouder dezelfde <ul>: ul#menu-ul-vb, en tellen dus mee.

a: de <a>'s binnen die <li>'s. Dat is er in elke <li> maar één.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide dezelfde <li> als ouder.

span: de <span> met het venstertje met informatie.

De hele selector in gewone taal: de <span>'s die gelijk op de <a> volgen die in de vijfde tot en met de achtste <li> van het menu zit.

top: auto; bottom: 0; span met informatie is veel te laag.

Binnen deze <span>'s zit het gele venstertje met informatie. Bij #menu-ul-vb a + span is de bovenkant van de <span>'s met top: 0; op gelijke hoogte gezet als de bovenkant van de bijbehorende <li>, en daarmee ook met de bovenkant van de <a> met de knop.

In browservensters smaller dan 760 px komen die knoppen onder elkaar te staan. De onderste venstertjes zouden daardoor onder het venster kunnen verdwijnen. Daarom wordt bij de onderste vier <span>'s de onderkant van de <span> gelijk gezet met de onderkant van de bijbehorende <li>.

Daarvoor moet ook top: 0; worden teruggezet naar de standaardinstelling auto; omdat de <span> met het venstertje anders niet hoger wordt dan de bijbehorende <li>. De tekst zie je wel, maar de achtergrond en dergelijke worden niet hoger dan de <li>. Op de afbeelding is top: 0; blijven staan, waardoor het grootste deel van de tekst in het venstertje nauwelijks leesbaar is, omdat de achtergrondkleur van het venstertje niet hoger wordt dan de bijbehorende <li>.

#menu-ul-vb li:focus-within a

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.

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb a[aria-current] {background: #444; color: white; cursor: default;}

#menu-ul-vb li:focus-within a:not([aria-current]) {background: white; color: black; text-decoration: underline; outline: black solid 1px; outline-offset: -1px;}

#menu-ul-vb a:focus-within[aria-current] {text-decoration: red line-through 3px; outline: black solid 1px;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:focus-within: maar alleen als het element (hier de <li>) of een van de nakomelingen daarvan de focus heeft.

a: de <a>'s binnen die <li> met focus. Binnen elke <li> zit één <a>.

De hele selector in gewone taal: de <a> binnen de <li> met focus, of waarvan een van de nakomelingen de focus heeft, binnen ul#menu-ul-vb.

pointer-events: auto;

Eerder is bij #menu-ul-vb a met pointer-events: none; de <a> ongevoelig gemaakt voor een aanraking of klik: de link werkt niet.

Zodra een <li> wordt aangeraakt, krijgt deze de focus en wordt de <a> hier weer gevoelig gemaakt voor een aanraking of klik: de link werkt weer. (De <li> kan de focus krijgen, omdat deze het attribuut tabindex="-1" heeft gekregen.)

Dit heeft alleen effect op een aanraakscherm. De eerste aanraking opent daar het venstertje met informatie, de tweede aanraking volgt de link.

Bij gebruik van een Tab-toets loopt de Tab-toets gewoon de <a>'s in het menu af en wordt door het indrukken van Enter de link gevolgd, ongeacht de waarde bij pointer-events.

Bij gebruik van een muis wordt bij de css binnen de media query @media (hover: hover) and (pointer: fine) geregeld dat bij hoveren over de <li> de <a> weer gaat reageren op een klik. Dit gaat zo snel, dat de <a> gelijk op een link reageert, zodra erop wordt geklikt.

#menu-ul-vb li:focus-within a + span

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.

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb li:nth-of-type(n + 5) a + span {top: auto; bottom: 0;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:focus-within: maar alleen als het element (hier de <li>) of een van de nakomelingen daarvan de focus heeft.

a: de <a>'s binnen die <li> met focus. Binnen elke <li> zit één <a>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze zitten beide in de zelfde <li>.

span: de <span> met het venstertje met informatie.

De hele selector in gewone taal: de <span> die gelijk volgt op de <a> binnen de <li> met focus, of waarvan een van de nakomelingen de focus heeft, binnen ul#menu-ul-vb.

left: 99%; z-index: 10; transform: translateX(0); transition-delay: 0s, 0.5s;

Deze eigenschappen zorgen ervoor dat het venstertje met informatie zichtbaar wordt. De beschrijving hiervan staat bij transition-property: transform, z-index;.

main

Alle <main>'s. Dat is er maar één: de belangrijkste inhoud van de pagina staat hierin. Hier is dat alleen een korte beschrijving van de werking van het menu.

background: white;

Witte achtergrond.

color: black;

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.

Dit is ook al bij <body> opgegeven, maar sommige mensen hebben bij álle elementen de kleuren veranderd. Het heeft immers weinig zin, als ze dat alleen bij de body doen, terwijl de sitebouwer de kleuren ook bij bijvoorbeeld de paragrafen heeft aangepast.

border: black solid 1px;

Zwart randje.

padding: 5px;

Kleine afstand tussen inhoud en buitenkant van de <main>.

h1

Alle <h1>'s. Dat is er maar één: de belangrijkste kop. Hier staat daar alleen maar in, welke pagina het is.

font-weight: normal;

Standaard is een <h> vet. Hier wordt dat veranderd in normale tekst.

text-align: center;

Tekst horizontaal centreren.

margin: 0;

Standaard heeft een <h1> een marge aan boven‑ en onderkant. Hier wordt die verwijderd.

h2

Alle <h2>'s.

font-size: 1.2em;

Lettergrootte.

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-bottom: 0;

Standaard heeft een <h2> een marge aan boven‑ en onderkant. Hier wordt de marge aan de onderkant weggehaald.

main p

Alle <p>'s in <main>.

margin-top: 0;

Standaard heeft een <p> een marge aan boven‑ en onderkant. Hier wordt de marge aan de bovenkant weggehaald.

css alleen voor vensters maximaal 759 px breed

@media screen and (max-width: 759px)

De css die binnen deze 'media query' staat, geldt alleen voor browservensters die maximaal 759 px breed zijn. In deze smallere vensters is het menu bij openen van de pagina verborgen. Pas nat het aanvinken van een <input>, wordt het 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.

screen: deze regel geldt alleen voor schermweergave.

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

(max-width: 759px): het venster van de browser mag maximaal 759px breed zijn. Is het venster breder, 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 screen and (max-width: 759px) { 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 – 1024 x 768 px, dan geldt deze media query mogelijk toch voor dat mobieltje. Terwijl dat toch echt meer dan 759 px breed 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 1024 x 768 px heeft wel degelijk meer dan 760 px in de breedte. 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'). (Vaak wordt foutief de eenheid dpi ('dots per inch') gebruikt. Die eenheid is voor printers.) 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 (1024 x 768), 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. 1024 x 768 gedeeld door 2 is 512 x 384 px. Het aantal css-pixels is 512 x 384 px en zit daarmee dus ruim onder de 760 px van deze media query.

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.

Binnen deze media query wordt het tonen en verbergen van het menu in browservensters smaller dan 760 px geregeld. Hier wordt :hover niet gebruikt, dus dit levert geen problemen op een aanraakscherm op. Daarom kan de css voor het openen en sluiten van het menu veel simpeler zijn dan de css voor binnen het menu zelf, die ook het tonen en verbergen van het venstertje met informatie moet regelen.

#menu-ul-vb

Deze selector werkt alleen in browservensters maximaal 759 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.)

#menu-vb {display: flex; flex-direction: column; width: 6.1rem; max-width: 45%; list-style-type: none; margin: 5px 0 0; padding: 0;}

Het element met id="menu-ul-vb". De <ul> waar het menu in zit.

visibility: hidden;

De hele <ul> met het menu onzichtbaar maken.

Dit is nodig voor schermlezers. Hier gelijk onder wordt de hele <ul> met het menu met transform links buiten het browservenster geplaatst, waardoor je het menu niet ziet. Schermlezers lezen het echter gewoon voor, ook al staat het buiten het venster. Door het menu onzichtbaar te maken, lezen schermlezers het niet meer voor.

transform: translateX(-100%);

Met de bij transform horende functie translateX() kun je 'n element ten opzichte van zichzelf verplaatsen. En als je die verplaatsing in procenten opgeeft, gelden die procenten ten opzichte van het element zelf. Omdat de browser bij het maken van de pagina alle maten weet, kan de browser altijd uitrekenen, hoeveel 100% is.

translateX() verplaatst in horizontale richting. Omdat de waarde negatief is, wordt naar links verplaatst. Met 100%, precies de breedte van ul#menu-ul-vb. Met andere woorden: 100% van de <ul>, de hele <ul>, staat nu links buiten het browservenster, ongeacht de breedte van het venster.

transition-property: transform, visibility; transition-duration: 0.5s, 0s; transition-delay: 0s, 0.5s;

In browservensters smaller dan 760 px is het menu bij openen van de pagina verborgen, omdat er weinig ruimte is in die vensters. Pas na het aanvinken van input#toon-menu-vb wordt het menu getoond. Om het menu zichtbaar te maken, worden daarbij de eigenschappen translateX() en visibility veranderd.

Als je bij :hover, :focus, :checked, :user-valid, en dergelijke een andere waarde bij een eigenschap opgeeft, kun je er met transition bij veel eigenschappen voor zorgen dat de oude waarde niet in één keer in de nieuwe waarde wordt veranderd, maar geleidelijk, gedurende een op te geven tijdsduur, of na een vertraging.

De drie eigenschappen hierboven die beginnen met transition horen bij elkaar. Je kunt ze ook combineren in een zogenaamde 'shorthand': transition. Daarachter komen dan in een bepaalde volgorde alle waarden die hierboven achter de twee aparte eigenschappen staan. Bij zo'n serie veranderingen zijn de aparte eigenschappen vaak duidelijker, maar dat is vooral een kwestie van persoonlijke voorkeur.

Het tonen van het menu door het aanvinken van input#toon-menu-vb gebeurt met behulp van :checked bij body:not(:has(main:focus-within)) #toon-menu-vb:checked ~ #menu-ul-vb.

Het tonen van het menu gebeurt op een iets andere manier dan het weer verbergen. Die andere manier van tonen en verbergen wordt bereikt door sommige eigenschappen verderop bij de selector met :checked te veranderen.

transition-property: transform, visibility: dit zijn de twee eigenschappen, waarvoor transition geldt, gescheiden door een komma: transform en visibility. De andere transition-eigenschappen hebben alleen invloed op deze twee eigenschappen.

Omdat deze eigenschappen verderop bij de selector met :checked niet worden veranderd, gelden de transition-eigenschappen zowel bij tonen als bij verbergen voor transform en visibility.

transition-delay 0s, 0.5s: hiermee wordt een vertraging aan het begin van de verandering opgegeven. Bij transition-property staan twee eigenschappen, gescheiden door een komma: transform en visibility. Hier staan twee tijdsduren: 0s en 0.5s, gescheiden door een komma. De eerste tijdsduur van nul seconden hoort bij de eerste eigenschap transform, en de tweede eigenschap van 'n halve seconde hoort bij visibility.

De horizontale verplaatsing met behulp van transform: translateX() begint gelijk, na nul seconden. De verandering van de visibility begint na 0,5 seconde, iets later dus.

Verderop bij de selector met :checked wordt een andere waarde opgegeven: transition-delay: 0s;: nul seconden. Omdat hier maar één waarde is opgegeven, geldt die voor transform én visibility, voor beide eigenschappen.

Wat ongelooflijk verwarrend is: de waarden hier (0 seconden en 'n halve seconde) gelden voor het verbergen van het menu, voor als :checked niet meer geldig is, als input#toon-menu-vb weer is uitgevinkt. Bij het verbergen is er dus voor beide eigenschappen geen vertraging.

De verderop bij de selector met :checked opgegeven waarde van nul seconden geldt bij het tonen van het menu.

Bij het tonen en weer verbergen van het menu moet de met transform: translateX() gemaakte horizontale verplaatsing gelijk beginnen, zonder vertraging. Daarom is de eerste waarde hier én de waarde bij :checked nul seconde voor transform. Het zou nogal verwarrend zijn als je iets aan- of uitvinkt om 'n menu te tonen, en er gebeurt dan pas na 'n tijdje iets.

Voor visibility is het een iets ander verhaal. Bij :checked heeft dat een vertraging van 0 seconden, dus het menu wordt bij openen gelijk zichtbaar. De horizontale verplaatsing met behulp van transform duurt 'n halve seconde. Als het menu dan onzichtbaar zou zijn, zou je die verplaatsing niet zien en knalt het menu in één keer tevoorschijn, als het alsnog zichtbaar wordt.

Bij het weer sluiten geldt het omgekeerde. Ook daar duur de horizontale verplaatsing 'n halve seconde. Gedurende die halve seconde moet het menu juist nog zichtbaar blijven. Als het gelijk onzichtbaar zou worden, zonder enige vertraging, zou je bij het sluiten de horizontale verplaatsing niet zien, maar verdwijnt het menu in één keer.

transition-duration: 0.5s, 0s;: omdat voor deze eigenschap verderop geen andere waarden zijn opgegeven bij de selector met :checked, zijn de waarden bij tonen en verbergen van het menu hetzelfde: de browser gebruikt automatisch dezelfde waarden bij :checked.

Hiermee wordt de duur van de verandering opgegeven. Bij transition-property staan twee eigenschappen, gescheiden door een komma: transform en visibility. Hier staan twee tijdsduren: 0.5s en 0s, gescheiden door een komma. De eerste tijdsduur van 'n halve seconde hoort bij de eerste eigenschap transform, en de tweede eigenschap van nul seconden hoort bij visibility. De horizontale verplaatsing met behulp van transform: translateX() duurt 'n halve seconde. De verandering van zichtbaar naar onzichtbaar en omgekeerd duurt 0 seconden: pats-boem in één keer.

(Overigens kán visibility alleen maar in één keer van visible naar hidden en omgekeerd veranderen. Voor een geleidelijke verandering zou je opacity moeten gebruiken, maar als je het menu met opacity zichtbaar en onzichtbaar maakt, lezen schermlezers het altijd voor, ook als het onzichtbaar is. Bij visibility: hidden; lezen schermlezers het niet voor.)

transition brengt een geheel eigen risico met zich mee. In de specificatie staat, welke eigenschappen met behulp van transition kunnen veranderen. Je kunt eventueel bij transition opgeven, voor welke eigenschappen het geldt. Als je bijvoorbeeld top en left beide wilt veranderen bij :checked, kun je bijvoorbeeld aangeven dat de geleidelijke verandering met behulp van transition alleen voor top geldt. left verandert dan gewoon in één keer.

Als je niet opgeeft, voor welke eigenschappen transition geldt, geldt het voor álle eigenschappen die bij :hover, :focus, :checked, en dergelijke worden veranderd. Als daar 'n eigenschap bij zit die op dit moment niet door transition kan worden vertraagd, verandert die gewoon in één keer. Maar als de specificatie of een van de browsermakers de geest krijgt en plotsklaps die eigenschap ook onder transition laat vallen, kan dat tot heel onverwachte resultaten leiden.

Stel dat je een bepaalde eigenschap heel traag wilt veranderen bij :hover en een andere eigenschap flitsend snel. Mogelijk wordt dat flitsend snel dan opeens ook heel traag, als transition in de toekomst die eigenschap ook gaat ondersteunen. Daarom moet je absoluut zeker weten dat ook in de toekomst geen problemen kunnen ontstaan. Bij twijfel: geef aan welke eigenschap(pen) transition mag aansturen.

In dit voorbeeld speelt dit risico niet, omdat bij transition de eigenschappen zijn opgegeven, waarvoor transition geldt. Daardoor blijft transition, wat er eventueel ooit nog gaat veranderen, hoe dan ook alleen voor die eigenschappen gelden.

body:not(:has(main:focus-within)) #toon-menu-vb:checked ~ #menu-ul-vb

Deze selector werkt alleen in browservensters maximaal 759 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.)

#menu-ul-vb {display: flex; flex-direction: column; width: 6.1rem; max-width: 45%; list-style-type: none; margin: 5px 0 0; padding: 0;}

#menu-ul-vb {visibility: hidden; transform: translateX(-100%); transition-property: transform, visibility; transition-duration: 0s, 0.5s; transition-delay: 0.5s, 0s;}

Dit is een tamelijk lange selector. De selector is zo lang, omdat er een aantal dingen in wordt geregeld. Het eerste deel body:not(:has(main:focus-within)) zorgt ervoor dat het menu wordt gesloten, als het scherm ergens buiten het menu, maar binnen <main>, wordt aangeraakt of ‑geklikt, of als met de Tab-toets een link, een <input>, of iets soortgelijks binnen <main> wordt bereikt. Dit deel is alleen nodig als JavaScript is uitgeschakeld. Als JavaScript is ingeschakeld, wordt dat met behulp van JavaScript geregeld. (Als JavaScript is ingeschakeld, sluit het menu ook als je het scherm ergens buiten <main> aanraakt of ‑klikt. Ook als <main> bijvoorbeeld lager is dan het venster, sluit het menu als je de onderkant van het venster aanraakt of ‑klikt.)

In stukjes gehakt wordt de selector hopelijk begrijpelijker.

body: het element waar de hele pagina in zit.

:not(:has(main:focus-within)): maar alleen onder bepaalde voorwaarden.

:not(:has()): tussen de haakjes achter :has staat de voorwaarde, waaraan <body> juist níét mag voldoen. Níét, omdat :has() weer tussen de haakjes achter () staat.

:not(:has(main:focus-within)): dit is de voorwaarde, waaraan <body> juist níét mag voldoen: het in <body> zittende element <main> mag niet de focus hebben, en ook geen van de nakomelingen van <main>, zoals een in <main> zittende <a> of <input>.

<main> kan normaal genomen niet de focus krijgen, maar omdat <main> het attribuut tabindex="-1" heeft gekregen, kan dat hier wel.

body:not(:has(main:focus-within)): bij elkaar: de hele pagina, behalve als <main> of een nakomeling daarvan de focus heeft.

#toon-menu-vb: het element met id="toon-menu-vb". De <input> waarmee door aan- of uitvinken het menu getoond of verborgen kan worden.

:checked: maar alleen als de <input> is aangevinkt (de <input> heeft het attribuut type="checked" en is 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#toon-menu-vb voor de ~ en #menu-ul-vb na de ~ hebben beide als ouder nav#menu-vb.

#menu-ul-vb: de <ul> met het menu.

De hele selector in gewone taal: doe iets met ul#menu-ul-vb, de <ul> met het menu, maar alleen als de ervoor zittende input#toon-menu-vb is aangevinkt, én alleen als <main> of een van de nakomelingen van <main> niet de focus heeft.

visibility: visible;

Bij #menu-ul-vb is ul#menu-ul-vb met het menu onzichtbaar gemaakt. Als het menu door het aanvinken van input#toon-menu-vb getoond moet worden, moet het uiteraard zichtbaar worden gemaakt.

transform: translateX(0);

Bij #menu-ul-vb is ul#menu-ul-vb met transform: translateX(-100%) links buiten het browservenster geplaatst, zodat het vanaf de linkerkant naar binnen kan schuiven tijdens het openen. Hier wordt de uiteindelijke horizontale positie van het menu opgegeven: aan de linkerkant van het venster.

transition-delay: 0s;

Deze eigenschap zorgt ervoor dat het menu zichtbaar blijft tijdens de horizontale beweging, waarmee het wordt gesloten. De beschrijving hiervan staat bij staat bij #menu-ul-vb.

Deze regel zorgt ervoor dat het menu wordt getoond, als input#toon-menu-vb is aangevinkt. Maar zodra het scherm ergens wordt aangeraakt of ‑geklikt, of als een element buiten het menu de focus heeft, wordt het menu weer verborgen. Op deze manier kun je, ook zonder JavaScript, makkelijk voorkomen dat het menu delen van de pagina verbergt. (Maar met JavaScript werkt dit beter, zeker voor schermlezers.)

css alleen voor apparaten met een precieze aanwijzer, zoals een muis

@media (hover: hover) and (pointer: fine)

De css die binnen deze 'media query' staat, is alleen bedoeld voor apparaten die worden bediend met een muis, een touchpad of een soortgelijke nauwkeurige aanwijzer.

De bedoeling is dat het venstertje met informatie gelijk opent, als je met een muis over een van de knoppen hovert. Omdat hoveren in het verleden (en nog wel) werd gebruikt om bijvoorbeeld een menu te openen, wordt hoveren op een aanraakscherm geïmiteerd.

Vaak is dat prima, maar hier niet. Als hoveren op een aanraakscherm zou werken, zou het venstertje met informatie bij aanraken van een knop worden geopend. Maar omdat je tegelijkertijd ook de in de knop zittende <a> aanraakt, wordt ook gelijk de link gevolgd. Daardoor zie je, afhankelijk van browser en besturingssysteem, het venstertje met informatie helemaal niet of alleen in een heel korte flits.

Deze query probeert te voorkomen dat op een aanraakscherm bij het aanraken van een knop bij de eerste aanraking gelijk de link wordt gevolgd, maar dat in plaats daarvan het venstertje met informatie wordt geopend.

Als op een apparaat gehoverd kan worden én de invoermethode is precies, dan zal dat (vrijwel) altijd een desktopcomputer of een laptop zijn. Alleen in dat geval wordt de css binnen deze media query uitgevoerd.

Deze methode is echter niet helemaal waterdicht. Als bijvoorbeeld een muis wordt aangesloten op een tablet, zul je in de regel twee keer moeten klikken: bij de eerste klik wordt het venstertje met informatie getoond, bij de tweede klik wordt de link gevolgd, net als bij een aanraking. Alles werkt dus, zoals het zou moeten werken.

Op een laptop met een aanraakscherm echter zal een aanraking in de regel hetzelfde werken als een klik met een muis: de link wordt gelijk gevolgd en het venstertje met informatie wordt niet of alleen in een flits getoond. Daarom is deze constructie niet geschikt, als in het venstertje met informatie belangrijke informatie staat.

@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 of er wel of niet gehoverd kan worden, en of de invoermethode ruw of precies is.

(hover: hover): het 'primaire invoermechanisme' (in het Engels 'primary input mechanism') kan hoveren. Met een muis of touchpad kun je boven iets gaan hangen, waardoor bijvoorbeeld de kleur van iets kan veranderen. Op een aanraakscherm kan dat niet. Weliswaar wordt hoveren vaak geïmiteerd, maar dat is geen echt hoveren zoals met een muis. Ook als je een muis aansluit op een tablet of smartphone is het primaire invoermechanisme niet de muis, maar nog steeds het aanraakscherm.

(hover: hover) staat tussen haakjes, dat hoort gewoon zo bij een @media-regel.

and: er komt nog een voorwaarde.

(pointer: fine): het primaire invoermechanisme moet 'precies' (in het Engels 'fine') zijn. Dat zijn bijvoorbeeld een muis en een touchpad, omdat je met beide heel precies kunt richten.

De andere twee waarden zijn 'none' (geen invoermechanisme) en 'coarse' (een grof invoermechanisme zoals een vinger). Dat geldt niet alleen voor bouwvakkersklauwen, maar ook voor die schattige peutervingertjes: met een muis en dergelijke kun je gewoon veel beter richten.

Ook deze voorwaarde staat weer tussen haakjes.

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

@media (hover: hover) and (pointer: fine) { body {color: silver;} (...) rest van de css voor deze media-regel (...) footer {color: gold;} }

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

#toon-menu-vb:focus-within + label, #toon-menu-vb:hover + label

Deze selector werkt alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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 het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#label-toon-menu-vb {cursor: pointer; text-decoration: underline; position: relative; left: -12px;}

Twee selectors, gescheiden door een komma. De eerste selector #toon-menu-vb:focus-within + label:

#toon-menu-vb: het element met id="toon-menu-vb". De <input> waarmee het menu in browservensters smaller dan 760 px kan worden getoond en verborgen.

:focus-within: maar alleen als het element (hier de <input>) of een van de nakomelingen daarvan 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 #nav-menu-vb.

label: de <label> die bij de <input> hoort.

De hele selector in gewone taal: doe iets met de <label> die gelijk op input#toon-menu-vb volgt, de <input> waarmee het menu getoond en verborgen kan worden, maar alleen als de <input> of een van de nakomelingen daarvan de focus heeft. (Omdat een <input> geen nakomelingen heeft, zou je hier ook :focus in plaats van :focus-within kunnen gebruiken.)

De tweede selector #toon-menu-vb:hover + label is vrijwel hetzelfde, alleen werkt deze als over de <input> wordt gehoverd.

background: white;

Witte achtergrond.

Op een aanraakscherm is deze verandering van kleur niet echt zinvol. Mensen zullen in de regel weten, wat ze hebben aangeraakt. En als ze dat niet weten, zien ze het aan het menu dat wordt getoond of verborgen.

Voor gebruikers van de Tab-toets is dit wel van belang, want die weten anders niet wanneer de <input> de focus heeft.

Bij hoveren is dit ook van belang, omdat gebruikers van de muis of een touchpad dan duidelijk kunnen zien, dat ze het menu kunnen openen of sluiten met een klik.

color: black;

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

#menu-ul-vb li:hover a:not([aria-current])

Deze selector werkt alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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 het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb li:focus-within a:not([aria-current]) {background: white; color: black; text-decoration: underline; outline: black solid 1px; outline-offset: -1px;}

#menu-ul-vb li:focus-within a {pointer-events: auto;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:hover: maar alleen als over die <li> wordt gehoverd.

a: de <a>'s binnen de <li>, waarover wordt gehoverd. Binnen elke <li> zit één <a>.

[aria-current]: van die <a>'s alleen de <a>'s die het attribuut aria-current hebben.

:not([aria-current]): maar dan omgekeerd. Omdat aria-current binnen :not() staat, worden juist alleen de <a>'s geselecteerd, die níét het attribuut aria-current hebben. De pseudo-class :not() geeft aan dat wat tussen de haakjes staat, juist níét geselecteerd moet worden.

De hele selector in gewone taal: doe iets met de <a>'s die niet bij de huidige pagina horen, maar alleen als over de <li> waar de <a> in zit wordt gehoverd.

background: white;

Witte achtergrond.

color: black;

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

text-decoration: underline;

Eerder zijn bij #menu-ul-vb a met pointer-events: none; de <a>'s ongevoelig gemaakt voor een klik: de links werken niet. Zodra over de een <li> wordt gehoverd, wordt iets hieronder met pointer-events: auto; de werking van de link hersteld. Dit gebeurt zo snel, dat de link altijd gewoon lijkt te werken.

Om de tekst in de <a> meer op een link te laten lijken, wordt deze onderstreept.

Op een aanraakscherm is dat uitschakelen van de links belangrijk, want daar moet bij de eerste aanraking het venstertje met informatie worden getoond. Pas bij de tweede aanraking moet de link worden gevolgd. Bij gebruik van een muis of touchpad is dat niet van belang, want daarbij wordt het venstertje met informatie al getoond door over de knop te hoveren.

outline: black solid 1px;

Zwart randje rond de <a>.

outline-offset: -1px;

Randje 1 px naar binnen verplaatsen. Hierdoor staat het precies op de goede plaats.

pointer-events: auto;

De <a> weer laten reageren op een klik. Bij text-decoration: underline; hier iets boven wordt het waarom hiervan iets uitgebreider beschreven.

#menu-ul-vb li:hover a[aria-current]

Deze selector werkt alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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 het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb a[aria-current] {background: #444; color: white; cursor: default;}

#menu-ul-vb a:focus-within[aria-current] {text-decoration: red line-through 3px; outline: black solid 1px;}

#menu-ul-vb li:focus-within a {pointer-events: auto;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:hover: maar alleen over die <li> wordt gehoverd.

a: de <a>'s binnen die <li> waarover wordt gehoverd. Binnen elke <li> zit één <a>.

[aria-current]: maar alleen als die <a> het attribuut aria-current heeft. Dit is de <a> die bij de huidige pagina hoort.

De hele selector in gewone taal: de <a> binnen de <li>, waarover wordt gehoverd, maar alleen als die <a> het attribuut aria-current heeft.

text-decoration: red line-through 3px;

Deze link hoort bij de huidige pagina en werkt niet. Om dat duidelijk te maken, wordt door de tekst in de <a> een rode streep gezet.

outline: black solid 1px;

Zwart randje rond de <a>.

#menu-ul-vb li:hover a + span

Deze selector werkt alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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 het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb li:nth-of-type(n + 5) a + span {top: auto; bottom: 0;}

#menu-ul-vb li:focus-within a + span {left: 99%; z-index: 10; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:hover: maar alleen als over die <li> wordt gehoverd.

a: de <a>'s binnen die <li> met focus. Binnen elke <li> zit één <a>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze zitten beide in de zelfde <li>.

span: de <span> met het venstertje met informatie.

De hele selector in gewone taal: de <span> die gelijk volgt op de <a> binnen de <li>, waarover wordt gehoverd.

left: 99%; z-index: 20; transform: translateX(0); transition-delay: 0s, 0.5s;

Deze eigenschappen zorgen ervoor dat het venstertje met informatie zichtbaar wordt. De beschrijving hiervan staat bij staat bij transition-property: transform, z-index;. Het enige verschil: hier wordt over de <li> gehoverd, terwijl bij de eerdere beschrijving de <li> de focus heeft.

css alleen voor vensters minimaal 760 px breed

@media screen and (min-width: 760px)

De opbouw van deze regel staat beschreven bij @media screen and (max-width: 759px). Er is één verschil: de breedte van het browservenster moet minimaal 760 px zijn.

In browservensters minimaal 760 px breed is voldoende ruimte om de acht knoppen van het menu altijd te tonen. Daarom is de css voor deze bredere vensters gedeeltelijk anders.

Omdat het menu hier altijd is geopend, zou je bij elke pagina alle acht links moeten passeren, voordat je bij de belangrijkste inhoud van de pagina komt. Heel irritant voor gebruikers van de Tab-toets en schermlezers. Daarom staat voor het menu een skip-link, waarmee het menu in één keer gepasseerd kan worden.

body

Deze selector werkt alleen in browservensters minimaal 760 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.)

body {background: #ff9; color: black; font-family: Arial, Helvetica, sans-serif; margin: 0;}

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-size: 110%;

Iets groter dan standaard in deze grotere browservensters. 't Zal de leeftijd zijn, maar de standaardgrootte is wat klein.

Als eenheid wordt de relatieve eenheid % 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.

#toon-menu-vb, #label-toon-menu-vb

Deze selector werkt alleen in browservensters minimaal 760 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.)

#toon-menu-vb {width: 1px; height: 1px; margin-left: 10px;}

#label-toon-menu-vb {cursor: pointer; text-decoration: underline; position: relative; left: -12px;}

De elementen met id="toon-menu-vb" en "label-toon-menu-vb". Met input#toon-menu-vb kan in browservensters smaller dan 760 px het menu worden getoond of verborgen, label#label-toon-menu-vb is de bij de <input> horende <label>.

display: none;

Verbergen.

In deze bredere browservensters is het menu altijd zichtbaar, dus de <input> en <label> om het menu te tonen en te verbergen zijn hier niet nodig.

#menu-vb

Deze selector werkt alleen in browservensters minimaal 760 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.)

#menu-ul-vb {background: blue; color: white; height: 2.5rem; line-height: 2.5rem; padding-bottom: 4px;}

Het element met id="menu-vb". De <nav> waar het hele menu in zit.

background: transparent;

Doorzichtige achtergrond.

Eerder heeft de <nav> een blauwe achtergrond gekregen, waardoor deze zichtbaar was als de blauwe balk bovenin het browservenster. Hier wordt de <nav> doorzichtig gemaakt.

width: 60rem;

In browservensters smaller dan 760 px is de <nav> even breed als het venster. Hier wordt de breedte voor deze bredere vensters beperkt tot 60 rem.

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

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

max-width: 96vw;

Maximumbreedte.

Hier gelijk boven is een breedte aan de <nav> gegeven, die afhankelijk is van de lettergrootte. Als die breedte onbeperkt zou mogen groeien, zou de <nav> met de knoppen breder dan het browservenster kunnen worden. Daarom wordt hier een maximumbreedte opgegeven.

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 96 vw is 96% van de breedte. De <nav> met de knoppen wordt hierdoor nooit breder dan 96% van de breedte van het venster, ongeacht de breedte van het venster.

margin: 30px auto 0;

Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 30px auto 0 auto in de volgorde boven – rechts – onder – links.

Boven een kleine afstand tot de bovenkant van het browservenster.

Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor komt de <nav> altijd horizontaal gecentreerd binnen <body> te staan. <body> is een blok-element en wordt daardoor normaal genomen automatisch even breed als de ouder ervan: <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser.

Hierdoor staat uiteindelijk de <nav> altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht de breedte van het venster. En daarmee ook de in de <nav> zittende knoppen.

Deze manier van horizontaal centreren van een blok-element werkt alleen, als het blok-element een breedte heeft, want anders zou het normaal genomen even breed worden als de ouder ervan. Dat is geregeld, want iets hierboven heeft de <nav> een breedte en een maximumbreedte gekregen.

#skippy

Deze selector werkt alleen in browservensters minimaal 760 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.)

#skippy {display: none;}

Het element met id="skippy". Een zogenaamde skip-link, waarmee in browservensters minimaal 760 px breed in één keer de acht knoppen met links gepasseerd kunnen worden.

background: white;

Witte 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.

Dit is ook al bij <body> opgegeven, maar sommige mensen hebben bij álle elementen de kleuren veranderd. Het heeft immers weinig zin, als ze dat alleen bij de body doen, terwijl de sitebouwer de kleuren ook bij bijvoorbeeld de paragrafen heeft aangepast.

Normaal genomen zou je de kleur van de tekst in een link door de browser laten bepalen, zodat te zien is of de link al is bezocht of niet. (Of je moet dat op een andere manier aangeven, zoals in dit voorbeeld gebeurt bij de acht knoppen met links.)

Hier maakt die kleur niets uit, omdat het niet uitmaakt of een skip-link als is bezocht of niet. Bovendien is het belangrijk dat de kleur van de tekst voldoende contrast heeft met de achtergrondkleur. Bij wit op blauw is dat het geval.

clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%);

Deze twee eigenschappen doen hetzelfde. De eerste eigenschap clip is voor oudere browsers, de tweede eigenschap clip-path is voor nieuwere. (Deze nieuwere eigenschap heeft veel meer mogelijkheden, maar die worden hier niet gebruikt.)

Oudere browsers kennen clip-path niet en negeren de tweede eigenschap. Nieuwere browsers kennen clip-path wel en omdat clip-path na clip in de css staat, 'wint' clip-path van clip en wordt clip-path gebruikt.

De skip-link is nog een beetje zichtbaar.

Gelijk hieronder krijgt de skip-link een breedte en hoogte van 1 px en aan alle kanten een border van 3 px breed. Daardoor zou de skip-link nog steeds zichtbaar zijn als een vierkant rood blokje met, als je heel goed kijkt, een wit blokje in het midden van 1 px breed en hoog. Deze twee eigenschappen verbergen ook dat laatste restje skip-link.

clip: rect(1px, 1px, 1px, 1px): geef het element weer binnen de vier opgegeven grenzen. De vier waarden horen bij bovenkant, rechterkant, onderkant en linkerkant.

Bovenkant en onderkant worden vanaf de bovenkant gerekend: geef het element weer vanaf 1 px vanaf de bovenkant tot aan 1 px vanaf de bovenkant. Oftewel: helemaal niets.

Linker- en rechterkant worden vanaf links gerekend: geef het element weer vanaf 1 px vanaf de linkerkant tot aan 1 px vanaf de linkerkant. Wat ook weer als resultaat helemaal niets oplevert.

clip-path: inset(50%): omdat maar één waarde is opgegeven, geldt deze voor boven, rechts, onder en links. Geef het element weer vanaf 50% van alle vier de kanten. Als je dat doet, blijft er helemaal niets over om weer te geven.

De skip-link is nu volledig onzichtbaar, maar nog wel bereikbaar voor schermlezers en Tab-toets.

display: block;

Eerder is de skip-link met display: none; verborgen, omdat deze in browservensters smaller dan 760 px niet nodig is. Hier wordt de link weer zichtbaar gemaakt.

width: 1px; height: 1px; overflow: hidden;

Breedte en hoogte minimaal maken.

Als de inhoud van een element er niet in past, zoals hier het geval is, wordt die inhoud normaal genomen toch getoond. In het algemeen is dat prima: beter 'n wat minder mooie lay-out dan dat er tekst of zo verdwijnt.

Hier moet de skip-link pas zichtbaar worden als deze de focus heeft, daarom wordt hier wat niet in de breedte en hoogte past met overflow: hidden; verborgen.

Breedte en hoogte 0 px maken is geen goed idee, omdat de link dan niet meer werkt in alle schermlezers en browsers.

font-size: 1.2em;

Lettergrootte.

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: 2.4em;

Regelhoogte. Deze is tamelijk groot, waardoor de skip-link meer opvalt.

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.

text-align: center;

Tekst horizontaal centreren.

border: red solid 3px;

Dikke rode rand, zodat de skip-link meer opvalt.

position: absolute;

Niet absoluut gepositioneerde skip-link verstoort de lay-out.

Door de skip-link absoluut te positioneren, trekken andere elementen er zich niets van aan. Zonder absolute positie zou de skip-link de positie van andere elementen beïnvloeden.

Op de afbeelding links is de skip-link absoluut gepositioneerd en heeft geen invloed op de positie van de andere elementen.

Op de afbeelding in het midden is de skip-link niet absoluut gepositioneerd. Daardoor wordt de <nav> met de knoppen iets omlaag gedrukt. Niet veel, want de skip-link is heel klein.

Op de afbeelding rechts heeft de skip-link de focus en is daardoor een stuk groter. Nu wordt het hele menu echt 'n heel stuk naar onderen gedrukt.

Door de skip-link absoluut te positioneren, blijft het menu met de knoppen altijd op dezelfde plaats staan, of de skip-link nu de focus heeft of niet.

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.

Een <a> is van zichzelf een inline-element, waardoor eigenschappen als breedte en hoogte 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.

z-index: 250;

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 skip-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 skip-link gegeven, dus dat is geregeld.

#skippy:focus

Deze selector werkt alleen in browservensters minimaal 760 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 het stijlbestand staat, zodat alles hier overzichtelijk bij elkaar staat.

#skippy {background: white; color: black; clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); width: 1px; height: 1px; overflow: hidden; font-size: 1.2em; line-height: 2.4em; text-align: center; margin: -1px; border: red solid 3px; padding: 0; position: absolute; top: 0; left: 0; z-index: 250;}

Het element met id="skippy", maar alleen als dit element de focus heeft. De skip-link is hierboven bij #skippy onzichtbaar gemaakt. Daardoor kan deze alleen met de Tab-toets (en door schermlezers) worden bereikt. Precies de bedoeling, want bij gebruik van een muis of aanraakscherm is de skip-link overbodig.

clip: auto; clip-path: none;

Eerder is de skip-link met clip: rect(1px, 1px, 1px, 1px); en clip-path: inset(50%); onzichtbaar gemaakt. Hier worden de standaardwaarden voor deze eigenschappen opgegeven, waardoor de skip-link zichtbaar wordt.

width: 10.5em; height: 4.8em;

Eerder heeft de skip-link een breedte en hoogte van 1 px gekregen. Dat leest mogelijk wat lastig. Daarom wordt hier een veel grotere breedte en hoogte opgegeven. Veel groter dan nodig is voor de tekst erin, zodat de skip-link goed opvalt en niet per ongeluk gepasseerd kan worden.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de breedte en hoogte mee veranderen met de lettergrootte. Bij gebruik van een absolute eenheid zoals px veranderen de breedte en hoogte niet mee met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid voor de breedte en hoogte wordt gebruikt.

#menu-ul-vb

Deze selector werkt alleen in browservensters minimaal 760 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.)

#menu-ul-vb {display: flex; flex-direction: column; width: 6.1rem; max-width: 45%; list-style-type: none; margin: 5px 0 0; padding: 0;}

Het element met id="menu-ul-vb". De <ul> waar het menu in zit.

flex-direction: row; Knoppen staan niet naast, maar onder elkaar.

Bij #menu-ul-vb is de <ul> met display: flex; in een 'flex container' veranderd. Daar is ook flex-direction: column; opgegeven: zet de directe kinderen van de <ul>, de 'flex items', niet naast, maar onder elkaar. Als dat voor bredere browservensters niet wordt aangepast, ziet dat eruit zoals op de afbeelding: acht onwijs brede knoppen. Wel netjes onder elkaar, maar toch niet helemaal ideaal.

Door de richting aan te passen komen de knoppen in browservensters minimaal 760 px breed niet onder, maar naast elkaar te staan. De breedte van de knoppen wordt iets hieronder bij #menu-ul-vb li aangepast.

width: 100%;

In browservensters smaller dan 760 px staan de knoppen onder elkaar. Voor die vensters is eerder een breedte van 6 rem opgegeven. Die breedte wordt hier aangepast, omdat de knoppen hier naast elkaar komen te staan.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier #menu-vb, die bij #menu-vb een breedte van 60 rem heeft gekregen, met een maximumbreedte van 96 vw. Hierdoor krijgt de <ul> ook deze breedte.

max-width: none;

Ook de eerder opgegeven maximumbreedte van 45% komt te vervallen.

#menu-ul-vb li

Deze selector werkt alleen in browservensters minimaal 760 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.)

#menu-ul-vb li {height: 2.2rem; line-height: 2.2rem; text-align: center; position: relative;}

Alle <li>'s binnen het element met id="menu-ul-vb". De acht <li>'s met de knoppen.

flex: 1 0 12.5%;

flex is een zogenaamde 'shorthand': een eigenschap die eigenlijk uit meerdere eigenschappen is samengesteld. In dit geval zijn dat flex-grow, flex-shrink en flex-basis. De eerste waarde achter flex hoort bij flex-grow, de tweede bij flex-shrink en de derde bij flex-basis.

Er wordt aangeraden hier altijd de shorthand te gebruiken, omdat deze drie eigenschappen elkaar sterk beïnvloeden. Als je er eentje vergeet, valt die terug op z'n standaardwaarde en dat kan lastig te vinden problemen opleveren. Als je ze alle drie gebruikt, zie je altijd wat de waarde bij elk van de drie eigenschappen is.

De richting waarin flex werkt (horizontaal of verticaal) is afhankelijk van de bij de flex container opgegeven flex-direction. Die flex container is hier #menu-ul-vb. waar bij #menu-ul-vb flex-direction: row; is opgegeven: naast elkaar.

Als voor flex niets wordt opgegeven, worden de standaardwaarden gebruikt. Dat ziet eruit als op de afbeelding hieronder:

De acht knoppen vullen niet de volle breedte.

De blauwe blokjes (en het ene zwarte blokje) zijn de <li>'s. Met behulp van flex kan deze weergave worden verbeterd.

flex-grow: de eerste waarde 1 hoort hierbij. In principe wordt een flex item niet breder dan nodig is om de inhoud ervan weer te geven. Dat is wat je op de afbeelding ziet gebeuren: de tekst past keurig binnen de <li>'s.

De standaardwaarde bij flex-grow is 0 de flex items (hier de <li>'s) mogen niet groter worden dan nodig is om de inhoud ervan weer te geven, ook al is er nog ruimte over in flex container #menu-ul-vb. Door de waarde bij flex-grow te veranderen in 1, mogen de flex items (de <li>'s) groeien de eventueel overblijvende ruimte in de flex container gebruiken. Daardoor wordt de volledige ruimte binnen ul#menu-ul-vb gebruikt.

flex-shrink: de tweede waarde 0 hoort hierbij. Dit is de tegenhanger van flex-grow. Deze eigenschap bepaalt of het flex-item eventueel mag krimpen. De standaardwaarde is hier 1: krimpen mag. Hier is dat niet de bedoeling, dus dit wordt veranderd in 0: krimpen mag niet.

flex-basis: dit is de 'ideale' breedte. Deze breedte krijgt het flex item, voordat er wordt gegroeid of gekrompen. Omdat er niet mag worden gekrompen, is dit hier gelijk de minimumbreedte. Het is niet de maximumbreedte, want groeien mag wel.

Groeien mág wel, maar kan hier ook niet. De ideale breedte is 12,5% en er zijn acht knoppen. 8 x 12,5% = 100%, dus de acht knoppen vullen altijd precies de volledige breedte van #menu-ul-vb. Precies de bedoeling. Bij #menu-ul-vb is een maximumbreedte opgegeven, die afhankelijk is van de breedte van het browservenster. Als #menu-ul-vb krimpt, krimpen de knoppen gewoon mee, maar ze blijven elk even groot: 12,5% van de breedte van #menu-ul-vb.

#menu-ul-vb li:first-child a

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

Voor dit element is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het nogal veel is. Hier wordt alleen een ronding gegeven aan het linker bovenhoekje van de eerste <a> .

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:first-child: maar niet alle <li>'s, alleen de <li> die een eerste kind is.

a: de link binnen die <li>.

De hele selector in gewone taal: de <a> binnen de eerste <li> binnen de <ul> met het menu: de meest linkse <a>.

border-top-left-radius: 8px;

Linkerbovenhoek rond maken.

#menu-ul-vb li:last-child a

{border-top-right-radius: 8px;}

Dit is precies hetzelfde als gelijk hierboven bij #menu-ul-vb li:first-child a, alleen gaat het hier om de <li> die het laatste kind is, de <li> met de meest rechtse <a>, en de rechterbovenhoek van die <a>.

#menu-vb #menu-ul-vb a + span

Deze selector werkt alleen in browservensters minimaal 760 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.)

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb li:nth-of-type(n + 5) a + span {top: auto; bottom: 0;}

#menu-ul-vb li:focus-within a + span {left: 99%; z-index: 10; transform: translateX(0); transition-delay: 0s, 0.5s;}

Alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis:

#menu-ul-vb li:hover a + span {left: 99%; z-index: 20; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-vb: de <nav> waar het hele menu in zit.

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> binnen die <nav>.

a: alle <a>'s in die <ul>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder dezelfde <li>.

span: de <span> die direct op de <a> volgt.

De hele selector in gewone taal: de <span> die gelijk op een <a> volgt, die binnen de <ul> zit die weer binnen de <nav> met het menu zit. Dit is de <span>, waarin het venstertje met informatie over de link zit.

Eerder is bij #menu-ul-vb a + span een aantal eigenschappen voor de <span> met het venstertje opgegeven. Een deel van die eigenschappen wordt hier voor browservensters minimaal 760 px breed aangepast.

Normaal genomen wordt css uitgevoerd in de volgorde, waarin het in het stijlbestand staat: wat later in het bestand staat, 'wint' van eerdere css. Als hier dezelfde selector #menu-ul-vb a + span zou worden gebruikt, zou deze dus moeten 'winnen' van de eerdere selector.

Maar hier doet zich een probleem voor. Voor de laatste vier <span>'s is bij #menu-ul-vb li:nth-of-type(n + 5) a + span de positie aangepast. Deze selector staat weliswaar eerder in de css, maar deze selector 'wint' toch van de selector hier, omdat die eerdere selector meer 'specificiteit' heeft. En specificiteit is belangrijker dan de volgorde in de css.

Voor de specificiteit worden elementen, pseudo-elementen en pseudo-classes geteld, waarbij elke soort een eigen gewicht krijgt. De eerdere selector voor de vier laatste <span>'s is hetzelfde als de selector hier, maar heeft daarnaast nog een extra pseudo-class: nth-of-type(). Daardoor heeft deze meer specificiteit dan de hier gebruikte selector #menu-ul-vb li a + span en 'wint' dus, ondank dat de selector hier lager in de css staat.

Door hier aan het begin van de selector #menu-vb toe te voegen, krijgt deze selector weer meer specificiteit dan de eerdere selector voor de laatste vier <span>'s.

Specificiteit is een heel eigen onderwerp. Je kunt er meer over vinden op de pagina met links onder het kopje CSS → Selectors, specificiteit, class, id, erfelijkheid, @import, @layer, @scope, en dergelijke.

opacity: 0;

Ondoorzichtig maken.

De bedoeling is eigenlijk dat de <span> met het venstertje met informatie van boven naar beneden langzaam het beeld inschuift. Zo'n langzame verandering kun je met behulp van transition regelen. De hoogte zou dan geleidelijk van height: 0; naar height: auto; moeten veranderen. Helaas kan een verandering van of naar auto nog niet. Er is wel een nieuwe eigenschap die dat kan (calc-size()), maar die is nog experimenteel en wordt nog onvoldoende ondersteund.

Daarom wordt de eigenschap opacity gebruikt. Als een <li> de focus heeft of erover wordt gehoverd, wordt de <span> met opacity: 1; ondoorzichtig gemaakt. De verandering van opacity: 0; naar opacity: 1; kan wel met behulp van transition geleidelijk aan gebeuren, zodat het venstertje toch niet in een keer min of meer in je gezicht knalt.

width: auto;

Voor browservensters smaller dan 760 px heeft de <span> eerder een breedte van 45 vw gekregen. Hier wordt die breedte verwijderd.

Een <span> is een inline-element, maar omdat het eerder position: absolute; heeft gekregen, is het veranderd in een soort blok-element. En een blok-element wordt normaal genomen even breed als de ouder ervan. Die ouder is hier de <li>, waar de <span> in zit. Daar door is de <span> met het venstertje even breed als de ook in die <li> zittende <a> met de knop.

height: 0; overflow: hidden;

Hoogte 0 px, waardoor de tekst uiteraard niet meer in de <span> past. Normaal genomen wordt tekst en dergelijke echter toch weergegeven, omdat dan misschien de lay-out wordt verpest, maar er verdwijnt in ieder geval geen tekst. overflow: hidden; zorgt ervoor, dat dat hier niet gebeurt. Wat niet in de <span> past, wordt gewoon niet weergegeven. En bij een hoogte van 0 px past er niets in de <span>, dus er wordt niets weergegeven.

Bij het tonen wordt de <span> met het venstertje met informatie geleidelijk aan ondoorzichtig, als de <li> de focus heeft of erover wordt gehoverd. Het weer verdwijnen gebeurt ook geleidelijk aan, maar bij het verdwijnen is dat geleidelijke juist storend. Je kunt met transition regelen dat het verschijnen geleidelijk aan gebeurt en het weer verdwijnen in één keer, maar het is simpeler om het met deze twee eigenschappen te doen.

Als de <li> de focus heeft of als erover wordt gehoverd, wordt de hoogte veranderd in height: auto;: zo hoog als nodig is om de tekst erin weer te geven. Die verandering van hoogte gebeurt in één keer, zonder vertraging. Dus ook al wordt de <span> met het venstertje geleidelijk aan weer onzichtbaar bij het verdwijnen, het venstertje verdwijnt toch in één keer, omdat de twee eigenschappen hier de tekst verbergen.

bottom: auto;

Eerder zijn de laatste vier <span>'s met bottom: 0; van onderaf gepositioneerd. In deze bredere browservensters wordt dat weggehaald.

left: 0;

Met de eerder opgegeven left: 99%; komen de <span>'s met de venstertjes precies naast de knoppen te staan. In deze bredere browserventers moeten ze niet naast, maar onder de knoppen komen te staan.

transition: opacity 0.8s;

Iets hierboven is met opacity: 0; de <span> onzichtbaar gemaakt. Verderop bij #menu-vb #menu-ul-vb li:focus-within a + span en #menu-vb #menu-ul-vb li:hover a + span wordt dit veranderd in opacity: 1;, waardoor de <span> met het venstertje met informatie zichtbaar wordt. (Bij de eerste selector als de <li> met de <span> de focus krijgt, bij de tweede selector als over de <li> wordt gehoverd.)

De eigenschap transition zorgt ervoor dat de verandering van onzichtbaar naar zichtbaar niet in één keer plaatsvindt, maar geleidelijk. Als opacity van 0 wordt veranderd naar 1, vindt die verandering geleidelijk aan plaats: geleidelijk van 0 naar 1.

Omdat transition alleen hier wordt gebruikt, en niet verderop bij de selectors met :focus-within en :hover, is de geleidelijke verandering van zichtbaar naar onzichtbaar en omgekeerd precies hetzelfde. Je kunt de verandering heen in een ander tempo en dergelijke later verlopen als de verandering weer teruggaat, maar dat gebeurt hier niet.

Er wordt één eigenschap met behulp van transition veranderd: opacity. Als je geen eigenschap opgeeft, vallen álle eigenschappen die bij focus of hoveren veranderen onder transition, maar zodra je een (of meer) eigenschappen opgeeft bij transition, vallen alleen die eigenschappen onder transition.

(Daardoor komt het ook dat de <span> met het venstertje toch in één keer zonder vertraging verdwijnt: de <span> wordt iets hierboven met height: 0; visibility: hidden; verborgen. En die verandering wordt niet door transition beïnvloed, die gebeurt gewoon bliksemsnel, want height en visibility worden hier niet genoemd bij transition.)

Als er slechts één tijd is opgegeven, zoals hier het geval is, is dat de tijdsduur van de verandering (een eventuele tweede tijd geeft een vertraging aan het begin aan). Het veranderen van onzichtbaar naar zichtbaar duurt 0,8 seconde (met 'n punt voor de decimalen, want css gebruikt de Amerikaanse notatie).

Je kunt ook nog het verloop van de verandering opgeven, bijvoorbeeld snel aan het begin en langzaam aan het eind. Hier wordt niets opgegeven, waardoor de standaardwaarde ease wordt gebruikt: langzaam aan het begin, sneller halverwege en weer langzaam aan het einde.

transition brengt een geheel eigen risico met zich mee. In de specificatie staat, welke eigenschappen met behulp van transition kunnen veranderen. Je kunt eventueel bij transition opgeven, voor welke eigenschappen het geldt. Als je bijvoorbeeld top en left beide wilt veranderen bij :checked, kun je bijvoorbeeld aangeven dat de geleidelijke verandering met behulp van transition alleen voor top geldt. left verandert dan gewoon in één keer.

Als je niet opgeeft, voor welke eigenschappen transition geldt, geldt het voor álle eigenschappen die bij :hover, :focus, :checked, en dergelijke worden veranderd. Als daar 'n eigenschap bij zit die op dit moment niet door transition kan worden vertraagd, verandert die gewoon in één keer. Maar als de specificatie of een van de browsermakers de geest krijgt en plotsklaps die eigenschap ook onder transition laat vallen, kan dat tot heel onverwachte resultaten leiden.

Stel dat je een bepaalde eigenschap heel traag wilt veranderen bij :hover en een andere eigenschap flitsend snel. Mogelijk wordt dat flitsend snel dan opeens ook heel traag, als transition in de toekomst die eigenschap ook gaat ondersteunen. Daarom moet je absoluut zeker weten dat ook in de toekomst geen problemen kunnen ontstaan. Bij twijfel: geef aan welke eigenschap(pen) transition mag aansturen.

In dit voorbeeld speelt dit risico niet, omdat bij transition de eigenschap is opgegeven, waarvoor transition geldt. Daardoor blijft transition, wat er eventueel ooit nog gaat veranderen, hoe dan ook alleen voor die ene eigenschap gelden.

#menu-vb #menu-ul-vb li:focus-within a + span

Deze selector werkt alleen in browservensters minimaal 760 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.)

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb li:nth-of-type(n + 5) a + span {top: auto; bottom: 0;}

#menu-ul-vb li:focus-within a + span {left: 99%; z-index: 10; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-vb #menu-ul-vb a + span {opacity: 0; width: auto; height: 0; overflow: hidden; bottom: auto; left: 0; transition: opacity 0.8s;}

Alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis:

#menu-ul-vb li:hover a + span {left: 99%; z-index: 20; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-vb: de <nav> waar het hele menu in zit.

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:focus-within: maar alleen als het element (hier de <li>) of een van de nakomelingen daarvan de focus heeft.

a: de <a>'s binnen die <li>. Binnen elke <li> zit één <a>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze zitten beide in de zelfde <li>.

span: de <span> met het venstertje met informatie.

De hele selector in gewone taal: de <span> die gelijk op een <a> in een <li> volgt, die binnen de <ul> zit die weer binnen de <nav> met het menu zit, maar alleen als die <li> de focus heeft. Dit is de <span>, waarin het venstertje met informatie over de link zit.

opacity: 1;

De eerder met opacity: 0; onzichtbaar gemaakte <span> zichtbaar maken. Waarom de <span> onzichtbaar en zichtbaar wordt gemaakt, staat uitgebreider beschreven bij #menu-vb #menu-ul-vb a + span.

height: auto;

De hoogte is eerder met height: 0; tot 0 px verminderd. Hier wordt de standaardwaarde weer opgegeven: zo hoog als nodig is om de inhoud ervan weer te geven. Waarom de hoogte eerder 0 px was, staat uitgebreider beschreven bij #menu-vb #menu-ul-vb a + span.

top: 2.3rem;

Eerder zijn de <span>'s absoluut gepositioneerd. 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 <li>, waar de <span> in zit. Deze heeft bij #menu-ul-vb li een relatieve positie gekregen.

Omdat de <a>'s met de knoppen even hoog zijn als de <li>, waar ze in zitten, staan de <span>'s met de venstertjes met informatie op deze hoogte precies onder de knoppen.

Als eenheid wordt de relatieve eenheid rem gebruikt, omdat bij gebruik van een absolute eenheid zoals px de afstand tot de bovenkant niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

main

Deze selector werkt alleen in browservensters minimaal 760 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.)

main {background: white; color: black; border: black solid 1px; padding: 5px;}

Alle <main>'s. Dat is er maar één: de belangrijkste inhoud van de pagina staat hierin. Hier is dat alleen een korte beschrijving van de werking van het menu.

width: 45rem;

Een <main> is een blok-element en wordt daardoor standaard even breed als z'n ouder <body>. Body is ook een blok-element en wordt daardoor weer even breed als z'n ouder <html> Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Daardoor zou <main> in brede vensters heel breed worden, wat veel te lange, moeilijk leesbare regels oplevert. Hier wordt de breedte beperkt tot 45 rem.

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

De minder bekende rem is ongeveer hetzelfde als de em. Alleen is de lettergrootte bij rem gebaseerd op de lettergrootte van het <html>‑element, waardoor de rem overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem niet.

max-width: 96vw;

Maximumbreedte.

Hier gelijk boven is een breedte van 45 rem opgegeven. Als de letters sterk worden vergroot, zouden de regels hierdoor breder worden dan het venster van de browser, waardoor je voortduren horizontaal zou moeten scrollen. In sommige browsers op sommige systemen verschijnt hierbij een horizontale scrollbalk. Door de breedte een maximum te geven, wordt dit voorkomen.

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 96 vw is 96% van de breedte. De <main> wordt hierdoor nooit breder dan 96% van de breedte van het venster, ongeacht de breedte van het venster.

margin: 0 auto;

Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 0 auto 0 auto in de volgorde boven – rechts – onder – links.

Boven en onder geen marge. Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor komt <main> altijd horizontaal gecentreerd binnen ouder <body> te staan. <body> is een blok-element en wordt daardoor normaal genomen automatisch even breed als de ouder ervan: <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser.

Hierdoor staat uiteindelijk <main> altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht de breedte van het venster. En daarmee ook de in <main> zittende tekst.

Deze manier van horizontaal centreren van een blok-element werkt alleen, als het blok-element een breedte heeft, want anders zou het normaal genomen even breed worden als de ouder ervan. Dat is geregeld, want iets hierboven heeft <main> een breedte van 45 rem en een maximumbreedte van 96 vw gekregen.

h1

Deze selector werkt alleen in browservensters minimaal 760 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.)

h1 {font-weight: normal; text-align: center; margin: 0;}

Alle <h1>'s. Dat is er maar één: de belangrijkste kop. Hier staat daar alleen maar in , welke pagina het is.

margin-top: 20px;

Kleine afstand tussen de <h1> en de bovenkant van <main>.

css alleen voor apparaten met een precieze aanwijzer, zoals een muis, én met vensters minimaal 760 px breed

@media (hover: hover) and (pointer: fine)

De css die binnen deze 'media query' staat, is alleen bedoeld voor apparaten die worden bediend met een muis of een touchpad of een soortgelijke nauwkeurige aanwijzer. De beschrijving hiervan is te vinden bij media (hover: hover) and (pointer: fine).

Er is één verschil: deze @media query staat binnen een andere media query: media screen and (min-width: 760px). Daarom geldt hier als extra voorwaarde dat het browservenster minimaal 760 px breed moet zijn. De css binnen deze media query werkt dus alleen op apparaten die worden bediend met een muis of een touchpad of een soortgelijke nauwkeurige aanwijzer én alleen in vensters die minimaal 760 px breed zijn.

#menu-ul-vb li:hover a

Deze selector werkt alleen in browservensters minimaal 760 px breed én alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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.)

#menu-ul-vb a {background: blue; color: white; display: block; text-decoration: none; border: white solid 1px; pointer-events: none;}

#menu-ul-vb a[aria-current] {background: #444; color: white; cursor: default;}

#menu-ul-vb li:focus-within a:not([aria-current]) {background: white; color: black; text-decoration: underline; outline: black solid 1px; outline-offset: -1px;}

#menu-ul-vb a:focus-within[aria-current] {text-decoration: red line-through 3px; outline: black solid 1px;}

#menu-ul-vb li:focus-within a {pointer-events: auto;}

#menu-ul-vb li:hover a:not([aria-current]) {background: white; color: black; text-decoration: underline; outline: black solid 1px; outline-offset: -1px; pointer-events: auto;}

#menu-ul-vb li:hover a[aria-current] {text-decoration: red line-through 3px; outline: black solid 1px;}

#menu-ul-vb li:first-child a {border-top-left-radius: 8px;}

#menu-ul-vb li:last-child a {border-top-right-radius: 8px;}

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:hover: maar alleen als over de <li> wordt gehoverd.

a: de <a>'s binnen die <li>, waarover wordt gehoverd. Binnen elke <li> zit één <a>.

De hele selector in gewone taal: de <a> binnen de <li> waarover wordt gehoverd binnen ul#menu-ul-vb.

pointer-events: auto;

Eerder zijn bij #menu-ul-vb a met pointer-events: none; de <a>'s ongevoelig gemaakt voor een klik: de links werken niet. Zodra over een <li> wordt gehoverd, wordt hier met pointer-events: auto; de werking van de link hersteld. Dit gebeurt zo snel, dat de link altijd gewoon lijkt te werken.

Op een aanraakscherm is dat uitschakelen van de links belangrijk, want daar moet bij de eerste aanraking het venstertje met informatie worden getoond, zonder dat de link wordt gevolgd. Pas bij de tweede aanraking moet de link worden gevolgd. Bij gebruik van een muis of touchpad is dat niet van belang, want daarbij wordt het venstertje met informatie al getoond door over de knop te hoveren.

#menu-vb #menu-ul-vb li:hover a + span

Deze selector werkt alleen in browservensters minimaal 760 px breed én alleen op apparaten, waarvan de belangrijkste aanwijzer een nauwkeurige is, zoals een muis. Voor andere apparaten 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.)

#menu-ul-vb a + span {background: yellow; color: black; box-sizing: border-box; width: 45vw; line-height: normal; border: black solid 1px; padding: 5px; position: absolute; top: 0; z-index: -1; transform: translateX(-80%); transition-property: transform, z-index; transition-duration: 0.5s, 0s;}

#menu-ul-vb li:nth-of-type(n + 5) a + span {top: auto; bottom: 0;}

#menu-ul-vb li:focus-within a + span {left: 99%; z-index: 10; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-ul-vb li:hover a + span {left: 99%; z-index: 20; transform: translateX(0); transition-delay: 0s, 0.5s;}

#menu-vb #menu-ul-vb a + span {opacity: 0; width: auto; height: 0; overflow: hidden; bottom: auto; left: 0; transition: opacity 0.8s;}

#menu-vb #menu-ul-vb li:focus-within a + span {opacity: 1; height: auto; top: 2.3rem;}

#menu-vb: de <nav> waar het hele menu in zit.

#menu-ul-vb: het element met id="menu-ul-vb". De <ul> waar het menu in zit.

li: de <li>'s binnen die <ul>.

:hover: maar alleen als over die <li> wordt gehoverd.

a: de <a>'s binnen die <li> waarover wordt gehoverd. Binnen elke <li> zit één <a>.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om de <span> die gelijk op de <a> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze zitten beide in de zelfde <li>.

span: de <span> met het venstertje met informatie.

De hele selector in gewone taal: de <span> die gelijk op een <a> volgt, die binnen de <ul> zit die weer binnen de <nav> met het menu zit, maar alleen als over die <li> wordt gehoverd. Dit is de <span>, waarin het venstertje met informatie over de link zit.

opacity: 1; height: auto; top: 2.3rem;

Deze eigenschappen zorgen ervoor dat het venstertje met informatie zichtbaar wordt. De beschrijving hiervan staat bij staat bij #menu-vb #menu-ul-vb li:focus-within a + span. Het enige verschil: hier wordt over de <li> gehoverd, terwijl bij de eerdere beschrijving de <li> de focus heeft.

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.) Alle niet-essentiële code is bruin.

Bij de uitleg van deze code zijn allerlei haakjes en dergelijke grotendeels weggelaten, want dat voert hier te ver. Als je je in dat soort dingen wilt verdiepen, kun je beter naar sites gaan die meer voor JavaScript zijn bedoeld.

Zonder JavaScript werkt dit menu wel, maar is het lastiger toegankelijk voor schermlezers en met Toetsenbordnavigatie. Daarom wordt een groot deel van het JavaScript tot de essentiële code gerekend. Ook andere gebruikers kunnen er voordeel van hebben, maar dat is eigenlijk min of meer toeval.

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 opene, zodat je niet steeds tussen uitleg en script heen en weer hoeft te vliegen. Het script is te vinden in de map 012-js.

Dit script wijzigt, in browservensters smaller dan 760 px, de html. Om dat te kunnen zien moet je niet de gewone broncode, maar de Gegenereerde code bekijken.

// menu-012.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 in een niet 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 als toonMenuVb. 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.

if(window.innerWidth >= 760) return;

Als het venster van de browser 760 px of meer breed is, wordt het script niet uitgevoerd. (In die vensters is de menubalk met de zes knoppen altijd zichtbaar.)

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 haakjes achter de if.

window: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen.

Die informatie kan door onder andere JavaScript gebruikt worden. De informatie is overzichtelijk opgeslagen in allerlei afdelingen en onderafdelingen, die in JavaScript 'object' heten.

Een van die objecten is het 'window' object. Daarin zit allerlei meer algemene informatie, die betrekking heeft op de pagina en op het browservenster. window geeft alleen maar aan, dat wat achter de punt staat te vinden is in het window-object.

innerWidth: zo'n stukje informatie is innerWidth: de breedte van het browservenster in px, inclusief een eventuele scrollbalk.

>=: meer dan of gelijk aan. Het =‑teken betekent 'is gelijk aan'. Het '>'‑teken wordt vaak gebruikt om groter dan aan te geven. De combinatie >= betekent daarom: meer dan of gelijk aan: wat links van deze twee tekens staat, moet meer zijn dan of gelijk zijn aan wat rechts van deze tekens staat.

760: gewoon een getal. De eenheid px wordt in JavaScript weggelaten, alleen de waarde is genoeg.

(window.innerWidth >= 760): als je het hele deel tussen de haakjes in gewone taal samenvat, staat hier: als de breedte van het venster van de browser 760 px of meer is. Als dat zo is, is aan de voorwaarde voldaan.

return;: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan.

Meestal wordt die code tussen accolades gezet (aan het begin van de code { en } aan het eind). Het script weet dan, wat bij deze if hoort.

Omdat het hier maar om heel weinig code gaat (één woord, om precies te zijn), staat alles op één regel. In dat geval mogen de accolades worden weggelaten.

De hier uit te voeren code is return: ga onmiddellijk terug naar waar je vandaan kwam en voer de rest van de code niet uit.

Omdat dit helemaal aan het begin van het script staat, betekent teruggaan in dit geval het hele script verlaten en daar verder helemaal niets van uitvoeren.

;: achter return staat nog een ;. Daarmee geef je in JavaScript het eind van een regel aan. Het is vergelijkbaar met een punt in gewone taal.

De hele regel in gewone taal: als het browservenster een breedte heeft van 760 px of meer, voer het script dan niet uit, maar ga terug naar waar je vandaan kwam: de pagina met html.

const toonMenuVb = document.querySelector("#toon-menu-vb");

Als je in de html een andere id dan 'toon-menu-vb' hebt gebruikt bij input#toon-menu-vb, moet je die id ook in bovenstaande regel aanpassen.

const toonMenuVb: 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: 'toonMenuVb'. Een variabele is een soort portemonnee: er kan van alles in zitten.

In toonMenuVb 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.

Een gebruikelijke manier om de namen van variabelen weer te geven is 'camelCase'. Hierbij staat aan het begin geen hoofdletter, maar bij elk nieuw woord in de naam wel: toonMenuVb. Dit is dezelfde naam als de id bij de <input>, alleen wordt die id geschreven als 'toon-menu-vb'. Maar koppeltekens mogen in JavaScript niet worden gebruikt in namen van variabelen. Door de koppeltekens te vervangen door hoofdletters, is gelijk duidelijk bij welke id de variabele toonMenuVb hoort. Je kunt ook een totaal andere naam gebruiken, maar zo'n geheugensteuntje is vaak heel handig.

Dingen als het gebruik van hoofdletters zijn geen absolute eisen, maar het zijn wel 'n soort afspraken, waar vrijwel elke programmeur zich aan houdt. Door je aan dit soort afspraken te houden, is je code ook leesbaarder voor anderen (en voor jezelf over acht jaar...).

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 toonMenuVb niet veranderen: input#toon-menu-vb 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.)

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 toonMenuVb 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 toonMenuVb binnen de buitenste functie (function () { aangemaakt. Binnen die buitenste functie staat het hele script. Daardoor kan toonMenuVb binnen het hele script worden gebruikt: de scope van toonMenuVb 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.querySelector("#toon-menu-vb"): het opzoeken van de <input>.

Het middelste stukje querySelector 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 querySelector() uit document kan JavaScript het eerste element opzoeken, dat aan een bepaalde voorwaarde voldoet, zoals het element met een bepaalde id, het eerste element met een bepaalde class, en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:

querySelector("#toon-menu-vb")

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 met id="toon-menu-vb" gezocht. Net zoals je in css in een selector #toon-menu-vb {...} zou gebruiken.

De gevonden input#toon-menu-vb wordt in de variabele toonMenuVb gestopt. Met de inhoud van die variabele 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 toonMenuVb = querySelector("toon-menu-vb"): sla input#toon-menu-vb op in toonMenuVb. Dat wil zeggen dat in variabele toonMenuVb een ongelooflijke hoeveelheid informatie over input#toon-menu-vb wordt gestopt, waar het script later gebruik van kan maken. Zo zit bijvoorbeeld alle css, die aan de <input> is gegeven, ook in toonMenuVb. Maar niet alleen de in het stijlbestand opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd.

Van al deze informatie in toonMenuVb kan het script gebruik maken. En ook kunnen veel van deze dingen worden veranderd door het script, zoals een kleur veranderen, aangevinkt veranderen in niet aangevinkt, of juist andersom.

Feitelijk is de <input> in de vorm van een object in toonMenuVb opgeslagen. Naast allerlei informatie die in dat object zelf in toonMenuVb wordt opgeslagen, kun je daardoor ook gebruik maken van allerlei methoden, die JavaScript gratis en voor niets toevoegt aan het object in toonMenuVb.

Eerder werd beweerd dat een met const aangemaakte variabele niet veranderd kan worden. Dat is ook zo voor wat er in toonMenuVb zélf is opgeslagen: je kunt geen <input> meer toevoegen of verwijderen of zoiets. De <input> is echter in de vorm van een object opgeslagen, en bínnen dat object kun je wel van alles wijzigen. Het script gebruikt het object in variabele toonMenuVb onder andere om bij input#toon-menu-vb de WAI-ARIA-code 'aria-expanded' te veranderen.

In plaats van het gebruik van variabele toonMenuVb zou je in de rest van het script ook elke keer document.querySelector("#toon-menu-vb") 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 één <input> gaat.

toonMenuVb.addEventListener("click", function() {

toonMenuVb: in variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb, de <input> die het menu toont en verbergt, opgeslagen in de vorm van een object. In dat object is een een enorme hoeveelheid over input#toon-menu-vb opgeslagen. Naast die in toonMenuVb opgeslagen informatie kun je ook gebruik maken van allerlei functies, die JavaScript gratis en voor niets toevoegt aan het object toonMenuVb. 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 toonMenuVb, dus eigenlijk aan input#toon-menu-vb. 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'.

Het openingshaakje ( staat gelijk achter addEventListener, het afsluitende ) staat aan het eind van de bij addEventListener horende code.

Omdat deze eventlistener aan input#toon-menu-vb is gekoppeld, werkt deze eventlistener overal binnen input#toon-menu-vb en de bij de <input> horende label#label-toon-menu-vb.

"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.)

function(): deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. Binnen deze 'functie' staat tussen {} de code, die uitgevoerd moet gaan worden.

In dit geval staat de code gelijk hierachter op de volgende regel, omdat de code vrij eenvoudig is. Maar heel vaak staat die code ergens anders, in een aparte functie. In dat geval staat hier de naam van die functie, en wordt de code uitgevoerd die bij die functie tussen {} staat.

De 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. Maar in dit geval wordt niets doorgegeven, dus tussen de haakjes staat niets.

Samengevoegd betekent toonMenuVb.addEventListener("click", function (): koppel aan de in variabele toonMenuVb zittende input#toon-menu-vb een eventlistener, die luistert naar een klik of een aanraking, en voer als dat gebeurt de functie uit.

this.setAttribute("aria-expanded", this.checked);

Dit is de code die tussen de { gelijk achter function() en de } op de volgende regel staat, zodat het script weet, waar de bij de functie horende code begint en eindigt.

this: in deze context verwijst het sleutelwoord this naar het object, dat de functie aanriep. Dat is hier de in variabele toonMevuVb opgeslagen input#toon-menu-vb.

setAttribute(): hiermee 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 het bij de <input> horende menu is uitgevouwen en daarmee zichtbaar, of ingeklapt en daarmee onzichtbaar is.

this.checked:

this: dit verwijst weer naar het object dat de functie aanriep: input#toon-menu-vb.

checked: hierin is opgeslagen, of input#toon-menu-vb 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.

this.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 this.setAttribute("aria-expanded", this.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 input#toon-menu-vb in de html aria-expanded="false" te staan. Als de <input> is aangevinkt, is this.checked true en komt bij input#toon-menu-vb in de html aria-expanded="true" te staan.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

});: dan zijn er nog de afsluitende accolade en het afsluitende haakje.

De afsluitende accolade is de tegenhanger van de { achter function() en geeft het einde van de bij function() horende code aan.

Het afsluitende haakje hoort bij het haakje gelijk achter addEventListener, want die was nog niet afgesloten.

De ; daarachter geeft het eind van de bij toonMenuVb.addEventlistener horende code aan.

(Om te bepalen welk haakje bij welk haakje hoort en dergelijke, hoeft je geen cursus helderziendheid te volgen. Elke goede editor geeft de bij elkaar horende haakjes, accolades, en dergelijke aan. Wat bepaald helpt bij het voorkomen van acute wanhoop bij dit soort haakjeskerstbomen.)

document.querySelector("body").addEventListener("keyup", openSluitVb);

document.querySelector("body"): het opzoeken van het element <body>.

Het middelste stukje querySelector 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 querySelector() uit document kan JavaScript het eerste element opzoeken, dat aan een bepaalde voorwaarde voldoet, zoals het element met een bepaalde id, het eerste element met een bepaalde class, 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.

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'.

"keyup": 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: 'keyup': als een toets weer wordt losgelaten, nadat deze is ingedrukt. Maakt niet uit welke toets. Deze regel roept de functie aan, die het eigenlijke werk gaat doen.

openSluitVb: 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 openSluitVb(e) { en zorgt dat het openen en sluiten van het menu toegankelijker is voor gebruikers van schermlezers en Tab-toets.

;: 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 een toets wordt ingedrukt en weer wordt losgelaten, maakt niet uit welke toets of waar, voer dan de functie 'openSluitVb' uit.

De eventlistener wordt hier aan <body> gekoppeld. Dat betekent dat de functie overal wordt aangeroepen, waar het scherm wordt aangeraakt of ‑geklikt. Je zou de eventlistener ook kunnen koppelen aan een deel van de pagina, maar dat is hier niet nodig: het maakt in dit geval niets uit, wanneer een toets wordt ingedrukt.

document.querySelector("body").addEventListener("click", openSluitVb);

Deze regel doet precies hetzelfde als die gelijk hierboven bij document.querySelector("body").addEventListener("keyup", openSluitVb); Het enige verschil: deze eventlistener luister naar een klik of, op een aanraakscherm, naar een aanraking.

function openSluitVb(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 toets wordt losgelaten, of als het browservenster ergens wordt aangeraakt of ‑geklikt. Het luisteren naar dat loslaten wordt geregeld bij document.querySelector("body").addEventListener("keyup", openSluitVb);, het luisteren naar het klikken of aanraken bij document.querySelector("body").addEventListener("click", openSluitVb);.

De code in deze functie zorgt ervoor dat het menu ook getoond en verborgen kan worden met Enter en Escape. Bovendien regelt deze functie mede dat het voor schermlezers belangrijke aria-expanded de juiste waarde krijgt.

* Als Escape wordt ingedrukt, wordt het menu verborgen.

Als Escape binnen het menu wordt ingedrukt, wordt de focus teruggezet op de <input>, waarmee het menu getoond en verborgen kan worden.

Als Escape buiten het menu wordt ingedrukt, wordt de focus op <main> gezet.

* Als de <input> waarmee het menu kan worden getoond en verborgen de focus heeft, kan het menu ook met de Enter-toets worden getoond of verborgen. (Normaal genomen heeft een Enter-toets geen effect op een <input>.)

* Als het browservenster buiten het menu wordt aangeraakt of ‑geklikt, wordt het menu verborgen.

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven.

openSluitVb: 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 open-sluit-vb kunnen gebruiken in plaats van openSluitVb.

(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, of door het aanraken of ‑klikken van het scherm. 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, een klik of een aanraking. 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 === "Escape") {

Deze regel is onderdeel van function openSluitVb(e)

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– Escape is ingedrukt.

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 openSluitVb(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.

{: 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 Escape is ingedrukt, voer dan de code tussen de {} achter de if uit.

toonMenuVb.checked = false;

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– Escape is ingedrukt.

toonMenuVbM: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

checked = false: in de eigenschap checked is opgeslagen, of de <input> is aangevinkt of niet. (De <input> is van het type 'checkbox' en kan dus worden aan- en uitgevinkt.) Als de <input> is aangevinkt, wordt het menu getoond. Als de <input> niet is aangevinkt, wordt het menu niet getoond. Dit wordt met behulp van css geregeld.

Met behulp van JavaScript kun je de <input> ook aan- of uitvinken. Dat is wat hier gebeurt: de <input> wordt uitgevinkt.

checked: in deze eigenschap is de waarde true (aangevinkt) of de waarde false (niet-aangevinkt) opgeslagen.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande eigenschap het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

false: dit is, wat in de eigenschap checked wordt opgeslagen: niet aangevinkt.

;: De puntkomma geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: verander de waarde van checked in 'false'. Als de waarde al 'false' was, gebeurt er gewoon niets.

Omdat hierdoor ook de selector #toon-menu-vb:checked in de css niet meer van toepassing is, wordt het menu verborgen.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

toonMenuVb.setAttribute("aria-expanded", false);

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– Escape is ingedrukt.

toonMenuVb: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

setAttribute(): hiermee 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 het bij de <input> horende menu is uitgevouwen en daarmee zichtbaar, of ingeklapt en daarmee onzichtbaar is.

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.

Deze regel zorgt ervoor dat aria-expanded de waarde false krijgt, waardoor schermlezers kunnen melden dat het menu niet zichtbaar is.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

if (e.target.parentElement.parentElement.id === "menu-ul-vb") {

Als je in de html een andere id dan 'menu-ul-vb' hebt gebruikt bij ul#menu-ul-vb, moet je die id ook in bovenstaande regel aanpassen. Dit is de enige plaats in het script, waar je dit moet aanpassen.

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– Escape is ingedrukt.

De bij deze if horende code wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

- Escape is ingedrukt terwijl een element, waarvan de grootouder ul#menu-ul-vb is, de focus had (met andere woorden: het element is een van de links in het menu).

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 openSluitVb(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.

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 de focus had op het ogenblik dat Escape werd ingedrukt.

parent: dit is zo'n stukje informatie uit target: het is de ouder van het element dat de focus had op het ogenblik dat Escape werd ingedrukt.

parent: en dit is weer de ouder van die ouder. De grootouder van het element dat de focus had dus.

id: de id van dat element, de id van de grootouder.

===: 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.

"menu-ul-vb": dit is de id die bij de ul met het menu hoort: ul#menu-ul-vb.

Elke <a> van het menu zit in een <li>, die weer in deze <ul> zit. De <ul> is dus de grootouder van de <a>. Alleen als een <a> de focus had op het ogenblik dat Escape werd ingedrukt, is de id van de grootouder 'menu-ul-vb'. Als welk ander element dan ook de focus had op het moment dat Escape werd ingedrukt, of als geen enkel element de focus had, kan de grootouder nooit de id 'menu-ul-vb' hebben, en wordt dus niet aan de voorwaarde voldaan.

{: 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 grootouder van het element dat de focus had op het ogenblik dat Escape werd ingedrukt 'menu-ul-vb' is.

toonMenuVb.focus();

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if en if gestelde voorwaarden is voldaan:

- Escape is ingedrukt terwijl een element, waarvan de grootouder ul#menu-ul-vb is, de focus had (met andere woorden: het element is een van de links in het menu).

toonMenuVb: in de variabele toonMenuVb is const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

focus(): dit is zo' methode die bij een object gebruikt kan worden. Zet de focus op het in de vorm van een object in toonMenuVb opgeslagen object, op input#toon-menu-vb.

De regel in gewone taal: zet de focus op input#menu-toon-vb. Maar vanwege de eerder met behulp van if() opgelegde beperkingen, gebeurt dit alleen als een <a> uit het menu de focus had, op het moment dat Escape werd ingedrukt.

Hierdoor wordt, als een link in het menu de focus had, niet het hele menu gesloten bij het indrukken van Escape, maar wordt de focus weer op de <input> gezet, waarmee het menu geopend en gesloten kan worden.

} else {

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– Escape is ingedrukt.

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 grootouder van het element dat de focus had op het ogenblik dat Escape werd ingedrukt, is niet 'menu-ul-vb' (het element was niet een van de knoppen met een link).

}: 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.

{: de code die wordt uitgevoerd, als aan deze else wordt voldaan, wordt tussen code tussen accolades gezet. Het script weet dan, wat bij deze else hoort. Aan het eind van de code bij deze else staat de afsluitende }.

De regel in gewone taal: voer de code tussen de {} bij de else uit, als niet aan de voorwaarde bij de if is voldaan, als de id van de grootouder van het element dat de focus had op het ogenblik dat Escape werd ingedrukt, niet 'menu-ul-vb' is.

document.querySelector("main").focus();

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:

– Escape is ingedrukt.

Deze regel hoort bij de iets hierboven staande } else {. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:

– De id van de grootouder van het element dat de focus had op het ogenblik dat Escape werd ingedrukt, is niet 'menu-ul-vb' (het element was niet een van de knoppen met een link).

Vaak is het beter om iets in een variabele op te slaan, zoals beschreven bij const toonMenuVb = document.querySelector("#toon-menu-vb");, omdat anders iets steeds opnieuw moet worden opgezocht. Of omdat er iets ingewikkelds met iets moet gebeuren. Hier gaat het om een simpele handeling, die mogelijk zelfs nooit wordt uitgevoerd.

Deze regel wordt alleen uitgevoerd, als Escape wordt ingedrukt, terwijl geen van de <a>'s in het menu de focus heeft. Dit zal geen tientallen keren achter elkaar gebeuren.

Daarom wordt hier de hele handel gelijk in één keer uitgevoerd: het opzoeken van een element en het uitvoeren van de bewerking daarop, zonder de tussenliggende stap van het opslaan van het element in een variabele.

document: dit is een zogenaamd 'object'. 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.

querySelector("main"): dit is een van die hierboven genoemde methodes. Deze methode is te vinden in het object document, vandaar dat document ervoor staat.

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("main")

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 <main> gezocht, net zoals je in css in een selector main {...} 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.

In document.querySelector("main") wordt dus het element <main> opgeslagen, ook weer in de vorm van een object. De stap voor het opslaan in een variabele wordt overgeslagen, maar verder werkt het min of meer hetzelfde als bij een variabele: ook in dit tijdelijke object (het wordt niet echt opgeslagen) zit heel veel informatie over <main>, en ook kunnen weer allerlei methoden worden gebruikt. Zo zit in het object bijvoorbeeld alle css, die aan <main> is gegeven. Maar niet alleen de in het stijlbestand opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd. Alle nakomelingen van <main> en hun css, attributen, enzovoort, zitten ook in het object.

Van al deze informatie in het object kan het script gebruik maken. En veel dingen binnen het object kunnen worden veranderd door het script, zoals een kleur veranderen, of een element verwijderen, of juist toevoegen.

.focus(): dit is zo'n methode. <main>, het element dat bij het gevonden object hoort, krijgt de focus.

Als gebruikers van de Tab-toets door het menu lopen, wordt het menu op een gegeven moment verlaten. Maar het menu staat dan nog wel open en kan een deel van de rest van de pagina afdekken. Gebruikelijk is dat je zo'n menu dan kunt sluiten door het indrukken van Escape. Dat zit niet standaard ingebakken in html, daarom is dit in de regels hierboven met behulp van JavaScript geregeld.

Als Escape wordt ingedrukt, terwijl één van de <a>'s in het menu de focus heeft, wordt de focus teruggezet op input#toon-menu-vb, waarmee het menu getoond en verborgen kan worden. Vervolgens kan met één toetsaanslag het menu weer worden getoond of in z'n geheel worden gepasseerd.

Maar als Escape wordt ingedrukt, terwijl geen van de <a>'s in het menu de focus had, zou het wat onhandig zijn de focus weer terug te zetten op input#toon-menu-vb. Daarom wordt de focus nu op <main> gezet, het element met de belangrijkste inhoud van de pagina. Normaal genomen kan <main> geen focus krijgen, maar door het toevoegen van tabindex="-1" aan <main>, kan dat hier wel.

;: De puntkomma geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: geef de focus aan <main>.

} else if (e.key === "Enter" && e.target.id === "toon-menu-vb") {

Als je in de html een andere id dan 'toon-menu-vb' hebt gebruikt bij input#toon-menu-vb, moet je die id ook in bovenstaande regel aanpassen.

Deze else if hoort bij een eerdere if. De code bij deze else if wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape is niet ingedrukt.

– Enter is ingedrukt, terwijl input#toon-menu-vb de focus had.

Als aan de voorwaarde bij de eerdere if, waar deze } else if { bij hoort, niet is voldaan, volgt hier een herkansing. Ook dit ziet er ingewikkeld uit, maar in stukjes gehakt wordt het weer veel overzichtelijker. (Die voorwaarde bij de eerdere if was dat Escape moest zijn 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 eerdere bij if gestelde voorwaarde niet is voldaan, kijk dan of aan deze andere voorwaarde misschien wel wordt voldaan.

(): tussen de haakjes staan de (in dit geval twee) voorwaarden, waaraan moet worden voldaan om de code bij deze else if uit te voeren:

e.key === "Enter" && e.target.id === "toon-menu-vb"

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function openSluitVb(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 eerste voorwaarde, waaraan moet worden voldaan, is dus dat Enter is ingedrukt.

De code binnen deze else if zorgt ervoor dat bij indrukken van Enter het menu wordt getoond, als dat was verborgen, en juist wordt verborgen, als dat al wordt getoond. Als dit zo wordt gelaten, zou Enter overal op de pagina werken. Als iemand dan ergens Enter zou indrukken, zou bijvoorbeeld 'Menu openen' veranderen in 'Menu sluiten', en omgekeerd. Ook zou op onverwachte momenten het menu getoond en verborgen kunnen worden door het indrukken van Enter. Daarom wordt nog een tweede voorwaarde voor het uitvoeren van de code bij de else if toegevoegd:

&& e.target.id === "toon-menu-vb"

&&: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de && moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&, wordt niet eens meer gekeken naar de voorwaarde achter de &&.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function openSluitVb(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.

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 de focus had op het ogenblik dat Escape werd ingedrukt.

id: de id van dat 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.

"menu-ul-vb": dit is de id die bij de ul met het menu hoort: ul#menu-ul-vb. Alleen als ul#menu-ul-vb de focus had op het ogenblik dat Enter werd ingedrukt, is de id van het element 'menu-ul-vb'. Als welk ander element dan ook de focus had op het moment dat Enter werd ingedrukt, of als geen enkel element de focus had, kan het element nooit de id 'menu-ul-vb' hebben, en wordt dus niet aan de voorwaarde voldaan.

De tweede voorwaarde, waaraan moet worden voldaan, is dus dat de id van het element dat de focus heeft, op het moment dat Enter wordt ingedrukt, 'toon-menu-vb' is.

{: 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 }. In dit geval is dat de } bij } else if (document.querySelector(#menu-vb").contains(e.target)) { iets verderop.

De hele regel in gewone taal: als de ingedrukte toets Enter is én als de id van het element dat de focus had op het ogenblik dat Enter werd ingedrukt 'toon-menu-vb' is.

De code tussen de {} achter de else if wordt alleen uitgevoerd, als aan beide voorwaarden is voldaan: Enter moet zijn ingedrukt terwijl input#toon-menu-vb de focus had. Als aan één van beide voorwaarden niet wordt voldaan, wordt de code tussen de {} niet uitgevoerd.

toonMenuVb.checked = !toonMenuVb.checked;

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt hoort bij de iets hierboven staande } else if. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape is niet ingedrukt.

– Enter is ingedrukt, terwijl input#toon-menu-vb de focus had.

toonMenuVb: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.

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.

toonMenuVb: is weer de variabele waarin input#toon-menu-vb is opgeslagen.

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'.

!toonMenuVb.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 toonMenuVb.checked voor het isgelijkteken het resultaat van !toonMenuVb.checked achter het isgelijkteken.

Als input#toon-menu-vb 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 input#toon-menu-vb uitgevinkt.

Als input#toon-menu-vb niet is aangevinkt, gebeurt het omgekeerde: de <input> wordt aangevinkt.

Omdat hierdoor ook de selector #toon-menu-vb:checked in de css wordt beïnvloed, wordt het menu getoond als dit was verborgen, en verborgen als het werd getoond.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

toonMenuVb.setAttribute("aria-expanded", toonMenuVb.checked);

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt hoort bij de iets hierboven staande else if. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape is niet ingedrukt.

– Enter is ingedrukt, terwijl input#toon-menu-vb de focus had.

toonMenuVb: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

setAttribute(): hiermee 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 het bij de <input> horende menu is uitgevouwen en daarmee zichtbaar, of ingeklapt en daarmee onzichtbaar is.

toonMenuVb.checked: de waarde die het attribuut moet krijgen. In de regel toonMenuVb.checked = !toonMenuVb.checked; hier gelijk boven heeft checked de waarde 'true' of 'false' gekregen: input#toon-menu-vb is wel of niet aangevinkt. Hier wordt dezelfde informatie aangepast voor schermlezers, door aria-expanded dezelfde waarde als checked te geven.

;: De puntkomma geeft het eind van dit deel van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: maak bij input#toon-menu-vb de waarde van het attribuut aria-expanded hetzelfde als de waarde van checked: 'false' als checked 'false is', en 'true' als checked 'true' is.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

} else if (document.querySelector("#menu-vb").contains(e.target)) {

Als je in de html een andere id dan 'menu-vb' hebt gebruikt bij nav#menu-vb, moet je die id ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function openSluitVb(e)

Deze else if hoort bij een eerdere if en een eerdere else if. De code bij deze else if wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape is niet ingedrukt.

– Enter is niet ingedrukt, terwijl input#toon-menu-vb de focus had.

– Het element dat werd aangeraakt of ‑geklikt is een nakomeling van nav#menu-vb.

Als aan de voorwaarde bij de if en aan de voorwaarden bij de eerdere else if, waar deze else if bij hoort, niet is voldaan, volgt hier een herkansing. Ook dit ziet er ingewikkeld uit, maar in stukjes gehakt wordt het weer veel overzichtelijker. (Die eerdere voorwaarde bij de if was dat Escape moest zijn ingedrukt, en bij de else if dat Enter wordt ingedrukt terwijl input#toon-menu-vb de focus heeft.)

} else if: de } aan het begin is de afsluitende } van de code, die wordt uitgevoerd als aan de voorwaarde bij de vorige else 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 en else if gestelde voorwaarden 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:

document.querySelector("#menu-vb").contains(e.target)

document.querySelector("#menu-vb"): 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("#menu-vb")

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 met id="menu-vb" gezocht, net zoals je in css in een selector #menu-vb {...} 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.

In document.querySelector("#menu-vb") wordt dus het element met id="menu-vb" opgeslagen, ook weer in de vorm van een object. In het voorbeeld gaat het om nav#menu-vb: de <nav> waar het menu in zit.. De stap voor het opslaan in een variabele wordt overgeslagen, maar verder werkt het min of meer hetzelfde als bij een variabele: ook in dit tijdelijke object (het wordt niet echt opgeslagen) zit heel veel informatie over nav#menu-vb, en ook kunnen weer allerlei methoden worden gebruikt.

Zo zit in het object bijvoorbeeld alle css, die aan nav#menu-vb is gegeven. Maar niet alleen de in het stijlbestand opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd. Alle nakomelingen van #menu-vb en hun css, attributen, enzovoort, zitten ook in het object.

Van al deze informatie in het object kan het script gebruik maken. En veel dingen binnen het object kunnen worden veranderd door het script, zoals een kleur veranderen, of een element verwijderen, of juist toevoegen.

contains(e.target): dit is zo'n methode. Er wordt gekeken, of het element dat is aangeraakt of ‑geklikt, binnen nav#menu-vb, het element dat bij het gevonden object hoort, zit.

contains(): dit is de methode. Tussen de haakjes staat het element, waarnaar gekeken moet worden.

e: dit is een object met onder andere informatie, dat bij het aanroepen van function openSluitVb(e) { in de vorm van een object is meegegeven. Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit het element, wat de functie heeft aangeroepen, het element dat werd aangeraakt of ‑geklikt toen de functie werd aangeroepen.

;: De puntkomma geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: kijk of het element dat werd aangeraakt of ‑geklikt toen de functie werd aangeroepen binnen nav#menu-vb zit.

return;

Deze regel is onderdeel van function openSluitVb(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder else if gestelde voorwaarde is voldaan:

- Het element dat werd aangeraakt of ‑geklikt is een nakomeling van nav#menu-vb.

Doe verder niets. Daar zorgt deze regel voor: stop en ga terug naar waar je vandaan kwam.

Dat 'terug naar waar je vandaan kwam' betekent: terug naar de plek, waarvandaan is aangeroepen. In dit geval is er geen terug, want function openSluitVb(e), waar deze return een onderdeel van is, is niet aangeroepen door een ander deel van het script, maar door een klik, een aanraking of het indrukken van een toets: deze functie is vanuit de html 'aangeroepen'. 'Terug naar waar je vandaan kwam' betekent hier dus: weg uit het script en 'terug' naar de html. Oftewel: de rest van het script wordt niet uitgevoerd.

De puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.

In de css is geregeld dat bij een aanraking van een van de knoppen met een link een venstertje met informatie wordt geopend. Daarom hoeft het script verder niets te doen: de css handelt dit af.

} else {

Deze regel is onderdeel van function openSluitVb(e)

Deze else hoort bij een eerdere if en twee eerdere else if's: else if en else if

De code bij deze else wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape werd niet ingedrukt.

– Enter is niet ingedrukt, terwijl input#toon-menu-vb de focus had.

– Het element dat werd aangeraakt of ‑geklikt is geen nakomeling van nav#menu-vb.

}: 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 voorwaarden bij de if en de eerdere else if 's 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 voorwaarden bij de if en de else if's niet wordt voldaan.

{: de code die wordt uitgevoerd, als aan de voorwaarde bij 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 }.

Als aan geen van de eerdere voorwaarden bij de if en de else if's is voldaan, is de enige mogelijkheid nog dat het scherm ergens buiten het menu is aangeraakt of ‑geklikt.

toonMenuVb.checked = false;

Deze regel hoort bij de iets hierboven staande else. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape werd niet ingedrukt.

– Enter is niet ingedrukt, terwijl input#toon-menu-vb de focus had.

– Het element dat werd aangeraakt of ‑geklikt is geen nakomeling van nav#menu-vb.

toonMenuVb: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

checked = false: in de eigenschap checked is opgeslagen, of de <input> is aangevinkt of niet. (De <input> is van het type 'checkbox' en kan dus worden aan- en uitgevinkt.) Als de <input> is aangevinkt, wordt het menu getoond. Als de <input> niet is aangevinkt, wordt het menu niet getoond. Dit wordt met behulp van css geregeld.

Met behulp van JavaScript kun je de <input> ook aan- of uitvinken. Dat is wat hier gebeurt: de <input> wordt uitgevinkt.

checked: in deze eigenschap is de waarde true (aangevinkt) of de waarde false (niet-aangevinkt) opgeslagen.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande eigenschap het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

false: dit is, wat in de eigenschap checked wordt opgeslagen: niet aangevinkt.

;: De puntkomma geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.

De hele regel in gewone taal: verander de waarde van checked in 'false'. Als de waarde al 'false' was, gebeurt er gewoon niets.

Omdat hierdoor ook de selector #toon-menu-vb:checked in de css wordt beïnvloed, wordt het menu verborgen.

Als het menu geopend is, verbergt dit een deel van de pagina. Je kunt het menu weer sluiten door op de <input> te klikken of door de <input> aan te raken, maar dan moet je precies op die <input> mikken. Nu sluit het menu ook, als het scherm elders wordt aangeklikt of aangeraakt.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)

toonMenuVb.setAttribute("aria-expanded", false);

Deze regel hoort bij de iets hierboven staande else. De regel wordt alleen uitgevoerd, als aan onderstaande voorwaarden is voldaan:

– Escape werd niet ingedrukt.

– Enter is niet ingedrukt, terwijl input#toon-menu-vb de focus had.

– Het element dat werd aangeraakt of ‑geklikt is geen nakomeling van nav#menu-vb.

toonMenuVb: in de variabele toonMenuVb is bij const toonMenuVb = document.querySelector("#toon-menu-vb"); input#toon-menu-vb in toonMenuVb opgeslagen. (Feitelijk is een object opgeslagen, waarin allerlei informatie over de <input> zit, zoals of de <input> is aangevinkt of niet.)

setAttribute(): hiermee 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, het bij de <input> horende menu is uitgevouwen en daarmee zichtbaar, of ingeklapt en daarmee onzichtbaar is.

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.

Deze regel zorgt ervoor dat aria-expanded de waarde false krijgt, waardoor schermlezers kunnen melden dat het menu niet zichtbaar is.

(Als je deze veranderingen wilt zien, moet je niet naar de gewone code, maar naar de Gegenereerde code kijken.)