Skip links en inhoudsopgave

Uitleg lange tekst met inhoudsopgave. Inhoudsopgave en tekst zijn onafhankelijk te scrollen

Laatst aangepast: .

Afbeelding 1: links een lange tekst, rechts de inhoudsopgave

Korte omschrijving

In de inhoudsopgave staan koppelingen naar de tekst. In bredere vensters is de inhoudsopgave altijd zichtbaar, in smallere vensters alleen op verzoek. Inhoudsopgave en tekst kunnen onafhankelijk van elkaar scrollen.

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, downloadt dan de hele handel (ga terug naar het voorbeeld en kies daar voor downloaden). In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.

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 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 blauw gekleurd. 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 (Kubuntu). 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.

Iets gevonden waar je wat aan hebt? Mooi. Als je je waardering wilt uiten, maak dan een donatie over aan War Child Nederland, een organisatie die kinderen uit oorlogsgebieden helpt hun trauma's te verwerken. Of - nog beter - wordt donateur:
Naar site van War Child Nederland

Achterliggend idee

De inhoudsopgave staat in een <nav>, de tekst staat in een <main>. Deze twee elementen staan samen weer in een gezamenlijke ouder: div#wrapper.

Door div#wrapper absoluut te positioneren binnen boven- en onderkant van het browservenster, wordt het venster van boven tot onder gevuld. <nav> en <main> kunnen dan absoluut binnen div#wrapper worden gepositioneerd, waardoor ze los van elkaar kunnen scrollen.

(In eerdere versies hadden inhoud en tekst geen gemeenschappelijke ouder. De inhoud stond fixed gepositioneerd, de tekst niet. Bij deze constructie scrolt op iOS de tekst mee met de inhoud, dus deze constructie moest worden aangepast.)

De inhoudsopgave bestaat uit een hele serie links, die in een ongeordende lijst <ul> worden gezet. Klikken op of aanraken van een link zorgt ervoor dat de juiste plek in de tekst bovenaan wordt gezet. De links verwijzen naar ankers in de tekst. Dat die tekst naast de inhoudsopgave staat maakt niets uit voor de werking.

Een tekst als deze is uitstekend geschikt om te werken met kopregels (<h1>, <h2>, enzovoort). De belangrijkste titel geef je <h1>, hoofdstukken <h2> en paragrafen en artikelen <h3>. Je zou ook paragrafen h3 kunnen geven en artikelen h4, maar paragrafen worden nogal onregelmatig gebruikt, dus dat is niet gedaan. Door het gebruik van kopregels kunnen schermlezers makkelijk door de tekst heen 'springen'.

In smallere browservensters ontstaat een probleem. Het lijkt wat onwaarschijnlijk dat mensen echt heel blij worden van een inhoudsopgave, die vier vijfde van het venster vult. Daarom wordt de inhoudsopgave in smallere vensters verborgen. Pas na het aanraken van een knop wordt de inhoudsopgave zichtbaar.

Boven de inhoudsopgave is een kleine ruimte vrijgelaten. Hierdoor is het begin van bij de gekozen link horende tekst te zien. Je kunt daardoor zien, of je de juiste tekst hebt gekozen, zonder dat je de inhoudsopgave eerst weer moet sluiten.

Browsers bewaren bezochte links in de browsergeschiedenis. Hierdoor kun je snel naar 'n bezochte site terug‑ of vooruitgaan met behulp van de Terug‑ of Vooruitknop van de browser, of met behulp van een toetscombinatie als Alt+← of Alt+→. Dat betekent echter ook dat alle gevolgde links in de inhoudsopgave worden opgeslagen, wat niet erg zinvol lijkt. Dit wordt voorkomen met behulp van JavaScript. Favorieten (of bookmarks, of hoe het beestje ook heet) kunnen wel gewoon worden gemaakt. Ook zitten de links gewoon in de lijst met bezochte adressen.

De voorvoegsels -moz-, -ms- en -webkit-

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

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

De belangrijkste browsers hebben elk een eigen voorvoegsel:

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

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

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

Internet Explorer: -ms-, naar de maker: Microsoft. (Edge gebruikt geen voorvoegsels, maar vanwege compatibiliteit met oudere sites kunnen er nog wat aanwezig zijn.)

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

In dit voorbeeld worden transform, -webkit-overflow-scrolling, -webkit-animation en @-webkit-keyframes gebruikt.

transform

Op dit moment moet je nog het volgende schrijven:

{-webkit-transform: ...; transform: ...;}

In de toekomst kun je volstaan met:

{transform: ...;}

-webkit-overflow-scrolling

Als op iOS niet de hele pagina, maar binnen een element wordt gescrold, zoals hier binnen <nav> en <main> gebeurt, gaat dat scrollen om een of andere duistere reden ongelooflijk schokkerig. Met deze eigenschap kan voor vloeiend scrollen worden gezorgd.

Omdat dit alleen voor iOS nodig is, wordt van deze eigenschap alleen de -webkit-versie gebruikt: buiten iOS is hij niet nodig (en werkt ook niet).

Deze eigenschap kan grote problemen opleveren en moet grondig worden getest. Meer hierover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Zoomen op iOS in vensters minstens 760 px breed.

-webkit-animation en @-webkit-keyframes

Deze twee eigenschappen worden alleen maar gebruikt om een bug in sommige oudere op webkit gebaseerde browsers te repareren. Daarom hoeft alleen maar op browsers gelet te worden, die op webkit zijn gebaseerd. In dit geval is daarom het volgende voldoende:

{-webkit-animation: ...;}

Hetzelfde geldt voor @keyframes:

@-webkit-keyframes {...}

In nieuwere browsers is deze bug niet meer aanwezig, dus de versie zonder voorvoegsel hoeft in dit geval helemaal niet gebruikt te worden.

(In het algemeen is het een bijzonder slechte gewoonte om van een eigenschap alleen één bepaalde versie te gebruiken. Dit gebeurt nogal eens voor iOS, waarmee Apple actief wordt geholpen om sites en dergelijke ontoegankelijk te maken voor andere browsers dan Safari. Ontwikkelaars die dit doen, werken mee aan de totstandkoming van eenzelfde wantoestand als in het verleden met het monopolie van Internet Explorer 6 heeft bestaan.

Maar in dit geval maakt het niet uit, omdat het alleen om een bug gaat. Andere browsers hebben deze css helemaal niet nodig.)

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

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

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

Voorlopig zijn we echter nog niet van deze voorvoegsels af. Als je ze gebruikt, gebruik dan álle varianten, en eindig met de variant zonder voorvoegsel, zoals die uiteindelijk ooit gebruikt gaat worden. Als je alleen de -webkit-variant gebruikt, ben je in feite 'n onbetaalde reclamemaker voor Apple. (Dit geldt dus niet voor de hierboven genoemde -webkit-overflow-scrolling, @-webkit-keyframes en -webkit-animation, want deze worden alleen gebruikt om een probleem in oudere webkit-browsers en op iOS op te lossen.)

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. In dit voorbeeld worden hiervan <nav> en <main> gebruikt. Beide 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.

<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, waarmee je naar andere pagina's en dergelijke kunt gaan.

Omdat <nav> alleen aangeeft dat hierin een of andere vorm van navigatie is ondergebracht, maar niet wat voor navigatie, staat gelijk onder de openingstag <nav> een <h2>. De <h2> geeft aan, wat voor soort navigatie hier staat: 'Inhoudsopgave grondwet'. Deze <h2> wordt links buiten het scherm geparkeerd, zodat je hem niet ziet. Maar schermlezers en dergelijke lezen de kop gewoon voor, ook al zie je hem niet. Hierdoor is voor gebruikers van schermlezers duidelijk, wat voor soort navigatie hier staat.

In het voorbeeld staat maar één soort navigatie, maar bijvoorbeeld op de site staan niet alleen de links van het voorbeeld, maar ook de navigatie voor de site zelf. Door op de site beide <nav>'s van een verborgen kop te voorzien, kunnen schermlezers en dergelijke achterhalen, om welke navigatie het gaat.

<main>

Hierbinnen staat de belangrijkste inhoud van de pagina (in dit voorbeeld is dat de tekst van de grondwet).

Met behulp van dit soort nieuwe semantische elementen kan bijvoorbeeld een schermlezer in één keer een heel menu passeren en gelijk naar de echte inhoud gaan. Alleen hadden deze nieuwe elementen tot voor kort één probleem: ze hadden in de praktijk nog weinig nut, omdat schermlezers en dergelijke ze nog niet herkenden. Daarom werd een zogenaamde WAI-ARIA-code toegevoegd aan deze elementen. Dat is een al veel langer bestaande code, die schermlezers en dergelijke wel herkennen. Voor <main> ziet dat er zo uit:

<main role="main">

Inmiddels is dit behoorlijk veranderd. Het advies is nu om deze speciale toevoeging niet meer te gebruiken, omdat de meeste schermlezers en dergelijke dit soort nieuwe elementen inmiddels herkennen.

WAI-ARIA-codes

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

Er wordt in dit voorbeeld één WAI-ARIA-code gebruikt: aria-hidden.

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.

In dit voorbeeld kunnen de <input> en de <label> voor schermlezers uiterst verwarrend zijn. In browservensters smaller dan 760 px wordt de inhoudsopgave niet getoond, maar kan deze met behulp van de <input> en de <label> zichtbaar worden gemaakt.

Voor schermlezers heeft dit echter geen enkel nut. De inhoudsopgave staat links buiten het scherm geparkeerd. Maar voor schermlezers is de inhoudsopgave gewoon zichtbaar en wordt dus gewoon voorgelezen. (Als ze met display: none; of visibility: hidden; zouden zijn verborgen, zou een schermlezer ze ook niet zien.)

Omdat voor schermlezers de inhoudsopgave altijd zichtbaar is, hebben <input> en <label> geen zin. Met behulp van aria-hidden worden ze verborgen:

<input id="checkbox-voor-smal" type="checkbox" aria-hidden="true">

<label id="toon-menu" for="checkbox-voor-smal" aria-hidden="true">Inhoudsopgave</label>

(Dit verhaal klopt niet helemaal voor de schermlezer TalkBack. Meer daarover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Schermlezers.)

Tabindex en Tab-toets

Links, invoervelden in formulieren, en dergelijke kunnen met behulp van de Tab-toets (of een soortgelijke 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. Een waarde van '0' wordt, afhankelijk van browser en besturingssysteem, verschillend behandeld, waarover iets hieronder bij Tabindex="0" meer.

tabindex="-1"

Een negatieve waarde van -1 zorgt ervoor dat het element volledig wordt genegeerd door de Tab-toets. Zelfs een link met een negatieve tabindex wordt volledig genegeerd. Normaal genomen heeft een tabindex="-1" maar één nut: je kunt dan met behulp van JavaScript toch focus aan het element geven, zonder dat gebruikers van de Tab-toets erdoor worden gehinderd.

In dit voorbeeld wordt tabindex="-1" niet gebruikt, maar het is eventueel te gebruiken op ongeveer dezelfde manier als hieronder bij tabindex="0" wordt beschreven.

tabindex="0"

Volgens de specificatie van html 4.01 moest een tabindex="0" pas worden bezocht, nadat tabindexen met een positief nummer waren bezocht. Sommige browsers hielden zich hier echter niet aan: een tabindex="0" werd gewoon tussen de positieve tabindexen gezet.

In html5 is de situatie nog fijner. Nu staat er alleen dat, wat betreft de volgorde, de gewoonte van het platform wordt gevolgd. Oftewel: doe maar raak. Maar hoe dan ook: als je tabindex="0" gebruikt, kan een element focus krijgen met behulp van de Tab-toets. Ook als dat element normaal genomen geen focus kan krijgen. Hiervan wordt in dit voorbeeld gebruik gemaakt.

Als de gebruiker van een Tab-toets bij een link aankomt en op Enter drukt, wordt die link gevolgd: je gaat naar een andere pagina.

Binnen de pagina zelf werkt dat op ongeveer dezelfde manier: je gaat naar een andere plaats op dezelfde pagina, 'n 'anker'. Als de gebruiker vervolgens nogmaals op de Tab-toets drukt, zou je verwachten dat vanaf dat anker verder wordt gegaan. Tot voor kort deed echter alleen Firefox dat. Sommige browsers gingen, bij nogmaals indrukken van de Tab-toets, terug naar de gevolgde link en gingen vanaf daar verder. Sommige browsers gedroegen zich nog vreemder: die gingen terug naar de bovenkant van de pagina, en gaven dan aan de bovenste link, knop, of iets soortgelijks focus.

Voor scrollen met de pijltjestoetsen en dergelijke gold hetzelfde: behalve in Firefox werd gescrold vanaf de link die was gevolgd, of vanaf de bovenkant van de pagina, maar niet vanaf het bezochte anker op de pagina.

Inmiddels gedragen alle browsers zich hetzelfde als Firefox: als een link binnen een pagina wordt gevolgd en je drukt vervolgens op de Tab-toets, gaat de focus naar de eerste link, knop, en dergelijke die volgt op het bezochte anker. Ook kan vanaf die plaats worden gescrold met de pijltjestoetsen en dergelijke, zonder dat het anker eerst aangeklikt of aangeraakt moet worden.

Maar dit werkt kennelijk nog niet helemaal perfect in alle browsers. Als je in Firefox, Internet Explorer en Edge een link in de inhoudsopgave volgt, gaat het zoals hierboven beschreven. Nogmaals tabben brengt je naar de eerstvolgende link en dergelijke na het bezochte anker, en scrollen met de pijltjestoetsen en dergelijke kan gelijk, zonder dat je eerst de tekst moet aanklikken of aanraken.

In Safari, Opera, UC browser en Google Chrome werkt dit ook zo, maar niet als de link die wordt gevolgd in een element staat, dat niet met de tekst mee kan scrollen. En dat is het geval met de <nav>, waarbinnen de links uit de inhoudsopgave staan. Die <nav> is absoluut gepositioneerd tussen boven- en onderkant van het browservenster.

Als je in deze browsers bijvoorbeeld op 'Artikel 10' klikt – of bij focus van die link op Enter drukt –, wordt de tekst met 'Artikel 10' bovenaan het browservenster gezet. Maar bij nogmaals indrukken van de Tab-toets krijgt de link met 'Artikel 11' uit de inhoudsopgave focus. Ook kun je de tekst pas scrollen, als de tekst is aangeklikt of aangeraakt. Zonder aanraken of -klikken van de tekst laten pijltjestoetsen en dergelijke niet de tekst, maar de inhoudsopgave scrollen. (Terug naar de bovenkant van de pagina gaat gelukkig geen enkele browser meer.)

Als je aan de ankers (het doel van de links in de inhoudsopgave) het attribuut tabindex="0" toevoegt, gedragen alle browsers zich op dezelfde manier als Firefox, Internet Explorer en Edge. Als een link naar een anker wordt gevolgd, brengt een volgende tab je naar de op het anker volgende link, knop, en dergelijke. Scrollen met de pijltjestoetsen en dergelijke gebeurt nu gelijk vanaf de plaats van het anker, zonder dat een klik of aanraking nodig is.

<h3 id="art-10" tabindex="0">Artikel 10</h3>

Hier boven staat het kopje van 'Artikel 10' in de tekst. Door het toevoegen van tabindex="0" gedragen ook deze browsers zich nu op dezelfde manier. Als de link naar 'Artikel 10' is gevolgd, kan gelijk vanaf die plaats worden gescrold. Een volgende tab gaat naar de eerste link, knop, en dergelijke in de tekst die volgt op 'Artikel 10', en niet naar de link in 'Artikel 11' in de inhoudsopgave.

Er is nog een bijkomend voordeel. Bij gebruik van de Tab-toets wordt een <h> normaal genomen genegeerd: alleen links, tekstvelden, knoppen, en dergelijke worden bezocht. Maar door de tabindex="0" worden ook de <h>'s (en andere elementen) nu bezocht door de Tab-toets.

Als een gebruiker door de inhoudsopgave heen tabt en op 'n gegeven moment 'n bepaalde link volgt, brengt een volgende tab de gebruiker naar de volgende <h>. Na de eerste keuze kan de gebruiker dus op precies dezelfde manier verdergaan met de Tab-toets, als wanneer door de inhoudsopgave werd getabd.

Als je tabindex="-1" gebruikt in plaats van tabindex="0", klopt bovenstaand verhaal nog steeds. Alleen kun je nu niet meer met de Tab-toets van anker naar anker springen, want een negatieve tabindex wordt genomen genegeerd door de Tab-toets. Alleen bij tabindex="0" worden elementen zoals een <h> door de Tab-toets bezocht.

Binnen de inhoudsopgave zelf staan ook nog skip-links:

<li class="li-skippy"><a class="skippy" href="l-hs-2">Skip links naar hoofdstuk 1</a></li>

Met behulp van deze links kunnen gebruikers van de Tab-toets (en van de meeste schermlezers) een deel van de inhoud overslaan. Deze skip-links staan binnen de <nav> met de inhoudsopgave, en het doel van de link, het anker, staat ook binnen de <nav>. Daarom spelen de hierboven beschreven problemen hier niet: alle browsers gaan verder vanaf het anker, als de Tab-toets nogmaals wordt ingedrukt.

(Meer over deze skip-links is te vinden bij <li class="li-skippy">...)

(Overigens zijn dit soort problemen soms minder groot, dan ze lijken. Schermlezers bijvoorbeeld kunnen van kopregel naar kopregel springen. Mensen die de muis echt niet kunnen gebruiken, hebben vaak een programmaatje, waardoor ze met behulp van toetsen de cursor kunnen bewegen, klikken, en dergelijke.)

tabindex="..."

Op de plaats van de puntjes moet een positief getal worden ingevuld: het volgnummer. Positieve tabindexen worden in dit voorbeeld niet gebruikt.

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 11 maart 2018.

Onder dit kopje staat alleen maar, hoe en waarin is getest. Alle eventuele problemen, ook die met betrekking tot zoomen, lettergroottes, toegankelijkheid, uitstaan 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

Windows 7 (1280 x 1024 px, resolution: 96 dpi):
Firefox, UC Browser, Google Chrome, Opera en Internet Explorer 11, in grotere en kleinere browservensters.

OS X 10.11.6 ('El Capitan') (1680 x 1050 px, resolution: 96: dpi, device-pixel-ratio: 1):
Firefox, Safari, Opera en Google Chrome, in grotere en kleinere browservensters.

Linux (Kubuntu 14.04 LTS, 'Trusty Tahr') (1280 x 1024 px, resolution: 96 dpi):
Firefox, Opera en Google Chrome, in grotere en kleinere browservensters.

Laptops

Windows 8.1 (1366 x 768 px, resolution: 96 dpi):
Bureaublad-versie: Firefox, UC Browser, Google Chrome, Opera en Internet Explorer 11, in grotere en kleinere browservensters.
Startscherm-versie: Internet Explorer 11.

Windows 10 (1600 x 900 px, resolution: 96 dpi):
Firefox, UC Browser, Google Chrome, Internet Explorer 11, Opera en Edge, in grotere en kleinere browservensters.

Tabletten

iPad met iOS 9.3.5 (1024 x768 px, device-pixel-ratio: 1):
Safari, Chrome for iOS, UC Browser, Firefox (alle portret en landschap).
Opera Mini (Opera Turbo) portret en landschap.

iPad met iOS 11.2.6 (2048 x 1536 px, device-pixel-ratio: 2 ('retina'):
Safari, Chrome for iOS, Firefox (alle portret en landschap).
Opera Mini (Opera Turbo) portret en landschap.

Android 4.4.2 ('Kitkat') (1280 x 800 px, resolution: 96 dpi):
Android browser, UC Browser, Firefox en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Android 4.4.2 ('Kitkat') (2560 x 1600 px, resolution: 192 dpi):
Android browser, UC Browser, Firefox en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Android 6.0 ('Marshmallow') (1920 x 1200 px, resolution: 144 dpi):
Dolphin, Samsung Internet, UC Browser, Firefox en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Android 7.0 ('Nougat') (1920 x 1200 px, resolution: 144 dpi):
Dolphin, Samsung Internet, UC Browser, Firefox en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Smartphones

Windows 10 Mobile (1280 x 720 px, resolution: 192 dpi):
Edge en UC browser (portret en landschap).

Android 4.1.2 ('Jelly Bean') (800 x 480 px, resolution: 144 dpi):
Chrome, Android browser, UC Browser en Firefox (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Android 7.0 ('Nougat') (1280 x 720 px, resolution: 192 dpi):
Dolphin, Samsung Internet, UC Browser, Firefox en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) 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 de iPad, Android, Windows Phone en Windows 10 Mobile, waar een touchscreen is gebruikt. Op Windows 8.1 en 10 is getest met een touchscreen, met een combinatie van toetsenbord en touchpad, en met een combinatie van toetsenbord en muis.

Als JavaScript is gebruikt, is op de desktop ook getest zonder JavaScript. (Op iOS, Android en Windows 10 Mobile is niet getest zonder JavaScript, omdat je JavaScript in een toenemend aantal mobiele browsers niet uit kunt zetten. Bovendien is een mobiel apparaat zonder JavaScript niet veel meer dan een duur en groot uitgevallen horloge.) 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, ChromeVox 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 7.

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

TalkBack is een in Android ingebouwde schermlezer. Er is getest in combinatie met Chrome op Android 4.4,2, 6.0 en 7.0.

VoiceOver is een in iOS en OS X ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS (9.3.5 en 11.0.2) en OS X 10.11.6.

ChromeVox is een schermlezer in de vorm van een extensie bij Google Chrome. Er is getest op een systeem met Kubuntu Linux 14.04.

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

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

Alleen op de hierboven genoemde systemen en browsers is getest. Er is dus niet getest op bijvoorbeeld 'n Blackberry. De kans is (heel erg) groot dat dit voorbeeld niet (volledig) werkt op niet-geteste systemen en apparaten. Om het wel (volledig) werkend te krijgen, zul je vaak (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 Android browser 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 eigenlijk geen beginnen aan.

De html is gevalideerd met de validator van w3c, de css ook. 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, omdat er anders 'n redelijke kans is 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!)

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 bij toegankelijkheid aanpassingen en problemen bij elkaar.

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, zoals zoomen en andere lettergrootte, Tab-toets, tekstbrowsers en schermlezers. Als laatste volgen algemene problemen in alle of in specifieke browsers.

Als in een onderdeel één of meer problemen wordt besproken, staat in een rood kadertje een korte samenvatting daarvan. Bij een onderwerp over toegankelijkheid zijn er soms geen problemen, maar alleen aanpassingen. In dat geval staat in een groen kadertje 'Geen problemen'.

Zonder JavaScript

Probleem: alles werkt, maar elke gevolgde link uit de inhoud wordt in de browsergeschiedenis opgeslagen.

Browsers slaan bezochte pagina's op in de browsergeschiedenis. Met behulp van de Terug‑ en Vooruitknop, of met toetscombinaties als Alt+← en Alt+→, kan door deze geschiedenis worden gebladerd.

Niet alleen pagina's, maar ook bezochte ankers op een pagina worden in de geschiedenis opgeslagen. Als je in dit voorbeeld een link in de inhoudsopgave volgt, wordt ook het bij die link horende anker opgeslagen. Als je tweehonderd links in de inhoudsopgave aanklikt en dan terug bladert door de geschiedenis, kom je al de bij die tweehonderd links horende ankers in omgekeerde volgorde tegen. Pas daarna kom je bij de pagina, waar je vandaan kwam, voordat je dit voorbeeld bezocht.

Zoiets kan handig zijn, omdat je zo de leesgeschiedenis van de pagina weer kunt terughalen. Maar bij ruim tweehonderd links naar alleen artikelen lijkt dit niet echt 'n fantastisch hulpmiddel.

Daarom wordt met behulp van JavaScript voorkomen dat links binnen de pagina worden opgeslagen in de browsergeschiedenis. Alle links naar een andere pagina worden gewoon opgeslagen, maar links naar ankers binnen de pagina met het voorbeeld niet.

Als JavaScript uitstaat, werkt alles nog gewoon. Alleen valt de browser dan terug op het standaardgedrag en worden ook de ankers in de geschiedenis opgeslagen.

In Android browser, Opera en UC browser op Android 4.1.2 en 4.4.2 werkt dit script niet. In deze browsers worden bezochte ankers altijd in de geschiedenis opgeslagen.

Als je dit script wilt testen, moet dat online gebeuren, of je moet zelf een server installeren. Omdat kwaadwillenden dit soort scripts zouden kunnen misbruiken, werkt het in steeds meer browsers niet meer offline. (Iers meer hierover is te vinden bij Dit script manipuleert...)

Zonder css

Geen problemen.

Zonder css werkt alles, maar het uiterlijk is – uiteraard – volkomen anders.

De inhoud staat niet naast, maar boven de tekst. Ook in smallere browservensters is de inhoud altijd zichtbaar. Het aankruisvakje om de inhoud in smallere vensters zichtbaar te maken is altijd zichtbaar, maar aan- of uitvinken heeft geen enkel effect.

Gebruikers Tab-toets

Probleem: in Firefox en Internet Explorer is de bestemming van de link niet altijd te zien.

  • Bovenaan de pagina is een skip-link aangebracht, waarmee in één keer de hele inhoudsopgave gepasseerd kan worden. Deze link wordt alleen zichtbaar, als de Tab-toets wordt gebruikt.
  • Binnen de inhoudsopgave is onder elk hoofdstuk een skip-link aangebracht, waarmee in één keer naar het volgende hoofdstuk in de inhoudsopgave kan worden gegaan. Deze links worden alleen zichtbaar, als de Tab-toets wordt gebruikt.
  • Als een link uit de inhoudsopgave focus heeft, verkleurt de achtergrond van de link. (Het kadertje dat normaal genomen de focus aangeeft, is door de constructie van de links wat onduidelijk.) Ook wordt de tekst wat vetter, zodat het herkennen van de focus niet alleen van kleur afhankelijk is.
  • Aan elk anker in de pagina (elk doel van 'n link in de inhoudsopgave) is het attribuut tabindex="0" aangegeven. Hierdoor kan, als een link is gevolgd, met de Tab-toets verder door de tekst worden gesprongen. De elementen die tabindex="0" hebben gekregen komen overeen met de links in de inhoudsopgave, dus in feite spring je door de inhoudsopgave. Meer hierover is te vinden bij Tabindex en Tab-toets.
  • Als met de Tab-toets door de inhoudsopgave wordt gelopen, wordt op een gegeven ogenblik de onderkant van het browservenster bereikt. Zodra dat gebeurt, wordt de inhoudsopgave door de browser naar boven geschoven, zodat de links zichtbaar blijven.

    In Firefox en Internet Explorer levert dit een probleem op, want in die browsers schuift de inhoud maar 'n heel klein stukje omhoog: precies genoeg om de link met focus aan de onderkant van het browservenster te zetten. Dat is precies dezelfde plaats, waar de bestemming van de link wordt getoond. Hierdoor is de link zelf niet meer te zien. Alle andere browsers zetten de link met focus halverwege het venster, zodat dit niet speelt.

    (Dit klopt eigenlijk niet helemaal: als de onderkant van het browservenster wordt bereikt, valt in alle browsers de link met focus weg onder de bestemming. Maar bij de volgende tab wordt de volgende link halverwege het venster gezet. Hierdoor is dit eigenlijk nauwelijks echt storend.)

    Dit is in Firefox en Internet Explorer niet alleen in dit voorbeeld het geval, het werkt altijd zo in deze browsers. Helaas is hier niets aan te doen, want je kunt dit soort intern gedrag van de browser niet van buitenaf veranderen. Je zou de inhoudsopgave korter kunnen maken, maar dat is echt foeilelijk.

    Internet Explorer heeft dit gedrag, Edge niet. Kennelijk heeft Microsoft dit dus veranderd. Ook voor Firefox is er dus nog hoop, zullen we maar optimistisch zeggen.

    Bij hoveren met de muis gaat het trouwens wel goed, want dan wordt de bestemming aan de andere kant van het browservenster gezet, als anders de link afgedekt zou worden.

    (In Firefox zat hier ooit de statusbar, waarin onder andere de bestemming werd getoond. Hierdoor stond de bestemming nooit over de eigenlijke pagina heen. In haar ijver om een volmaakte kloon van Google Chrome te worden, is deze statusbar helaas verwijderd. Het tonen van de bestemming is echter kennelijk (nog) niet gekloond van Google Chrome.)

Zoomen en andere lettergrootte

Probleem: bij zoomen kan op iOS niet worden gescrold in vensters minimaal 760 px breed.

Zoomen (vergroten en verkleinen) en een andere lettergrootte levert geen problemen op, behalve op het technisch superieure iOS.

Dit probleem heeft te maken met -webkit-overflow-scrolling.

In browservensters smaller dan 760 px wordt dit alleen gebruikt bij nav en levert het kennelijk geen problemen op. In vensters met een breedte van minstens 760 px wordt het ook gebruikt bij main en levert het wel problemen op.

Zodra je in iOS inzoomt (vergroot), kan niet meer worden gescrold. Tekst die niet op het scherm staat op het moment dat je inzoomt, komt gewoon niet meer tevoorschijn.

Bij nav en main staat de regel -webkit-overflow-scrolling: touch;. Zodra deze regel wordt verwijderd, speelt dit probleem niet meer. Het scrollen heeft dan echter de opmerkelijke soepelheid van een een tapdansende olifant met eksterogen en acute jicht. Normaal genomen blijft de tekst nog even doorscrollen, maar zonder deze regel stopt het scrollen onmiddellijk als het scherm wordt losgelaten. Wat niet echt lekker werkt.

Hier is geen oplossing voor: of zoomen kan niet, of scrollen loopt niet echt lekker.

Als je deze eigenschap gebruikt, moet je trouwens altijd heel grondig testen. Internet staat vol met verhalen over verdwijnende tekst en andere rampen bij gebruik van deze eigenschap.

Er zijn al jaren allerlei problemen met -webkit-overflow-scrolling. En er worden ook al jaren oplossingen gegeven, die helaas geen van alle werkten in dit geval. Omdat Apple zich weer 'ns volledig in stilzwijgen hult, is de oorzaak van deze bug onbekend. En of 't ooit wordt gerepareerd, tja, er zijn al jaren problemen mee, dus... Omdat dit Apple is, is het trouwens mogelijk geen bug, maar 'n feature.

Omdat Apple op iOS alle browsers verplicht gebruik te maken van de superieure weergave-machine van Safari, speelt dit probleem in alle browsers op iOS.

Tekstbrowsers

Geen problemen.

In Lynx en WebbIE werkt alles, zoals het hoort te werken. Inclusief de skip-links.

Lynx toont ook het aankruisvakje om in smallere browservensters de inhoudsopgave te tonen. Werken doet dat verder niet, want de inhoudsopgave is gewoon altijd zichtbaar. WebbIE laat het aankruisvakje en de bijbehorende <label> niet zien, omdat WebbIE de WAI-ARIA-code aria-hidden ondersteunt.

Schermlezers

Probleem: in TalkBack werken de links in de inhoud niet in browservensters smaller dan 760 px.
Probleem: in TalkBack werken de skip-links niet in browservensters minimaal 760 px breed.

  • In smallere browservensters wordt de inhoudsopgave getoond en verborgen met behulp van een <input type="checkbox"> (aankruisvakje). Deze <input> met bijbehorende <label> en dergelijke worden met behulp van de WAI-ARIA-code aria-hidden="true" voor schermlezers verborgen.

    In alle geteste schermlezers zijn alle links gewoon te gebruiken, ook de skip-links. Alleen in TalkBack werken de links niet, omdat in TalkBack minimaal 1 x 1 px van een link, knop, en dergelijke op het scherm moet staan. Bovendien mag die pixel niet worden verborgen onder een andere element. In dit voorbeeld zouden meer dan tweehonderd links elk één pixel ergens moeten hebben staan. Als iemand per ongeluk zo'n pixel aan zou raken, wordt de link gevolgd. Dat gaat dus niet.

    Je zou het aankruisvakje om de inhoudsopgave te tonen kunnen laten werken in schermlezers, maar dat helpt weinig. Als de links pas later zichtbaar worden, werken ze grotendeels niet in TalkBack. (Wanneer en waarom ze wel of niet werken, is één van de grote kosmische raadselen.)

    Je kunt in TalkBack nog steeds naar <main> gaan en daar de <h>'s volgen.

    (Dit speelt alleen in browservensters smaller dan 760 px, want in bredere vensters is de inhoudsopgave gewoon zichtbaar. Hopelijk wordt dit ooit opgelost, want er wordt al jaren over geklaagd.)

  • In TalkBack werken in browservensters met een breedte van minstens 760 px de skip-links binnen de inhoudsopgave in de <nav> niet. (Met deze skip-links kan in de inhoudsopgave in één keer een heel hoofdstuk worden overgeslagen.) Dit heeft dezelfde oorzaak als hierboven staat beschreven voor smallere vensters: er moet minstens 1 x 1 px van de link op het scherm staan.
  • De hele inhoudsopgave staat in een <ul>, die weer in een <nav> staat, zodat deze voor schermlezers snel is te passeren.
  • Bovenin de <nav> staat een <h2> met 'Inhoudsopgave grondwet', zodat duidelijk is waarvoor de <nav> dient. Deze <h2> is verder onzichtbaar, alleen schermlezers 'zien' de <h2>.
  • Alle artikelen zijn ondergebracht in <h>'s. Schermlezers kunnen dus van <h> naar <h> springen. Elke link in de inhoudsopgave heeft een bijbehorende <h>, dus eigenlijk hebben schermlezers die hele inhoudsopgave niet echt nodig.

    De <h>'s zijn ingedeeld van belangrijk naar minder belangrijk. Alleen de <h1> met de titel staat niet helemaal goed, want die staat onder de <h2> in de <nav>. Het zou hier wat onlogisch zijn van de <h2> in de <nav> de belangrijkste kopregel te maken.

Opera Mini op Android in vensters smaller dan 760 px

De inhoudsopgave kan niet terug naar boven worden gescrold

Dit speelt in versie 4.1.2 en in versie 7.0 (andere versies zijn niet getest), en alleen in browservensters smaller dan 760 px.

Als de <nav> met de inhoudsopgave naar beneden wordt gescrold, kan de <nav> niet meer terug naar boven worden gescrold. Althans: als je de <nav> scrolt, blijft deze staan, maar de onder de <nav> zittende <main> met de tekst scrolt wel. Pas als de <main> helemaal bovenaan staat, kan de <nav> met de inhoudsopgave weer naar boven worden gescrold.

Een oplossing hiervoor is niet gevonden. Het is op het ogenblik zelfs onduidelijk of Opera Mini position: fixed; (wat is gebruikt om de <nav> in die smallere vensters vast te zetten) wel of niet ondersteunt. Volgens caniuse.com zou Opera Mini dit niet ondersteunen, maar volgens nogal wat andere sites wel. Opera zelf heeft geen overzicht van wat wel en niet wordt ondersteund. Ook heeft Opera Mini geen manier om enigszins fatsoenlijk naar bugs te kunnen zoeken.

Dit maakt het feitelijk onmogelijk om naar een oplossing te zoeken, want misschien wordt het gewoon helemaal niet ondersteund, of misschien is het 'n bekende bug.

Testen met @supports op ondersteuning voor position: fixed; kan in dit geval ook niet worden gebruikt, omdat een aantal oudere browsers position: fixed; wel ondersteunt, maar @supports niet. css speciaal voor Opera Mini zou daardoor ook voor deze browsers werken, terwijl het voor die browsers niet nodig is. Bovendien zijn er ook verhalen dat ook heel oude versies van Opera Mini ten onrechte beweren dat ze position: fixed; ondersteunen, als je @supports gebruikt.

Sommige versies van Android browser, Opera Mobile en UC browser op Android 4.1.2 en 4.4.2

Bij gebruik van de Terug-knop van de browser worden alle bezochte ankers nogmaals bezocht

Als je tweehonderd links in de inhoudsopgave hebt gevolgd en je gebruikt de Terug-knop van de browser, worden eerst de tweehonderd bij die links horende ankers in omgekeerde volgorde weergegeven.

Dit is standaardgedrag van elke browser, dat meestal erg handig is. Maar hier niet. In alle andere browsers wordt dit standaardgedrag voorkomen met behulp van JavaScript. In de genoemde browsers werkt dit script niet altijd. Omdat fabrikanten van smartphones en tabletten zelf Android kunnen aanpassen, is niet precies te zeggen, in welke versies zich dit voordoet.

Omdat het hier om tamelijk oude versies van Android gaat, is hier niet verder naar gekeken. In Firefox en Chrome (en waarschijnlijk in nog veel meer op Chrome gebaseerde browsers) op deze versies van Android werkt het script wel. Mensen die met alle geweld één van de genoemde drie browsers willen gebruiken, moeten dit dan maar zien als een meditatieve oefening.

Sommige versies van Android browser en Opera op Android 4.1.2 en Android 4.4.2 in vensters smaller dan 760 px

Er wordt in de balk bovenaan niet altijd een gradiënt getoond

Soms wordt in de balk bovenin geen gradiënt, maar een effen witte kleur getoond. Omdat fabrikanten van smartphones en tabletten zelf Android kunnen aanpassen, is niet precies te zeggen, in welke versies zich dit voordoet.

Deze browsers hebben een oudere syntax nodig. Als je gelijk voor de regel voor de gradiënt bij #toon-menu de volgende regel invoegt:

background: -webkit-linear-gradient(top, #fff 0%, #bbb 100%);

hebben ook deze browsers een gradiënt. Omdat deze browsers snel aan het verdwijnen zijn, is dat in dit voorbeeld niet gedaan.

Alle browsers als offline wordt gewerkt

Alle bezochte artikelen worden in de geschiedenis opgeslagen

Met behulp van JavaScript wordt voorkomen dat alle bezochte artikelen in de geschiedenis van de browser worden opgeslagen. Hiermee wordt voorkomen dat je, bij gebruik van de Terug-knop, de Vooruitknop of een toetscombinatie als alt+← of alt+→, eerst weer door alle bezochte artikelen gaat, voordat je 'n vorige of volgende pagina bereikt.

Browsers worden steeds strenger beveiligd tegen misbruik. Dit script zou misbruikt kunnen worden, daarom werkt het in veel browsers alleen online of op een zelf geïnstalleerde server. Als je het script wilt testen, kan dat alleen online of op een zelf geïnstalleerde server betrouwbaar. Bij testen op de desktop is er een grote kans dat dit script niet werkt.

Een iets uitgebreider verhaal hierover is te vinden bij Dit script manipuleert...

Wijzigingen

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

:

Nieuw opgenomen

2 februari 2009:

:focus en :active werken nu hetzelfde als :hover bij de links in de linkerkolom.

10 april 2009:

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

11 oktober 2010:

Kleine wijzigingen in de tekst aangebracht, vooral over toegankelijkheid.

11 maart 2018:

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

tekst-029-dl.html: de pagina met het voorbeeld.

tekst-029.pdf: deze uitleg (aangepast aan de inhoud van de download).

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

029-css-dl:

tekst-029-dl.css: stylesheet voor tekst-029-dl.html.

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

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, downloadt dan de hele handel (ga terug naar het voorbeeld en kies daar voor downloaden). In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.

<!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">

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

<meta charset="utf-8">

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

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

Deze regel moet zo hoog mogelijk komen te staan, als eerste regel binnen de <head>, omdat hij 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 breedte. En dat is een probleem. Sites waren, in ieder geval tot voor kort, 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 hij 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 hij of zij 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. Op deze pagina bijvoorbeeld wordt in browservensters smaller dan 760 px de inhoudsopgave 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.

Een iPad in portretstand bijvoorbeeld is 768 px breed. 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. Voor een iPad in portretstand dus 768 px.

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 gebruik ik niet. De bezoeker kan zelf nog gewoon zoomen, wat belangrijk is voor mensen die wat slechter zien.

<a class="skippy" href="#content">Skip inhoudsopgave grondwet</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 de inhoudsopgave staan meer dan tweehonderd links. Voordat de eigenlijke tekst wordt bereikt, moet de gebruiker eerst door al die links heen. Voor elke link moet één keer de Tab-toets worden ingedrukt. 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 en 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 links buiten het scherm geparkeerd en daardoor onzichtbaar. Zodra de link focus heeft, wordt deze met behulp van css op het scherm geplaatst. 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 de meeste schermlezers werkt deze link ook. Maar schermlezers hebben ook andere manieren om een serie links te passeren.)

<input id="checkbox-voor-smal" type="checkbox" aria-hidden="true">

<label id="toon-menu" for="checkbox-voor-smal" aria-hidden="true">Inhoudsopgave</label>

In browservensters smaller dan 760 px is de inhoudsopgave links buiten het scherm geplaatst, waardoor deze onzichtbaar is. Pas na aanklikken of aanraken van een knop wordt de inhoudsopgave getoond.

Voor schermlezers maakt het niets uit dat de inhoudsopgave links buiten het scherm staat: het wordt gewoon voorgelezen. Daarom is de inhoudsopgave ook niet verborgen met display: none; of visibility: hidden;. Als de inhoudsopgave daarmee zou worden verborgen, zou het ook voor schermlezers onzichtbaar zijn. Maar dat het buiten het scherm staat, maakt niets uit.

Met behulp van de <input> in de eerste regel hierboven kan de inhoudsopgave worden geopend. Aan deze <input> hebben schermlezers niets, het werkt alleen maar verwarrend. Daarom worden de <input> en de bij de <input> horende <label> voor schermlezers verborgen met de WAI-ARIA-code aria-hidden="true".

Vaak wordt een <input> binnen de bijbehorende <label> gezet:

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

Dat heeft een aantal voordelen. Hier kan dat echter niet, omdat de <input> in een aantal selectors met behulp van ~ en + is 'gekoppeld' aan latere elementen. Als de <input> binnen de <label> zou staan, werken ~ en + niet.

(De schermlezer TalkBack leest de links wel voor, maar ze werken niet. Meer hierover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Schermlezers.)

<h2>Inhoudsopgave grondwet</h2>

Mensen die de inhoudsopgave kunnen zien, zullen gelijk herkennen, waar die voor is. Maar voor gebruikers van schermlezers is dat niet duidelijk. De <nav>, waarin de inhoudsopgave staat, geeft aan dat het om een serie links om te navigeren gaat, maar niet om wat voor soort links.

Daarom wordt deze < h2> bovenaan de <nav> gezet. Omdat de <h2> links buiten het scherm wordt geparkeerd, is hij onzichtbaar. Maar schermlezers lezen hem gewoon voor.

In theorie zou je hetzelfde moeten kunnen doen met:

<nav aria-label="Inhoudsopgave grondwet">

De WAI-ARIA-code tekst achter aria-label is onzichtbaar, maar wordt wel voorgelezen door schermlezers. Althans: dat zou zo moeten zijn. Veel schermlezers negeren dit attribuut echter, als het bij <nav> wordt gebruikt. Daardoor is het in de praktijk op dit moment nog volstrekt onbruikbaar.

Deze <h2> is de eerste <h> op de pagina. Normaal genomen staan <h>'s van belangrijk naar minder belangrijk, van <h1> naar <h6>. Bovenaan staat dan de belangrijkste kop, de <h1>. Dat is handig voor schermlezers, die snel naar kopregels van een bepaald niveau kunnen springen. Als de titel van de pagina in een <h1> staat, de titels van hoofdstukjes in een <h2>, van paragrafen in een <h3>, enzovoort, is dat heel overzichtelijk.

Hier echter is de eerste kopregel in de html niet de titel van de pagina of iets soortgelijks, maar de omschrijving van de inhoudsopgave. Daarom is het in dit geval beter die eerste kopregel toch in een <h2> te zetten. De <h1> met de kopregel met de echte titel volgt daar dan op.

<li class="li-skippy"><a class="skippy" href="#l-hs-2">Skip links naar hoofdstuk 1</a></li>

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 de inhoudsopgave staan meer dan tweehonderd links. Voordat de eigenlijke tekst wordt bereikt, moet de gebruiker eerst door al die links heen. Voor elke link moet één keer de Tab-toets worden ingedrukt. Daarom is bovenaan de pagina een zogenaamde skip-link geplaatst, waarmee in één keer de hele inhoudsopgave kan worden gepasseerd.

Dat is mooi, maar niet als je naar 'n additioneel artikel onderaan wilt, want dan moet je nog steeds zo'n 170 keer de Tab-toets indrukken.

Daarom is onder elke link naar een hoofdstuk in de inhoudsopgave nog een skip-link gezet. Deze staat normaal genomen links buiten het scherm, maar wordt zichtbaar als de Tab-toets deze bereikt. Door dan op Enter te drukken, worden alle links van dat hoofdstuk in één keer gepasseerd. Nu kunnen de additionele artikelen met acht tabs worden bereikt.

Ook schermlezers kunnen van deze links gebruikmaken, behalve TalkBack. Deze schermlezer leest de links wel voor, maar ze werken niet. Meer hierover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Schermlezers.)

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 blauw gekleurd. Alle niet-essentiële code is bruin. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)

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, downloadt dan de hele handel (ga terug naar het voorbeeld en kies daar voor downloaden). In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.

Technisch gezien is er geen enkel bezwaar om de css in de stylesheet 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, garandeer ik je 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 de stylesheet 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 stylesheets, maar dat heeft een simpele reden: deze uitleg is in feite één groot commentaar.

Op internet zelf is het goed, als de stylesheet 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 de stylesheet uploaden. Inspringingen bijvoorbeeld zijn voor mensen handig, een computer heeft ze niet nodig.

Je hebt dan eigenlijk twee stylesheets. De uitgebreide versie waarin je dingen uitprobeert, verandert, enzovoort, met commentaar, inspringingen, en dergelijke. Dat is de mensvriendelijke versie. Daarnaast is er dan een stylesheet die 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 Gereedschap → Snelheid, testen, gzip, comprimeren links naar sites vinden, waar je bestanden kunt comprimeren.

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

css voor alle vensters

/* tekst-029-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.

@-webkit-keyframes bugfix-0 {from {padding-left: 0;} to {padding-left: 0;}}

Bij nav wordt een animatie toegevoegd om een bug in oudere versies van Android browser en Opera Mini op Android 4.1.2 op te lossen. Die animatie wordt hier opgegeven.

Op een aantal plaatsen wordt in een selector gebruik gemaakt van het teken ~ (de 'general sibling'-selector) om in browservensters smaller dan 760 px de inhoudsopgave te tonen of te verbergen. De genoemde browsers tonen de inhoudsopgave niet, omdat ze problemen hebben met de ~. Door het toevoegen van een animatie werkt het toch zoals bedoeld.

Als je nou denkt dat hier niets wordt uitgevoerd, omdat de padding links van 0 px in een padding links van 0 px wordt veranderd, dan heb je helemaal gelijk. Toch neutraliseert deze flauwekul de bug. De padding links is 0 px, omdat bij <nav> helemaal geen padding is opgegeven. In dat geval heeft de padding de standaardwaarde van 0. Zou de padding links bij <nav> wel een waarde hebben, dan zou je hier die waarde gebruiken.

Bij nav, waar de animatie daadwerkelijk wordt gebruikt, staat bij -webkit-animation het sleutelwoord infinite: de animatie wordt eindeloos herhaald. Verder staat daar nog de waarde 1s 1 seconde. Dat is de tijdsduur van de animatie. Door hier een tamelijk lange waarde voor te nemen, wordt de animatie niet al te vaak afgespeeld. (Voor zover je hier van 'afspelen' kan spreken, want er gebeurt gewoon helemaal niets.)

Omdat er in feite helemaal niets gebeurt bij deze animatie, wordt de pagina ook niet opnieuw opgemaakt door de browser. Kennelijk werkt dit als 'n soort waakhond: áls er iets is veranderd, wordt het weergegeven. Anders gebeurt er niets.

Normaal genomen is het een bijzonder slecht idee css te gebruiken voor slechts één weergave-machine: webkit. Maar in dit geval is dit terecht, want de bug zit alleen in op webkit gebaseerde browsers. Het heeft dus geen zin om Internet Explorer of Firefox ook met deze ongein te belasten.

Waarom dit alleen in webkit-browsers werkt, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

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 stylesheet, 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; padding: 0;

Slim om te doen vanwege verschillen tussen browsers.

.skippy, .li-skippy .skippy

Afbeelding 2: skip-link om inhoud te passeren

De elementen met class="skippy". Dit zijn de skip-link bovenaan de pagina en de skip-links binnen de inhoudsopgave.

Hiernaast staat de skip-link bovenaan de pagina afgebeeld. Deze link is natuurlijk foeilelijk, en dat is precies de bedoeling: nu valt hij goed op.

Al deze elementen zijn een <a> met class="skippy". In principe zou daarom alleen de selector voor de komma genoeg zijn. Maar verderop bij li a:hover, li a:focus wordt css opgegeven voor alle <a>'s binnen een <li>, waardoor het uiterlijk van de <a>'s verandert bij hoveren over of focus van 'n <a> in de inhoudsopgave.

Ook de skip-links in de inhoudsopgave zijn <a>'s binnen een <li>, dus ook deze zouden daardoor van uiterlijk veranderen. Dat is niet de bedoeling. Daarom staat hier achter de komma nog een tweede selector: .li-skippy .skippy: elementen met class="skippy" binnen een element met class="li-skippy". Skip-links in de inhoudsopgave hebben class "skippy" en staan binnen een <li> met class="li-skippy".

Normaal genomen wordt css uitgevoerd in de volgorde, waarin het in de stylesheet staat. Maar dat kan worden overruled door 'n eerdere selector mee specificiteit, meer 'gewicht', te geven.

Voor specificiteit worden alle selectors apart bekeken. De verderop staande selectors li a:hover en li a:focus moeten apart worden bekeken, ze worden niet 'bij elkaar opgeteld'. In beide selectors zitten twee elementen: li en a. Daarnaast zit in elke selector nog een pseudo-class: :hover en :focus. Voor wat betreft specificiteit heeft een pseudo-class evenveel gewicht als een gewone class. De specificiteit van beide selectors is dus twee elementen en 1 class.

De hier gebruikte selector .li-skippy .skippy bestaat uit twee classes: .li-skippy en .skippy.

We hebben dus hier een selector met twee classes, en verderop een selector met één element en één class. Een selector met meer classes heeft altijd meer specifiteit, meer 'gewicht', dan een selector met minder classes (als er geen id's aanwezig zijn). Oftewel: de hier gebruikte .li-skippy .skippy heeft meer specificiteit dan de verderop gebruikte li a:hover en li a:focus. Hierdoor wordt toch de css van de selector hier gebruikt voor de skip-links, en niet die van de selectors verderop.

Als hier alleen .skippy zou zijn gebruikt, zou maar één class aanwezig zijn. In dat geval zou de css van de selectors verderop worden gebruikt. Deze staan lager in de stylesheet, én ze hebben meer specificiteit: één element en één class.

Hetzelfde verhaal geldt voor een andere selector verderop, li a: alle <a>'s binnen een <li>. Ook skip-links in de inhoudsopgave vallen onder deze selector. In li a zitten alleen twee elementen, daarom heeft de selector .li-skippy .skippy met z'n twee classes meer specificiteit en 'wint' deze dus van li a.

De skip-link bovenaan de pagina is grotendeels hetzelfde als de skip-links in de inhoudsopgave, daarom wordt hier voor alle skip-links css opgegeven. Hier iets onder bij .li-skippy .skippy worden dan voor de skip-links in de inhoudsopgave 'n paar dingen aangepast.

background: white;

Witte achtergrond.

color: black;

Normaal genomen krijgt de tekst in een link een afwijkende kleur. Dat is hier niet nodig.

width: 12em;

Breedte. De skip-link moet goed opvallen, zodat deze niet wordt gemist. Daarom wordt de skip-link heel breed gemaakt.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de breedte mee verandert met een eventuele andere lettergrootte. Bij gebruik van een absolute eenheid zoals px zou dat niet gebeuren. Zoomen kan wel altijd, ongeacht welke eenheid voor de breedte wordt gebruikt.

font-size: 2em;

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

line-height: 2em;

Regelhoogte.

Omdat verder geen hoogte is opgegeven voor het element, is dit tevens de hoogte van de skip-link.

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.

Tekst wordt normaal genomen automatisch halverwege de regelhoogte gezet, dus de tekst staat hierdoor gelijk verticaal gecentreerd.

text-align: center;

Tekst horizontaal centreren.

text-decoration: underline;

Standaard wordt tekst in een <a> onderstreept. Bij li a verderop wordt de onderstreping echter weggehaald. Ook de skip-links in de inhoudsopgave vallen onder de selector li a. Daarom wordt hier expliciet opgegeven dat in de skip-links de onderstreping moet blijven staan.

Hoe het komt dat deze regel 'wint' van de lagere regel bij li a wordt iets hierboven bij de selector beschreven.

border: red solid 3px;

Brede rode rand in het kader van opvallen.

position: absolute;

Om de skip-link op de juiste plaats neer te kunnen zetten.

Hier iets onder bij .li-skippy .skippy wordt dit voor de skip-links in de inhoudsopgave veranderd in position: fixed; dus dit geldt alleen voor de skip-link bovenaan de pagina.

Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een andere positie dan 'static' heeft. Als die 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. Daardoor zijn eigenschappen als breedte niet te gebruiken. Door de <a> absoluut te positioneren, verandert de <a> in een soort blok-element en zijn dit soort eigenschappen wel te gebruiken.

top: 20px;

20 px vanaf de bovenkant neerzetten.

left: -20000px;

Ver links buiten het scherm parkeren. Pas als dat nodig is, wordt bij .skippy:focus de link binnen het scherm gezet en daarmee zichtbaar.

z-index: 50;

Door de hogere z-index staan deze links altijd bovenaan, ongeacht wat er verder op de pagina staat.

Een z-index werkt alleen in bepaalde omstandigheden. Een van die omstandigheden is een absolute of fixed positie. Die absolute positie is iets hierboven opgegeven, dus dat is geregeld.

(Voor de skip-links wordt dat gelijk hieronder veranderd in een fixed positie, maar ook dat zorgt ervoor dat een z-index werkt.)

.li-skippy .skippy

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.

.skippy, .li-skippy .skippy {background: white; color: black; width: 12em; font-size: 2em; line-height: 2em; text-align: center; text-decoration: underline; border: red solid 3px; position: absolute; top: 20px; left: -20000px; z-index: 50;}

Afbeelding 3
Afbeelding 3: De skip-link die verschijnt als, nadat 'Hoofdstuk' 1 is bereikt, nogmaals de Tab-toets wordt ingedrukt

De elementen met class="skippy" die binnen een element met class="li-skippy" staan. Dit zijn de skip-links binnen de inhoudsopgave.

De meeste css is iets hierboven bij .skippy, .li-skippy .skippy al opgegeven, samen met de css voor de skip-link bovenaan de pagina. Voor de skip-links binnen de inhoudsopgave worden hier alleen wat kleine wijzigingen opgegeven, omdat deze er iets anders uitzien dan de skip-link bovenaan de pagina.

width: 20em;

Omdat voor de breedte de eenheid em is gebruikt, is de breedte gebaseerd op de lettergrootte van de skip-links. Bij een andere lettergrootte verandert de breedte mee met de lettergrootte. (Bij een vaste eenheid als px zou dat niet gebeuren.)

Iets hierboven is bij .skippy, .li-skippy .skippy een lettergrootte van 2 em opgegeven. Maar een lettergrootte in em wordt geërfd, is gebaseerd op de lettergrootte van de voorouders van het element.

De skip-links in de inhoudsopgave zijn nakomelingen van <nav>, waarin de hele inhoudsopgave gaat. Bij nav wordt de lettergrootte van <nav> verkleind tot 0,7 em. De voor de skip-links opgegeven lettergrootte van 2 em is gebaseerd op die 0,7 em. Als je het met de rest van de pagina vergelijkt is de feitelijke lettergrootte van de skip-links in de inhoudsopgave daardoor niet 2 keer de normale lettergrootte, maar 2 keer 0,7 keer de normale lettergrootte, oftewel: 1,4 em.

Voor de lettergrootte is dat prima, want de tekst van deze skip-links is wat lang. Als de bezoeker de lettergrootte zelf ook heeft vergroot, kan de tekst daardoor wat slecht gaan passen.

Maar de breedte van de skip-link wordt hierdoor wat te weinig, want die is gebaseerde op de lettergrootte en dus eigenlijk geen niet meer de eerder bij .skippy, .li-skippy .skippy opgegeven 12 em, maar 0,7 x 12 em. Daarom wordt de breedte voor de skip-links in de inhoudsopgave hier verhoogd.

font-weight: normal;

Bij li a:hover, li a:focus wordt de tekst vet gemaakt, als over een link wordt gehoverd of als deze focus heeft. Dat is prima voor de gewone links in de inhoudsopgave, maar deze skip-links zijn bepaald al groot genoeg. Als ze ook nog vet worden, is dat niet mooi. Daarom wordt hier opgegeven dat de tekst in de skip-links niet vet wordt.

(Hoe het kan dat deze regel de verderop bij li a:hover, li a:focus staande css overrulet, staat iets hoger beschreven bij de selector .skippy, .li-skippy .skippy.)

line-height: 5em;

Zelfde verhaal als bij width: 20em; iets hier boven, maar nu voor de regelhoogte. Omdat de eenheid em is gebruikt, is de regelhoogte gebaseerd op de lettergrootte. En omdat deze kleiner is, zijn meer em nodig om de regelhoogte hoog genoeg te maken. (Hoog genoeg om goed op te vallen, de tekst past er altijd wel in.)

position: fixed;

Bij .skippy, .li-skippy .skippy zijn de skip-links absoluut gepositioneerd, om ze op de juiste plaats neer te kunnen zetten.

Voor de skip-link bovenaan de pagina is dat prima, want die staat gepositioneerd ten opzichte van het venster van de browser. En dat venster wordt nooit verplaatst, tenzij je het hele scherm optilt of – in een groter scherm – het hele venster verschuift. En dan gaat die skip-link gewoon mee.

Bij een absolute positie wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Bij de skip-links in de inhoudsopgave is zo'n voorouder, anders dan bij de skip-link voor de hele pagina, wel aanwezig. De skip-links zijn nakomelingen van <nav>, die bij nav fixed wordt gepositioneerd. De skip-links in de inhoudsopgave worden hierdoor gepositioneerd ten opzichte van <nav>.

En dat is een probleem, want <nav> kan worden gescrold. Als je de skip-links positioneert ten opzichte van de bovenkant van <nav>, en je gaat <nav> scrollen, dan komen de meeste skip-links (ver) boven het venster van de browser te staan, waardoor ze onzichtbaar zijn.

Daarom worden de skip-links in de inhoudsopgave fixed gepositioneerd. Nu staan ze vast ten opzichte van het venster van de browser. Ook al zijn ze nakomelingen van <nav>, ze scrollen nu niet meer mee met <nav>.

Een <a> is van zichzelf een inline-element. Daardoor zijn eigenschappen als breedte niet te gebruiken. Door de <a> fixed te positioneren, verandert de <a> in een soort blok-element en zijn dit soort eigenschappen wel te gebruiken.

top: 200px;

200 px vanaf de bovenkant van het browservenster neerzetten. Dat is beter dan aan de bovenkant van het venster, want hier vallen ze meer op.

.skippy:focus

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.

.skippy, .li-skippy .skippy {background: white; color: black; width: 12em; font-size: 2em; line-height: 2em; text-align: center; text-decoration: underline; border: red solid 3px; position: absolute; top: 20px; left: -20000px; z-index: 50;}

.li-skippy .skippy {width: 20em; font-weight: normal; line-height: 5em; position: fixed; top: 200px;}

Als een element met class='skippy' focus heeft.

Alleen de <a>'s die bij een skip-link horen hebben class='skippy'. Deze zijn bij .skippy, .li-skippy .skippy links buiten het scherm geparkeerd, zodat ze onzichtbaar zijn. Maar voor gebruikers van de Tab-toets zijn ze gewoon bereikbaar. Ook schermlezers kunnen ze gewoon gebruiken. (Met uitzondering van TalkBack, waarover meer is te vinden bij Bekende problemen (en oplossingen) onder het kopje Schermlezers.)

left: 30px;

30 px vanaf de rechterkant van het browservenster neerzetten. Op deze plaats passen ze altijd binnen het venster, maar staan ook altijd pontificaal lelijk te wezen bovenop de inhoudsopgave. Wat precies de bedoeling is, want dan vallen ze lekker op.

#checkbox-voor-smal

Het element met id="checkbox-voor-smal". Dit is een <input type="checkbox">. In browservensters smaller dan 760 px wordt de inhoudsopgave pas getoond, als dit aankruisvakje is aangevinkt.

Het aankruisvakje zelf is verborgen, omdat het nou niet echt mooi is te noemen. Aankruisvakjes zijn ook uitermate moeilijk goed op te maken. Daarom vindt de bediening van de <input> plaats door middel van de bij de <input> horende <label>, die wel fatsoenlijk is op te maken.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. Als inhoudsopgave of tekst worden gescrold, scrolt de <input> niet mee.

De bij de <input> horende <label> wordt iets hieronder bij #toon-menu op dezelfde manier vastgezet. Hierdoor staat deze <input> altijd onder de bij de <input> horende <label> en zie je de lelijkerd niet.

(Mogelijk is dit niet meer echt nodig en zou de <input> ook gewoon mogen scrollen. In ieder in het verleden gaf het echter soms problemen, als de <input> buiten het browservenster kwam te staan. Dit werkt in ieder geval, dus lekker zo laten.)

top: 0;

Bovenaan het browservenster neerzetten.

#toon-menu

Het element met id="toon-menu". De bij input#checkbox-voor-smal horende <label>.

background: white;

Witte achtergrond. Deze regel is voor sommige versies van Android browser en Opera Mini op Android 4.1.2 en 4.4.2. Hier gelijk onder wordt als achtergrond een gradiënt opgegeven. Deze browsers hebben een andere, oudere syntax nodig voor die eigenschap, dus die negeren de regel hier gelijk onder en gebruiken de hier opgegeven witte achtergrond.

Voor het geval je deze twee snel uitstervende fossielen toch 'n gradiënt wilt geven, staat die oudere syntax bij Bekende problemen (en oplossingen).

(Iets klopt hier niet. Ah, ik zie het al: 'n fossiel ís al gestorven. Oeps.)

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

Je kunt een gradiënt zelf uitvogelen, maar vaak is het makkelijker om gebruik te maken van een gradiënt-editor, zoals die op colorzilla.com. Deze levert ook gelijk de oudere vormen van de code, zodat je je daar zelf niet in hoeft te verdiepen. Je kunt zelfs code voor oudere versies van Internet Explorer laten maken. (Nachtmerrie-code die gebruik maakt van een eigen techniek van Microsoft: filters.)

(Overigens is het wel raadzaam die code op te schonen, omdat er wel heel erg veel code voor heel erg oude versies van verschillende browsers in staat.)

In dit geval is geen gradiënt-editor gebruikt, omdat het hier allemaal mooie ronde getallen en codes zijn.

linear-gradient valt in een aantal delen uiteen:

to bottom: de kant waar de gradiënt naartoe gaat. Omdat alleen de onderkant is opgegeven, loopt de gradiënt loodrecht van boven naar onder.

Na de richting staan twee keer twee waarden, gescheiden door een komma. De eerste waarde is steeds de kleur, de tweede de plaats waar die kleur staat.

#fff 0%: de kleur #fff (wit) op 0% vanaf de bovenkant van de <span>'s neerzetten (dat is helemaal bovenaan).

#bbb 100%: de kleur #bbb (lichtgrijs) op 100% vanaf de bovenkant van de <span>'s neerzetten (dat is helemaal onderaan).

Er zijn slechts twee plaatsen opgegeven die een bepaalde kleur moeten krijgen: boven- en onderaan. De overgang tussen die kleuren verloopt geleidelijk, dat regelt de browser verder. Dit is een vrij simpele gradiënt, maar je kunt ook tig kleuren op tig plaatsen aangeven. En ook nog in allerlei richtingen.

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

width: 100%;

Breedte.

Een <label> is van zichzelf een inline-element. Daardoor wordt het normaal genomen niet breder dan nodig is om de inhoud ervan weer te geven.

Eigenschappen als breedte werken normaal genomen niet bij inline-elementen, maar iets hieronder wordt de <label> fixed gepositioneerd. Daardoor verandert de <label> in 'n soort blok-element, waardoor dit soort eigenschappen wel werken.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier <body>, een blok-element. Een blok-element wordt normaal genomen even breed als z'n ouder. De ouder van <body> is <html>, het buitenste element. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk wordt ook <label> daardoor even breed als het venster, ongeacht de breedte van het venster.

line-height: 30px;

Regelhoogte.

Omdat verder geen hoogte is opgegeven, is dit tevens de hoogte van de <label>. Tekst wordt automatisch halverwege de regel gezet, dus de tekst staat verticaal gecentreerd.

text-decoration: underline;

Tekst onderstrepen, zodat iets duidelijker is dat er iets gebeurt, als je de tekst aanraakt of -klikt.

border-bottom: black solid 1px;

Zwart randje aan de onderkant.

padding-left: 10px;

Klein beetje ruimte links van de tekst.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. Als inhoudsopgave of tekst worden gescrold, scrolt de <label> niet mee. Hierdoor kan de inhoudsopgave altijd worden geopend, ook als de tekst al is gescrold.

top: 0;

Bovenaan het browservenster neerzetten.

#toon-menu::after

Met behulp van ::after wordt bij label#toon-menu een pseudo-element gemaakt, waarin tekst komt te staan.

content: "open";

In de html is bij <label id="toon-menu"> als tekst 'Inhoudsopgave ' opgegeven (met een spatie aan het eind). In het door ::after bij label#toon-menu gemaakte pseudo-element wordt 'open' gezet, waardoor de uiteindelijk op het scherm zichtbare tekst 'Inhoudsopgave open' wordt.

Omdat 'open' in een met behulp van ::after gemaakt pseudo-element staat, kan de tekst 'open' met behulp van css worden veranderd, zoals gelijk hieronder gebeurt, als het aankruisvakje is aangevinkt.

#checkbox-voor-smal:checked + #toon-menu::after

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.

#toon-menu::after {content: "open";}

#checkbox-voor-smal:checked: het element met id="checkbox-voor-smal", maar alleen als dit is aangevinkt. Dit is de <input type="checkbox"> die bepaalt, of in smallere browservensters het menu wel of niet is te zien.

+: 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 beiden als ouder <body>.

#toon-menu::after: het met behulp van ::after bij #toon-menu gemaakte pseudo-element.

De hele selector in normale mensentaal: als de <input> is aangevinkt, doe dan iets met het pseudo-element bij de in de html op de <input> volgende <label>

content: "dicht";

Tekst in het pseudo-element veranderen in 'dicht'.

Eerder is bij #toon-menu::after als tekst 'open' opgegeven. Door dat hier te veranderen in 'dicht', wordt de tekst op het scherm 'Inhoudsopgave dicht'.

nav

Alle <nav>'s. Dat is er hier maar eentje: de <nav> met de inhoudsopgave.

background: #f0f0ff; Afbeelding 4: achtergrond is door scrollbalk heen te zien

Achtergrondkleur. Dit is dezelfde achtergrondkleur als die van <main>, waarin de tekst staat.

Omdat de inhoudsopgave in de <nav> kan worden gescrold, verschijnt in sommige browsers rechts van de inhoudsopgave een scrollbalk. In sommige browsers hebben de pijltjes aan de boven- en onderkant van de scrollbalk een doorzichtige achtergrond. Bovendien is een kleine ruimte links en rechts van de scrollbalk doorzichtig. Op de doorzichtige plaatsen is de onder de scrollbalk zittende achtergrond zichtbaar: de achtergrondkleur van <body>.

De afbeelding laat de scrollbalk van Firefox op Linux zien. Voor de duidelijkheid heeft <body> even 'n rode achtergrond gekregen.

Door <nav> dezelfde achtergrondkleur als <main> te geven, wordt dit probleem opgelost.

font-size: 0.7em;

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

width: 11rem;

Breedte.

De eenheid rem is ongeveer hetzelfde als de bekendere eenheid em. De em is op de lettergrootte van het element gebaseerd. Bij een grotere letter is de em groter dan bij een kleinere letter. Ook een breedte in em verandert daardoor mee met de lettergrootte.

Hier gelijk boven is een lettergrootte van 0,7 em opgegeven. Daardoor is 1 em 0,7 keer zo klein als in een element met een lettergrootte van 1 em. En dat is voor de breedte een probleem.

In browservensters smaller dan 760 px staat de <nav> met de inhoudsopgave, als die wordt geopend, over de <main> met de tekst heen. Maar in bredere vensters staat de <nav> naast de <main>: ze moeten op elkaar aansluiten.

Afbeelding 5
Afbeelding 5: Links staat de <nav>, 11 em breed. Rechts staat de <main> op 11 em vanaf links. Omdat de em niet even groot is in <nav> en <main>, ontstaat een kier tussen <nav> en <main>.

In browservensters breder dan 760 px heeft <main> een lettergrootte van 1,1 em (geërfd van <body>). Terwijl <nav> dus een lettergrootte van 0,7 em heeft. 1 em in de <nav> is dus slechts ongeveer twee derde van 1 em in <main>. Als je nu <nav> 11 em breed zou maken, en <main> op 11 em vanaf links neer zou zetten, krijg je 'n kier tussen <nav> en <main>. Meestal is em de beste eenheid, maar hier niet.

Met een absolute eenheid als px kun je <nav> en <main> perfect op elkaar laten aansluiten, maar dat kan problemen opleveren bij een grotere letter. Als de bezoeker de letters heeft vergroot, valt mogelijk 'n deel van de tekst weg, omdat die niet meer binnen de <nav> of <main> past.

De eenheid rem is in dit geval beter. Deze is ook op de lettergrootte gebaseerd, maar dan op de lettergrootte van <html>. Deze lettergrootte is overal op de pagina even groot, ongeacht de lettergrootte van het betreffende element, of van een geërfde lettergrootte. Als je bij <nav> 11 rem breed maakt en <main> op 11 rem vanaf links neerzet, sluiten ze altijd op elkaar aan.

Als de bezoeker de lettergrootte verandert, verandert de rem mee met de lettergrootte. Bij een grotere letter zal er geen tekst verdwijnen. Maar omdat de rem overal evenveel verandert, blijven <nav> en <main> goed op elkaar aansluiten.

overflow: auto;

Standaard staat overflow op visible: als de inhoud niet in een element past, wordt dit toch getoond. Normaal genomen is dat prima. Mogelijk wordt de lay-out wat verstoord, maar er verdwijnt in ieder geval geen tekst of zo. Maar hier is dat niet gewenst.

Om de inhoudsopgave los van de rest van de pagina te kunnen scrollen, is de <nav> boven- en onderaan vastgezet. In browservensters smaller dan 760 px op 120 px van de bovenkant van het venster, in bredere vensters bovenaan. In alle vensters is de onderkant van <nav> vastgezet aan de onderkant van het venster.

De inhoudsopgave is veel te lang om binnen die hoogte te passen. Normaal genomen zou die inhoud toch worden getoond, omdat dat vastzetten normaal genomen wordt genegeerd: de <nav> kan worden gescrold. Maar de hele pagina scrolt dan mee, ook de tekst. Dat is niet de bedoeling.

Met overflow: auto; blijft de <nav> zelf op z'n plaats staan, maar de inhoudsopgave in de <nav> kan nu wel worden gescrold. De rest van de pagina scrolt nu niet mee. Afhankelijk van browser en besturingssysteem kan aan de rechterkant van <nav> een scrollbalk verschijnen, maar dat hoeft niet.

-webkit-overflow-scrolling: touch;

Zoals gelijk hierboven beschreven, wordt de inhoud van de <nav> gescrold, los van de rest van de pagina. Zodra je echter in iOS binnen een element wilt scrollen, gebeurt dat echt ongelooflijk schokkerig. Zo schokkerig, dat scrollen feitelijk onmogelijk is. Deze regel lost dat op, nu scrolt de inhoudsopgave vloeiend.

Deze eigenschap kan grote problemen opleveren en moet grondig worden getest. Meer hierover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Zoomen op iOS in vensters minstens 760 px breed.

border-top: black solid 1px;

Zwart randje aan de bovenkant.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. Nu kan de inhoudsopgave los van de rest van de pagina worden gescrold.

top: 120px;

Bovenkant van de <nav> met de inhoudsopgave 120 px vanaf de bovenkant van het browservenster vastzetten. Door de opening boven de <nav> kun je het begin van het artikel en dergelijke zien, dat je in de inhoudsopgave hebt gekozen. Hierdoor kun je snel zien, of dit het juiste is. Als de <nav> helemaal tot bovenaan het venster zou lopen, zie je nauwelijks, wat je hebt gekozen.

bottom: 0;

Onderaan het browservenster vastzetten.

nav h2

Alle <h2>'s in een <nav>. Er is hier maar één <nav>, waarin maar één <h2> zit met als tekst 'Inhoudsopgave grondwet'. Deze <h2> is bedoeld voor schermlezers, zodat die weten waarover de navigatie in de <nav> gaat.

position: absolute;

Om de <h2> op de juiste plaats te zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een andere positie dan 'static' heeft. Dat is hier de <nav>, waarin de <h2> zit.

left: -20000px;

Ver links buiten het scherm parkeren. Hierdoor is de <h2> onzichtbaar, maar schermlezers lezen hem gewoon voor.

ul

Alle <ul>'s. Dat is er hier maar eentje: de inhoudsopgave staat erin.

text-align: center;

Tekst horizontaal centreren.

list-style: none;

De standaard balletjes en dergelijke bij een <ul> zijn hier niet welkom.

margin: 0; padding: 0;

Verschillende browsers hebben hiervoor verschillende standaardwaarden. Nu zijn ze overal hetzelfde.

li

Alle <li>'s. Elke link in de inhoudsopgave staat in een eigen <li>.

border: black solid 1px;

Zwart randje.

.li-skippy

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.

li {line-height: 2em; border: black solid 1p;}

De elementen met class="li-skippy". In de inhoudsopgave zitten ook skip-links, waarmee gebruikers van de Tab-toets (en de meeste schermlezers) in één keer alle links naar een hoofdstuk kunnen passeren. Deze skip-links zitten in een li.li-skippy.

border: none;

De skip-links zijn niet te zien, omdat de <a>'s in een <li> met class="li-skippy" bij .li-skippy .skippy links buiten het scherm zijn geparkeerd. Maar dat zijn de <a>'s, de <li>'s zelf staan nog gewoon in de inhoudsopgave.

Ook deze <li>'s krijgen de iets hierboven bij li voor alle <li>'s opgegeven border. Deze border is gewoon zichtbaar in de inhoudsopgave. Omdat deze <li>'s verder niets bevatten, zie je die border als een lijn van 2 px dik. Door de border bij deze <li>'s weg te halen, wordt dat voorkomen.

li a

Voor een deel van 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.

.skippy, .li-skippy .skippy {background: white; color: black; width: 12em; font-size: 2em; line-height: 2em; text-align: center; text-decoration: underline; border: red solid 3px; position: absolute; top: 20px; left: -20000px; z-index: 50;}

.li-skippy .skippy {width: 20em; font-weight: normal; line-height: 5em; position: fixed; top: 200px;}

Alle <a>'s binnen een <li>. Dit zijn de links in de inhoudsopgave.

Voor de skip-links in de inhoudsopgave is eerder al css opgegeven, de css hier geldt voor de gewone links naar de ankers in de tekst in <main>.

background: #fafaff;

Achtergrondkleurtje.

color: black;

Normaal genomen krijgt tekst in een <a> een kleurtje. Dat is hier niet mooi.

display: block; Afbeelding 6: de puinhoops als de links geen blok-element zijn

Een <a> is van zichzelf een inline-element. Daardoor wordt de <a> niet breder dan nodig is om de erin zittende tekst en dergelijke weer te geven. Ook de iets hierboven opgegeven achtergrondkleur wordt niet breder dan de <a> en vult dus niet de volle breedte van de inhoudsopgave. De iets hieronder opgeven border rechts komt niet helemaal rechts in de inhoudsopgave te staan, maar gelijk rechts van de tekst.

Op de afbeelding zijn de <a>'s inline-elementen. Vooral de groene border rechts, die aangeeft dat de link al is bezocht, staat helemaal verkeerd.

Door de <a>'s in een blok-element te veranderen, worden ze automatisch even breed als hun ouder, de <li>. Hierdoor vult de achtergrondkleur de volle breedte van de inhoudsopgave en komt de groene border helemaal rechts te staan.

Er is nog een ander voordeel. Om een link te laten werken, moet je de link aanraken of -klikken. Bij een inline-element is de <a> niet groter dan de tekst en moet je heel precies mikken. Bij een blok-element is de <a> even breed als de <li>, zodat je niet zo precies hoeft te mikken.

line-height: 2em;

Regelhoogte. Tekst wordt automatisch halverwege de regelhoogte gezet, zodat de tekst automatisch verticaal is gecentreerd. Omdat verder geen hoogte is opgegeven voor de <a>, is dit ook de hoogte van de <a>.

Als eenheid wordt de relatieve eenheid em gebruikt, omdat bij een absolute eenheid als px de regelhoogte niet mee verandert met de lettergrootte.

text-decoration: none;

Normaal genomen wordt tekst in een <a> onderstreept. Hier is al duidelijk genoeg dat het links zijn en is het niet mooi.

border-right: solid 3px #fafaff;

Border rechts. Deze border heeft dezelfde kleur als de achtergrond van de <a>, zodat je hem niet ziet. Als de <a> is bezocht, wordt deze border groen. Door de border ook hier al neer te zetten, ook al zie je hem niet, wordt vast ruimte gereserveerd. Als je dat niet zou doen, wordt de <a> bij een bezochte link 3 px breder, of de tekst verspringt wat omdat de ruimte voor de tekst opeens 3 px minder is.

li a.hs

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.

li a {background: #fafaff; color: black; display: block; text-decoration: none; border-right: solid 3px #fafaff;}

De <a>'s met class="hs" in een <li>. Dit zijn de links naar hoofdstukken.

background: #dbcbff;

Achtergrondkleur. Door de achtergrondkleur anders te maken dan bij een link naar een gewoon artikel, vallen de links naar een hoofdstuk meer op.

border-right-color: #dbcbff;

Bij li a heeft de border rechts dezelfde kleur gekregen als de achtergrond van de <a>, omdat deze border pas zichtbaar moet worden, als de link is bezocht. Hier gelijk boven is de achtergrondkleur verandert, dus moet ook de kleur van de border worden veranderd.

li a.par

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.

li a {background: #fafaff; color: black; display: block; text-decoration: none; border-right: solid 3px #fafaff;}

De <a>'s met class="par" in een <li>. Dit zijn de links naar paragrafen.

background: #c2c2ff;

Achtergrondkleur. Door de achtergrondkleur anders te maken dan bij een link naar een gewoon artikel, vallen de links naar een paragraaf meer op.

border-right-color: #c2c2ff;

Bij li a heeft de border rechts dezelfde kleur gekregen als de achtergrond van de <a>, omdat deze border pas zichtbaar moet worden, als de link is bezocht. Hier gelijk boven is de achtergrondkleur verandert, dus moet ook de kleur van de border worden veranderd.

li a:hover, li a:focus

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.

.skippy, .li-skippy .skippy {background: white; color: black; width: 12em; font-size: 2em; line-height: 2em; text-align: center; text-decoration: underline; border: red solid 3px; position: absolute; top: 20px; left: -20000px; z-index: 50;}

.li-skippy .skippy {width: 20em; font-weight: normal; line-height: 5em; position: fixed; top: 200px;}

.skippy:focus {left: 30px;}

li a {background: #fafaff; color: black; display: block; text-decoration: none; border-right: solid 3px #fafaff;}

li a.hs {background: #dbcbff; border-right-color: #dbcbff;}

li a.par {background: #c2c2ff; border-right-color: #c2c2ff;}

De <a>'s in een <li>, maar alleen als erover wordt gehoverd, of als deze focus hebben. (Focus is vooral van belang voor gebruikers van de Tab-toets.)

background: #c7c7cc;

Achtergrondkleur veranderen, zodat duidelijk is over welke link wordt gehoverd, of welke link focus heeft.

font-weight: bold;

Sommige mensen hebben moeite kleuren te onderscheiden. Daarom wordt ook de tekst wat vetter gemaakt.

Als de gebruikte lettersoort een vette variant heeft, wordt die gebruikt. De meeste lettersoorten hebben wel een vette variant, soms zelfs meerdere. Een vette variant wordt speciaal ontworpen, het is iets anders dan 'alle lijntjes wat dikker maken'. Sommige letters kunnen er zelfs heel anders uitzien.

Als de lettersoort geen vette variant heeft, maakt de browser de letter vet. Dat wil zeggen dat de browser gewoon alle lijntjes wat dikker maakt. Dat kan mooi zijn, maar vaak is het foeilelijk. Het is heel iets anders dan een speciaal ontworpen font. Elke rechtgeaarde typograaf gruwt hiervan.

li a:visited

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.

.skippy, .li-skippy .skippy {background: white; color: black; width: 12em; font-size: 2em; line-height: 2em; text-align: center; text-decoration: underline; border: red solid 3px; position: absolute; top: 20px; left: -20000px; z-index: 50;}

.li-skippy .skippy {width: 20em; font-weight: normal; line-height: 5em; position: fixed; top: 200px;}

.skippy:focus {left: 30px;}

li a {background: #fafaff; color: black; display: block; text-decoration: none; border-right: solid 3px #fafaff;}

li a.hs {background: #dbcbff; border-right-color: #dbcbff;}

li a.par {background: #c2c2ff; border-right-color: #c2c2ff;}

li a:hover, li a:focus {background: #c7c7cc; font-weight: bold;}

De <a>'s in een <li>, maar alleen als de link al is bezocht.

Hier iets boven staat li a:hover, li a:focus. Dat staat dus boven de selector hier: li a:visited. Normaal genomen staat :visited voor :hover en :focus. Als je een bezochte link bijvoorbeeld rood maakt en bij hoveren over de link moet die groen worden, verandert de link niet als :visited onder :hover staat.

Hier wordt bij :hover en :focus de achtergrondkleur veranderd, en de tekst wordt vetter. Bij :visited wordt de kleur van de rechterborder verandert. Dat zijn twee verschillende dingen, die elkaar niet bijten. Door hier :visited onder :hover en :focus te zetten, blijft de rechterborder bij een bezochte link altijd groen. Ook bij hoveren over of focus van de link is daardoor nog duidelijk dat de link al is bezocht. Bij meer dan tweehonderd links is dat wel handig.

border-right-color: green;

Rechterborder groen maken.

Bij li a is al een border aangebracht, maar die zie je niet, omdat de kleur van de border hetzelfde is als die van de achtergrond. Hier wordt de border zichtbaar, omdat de kleur wordt veranderd.

Door bij li a al een border aan te brengen, wordt voorkomen dat een bezochte link 3 px breder wordt, of dat de tekst in de <a> verspringt, omdat er 3 px minder ruimte is.

Bij een bezochte link kunnen nog maar heel weinig dingen worden veranderd. Het bleek nogal eenvoudig om na te gaan, welke sites iemand had bezocht, als het uiterlijk van een bezochte link met css kon worden veranderd. Een van de weinige dingen die nog kan worden veranderd, is de kleur van een border.

main

Alle <main>'s. Er is maar één <main> met daarin de belangrijkste inhoud van de pagina. Dat is hier de tekst van de grondwet.

background: #f0f0ff;

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

display: block;

Oudere browsers kennen <main> niet. Een onbekend element is standaard een inline-element. Daarom wordt hier specifiek opgegeven dat <main> een blok-element is.

padding: 5px;

Kleine afstand tussen inhoud en buitenkant van <main>.

h1

Alle <h1>'s. Dat is er maar eentje: de belangrijkste kop.

font-size: 1.2em;

Een <h1> heeft een wel heel enthousiaste lettergrootte. Die wordt hier iets kleiner gemaakt.

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

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

Aan de onderkant de standaardmarge van de <h1> verwijderen.

In browservensters smaller dan 760 px staat bovenaan het venster een <label> in de vorm van een balk, waarmee de inhoudsopgave verborgen en getoond kan worden. Om te voorkomen dat de <h1> hier gedeeltelijk onder verdwijnt, wordt de <h1> iets lager neergezet.

.bron

Alle elementen met class="bron". Dat is er maar eentje: de bronvermelding van de tekst gelijk onder de <h1> bovenaan de pagina.

font-size: 0.7em;

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

h2

Voor een deel van 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.

nav h2 {position: absolute; left: -20000px;}

Alle <h2>'s. Elke hoofdstuktitel staat in een <h2>. Voor schermlezers staat ook nog een <h2> bovenin de nav, maar die staat onzichtbaar links buiten het scherm.

font-size: 1.2em;

Standaardlettergrootte van de <h2> iets verkleinen. 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: -30px 0 0; padding-top: 30px;

Omdat bij de marge voor links geen waarde is opgegeven, wordt krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk margin: 30px 0 0 0; in de volgorde boven – rechts – onder – links.

Aan de onderkant de standaardmarge van een <h2> verwijderen.

Aan de bovenkant wordt de <h2> met een negatieve marge 30 px naar boven gezet.

In de inhoudsopgave zit een link naar onder andere elke <h2>, elke hoofdstuktitel, in <main>. Als die link wordt gevolgd, wordt de <h2> bovenin het browservenster gezet. Maar daar ontstaat een probleem: aan die bovenkant staat al een <label> in de vorm van een balk, waarmee de inhoudsopgave getoond en verborgen kan worden. Als de <h2> bovenaan het venster wordt gezet, verdwijnt de <h2> onder die balk.

Omdat de <label> aan de bovenkant een fixed positie heeft, scrolt de balk niet mee met de rest van de pagina. Dit probleem doet zich dus op de hele pagina voor. Niet alleen bij het bovenste hoofdstuk, maar ook bij lagere hoofdstukken.

Om te voorkomen dat de <h2> onder de balk aan de bovenkant van het browser verdwijnt, wordt met padding-top: 30px; een padding aan de bovenkant van de <h2> gegeven. Nu staat de tekst in de <h2> onder de balk aan de bovenkant.

Dit levert echter weer een ander probleem op: nu staat overal op de pagina een opening van 30 px boven elk hoofdstuktitel. Als dat nou alleen bij de hoofdstuktitels zo zou zijn, zou daar nog mee zijn te leven, hoewel het niet mooi is. Maar hetzelfde probleem speelt bij alle artikelen en paragrafen, want ook daarnaartoe staan links vanuit de inhoudsopgave.

Om deze opening aan de bovenkant weer te verwijderen, wordt aan de bovenkant van de <h2> een negatieve marge gegeven die even groot is als de padding aan de bovenkant: 30 px. Een negatieve marge verplaatst een element daadwerkelijk. Bij bijvoorbeeld een relatieve positie wordt het element ogenschijnlijk verplaatst, maar in werkelijkheid blijft het gewoon op z'n plaats staan. Het wordt alleen op een andere plaats getoond. Een marge echter verplaatst het element daadwerkelijk. En daarmee ook de op het element volgende elementen, zodat er geen lege ruimte onder de <h2> ontstaat.

Afbeelding 7: de correctie voor de fixed header zichtbaar gemaakt

Op de afbeelding is de zilverkleurige balk aan de bovenkant van het browservenster doorzichtig gemaakt. De <h2> heeft een rode outline gekregen. De outline loopt tot aan de bovenkant door, dus de <h2> staat echt bovenaan het venster. Door de padding aan de bovenkant van de <h2> staat de tekst in de <h2> echter netjes onder de menubalk.

Op deze manier kan worden gecorrigeerd voor een vaststaande header bovenaan het browservenster. Dit corrigeren hoeft alleen in vensters smaller dan 760 px, want in bredere vensters is de inhoudsopgave gewoon altijd zichtbaar. Voor bredere vensters worden daarom later de marges en paddings aangepast.

h3

Alle <h3>'s. Elk paragraafkopje staat in een <h3>, en de meeste artikelkopjes ook.

font-size: 1.1em;

Standaardlettergrootte van de <h3> iets verkleinen. 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: -30px 0 0; padding-top: 30px;

In de inhoudsopgave staat een link naar elke <h3>. Deze negatieve marge en padding aan de bovenkant zijn nodig, om te corrigeren voor de vaststaande balk bovenaan het browservenster. Dit werkt hetzelfde als de correctie voor de <h2>'s, waarvan de beschrijving bij margin: -30px 0 0; padding-top: 30px; staat.

h4

Alle <h4>'s. De meeste artikelkopjes staan in een <h3>. Artikelkopjes van artikelen die in een paragraaf staan, staan echter in een <h4>, omdat de <h3> dan wordt gebruikt voor de paragraaf.

margin: -30px 0 0; padding-top: 30px;

In de inhoudsopgave staat een link naar elke <h3>. Deze negatieve marge en padding aan de bovenkant zijn nodig, om te corrigeren voor de vaststaande balk bovenaan het browservenster. Dit werkt hetzelfde als de correctie voor de <h2>'s, waarvan de beschrijving bij margin: -30px 0 0; padding-top: 30px; staat.

p

Voor een deel van 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.

.bron {font-size: 0.7em;}

Alle <p>'s.

margin: 0;

Een <p> heeft standaard een marge aan boven- en onderkant. Die wordt hier weggehaald.

.lid

De elementen met class="lid". Sommige artikelen zijn opgesplitst in leden. Elke <p> met een lid heeft deze class.

text-indent: -1.2em;

De eerste regel van de <p> 1,2 em naar links zetten. Hierdoor verdwijnt een deel van de eerste regel in browservensters smaller dan 760 px links buiten het venster, en in bredere vensters onder de inhoudsopgave. Dat wordt gelijk hieronder gecorrigeerd.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de grootte van de verplaatsing mee verandert met de lettergrootte.

margin-left: 1.2em;

Hier gelijk boven is de eerste regel van de <p> 1,2 em naar links gezet, waardoor een deel van die regel links buiten het scherm of onder de inhoudsopgave verdwijnt. Door hier een marge aan de linkerkant op te geven die even groot is als de verplaatsing hierboven, wordt dit weer gecorrigeerd. Maar de eerste regel staat nu wel 1,2 em meer naar links dan de rest van de tekst in de <p>, waardoor het cijfer van het lid links buiten de tekst van het lid staat.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de grootte van de marge mee verandert met de lettergrootte.

.onderdeel

De elementen met class="onderdeel". Een onderdeel van een lid blijkt een 'onderdeel' te heten. Elk onderdeel zit in een <p> met class="onderdeel".

text-indent: -1.2em;

De eerste regel van de <p> 1,2 em naar links zetten. Hierdoor verdwijnt een deel van de eerste regel in browservensters smaller dan 760 px links buiten het venster, en in bredere vensters onder de inhoudsopgave. Dat wordt gelijk hieronder gecorrigeerd.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de grootte van de verplaatsing mee verandert met de lettergrootte.

margin-left: 2.4em;

Hier gelijk boven is de eerste regel van de <p> 1,2 em naar links gezet, waardoor een deel van die regel links buiten het scherm of onder de inhoudsopgave verdwijnt. Door hier een marge aan de linkerkant op te geven, wordt dit weer gecorrigeerd.

Door de marge groter te maken dan de verplaatsing hierboven, springt het hele onderdeel in, maar de eerste regel staat nog steeds 1,2 em meer naar links dan de rest van de tekst in de <p>. Hierdoor staat de letter van het onderdeel nog steeds buiten de tekst van het onderdeel.

Als eenheid wordt de relatieve eenheid em gebruikt, zodat de grootte van de marge mee verandert met de lettergrootte.

.lid::first-letter, .onderdeel::first-letter

De eerste letter van de elementen met class="lid" of class="onderdeel". Bij leden en onderdelen is de eerste letter een letter of cijfer, direct gevolgd door een punt. Als op het eerste teken een leesteken als een punt volgt, wordt die tot de eerste letter gerekend en valt dus ook binnen deze selector.

font-weight: bold;

Vet maken.

Als de gebruikte lettersoort een vette variant heeft, wordt die gebruikt. De meeste lettersoorten hebben wel een vette variant, soms zelfs meerdere. Een vette variant wordt speciaal ontworpen, het is iets anders dan 'alle lijntjes wat dikker maken'. Sommige letters kunnen er zelfs heel anders uitzien.

Als de lettersoort geen vette variant heeft, maakt de browser de letter vet. Dat wil zeggen dat de browser gewoon alle lijntjes wat dikker maakt. Dat kan mooi zijn, maar vaak is het foeilelijk. Het is heel iets anders dan een speciaal ontworpen font. Elke rechtgeaarde typograaf gruwt hiervan.

css voor vensters maximaal 759 px breed

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

De css die hier tot nu toe staat, geldt voor alle browservensters.

De css die binnen deze media query staat, geldt alleen voor browservensters die maximaal 759 px breed zijn.

In sommige versies van Opera Mini en oudere versies van Android browser op Android zit een bug, waardoor de inhoudsopgave niet kan worden getoond. Omdat dit alleen in deze smallere vensters speelt, is er geen enkele reden bredere vensters op te zadelen met de css in deze media query.

En omdat deze media query toch aanwezig was, is de rest van de css voor het tonen en verbergen van de inhoudsopgave hier ook maar neergezet.

@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 bepaalde fysieke 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 browservenster mag maximaal 759 px breed zijn. Is het venster breder, dan wordt de css die binnen deze media-regel staat genegeerd.

Gelijk na deze regel komt een { te staan, en aan het einde van de css die binnen deze regel 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-regel (...) footer {color: gold;} }

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

nav

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.

nav {background: #f0f0ff; font-size: 0.7em; width: 11rem; overflow: auto; -webkit-overflow-scrolling: touch; border-top: black solid 1px; position: fixed; top: 120px; bottom: 0;}

Alle <nav>'s. Dat is er hier maar eentje: de <nav> met de inhoudsopgave.

-webkit-animation: bugfix-0 infinite 1s;

In oudere versies van Android browser en sommige versies van Opera Mini op Android zit een bug, waardoor de <nav> met de inhoudsopgave niet wordt getoond, als dat zou moeten. Met behulp van deze regel wordt de <nav> toch getoond. Meer hierover bij @-webkit-keyframes bugfix-0.

-webkit-transform: translateX(-110%); transform: translateX(-110%);

Hier staat in feite twee keer hetzelfde: transform: translateX(-110%);. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Met behulp van de transform-functie translateX() kan een element horizontaal worden verplaatst. Als een negatieve waarde wordt gebruikt, zoals hier het geval is, wordt naar links verplaatst.

Als bij deze functie een percentage wordt gebruikt, geldt dat percentage ten opzichte van het element zelf. Met -100% wordt de hele <nav> met inhoud en al precies één keer z'n eigen breedte naar links gezet. Met -110%, wat hier is gebruikt, komt daar nog 10% bij. Die 10% is 'n kleine speling, omdat niet elke browser even goed in rekenen blijkt te zijn.

De breedte van het element dat wordt verplaatst met translateX() hoeft niet bekend te zijn. Bij het weergeven van de pagina kent de browser de breedte, en op dat moment wordt uitgerekend, hoeveel 110% is.

will-change: transform;

Dit is een tamelijk nieuwe eigenschap, die nog niet in alle browsers wordt ondersteund. In de browsers die het al ondersteunen, is het een aankondiging dat bepaalde eigenschappen binnenkort gewijzigd gaan worden. Achter will-change worden de eigenschappen die gaan wijzigen opgegeven, gescheiden door een komma. De browser kan de wijzigingen hierdoor als het ware al voorbereiden, voordat ze feitelijk plaatsvinden. Dit is minder belastend voor de processor.

Als will-change zo fantastisch is, waarom dan niet altijd alles achter will-change zetten? Dat kan makkelijk met iets als * {will-change: top, right, bottom, left, width, transform, opacity; (...);}. (De * , 'universal selector', selecteert álles. Meestal is het een bijzonder slecht idee deze te gebruiken.)

Het voorbereiden van de verandering kost echter capaciteit. Als will-change te vaak wordt gebruikt, kan het daardoor zelfs tot een vertraging leiden. In dit geval kan het worden gebruikt, omdat redelijk zeker is dat de <nav> verplaatst (getoond en verborgen) gaat worden. Als dat niet redelijk zeker zou zijn, kun je will-change beter niet gebruiken.

#checkbox-voor-smal:checked ~ #wrapper nav

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.

nav {background: #f0f0ff; font-size: 0.7em; width: 11rem; overflow: auto; -webkit-overflow-scrolling: touch; border-top: black solid 1px; position: fixed; top: 120px; bottom: 0;}

nav {-webkit-animation: bugfix-0 infinite 1s; -webkit-transform: translateX(-110%); transform: translateX(-110%); will-change: transform;}

#checkbox-voor-smal:checked: het element met id="checkbox-voor-smal". Dit is een <input type="checkbox">. Maar alleen als dit aankruisvakje is aangevinkt (:checked).

~: 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#checkbox-voor-smal voor de ~ en #wrapper na de ~ hebben beide als ouder <body>.

#wrapper: het element met id="wrapper". Dit is een overkoepelende <div>, waarin de <nav> met de inhoudsopgave en de <main> met de tekst staan.

nav: alle <nav>'s in #wrapper. Dat is er maar één: de <nav> met de inhoudsopgave.

De hele selector in normale mensentaal: doe iets met de <nav> met de inhoudsopgave, maar alleen als input#checkbox-voor-smal is aangevinkt. (Deze <input> wordt bediend via de bij de <input> horende <label>. De balk bovenaan het browservenster is in werkelijkheid deze <label>.)

-webkit-transform: translateX(0); transform: translateX(0);

Hier staat in feite twee keer hetzelfde: transform: translateX(0);. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Hier iets boven bij nav is de <nav> met de inhoudsopgave naar links buiten het browservenster verplaatst met transform: translateX(-111%);. Hier wordt de verplaatsing weggehaald, waardoor de <nav> – en daarmee de erin zittende inhoudsopgave – zichtbaar wordt.

css 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: het browservenster moet minimaal 760 px: breed zijn (min-width: 760px). In vensters van minimaal deze breedte is de inhoud altijd zichtbaar.

Als je nou 'n mobieltje hebt met een resolutie van – ik roep maar wat – 1024 x 768 px, dan geldt deze media query toch niet voor dat mobieltje. Terwijl dat toch echt meer dan 760 px breed is. Een vuig complot van gewetenloze multinationals? Voordat je je gaat beklagen bij Radar, zou ik eerst even verder lezen.

Steeds meer 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 het aantal pixels, waarvoor je hebt betaald. Maar die zitten dichter bij elkaar. Op een gewoon beeldscherm zitten 96 pixels per inch, wat wordt uitgedrukt met de eenheid dpi ('dots per inch'). Als dat mobieltje een resolutie van 192 dpi 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 dpi van het mobieltje is twee keer zo veel als de 96 dpi 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. Je bent dus niet opgelicht, of in ieder geval niet wat betreft het aantal pixel.

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

Hetzelfde verhaal geldt voor hogere resoluties, Een tablet met een breedte van 4096 schermpixels en een dpi 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 schermpixel.

(Overigens kun je met behulp van media query's ook testen op de resolutie met gebruik van het sleutelwoord 'resolution'. Apple gebruikt het niet-standaard 'device-pixel-ratio', maar het idee is hetzelfde. Dit kan bijvoorbeeld handig zijn om te bepalen, hoe groot een foto moet zijn.)

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.

body

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

body {background: #ff9; color: black; font-family: Arial Helvetica, sans-serif; margin: 0; padding: 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. 't Zal de leeftijd zijn, maar ik vind de standaardgrootte wat te 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.

#checkbox-voor-smal, #toon-menu

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

#checkbox-voor-smal {position: fixed; top: 0;}

#toon-menu {background: white; background: linear-gradient(to bottom, #fff 0%, #bbb 100%); color: black; width: 100%; line-height: 30px; text-decoration: underline; border-bottom: black solid 1px; padding-left: 10px; position: fixed; top: 0;}

De elementen met id="checkbox-voor-smal" en id="toon-menu". De <input type="checkbox"> en de bijbehorende <label>. Hiermee wordt in browservensters smaller dan 760 px de inhoudsopgave verborgen en getoond.

display: none;

In deze bredere browservensters wordt de inhoudsopgave altijd getoond, dus zijn deze <input> en <label> niet meer nodig.

#wrapper

Het element met id="wrapper". Een overkoepelende <div> waarin de <nav> met de inhoudsopgave en de <main> met de tekst staan.

width: 100%;

Breedte.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier <body>, een blok-element. Een blok-element wordt normaal genomen automatisch even breed als z'n ouder. De ouder van <body> is <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Hierdoor wordt div#wrapper ook even breed als het venster, ongeacht de breedte van het venster.

max-width: 800px;

Maximumbreedte.

De <nav> met de inhoudsopgave heeft bij nav een breedte van 11 em gekregen. De rest van het de breedte van het browservenster is voor <main> met de tekst.

In bredere vensters zouden de regels in <main> heel breed en daardoor lastig leesbaar worden. Door het opgeven van een maximumbreedte wordt dit voorkomen.

margin: 0 auto;

Omdat voor onder en links geen waarden zijn opgegeven, krijgen deze 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. Oftewel: #wrapper staat altijd gecentreerd ten opzichte van z'n ouder. Die ouder is het blok-element <body>. Een blok-element wordt normaal genomen automatisch even breed als z'n ouder. De ouder van <body> is <html> Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Hierdoor staat #wrapper automatisch horizontaal gecentreerd in het venster, ongeacht de breedte van het venster.

Deze manier van horizontaal centreren van een blok-element werkt alleen, als het blok-element een breedte heeft.

Maar hier doet zich een eigenaardig verschijnsel voor. #wrapper is absoluut gepositioneerd, en bij een absoluut gepositioneerd element werkt de truc om met auto te centreren normaal genomen niet. Hier werkt die echter toch.

Iets hieronder wordt met right: 0; en left: 0; opgegeven dat #wrapper van de linker- tot de rechterkant van het browservenster moet lopen. Iets hierboven is echter een maximumbreedte van 800 px opgegeven. In vensters met een breedte van maximaal 800 px werkt dit prima. Maar in bredere vensters wordt hier een onmogelijke opdracht gegeven.

Als het browservenster bijvoorbeeld 1000 px breed is en #wrapper moet van left: 0; tot right: 0; lopen, én mag vanwege de max-width: 800px; niet breder dan 800 px zijn, dan kan aan één van die drie eigenschappen onmogelijk worden voldaan.

De browser raakt hier dermate van in de war, dat right: 0; en left: 0; gewoon worden genegeerd en margin: 0 auto; toch werkt. Oftewel: bij een absolute positie werkt margin: 0 auto; niet, tenzij je ook left: 0; en right: 0; opgeeft.

(Deze weinig bekende truc werkt trouwens ook bij position: fixed;. Je kunt de horizontale plaatsing nog beïnvloeden door bij right of left een andere waarde dan 0 in te vullen. En het is ook niet zo dat de browser in de war raakt. Dit staat gewoon in de specificatie. Alleen is het zo weerzinwekkend technisch opgeschreven dat naar verluidt zelfs Einstein het pas na 38 keer lezen begreep.)

position: absolute;

Om de <div> op de juiste positie neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een andere positie dan 'static' heeft. Als die er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.

top: 0; right: 0; bottom: 0; left: 0;

Omdat de <div> aan alle vier de kanten is vastgezet aan de buitenkant van het browservenster, vult de <div> het volledige venster.

div#wrapper heeft slechts twee kinderen: <nav> met de inhoudsopgave, en <main> met de tekst.

<nav> is eerder bij nav met bottom: 0; aan de onderkant van div#wrapper vastgezet. Iets hieronder wordt <nav> bij nav met top: 0; aan de bovenkant van div#wrapper vastgezet.

<main> wordt iets hieronder bij main met top: 0; en bottom: 0; ook aan boven- en onderkant van div#wrapper vastgezet.

De tekst in <nav> en <main> is echter veel te veel om binnen div#wrapper te passen, want div#wrapper is niet hoger dan het venster van de browsers. Normaal genomen zou daardoor de pagina toch gescrold kunnen worden, ondanks dat div#wrapper is vastgezet aan het venster. Normaal genomen is dat ook het beste: de lay-out wordt misschien verstoord, maar er verdwijnt in ieder geval geen tekst en dergelijke.

Hier ligt dat echter anders. Eerder is bij nav overflow: auto; opgegeven, en iets hieronder gebeurt dat bij main ook voor <main>. Daardoor kunnen <nav> en <main> zelf worden gescrold, zonder dat div#wrapper of de hele pagina scrollen.

(right: 0; en left: 0; worden hier eigenlijk alleen gebruikt, om div#wrapper horizontaal te centreren. Hoe dat precies werkt, is iets hoger te vinden bij margin 0 auto;.)

nav

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

nav {background: #f0f0ff; font-size: 0.7em; width: 11rem; overflow: auto; -webkit-overflow-scrolling: touch; border-top: black solid 1px; position: fixed; top: 120px; bottom: 0;}

Alle <nav>'s. Dat is er hier maar eentje: de <nav> met de inhoudsopgave.

border-top: none;

In browservensters smaller dan 760 px staat de <nav> met de inhoudsopgave op 120 px vanaf de bovenkant. Daarom is daar aan de bovenkant een randje nodig. Hier staat de inhoudsopgave bovenaan, daarom wordt dat randje hier weggehaald.

top: 0;

In browservensters smaller dan 760 px is ruimte boven de inhoudsopgave vrijgelaten, zodat mensen kunnen zien, welke link ze hebben gevolgd. Hier is dat niet nodig, daarom wordt de <nav> helemaal bovenaan gezet.

position: absolute;

Eerder was <nav> fixed gepositioneerd, zodat in browservensters smaller dan 760 px de inhoudsopgave los van de rest van de pagina gescrold kon worden.

Hier is dat niet meer nodig, want <nav> is boven- en onderaan vastgemaakt aan z'n ouder div#wrapper, die bij #wrapper weer is vastgezet aan het venster van de browser. Omdat position: fixed; in het onvolprezen iOS voor de wildste problemen kan zorgen, wordt het hier veranderd in position: absolute;.

Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een andere positie dan 'static' heeft. Dat is hier div#wrapper.

main

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: #f0f0ff; color: black; display: block; padding: 5px;}

Alle <main>'s. Er is maar één <main> met daarin de belangrijkste inhoud van de pagina. Dat is hier de tekst van de grondwet.

overflow: auto;

<main> wordt iets hieronder vastgezet aan boven- en onderkant van div#wrapper, die bij #wrapper is vastgezet aan het venster van de browser. Daardoor kan <main> niet hoger worden dan het venster. Er staat echter veel meer tekst in <main>, dan binnen het venster past.

Normaal genomen wordt dat teveel toch getoond, omdat overflow standaard op visible staat. Normaal genomen is dat ook prima: de lay-out wordt misschien wat verstoord, maar er verdwijnt in ieder geval geen tekst en dergelijke.

Hier is dat echter niet nodig. Door deze regel kan de tekst binnen <main> worden gescrold, zonder dat dat invloed heeft op de rest van de pagina. Afhankelijk van browser en besturingssysteem kan rechts van <main> een verticale scrollbalk verschijnen.

-webkit-overflow-scrolling: touch;

Zoals gelijk hierboven beschreven, wordt de inhoud van <main> gescrold, los van de rest van de pagina. Zodra je in iOS binnen een element wilt scrollen, gebeurt dat echt ongelooflijk schokkerig. Zo schokkerig, dat scrollen feitelijk onmogelijk is. Deze regel lost dat op, nu scrolt de inhoudsopgave vloeiend.

Deze eigenschap kan grote problemen opleveren en moet grondig worden getest. Meer hierover is te vinden bij Bekende problemen (en oplossingen) onder het kopje Zoomen op iOS in vensters minstens 760 px breed.

border-right: black solid 1px;

Lijntje rechts van <main>.

position: absolute;

Om <main> op de juiste plaats neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een andere positie dan 'static' heeft. Dat is hier div#wrapper.

top: 0; bottom: 0;

Aan boven- en onderkant van div#wapper neerzetten. Omdat div#wrapper bij #wrapper even hoog is gemaakt als het venster van de browser, is <main> ook even hoog als het venster.

left: 11rem;

<nav> met de inhoudsopgave is bij nav 11 rem breed gemaakt. Door <main> op 11 rem vanaf links neer te zetten, sluit het aan op <nav>. De hier gebruikte eenheid rem werkt ongeveer hetzelfde als de eenheid em, maar is gebaseerd op de lettergrootte van <html>. Hierdoor is de rem op de hele pagina even groot. Meer uitleg over de rem is te vinden bij nav onder width: 11rem;.

h1

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

h1 {font-size: 1.2em; margin: 35px 0 0;}

Alle <h1>'s. Dat is er maar eentje: de belangrijkste kop.

margin-top: 20px;

In bredere browservensters staat geen balk bovenaan het venster. Daarom kan de eerder opgegeven marge aan de bovenkant iets minder worden.

p

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

.bron {font-size: 0.7em;}

p {margin: 0;}

Alle <p>'s.

margin: 10px 0;

Omdat voor onder en links niets is opgegeven, krijgen onder en links automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 10px 0 10px 0 in de volgorde boven – rechts – onder – links.

In deze bredere browservensters is wat meer ruimte, daarom wordt hier aan boven- en onderkant een kleine marge teruggezet.

Als twee verticale marges elkaar raken, zoals hier het geval is bij twee boven elkaar staande <p>'s, overlappen de marges elkaar. Ook tussen twee <p>'s is de marge daardoor slechts 10 px en niet 20 px (marge van 10 px aan boven- en onderkant), zoals je misschien zou verwachten.

h2

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

nav h2 {position: absolute; left: -20000px;}

h2 {font-size: 1.2em; margin: -30px 0 0; padding-top: 30px;}

Alle <h2>'s. Elke hoofdstuktitel staat in een <h2>. Voor schermlezers staat ook nog een <h2> bovenin de <nav>, maar die staat onzichtbaar links buiten het scherm.

margin: 20px 0;

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

Browservensters smaller dan 760 px hebben bovenin het venster een balk staan. Daarom is voor die kleinere vensters een correctie nodig om ankers (links binnen de pagina) goed te laten werken: een negatieve marge en padding aan de bovenkant. Hoe dit precies werkt, is te vinden bij h2. Die correctie is in bredere vensters niet nodig.

Aan boven- en onderkant een marge van 20 px, zodat de hoofdstuktitel er wat uitspringt.

padding: 0;

Om de reden die gelijk hierboven wordt genoemd, is de eerder opgegeven negatieve padding aan de bovenkant niet meer nodig.

h3, h4

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

h3 {font-size: 1.1em; margin: -30px 0 0; padding-top: 30px;}

h4 {margin: -30px 0 0; padding-top: 30px;}

Alle <h3>'s en <h4>'s. Elk paragraaf- en artikelkopje staat in een <h3> of in een <h4>.

margin: 40px 0 20px;

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

Browservensters smaller dan 760 px hebben bovenin het venster een balk staan. Daarom is voor die kleinere vensters een correctie nodig om ankers (links binnen de pagina) goed te laten werken: een negatieve marge en padding aan de bovenkant. Hoe dit precies werkt, is te vinden bij h2. Die correctie is in bredere vensters niet nodig.

Bovenaan een wat grotere marge, onderaan een wat kleinere.

padding: 0;

Om de reden die gelijk hierboven wordt genoemd, is de eerder opgegeven negatieve padding aan de bovenkant niet meer nodig.

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 blauw gekleurd. Alle niet-essentiële code is bruin. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)

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.

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.

Met behulp van dit JavaScript wordt voorkomen dat alle bezochte artikelen in de geschiedenis van de browser worden opgeslagen. Hiermee wordt voorkomen dat je, bij gebruik van de Terug-knop Vooruitknop of een toetscombinatie als alt+← of alt+→, eerst weer door alle bezochte artikelen gaat, voordat je 'n vorige of volgende pagina bereikt.

Dit script manipuleert het adres van de browser. Daarom zou het misbruikt kunnen worden om de bezoeker naar een bepaald adres te leiden. Browsers worden steeds strenger beveiligd tegen dit soort misbruik. Daarom werkt het script in veel (alle?) browsers alleen online of op een zelf geïnstalleerde server goed. Gewoon op de desktop is er een grote kans dat dit script niet werkt.

Deze beveiliging is volkomen terecht. Als je toevallig Halbe heet, zou je niet willen dat 'n script op de site van De Telegraaf het adres van de pagina kan veranderen in '/computer/halbe/erotische-fantasieën/ik-zag-putins-kruisraket-stijgen.doc'. Om daar vervolgens 'n schermafdruk van te versturen naar de site van De Telegraaf.

Om dezelfde reden kan alleen een adres binnen hetzelfde domein (dezelfde site) worden opgegeven. Een script op 'www.css-voorbeelden.nl' kan alleen een adres opgeven dat begint met 'www.css-voorbeelden.nl'. Een adres dat met iets anders begint, wordt gewoon genegeerd. (En levert in de console – de duistere ingewanden van de browser – 'n foutmelding op.)

Ik heb eindeloos geprobeerd het adres te veranderen in allerlei bestanden die begonnen met 'ing.nl/overboekingen/betalingen/', maar dat leverde slechts één geslaagde overboeking op: naar mijn hoster vanwege buitensporig datagebruik.

Serieus: het zal duidelijk zijn, waarom aan dit soort scripts zulke strenge beperkingen worden opgelegd. Als je ergens iets leest over een beveiligingslek in een browser, gaat het vaak om dit soort dingen. Iemand heeft 'n gat gevonden, waardoor 'n script toch zoiets kan doen. Daarom is het ook zo ontzettend belangrijk je browser altijd zo snel mogelijk te updaten. (En een browser te gebruiken die nog wordt onderhouden.)

<script> en </script>

Dit is gewone html: het zijn de openings- en sluittag voor het script. In html5 is de toevoeging type="text/javascript" niet meer nodig.

Als je het script op meer pagina's wilt gebruiken, kun je er heel simpel een extern script van maken. Je zet het hele script (behalve <script> en </script>, want dat is html en geen JavaScript) in een gewoon tekstbestand met de extensie '.js'. In de <script>-tag komt het pad naar en de naam van dat bestand te staan:

<script src="pad-naar-script/script.js"></script>

Normaal genomen wordt het verwerken van de html onderbroken, zodra de browser een script tegenkomt. Het script zou immers nodig kunnen zijn voor de opbouw van de pagina.

Daarom staat het script hier helemaal onderaan de html, gelijk boven </body>. Dit script is niet nodig voor de opbouw van de pagina. Door het onderaan te zetten, wordt voorkomen dat het vertragend werkt.

Een andere mogelijkheid is om het wel bovenaan in de <head> te zetten. Veel sitebouwers zetten scripts en dergelijke graag bij elkaar, voor de overzichtelijkheid. In dat geval kun je hetzelfde bereiken met:

<script src="pad-naar-script/script.js" async></script>

Door het toevoegen van het sleutelwoord async weet de browser dat dit script niet nodig is voor de opbouw van de pagina. Het script wordt nu gedownload, terwijl de html gewoon verder wordt verwerkt. Zodra het script is gedownload, wordt het verwerkt, waarbij het verwerken van de html alsnog wordt onderbroken. Maar deze onderbreking is zo kort, dat je dat nauwelijks merkt.

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

;: met de puntkomma wordt in JavaScript een regel afgesloten. Het is te vergelijken met een gewone punt in een tekst.

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 een functie in die reageert op het klikken op of aanraken van een link. Om die functie goed te laten werken, moet de computer 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 ). 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 ook de code niet in een alles omvattende functie zetten. Dan wordt de code gelijk uitgevoerd. Maar dat brengt belangrijke risico's met zich mee. In dit script worden namen voor variabelen gebruikt als 'links' en 'aantal'. 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 'name conflict' of '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.

Een klein aantal oudere browsers ondersteunt dit niet, maar die hebben er verder geen last van, omdat ze de regel gewoon negeren.

for (i = 0; i < aantal; i++) {

for: doe iets met iets, zolang iets waar is. Nou, is dat lekker vaag of niet? Wat er waarmee moet gebeuren, komt later. Dat staat tussen de {} aan het eind van de regel. (In de echte code staat de laatste } lager, achter wat er waarmee moet gebeuren.)

Hier eerst het deel dat ervoor zorgt dat elk eerder gevonden <a>-object netjes aan de beurt komt.

(: met dit haakje opent het deel dat ervoor zorgt, dat elk <a>-object aan de beurt komt.

Aan het eind van de regel staat de bijbehorende ), waardoor het script weet dat dit het einde van dit deel van de code is.

i = 0;: 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. In dit geval is dat resultaat heel simpel: het is het getal 0.

De variabele heet hier 'i'. De variabele is eerder al aangemaakt bij var links, aantal, i, ..., maar wordt nu pas echt gebruikt. i wordt gebruikt als een teller (waarover later meer), en bij dit soort tellers is het gebruikelijk ze de korte naam 'i' te geven. Dat soort gewoontes maakt het voor mensen een stuk makkelijker elkaars code te lezen en te begrijpen.

Omdat i een variabele is, kan de waarde ervan veranderen, variëren. Wat later in de regel ook gaat gebeuren: elke keer als er een <a>-object is afgehandeld, wordt i met 1 verhoogd. Dat gebeurt in het laatste deel van de regel.

De ; geeft aan dat dit stukje code, waarin de variabele i z'n beginwaarde krijgt, hier eindigt.

i++: eerst het laatste deel van de regel, het middelste deel komt gelijk hieronder.

Elke keer als een in links zittend <a>-object is afgehandeld, 1 optellen bij i. Omdat programmeurs liederlijk lui zijn, wordt 'er 1 bij optellen' ingekort tot ++.

Als i 0 is, betekent i++: i wordt 0 + 1 ('1' dus).

Als i 1 is, betekent i++: i wordt 1 + 1 ('2' dus).

Als i 2 is, betekent i++: i wordt 2 + 1 ('3' dus).

Als i 210 is, betekent i++: i wordt 210 + 1 ('211' dus).

En meer dan 211 wordt het niet, vanwege redenen die gelijk hieronder staan.

(Op een pagina met een ander aantal <a>'s dan 211, zal eerder of later worden gestopt met het verhogen van i.)

i < aantal;: het middelste deel van de regel. In aantal is eerder het aantal <a>-objecten in links opgeslagen. Omdat er in dit voorbeeld 211 <a>'s op de pagina staan, is de waarde van aantal 211.

i bevat een getal. Voordat het eerste <a>-object is verwerkt, is dat getal 0, want dat is hierboven opgegeven.

Het teken < betekent: kleiner dan.

De ; aan het einde geeft aan, dat dit stukje code, de voorwaarde waaraan moet worden voldaan, hier eindigt.

In gewone mensentaal staat hier: zolang teller i kleiner is dan aantal.

Als niet meer aan deze voorwaarde wordt voldaan, als teller i niet meer kleiner is dan aantal, stop dan met het uitvoeren van de code die tussen de {} van de for-lus staat. Omdat aantal even groot is als het aantal <a>-objecten in links, geeft dit een mogelijkheid om elk <a>-object precies één keer te verwerken. En er, als ze allemaal verwerkt zijn, mee te stoppen.

): dit haakje hoort bij de ( gelijk achter for. Tussen deze twee haakjes staat de code die ervoor zorgt dat elk <a>-object aan de beurt komt.

{: het laatste teken op de regel. Hiermee geef je aan dat hierna de code volgt die uitgevoerd moet worden. Tot nu toe is alleen gezorgd dat er bij elk <a>-object íéts moet gebeuren, maar nog niet wát. Dat wát volgt na deze {. Na de code die moet worden uitgevoerd staat nog een afsluitende }. Hiermee geef je aan dat het uitvoerende deel van de code bij de for-lus hier stopt.

Dat uitvoerende deel is op een nieuwe regel gezet, net zoals de afsluitende }. Dat soort dingen zijn informele afspraken, omdat het de code voor mensen leesbaarder maakt. Wat de computer betreft zou je alles ook achter elkaar kunnen zetten op één onwijs lange regel. Alleen is niet alleen die regel dan onwijs, ook de gemiddelde programmeur zou heel snel bijzonder onwijs worden, als alles op één lange regel wordt gezet.

Elke keer als een <a>-object is behandeld (met het stukje code tussen de {}, wat hieronder aan de beurt komt), wordt i door middel van i++ met 1 verhoogd.

Aan het begin heeft i de waarde 0. Na de eerste ronde heeft i de waarde 1. Na de tweede ronde heeft i de waarde 2. Na de derde ronde heeft i de waarde 3. Na de 211e ronde heeft i de waarde 211. Waarmee i niet meer kleiner is dan aantal, dan het aantal <a>-objecten.

Omdat niet meer aan de voorwaarde i < aantal wordt voldaan, wordt gestopt met het behandelen van de <a>-objecten. En dat komt goed uit, want alle <a>-objecten zijn precies allemaal één keer aan de beurt gekomen. Niet meer, niet minder. Bij allemaal is de tussen de {} staande code één keer toegepast.

De code tussen de haakjes in gewone taal: zet teller i op 0 als de for de eerste keer wordt uitgevoerd. Herhaal de for zolang teller i lager is dan het aantal <a>-objecten. Verhoog teller i elke keer als de for wordt uitgevoerd met 1.

function geenHistory (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 link wordt aangeklikt of -geraakt. (Of, als de link focus heeft, door het indrukken van Enter.) Het luisteren naar een aanraking of klik wordt geregeld bij links[i].addEventListener("click" geenHistory);

De code in deze functie voorkomt dat de gevolgde link in de geschiedenis van de browser wordt opgeslagen.

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven.

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

(e): die haakjes horen nou eenmaal zo na de naam van een functie. 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 klikken op of aanraken van een <a>. In e zit bijvoorbeeld de hoogte en breedte van de <a>. En of toevallig ook Shift is ingedrukt. En nog 'n waanzinnige andere hoop informatie, waarvan het overgrote deel hier verder niet wordt gebruikt.

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 volgen van een <a>. 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 }.

location.replace(e.target.href);

Deze regel is onderdeel van function geenHistory (e)

In deze ene regel gebeurt eigenlijk alles. De rest van het script was alleen voorbereiden, de juiste <a>'s eruit filteren, en dergelijke. Maar Hier Gebeurt Het. Dus.

Als een link wordt gevolgd, wordt deze normaal genomen opgeslagen in de geschiedenis van de browser. Met de Terug- en Vooruit-knop van de browser, en met toetscombinaties als Alt+← en Alt+→, kun je door die geschiedenis bladeren. Op die manier kun je snel eerder bezochte pagina's terugvinden.

Op deze pagina zijn 211 <a>'s aanwezig, waarvan er 210 een link vanuit de inhoudsopgave naar de tekst in <main> zijn. Het is tamelijk zinloos om al die links binnen de pagina in de geschiedenis op te slaan. Je zou dan, als je elke link in de inhoudsopgave hebt gevolgd, 210 keer de Terug-knop moeten indrukken, voordat je bij 'n vorige pagina aankomt.

Deze regel zorgt ervoor dat de link wel wordt gevolgd, maar dat deze niet in de geschiedenis terecht komt. Je kunt er nog wel gewoon 'n favoriet (of bookmark, of hoe het beestje ook heet) van maken. Ook zit het adres nog gewoon in de lijst met bezochte adressen.

location: 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 informatie, die betrekking heeft op de pagina. De informatie in 'window' is weer opgesplitst in allerlei kleinere objecten. Een van die objecten is 'location'. Hierin zit alles dat met het adres van de pagina te maken heeft: protocol, domeinnaam, naam van de pagina, noem maar op.

(Eigenlijk zou je dus window.location.pathname moeten gebruiken, maar omdat 'window' zo vaak voorkomt, mag je dat weglaten: de browser vult dat automatisch aan.)

De informatie die in location zit, kan worden gewijzigd. Als dat gebeurt, verschijnt er een nieuw adres in de adresbalk van de browser, en de browser zal naar het nieuwe adres gaan.

Vanwege de veiligheid kan een adres alleen maar binnen dezelfde site worden veranderd. Vanwege diezelfde veiligheid zijn er ook beperkingen, als je dit script op de desktop uitvoert. Testen kan om die reden alleen online, of als je zelf 'n server installeert. Een iets uitgebreider verhaal hierover is te vinden bij Dit script manipuleert...

replace: dit is een in JavaScript ingebouwde functie, een stukje bij elkaar horende code. Dat het een functie is, kun je weer zien aan de () achter de functie. De code in deze functie wordt uitgevoerd: vervang ('replace') het adres met het adres dat tussen de haakjes achter replace staat.

Je kunt het adres op meerdere manieren veranderen. In dit geval is het voordeel van replace() dat het nieuwe adres niet in de geschiedenis van de browser wordt opgeslagen. Je kunt er nog wel gewoon 'n favoriet (of bookmark, of hoe het beestje ook heet) van maken. Ook zit het adres nog gewoon in de lijst met bezochte adressen.

(e.target.href): dit is het nieuwe adres, alleen is het zo op het oog niet echt herkenbaar als een adres. Het adres zit verstopt in een variabele.

e: dit is de e die tussen de haakjes bij function geenHistory (e) stond. e is een object met een hele berg aan informatie over de link, die is aangeraakt of -geklikt. Deze informatie is beschikbaar voor de code binnen de functie en kan hier dus worden gebruikt.

Omdat een browser niet zo slim is, moet je hem wel even vertellen dat hij in e moet zoeken, vandaar dat e aan het begin staat.

target: dit is de specifieke <a>, en alle bij die <a> horende informatie, die is aangeraakt of -geklikt. In e zit nog meer informatie, zoals waar precies op het scherm is geklikt. Maar in target zit de informatie die bij de <a> hoort.

href: in href zit het adres van de <a>. Bij <a href="#art-1"> (de link uit de inhoudsopgave naar artikel 1) is dat bijvoorbeeld 'https://www.css-voorbeelden.nl/tekst/ankers/tekst-029.html#art-1". In de html staat als doel in de href alleen '#art-1", maar de browser maakt daar automatisch een volledig adres van. (Dit adres hoort natuurlijk bij de site met dit voorbeeld. Op een andere site zul je 'n ander adres krijgen.)

e.target.href: bij elkaar: het doel van de <a>, aangevuld tot het volledige adres.

De hele regel in normale mensentaal: vervang het adres van de pagina door het adres van de link, waarop is geklikt (of die is aangeraakt, of op 'n andere manier is gevolgd). Als gevolg hiervan zal de browser naar het nieuwe adres gaan: het doel van de <a> in de inhoudsopgave. Dit nieuwe adres wordt ook zichtbaar in de adresbalk bovenin de browser. (Niet alle browsers tonen het volledige adres.)

Hoewel dit adres niet in de geschiedenis van de browser wordt opgeslagen, kan er gewoon een favoriet of bookmark (of hoe het beestje verder ook heet) van worden gemaakt. Ook staat het adres gewoon in de lijst met bezochte pagina's.