Uitleg slideshow met (thumbnails en) foto's. Gebruikt weinig bandbreedte
Laatst aangepast: .

Korte omschrijving
Door de thumbnails kan gescrold worden. Door klikken, aanraken of gebruik van de knoppen, kan de bijbehorende grote afbeelding worden getoond. In de regel wordt een grote afbeelding alleen gedownload, als deze wordt getoond. Er wordt dus zo weinig mogelijk bandbreedte gebruikt.
BELANGRIJK
Deze uitleg hoort bij het voorbeeld dat in de download zit. Het voorbeeld uit de download verschilt iets van het voorbeeld hier op de site. In de download ontbreekt bijvoorbeeld de navigatie voor de site. Ook in de kopregels zit vaak wat verschil. Daarnaast kunnen er nog andere (meestal kleine) verschillen zijn.
Als je deze uitleg leest naast de broncode van het voorbeeld op de site, kan het dus bijvoorbeeld zijn dat 'n <h1> uit de css bij 'n <h2> uit de html hoort. Maar het gaat niet om hele grote, fundamentele afwijkingen.
Als je dit lastig vindt, kun je bovenaan de pagina de hele handel downloaden. In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.
Als je deze handleiding graag uitprint (zonde van het bos), gebruik dan de pdf in de download. Deze pagina is niet geoptimaliseerd voor printen, de pdf kan wel makkelijk worden geprint.
Alles op deze site kan vrij worden gebruikt, met drie beperkingen:
* Je gebruikt het materiaal op deze site volledig op eigen risico. Het kan prima zijn dat er fouten in de hier verstrekte info zitten. Voor eventuele schade die door gebruik van materiaal van deze site ontstaat, in welke vorm dan ook, zijn www.css-voorbeelden.nl en medewerkers daarvan op geen enkele manier verantwoordelijk.
* Deze uitleg wordt min of meer regelmatig bijgewerkt. Het is daarom niet toegestaan deze uitleg op welke manier dan ook te verspreiden, zonder daarbij duidelijk te vermelden dat de uitleg afkomstig is van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie is te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.
* Het kan zijn dat materiaal is gebruikt dat van anderen afkomstig is. Dat materiaal kan onder een bepaalde licentie vallen, waardoor het mogelijk niet onbeperkt gebruikt mag worden. Als dat zo is, wordt dat vermeld onder Inhoud van de download en licenties.
Een link naar www.css-voorbeelden.nl wordt trouwens altijd op prijs gesteld.
Alle code is geschreven in een afwijkende
lettersoort en -kleur. De code die te maken heeft met de basis van dit voorbeeld (essentiële code), is in de hele uitleg onderstippeld blauw
. Alle niet-essentiële code is bruin
. (In de inhoudsopgave staat alles vanwege de leesbaarheid in een gewone letter.)
Opmerkingen
- De gebruikte foto's zijn afkomstig van unsplash.com. De maat van de foto's is aangepast, om meerdere maten te kunnen gebruiken in dit voorbeeld. Ook is een breedte aangebracht op de foto's, zodat je kunt zien, welke grootte wordt gebruikt.
- Hoewel dat niet absoluut nodig is, is het wel heel handig als de afbeeldingen dezelfde maat hebben. Je kunt de afbeeldingen dan eenvoudig centreren, er 'n kadertje om zetten, 'n onderschrift eronder zetten, enzovoort. En het spaart heel veel css uit, omdat je steeds dezelfde maten kunt gebruiken. Als de afbeeldingen verschillende maten hebben, moet de css op diverse plaatsen worden aangepast.
-
In principe werkt dit voorbeeld zonder JavaScript. Maar echt lekker werkt het dan niet. (Een volledig overzicht van wat er niet of maar gedeeltelijk werkt, is te vinden bij Kort overzicht van problemen die met JavaScript worden opgelost.)
Eigenlijk is JavaScript gewoon onmisbaar, om dit voorbeeld acceptabel te laten werken. En dat kun je je afvragen, of het niet beter is om gewoon een kant-en-klare slideshow te gebruiken, zoals er op tal van plaatsen zijn te vinden. Waarbij soms ook op toegankelijkheid is gelet.
Als dan toch eigenlijk JavaScript nodig is, kun je waarschijnlijk beter gelijk helemaal voor een slideshow gaan, die gewoon volledig JavaScript gebruikt. En daardoor beter werkt (en als het goed is ook toegankelijker is), dan de slideshow die hier staat.
Waarom dan toch dit voorbeeld? Gewoon, voor de lol. De vorige versie stamde nog uit de tijd dat je geen mobiel internet had, en die werkte best lekker. En omdat het een leuke uitdaging is om zoveel mogelijk uit dat tijdperk om te bouwen, is dat dus gebeurd. En allicht staan er wat dingen in, die elders handig kunnen zijn. Maar zelf zou ik 'in het echt' deze slideshow niet meer gebruiken.
Als je voluit JavaScript gebruikt voor een slideshow, werkt deze (uiteraard) niet zonder JavaScript. Maar de css kan veel simpeler. In JavaScript kun je gewoon testen, welke toets er is ingedrukt en dan iets als
nextElementSibling
gebruiken in plaats van iets als#thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus-within + div + div .groot
. Dat ziet er wel indrukwekkend uit en zo, maar als je psychiater het ziet, loop je 'n redelijke kans op een gedwongen opname. -
In browservensters smaller dan 760 px of lager dan 529 px, zijn de grote afbeeldingen gewone afbeeldingen en kunnen op de gebruikelijke manier worden opgeslagen.
In browservensters minimaal 760 px breed en minimaal 530 px hoog, zijn de grote afbeeldingen achtergrond-afbeeldingen. Ze kunnen niet (simpel) worden opgeslagen. Maar dat is eventueel vrij eenvoudig toe te voegen met behulp van wat css en/of JavaScript en 'n extra link of zo.
(Uiteraard kun je met behulp van het ontwikkelgereedschap de afbeeldingen opslaan, maar de gemiddelde gebruiker zal bij het woord 'ontwikkelgereedschap' aan iets als genetische manipulatie denken. Je kunt ook heel sterk vergroten, want dan gaat ook een groot browservenster zich gedragen als een mobieltje, en worden de afbeeldingen gewone <img>'s. Maar uiteraard is ook dat geen briljante oplossing.)
- De tekst onder de slideshow is met
position: fixed;
gepositioneerd. Dit is niet echt ideaal, het kan ook met dingen als een maximumhoogte en marges. Omdat er alleen maar flauwekultekst in staat, is dat hier niet gedaan. (En het zou kunnen dat voor Safari op OS X en voor iPads wel de hier gebruikte constructie metposition: fixed;
nodig is.)
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.
Achterliggend idee
De vorige versie van dit voorbeeld is meer dan tien jaar oud. In die versie werden de grote afbeeldingen pas gedownload, als ze werden getoond. Elke grote afbeelding stond in een eigen <span>, die weer in een link stond. Bij hoveren, klikken op of tabben naar die link werd met behulp van css een achtergrond-afbeelding in die <span> gezet: de grote afbeelding.
Inmiddels laden browsers elke achtergrond-afbeelding die in de css is opgenomen, ook als die nog niet wordt getoond. Deze constructie werkte dus niet meer. Gelukkig voorkomt display: none;
het laden van een achtergrond-afbeelding wel. Pas als het element wordt weergegeven, wordt de achtergrond-afbeelding geladen.
Mobieltjes en tablets bestonden bij de vorige versie nog nauwelijks. Daarop werkte de vorige versie dan ook niet of – in het beste geval – heel krakkemikkig.
In de laatste tien jaar hebben css en html bovendien een ongelooflijk aantal nieuwe mogelijkheden gekregen, zoals het bij scrollen automatisch centreren van een afbeelding.
Kortom: de code moest volledig worden herschreven. 'n Paar aanpassingen volstonden niet meer. En als je dan toch alles herschrijft, kun je ook gelijk de nieuwe mogelijkheden van css en html gebruiken.
Kleine vensters
In heel smalle schermen, zoals bij smartphones, heeft een slideshow geen enkel nut, want de 'grote' foto's zijn zelf nauwelijks groter dan 'n thumbnail. Zou je boven 'n grote foto nog een rij thumbnails zetten, dan houd je helemaal geen grote foto meer over.
Daarom zijn in smartphones alle grote foto's gewoon zichtbaar. In portretstand staan ze onder elkaar, in landschapsstand naast elkaar.
Door gebruik te maken van <picture> met <source> kan de afbeelding worden aangepast aan de resolutie van het scherm: hogeresolutieschermen en/of bredere schermen krijgen afbeeldingen met een hogere resolutie.
Door bij elke <img> gebruik te maken van loading="lazy"
, worden niet alle afbeeldingen bij openen van de pagina al gedownload. Slecht een aantal afbeeldingen wordt gedownload, en pas bij scrollen worden er meer gedownload. Omdat er altijd 'vooraf' al wat afbeeldingen worden gedownload, worden de afbeeldingen nog steeds snel getoond. Hoeveel afbeeldingen er 'vooraf' worden gedownload verschilt per browser en is niet in te stellen.
Met behulp van scroll snap
wordt bij scrollen elke afbeelding in portretstand bovenaan het browservenster gezet en in landschapsstand in het midden van het venster. Hierdoor is bij scrollen altijd de hele afbeelding zichtbaar.
Vensters minimaal 760 px breed en minimaal 530 px hoog
Thumbnails
In grotere browservensters is bij het openen van de pagina alleen een rij thumbnails zichtbaar. De bezoeker kan dan kiezen, welke grote afbeelding(en) worden getoond.
Die thumbnails zitten in precies dezelfde <picture> als voor de kleinere browservensters hierboven, maar in een andere <source>. De grote afbeeldingen uit die <picture> worden in deze bredere vensters hier helemaal niet gebruikt, en dus ook niet gedownload.
(Althans: het zijn wel dezelfde grote afbeeldingen die worden gebruikt, maar dan met behulp van css als achtergrond-afbeelding. Dezelfde afbeeldingen worden gebruikt, maar op een volstrekt andere manier.)
Alleen de thumbnails worden al gedownload. Maar omdat dat kleine afbeeldingen zijn, kost dat weinig bandbreedte. In het voorbeeld worden maar dertig thumbnails gebruikt, en die worden allemaal gelijk gedownload. Maar als je drieduizend thumbnails zou gebruiken, zouden die niet allemaal worden gedownload, omdat ze in dezelfde <img> als hierboven met loading="lazy"
s worden gebruikt. Alleen een klein 'voorraadje' wordt alvast gedownload, zodat de thumbnail bij scrollen al is gedownload en snel kan worden getoond.
Grote afbeeldingen
Als je dat niet voorkomt, worden alle grote afbeeldingen gelijk bij het openen van de pagina geladen. Of ze nu worden getoond of niet. Zeker bij een groot aantal grote afbeeldingen kan dat enorm veel bandbreedte kosten.
Zoals hierboven bij Kleine vensters beschreven, staan de afbeeldingen daar onder of naast elkaar. Om ze te zien, moet je scrollen. Door bij elke <img> loading="lazy"
te gebruiken, worden de afbeeldingen pas gedownload, als dat nodig is.
In deze grotere vensters staan de afbeeldingen echter niet naast elkaar, maar altijd in het midden van het browservenster, over elkaar heen. De truc met loading="lazy"
werkt hier dus niet bij de grote afbeeldingen, want die worden niet gescrold. Wat de browser betreft, staan ze gewoon áltijd op het scherm.
De JavaScript API Intersection Observer werkt ongeveer hetzelfde als loading="lazy"
, maar ook die werkt alleen als er wordt gescrold.
Daarom worden de grote afbeeldingen als achtergrond-afbeelding in een <span> gezet, die met display: none;
wordt verborgen. Hierdoor worden de afbeeldingen pas geladen, als die <span> met display: block;
zichtbaar wordt gemaakt.
Als je niet alle grote afbeeldingen wilt zien, werkt dat prima. Je pikt er eentje uit die je wilt zien, en pas dan wordt die gedownload. Als je echter álle afbeeldingen wilt zien (met behulp van de daarvoor bedoelde knoppen of met de Tab-toets), wordt dat wat irritant: bij elke nieuwe afbeelding duurt het even, voordat deze is gedownload. Daarom worden, als je alle grote afbeeldingen wilt zien, bij het tonen van een afbeelding ook alvast de volgende twee grote afbeeldingen gedownload. Dat kan heel simpel: gewoon de twee volgende <span>'s met een grote afbeelding ook al display: block;
geven.
Nu ontstaat er echter een nieuw probleem: er zijn nu soms drie afbeeldingen (gedeeltelijk) zichtbaar. Daarom worden de afbeeldingen die niet worden getoond. ook nog boven het scherm geparkeerd, uit het zicht.
Met behulp van image-set()
wordt op hogeresolutieschermen een betere kwaliteit achtergrond-afbeelding getoond. Verder worden nog wat eigenschappen als calc()
gebruikt om de grootte van de afbeelding aan te passen aan de grootte van het browservenster.
Welke grote afbeelding wanneer precies wordt getoond, wordt geregeld met behulp van wat hocus pocus met dingen als :focus
en :focus-within
.
Voor Firefox en iOS zijn nog wat aanpassingen nodig, omdat die wat kuren hebben.
Als een grote afbeelding is geopend, staat boven de thumbnails de naam van die afbeelding.
Knoppen
Linksboven staat een knop met een vraagteken. Als je die aanraakt of -klikt, opent een korte uitleg.
Rechtsboven staan zes knoppen. Deze knoppen zijn gewone links, die naar de eerste, de vorige, de volgende of laatste thumbnail linken.
De meest linkse en rechtse knop brengen je in één keer naar de eerste en laatste thumbnail.
De tweede en derde knop openen de volgende grote afbeelding. Door aanraken of -klikken van deze knoppen, kun je door alle grote afbeeldingen lopen.
De vierde en vijfde knop laten je door de thumbnails lopen. Eigenlijk hetzelfde als bij de tweede en derde knop, maar nu worden de grote afbeeldingen niet getoond, en dus ook niet gedownload. Wel verschijnt de naam van de bij de thumbnail horende grote afbeelding boven de rij met thumbnails.
In elke knop staat een afbeelding van de thumbnail of grote afbeelding, waar de knop naartoe linkt. Bij de eerste, vierde, vijfde en zesde knop – die de grote afbeelding niet openen –, is die afbeelding heel klein. Bij de tweede en derde knop – die de grote afbeelding wel openen –, is die wat groter.
Schermlezers en Tab-toets
\De grote afbeeldingen zijn ook te zien in schermlezers. Dat lijkt misschien vreemd, maar niet elke gebruiker van een schermlezer is volledig blind. Hoe de grote afbeelding precies kan worden getoond, is afhankelijk van de gebruikte schermlezer en de instellingen daarvan.
De grote afbeeldingen kunnen ook worden getoond door gebruik van de Tab-toets.
Bij schermlezers en Tab-toets zijn de zes knoppen rechtsboven uitgeschakeld. Je hebt daardoor geen keuze tussen wel of niet tonen van de grote afbeelding. Maar het alternatief is dat je tussen elke grote afbeelding zes knoppen moet passeren, en daar wordt vermoedelijk niemand gelukkiger van.
(Het is in theorie mogelijk om dit ook allemaal mogelijk te maken voor gebruikers van schermlezers en toetsenbord, maar dat wordt behoorlijk ingewikkeld, en zonder 'n berg JavaScript is dat niet mogelijk. Dit is één van de redenen, waarom je vermoedelijk beter een goede, toegankelijke, kant-en-klare slideshow op JavaScript gebaseerde slideshow kunt gebruiken.
Hier is dit allemaal niet gedaan, omdat het er op deze site juist om gaat dingen ook zonder JavaScript te laten werken.)
JavaScript
In principe werkt deze slideshow zonder JavaScript. Maar met nogal wat ongemakken. Zo wordt bijvoorbeeld de geschiedenis opgeslagen: elke keer als je 'n knop gebruikt, wordt dat opgeslagen. Als je twintig keer vooruit gaat met behulp van 'n knop, moet je 21 keer op de Terug-knop van de browser drukken, om naar de vorige pagina terug te gaan. Als een grote afbeelding wordt gesloten door buiten de afbeelding het scherm aan te raken of te klikken, wordt teruggegaan naar de eerste thumbnail.
Zo is er nog een heel scala aan grotere en kleinere irritantigheden, die worden opgelost met behulp van JavaScript. Als je een lekker werkende slideshow zonder JavaScript wilt maken, kan dat eigenlijk alleen een heel simpele slideshow zijn, zonder toeters en bellen.
Kort overzicht van problemen die met JavaScript worden opgelost
De slideshow werkt zonder JavaScript nog steeds, maar met allerlei kleinere en grotere onhandigheden.
Hieronder staat een kort lijstje met dingen die zonder JavaScript niet of niet goed werken. Bij Zonder JavaScript is deze zelfde lijst te vinden, maar dan met veel meer toelichting, (soms) bijbehorende oplossingen, en dergelijke. Omdat dat nogal 'n lang verhaal is, staat dezelfde lijst hier ook, maar dan veel beknopter.
- In browservensters smaller dan 760 px en/of lager dan 530 px wordt helemaal geen JavaScript gebruikt, dus daarin maakt het aan- of uitstaan van JavaScript helemaal geen verschil.
- Als de thumbnails zijn gescrold, wordt bij herladen van de pagina niet altijd de eerste thumbnail helemaal links gezet. Soms blijven de thumbnails staan, waar ze stonden. Soms worden dan wel de bij de eerste thumbnail horende knoppen getoond, wat nogal verwarrend is. Dit verschilt per browser (en soms zelfs per besturingssysteem). Het maakt soms ook verschil of er al een knop is gebruikt of niet.
- Elke keer als een knop naar een vorige of volgende thumbnail of grote afbeelding wordt gebruikt, wordt in feite een link gevolgd. Die links worden in de geschiedenis van de browser opgeslagen. Als je dertig keer op de knop naar de volgende thumbnail drukt, zitten er dertig ingangen in de geschiedenis bij. Je moet dan 31 keer op de Terug-knop van de browser drukken om naar de vorige pagina te gaan.
- Bij het indrukken van Escape wordt de grote afbeelding niet gesloten.
- Bij het indrukken van Escape wordt de hulp niet gesloten.
- Bij het indrukken van Enter op een geselecteerde thumbnail, wordt de bijbehorende grote afbeelding niet getoond.
- Als een grote afbeelding wordt gesloten door klikken op of aanraken van het scherm, of door het indrukken van Escape, wordt teruggegaan naar de eerste thumbnail. Als de tiende grote afbeelding was geopend, moet je dus weer tien keer een knop indrukken om daar weer aan te komen. (Of gewoon rechtstreeks de tiende thumbnail aanraken of -klikken, dat werkt natuurlijk ook...)
- Als de hulp wordt geopend, terwijl een thumbnail is geselecteerd of een grote afbeelding is geopend, wordt bij sluiten van de hulp teruggegaan naar de eerste thumbnail, en niet naar de laatst geselecteerde thumbnail.
- Het venstertje met de uitleg sluit niet bij indrukken van Escape.
- Bij gebruik van de Tab-toets om door alle grote afbeeldingen te lopen worden de twee volgende afbeeldingen niet alvast gedownload. Hierdoor kan een kleine vertraging optreden, als een grote afbeelding voor de eerste keer wordt getoond.
Voorwaarden van het JavaScript waaraan html en css moeten voldoen
Voor deze slideshow wordt gebruik gemaakt van JavaScript. Zonder JavaScript werkt deze slideshow wel, maar niet echt lekker. (Een volledig overzicht van wat niet of maar gedeeltelijk werkt, is te vinden bij Kort overzicht van problemen die met JavaScript worden opgelost.)
Om dit script goed te laten werken, moeten html en css aan een aantal voorwaarden voldoen. Dit lijkt mogelijk een wat lange lijst, maar het gaat om simpele voorwaarden.
(Ook de css is grotendeels gebaseerd op id's en classes. Hieronder gaat het echter alleen om de voorwaarden voor JavaScript, omdat die nogal verstopt kunnen zitten in allerlei obscure code. Maar als je als 'n dolle allerlei elementen, id's en classes gaat veranderen, levert dat uiteraard ook problemen met de css op.)
Structuur
Onderstaande ziet er mogelijk wat ingewikkeld uit. Als je de html erbij pakt, zul je zien dat dat enorm meevalt.
- De hele slideshow moet in een <div> zitten. In het voorbeeld is dat <div id="thumbs">. Als je een ander blok-element wilt gebruiken in de html, moet je dat ook in het script (en in de css) wijzigen.
-
Elke thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke, moet in een <div> zitten, die een direct kind is van de hierboven genoemde
div#thumbs
. In het voorbeeld zijn ditdiv#div-1
tot en metdiv#div-30
.<div id="thumbs">
(...)
<div id="div-1"></div>
<div id="div-30"></div>
(...)
</div>
In de code hierboven zijn
div#div-1
endiv#div-30
een direct kind vandiv#thumbs
: er zit geen enkel ander element tussen ouderdiv.thumbs
en kinderendiv#div-1
endiv#div-30
.<div id="thumbs">
(...)
<div id="stoorzender">
<div id="div-1"></div>
<div id="div-30"></div>
</div>
(...)
</div>
In de code hierboven zijn de middelste
div#div-1
endiv#div-30
geen direct kind vandiv#thumbs
, omdatdiv#stoorzender
ertussen zit. Deze code levert problemen met het script op. -
De knop met het vraagteken en de bijbehorende uitleg moeten samen in een eigen <div> zitten. In het voorbeeld is dat
div#uitleg
:<div id="uitleg" tabindex="0">
<span id="vraagteken" aria-hidden="true">?</span>
<div id="hulptekst" tabindex="0">
(...)
</div>
</div>
- Thumbnails en afbeeldingen voor kleinere browservensters zitten in een <picture>. Die <picture> moet een direct kind zijn van de iets hierboven genoemde <div id="div-1"> tot en met <div id="div-30">. In het voorbeeld zijn dit
picture#picture-1
,picture#picture-2
,picture#picture-3
tot en metpicture#picture-30
. (Wat een direct kind is, staat iets hierboven bij Elke thumbnail...) - De links naar de eerste en laatste thumbnail moeten een direct kind van de iets hierboven genoemde
div#thumbs
zijn. In het voorbeeld zijn dita.eerste
ena.laatste
. (Wat een direct kind is, staat iets hierboven bij Elke thumbnail...) - De links naar de volgende en vorige thumbnail en grote afbeelding moeten een direct kind zijn van de iets hierboven genoemde <div id="div-1"> tot en met <div id="div-30">. In het voorbeeld zijn dit
a.vorige-picture
,a.volgende-picture
,a.vorige-div
ena.volgende-div
. (Wat een direct kind is, staat iets hierboven bij Elke thumbnail...)
Id's en classes
- <html> moet een class="no-js" hebben: <html lang="nl" tabindex="-1" class="no-js">. Als je een andere class wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen.
- De hele slideshow moet in een <div id"thumbs"> zitten. In het voorbeeld is dat <div id="thumbs">. Als je een andere id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen.
-
In sommige selectors in de css komt de class 'gebruikt-tab' voor. Die class wordt volledig toegevoegd en verwijderd door het script. Als je een andere class wilt gebruiken in de css, moet je die ook in het script wijzigen.
(Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
- De knop met het vraagteken en de bijbehorende uitleg moeten samen in een eigen div met id="uitleg" zitten. In het voorbeeld is dat
div#uitleg
. Als je een andere id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen. - De knop met het vraagteken moet een id="vraagteken" hebben. In het voorbeeld is dat
span#vraagteken
. Als je een andere id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen. - Elke thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke, moet in een <div> zitten. De id van elk van die <div>'s moet beginnen met 'div-' en eindigen op een nummer. In het voorbeeld zijn dat
div#div-1
,div#div-2
,div#div-3
tot en metdiv#div-30
. Als je een ander begin van die id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen. - Elke <picture> moet een id hebben die begint met 'picture-' en eindigt op een nummer. In het voorbeeld zijn dit
picture#picture-1
,picture#picture-2
,picture#picture-3
tot en metpicture#picture-30
. Als je een ander begin van die id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen. - De eerste <picture> moet een id="picture-1" hebben. Als je een andere id wilt gebruiken in de html, moet je die ook in het script (en in de css) wijzigen.
Tabindex
Op een fors aantal plaatsen wordt in de html het attribuut tabindex
gebruikt. Dit attribuut wordt besproken bij Tabindex en Tab-toets. Je kunt ook in de html naar 'tabindex=' zoeken. Meestal zal gelijk duidelijk zijn, waarom dit attribuut wordt gebruikt.
(Ook voor de css en voor toegankelijkheid is soms een tabindex nodig. Hieronder gaat het echter alleen om de tabindexen die nodig zijn voor JavaScript, omdat die nogal verstopt kunnen zitten in allerlei obscure code. Een volledig overzicht van álle gebruikte tabindexen is te vinden bij Tabindex en Tab-toets.)
- Voor Safari op OS X en Firefox moet het script de focus aan het begin van de pagina neer kunnen zetten. In het voorbeeld is dit opgelost door de hele pagina in een <main tabindex="-1"> te zetten. De tabindex="-1" maakt het mogelijk om <main> met behulp van JavaScript focus te geven, zonder dat dit gebruikers van de Tab-toets hindert.
- Elke thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke, moet in een <div> zitten. In het voorbeeld zijn dit
div#div-1
tot en metdiv#div-30
. In bepaalde situaties moet het script de focus aan die <div> kunnen geven. Daarom heeft elk van die <div>'s een tabindex gekregen: <div id="div-1" tabindex="0">. De tabindex="0" maakt het mogelijk om de <div>'s met behulp van JavaScript focus te geven. (Bovendien kunnen gebruikers van de Tab-toets hierdoor ook de grote afbeeldingen zichtbaar maken.) - Thumbnails en afbeeldingen voor kleinere browservensters zitten in een <picture>. In het voorbeeld zijn dit
picture#picture-1
tot en metpicture#picture-30
. In bepaalde situaties moet het script de focus aan die <picture> kunnen geven. Daarom heeft elk van die <picture>'s een tabindex gekregen: <picture id="picture-1" tabindex="-1">. De tabindex="-1" maakt het mogelijk om de <picture>'s met behulp van JavaScript focus te geven, zonder dat dit gebruikers van de Tab-toets hindert.
De voorvoegsels -moz- en -webkit-
Voordat een nieuwe css-eigenschap wordt ingevoerd, is er in de regel een experimentele fase. Browsers passen het dan al toe, maar met een aangepaste naam. Tijdens deze fase kunnen problemen worden opgelost en worden veldslagen uitgevochten, over hoe de standaard precies moet worden toegepast.
Als iedereen het overal over eens is en alle problemen zijn opgelost, wordt de officiële naam uit de standaard gebruikt.
De belangrijkste browsers hebben elk een eigen voorvoegsel:
Firefox: -moz-
, naar de maker: Mozilla.
Op webkit gebaseerde browsers, zoals Edge, Google Chrome, Opera, Safari en Android browser: -webkit-
.
(Google Chrome is van webkit overgestapt op een eigen weergave-machine: Blink. Blink gaat geen voorvoegsels gebruiken. Het is echter een aftakking van webkit, dus het zal nog wel even duren voor -webkit-
hier helemaal uit is verdwenen. Ook Opera en Edge gebruiken Blink.)
In oudere code kun je ook nog de voorvoegsels -o-
(Opera) en -ms-
(Microsoft Internet Explorer) tegenkomen.
Zodra de experimentele fase voorbij is, wordt het voorvoegsel weggelaten. Omdat dat moment niet bij alle browsers hetzelfde is, zet je nu ook al de officiële naam erbij. Deze wordt als laatste opgegeven. Bijvoorbeeld Safari herkent -webkit-appearance
. Zodra Safari appearance
gaat herkennen, zal dit -webkit-appearance
overrulen, omdat het er later in staat. Dat ze er beide in staan, is dus geen enkel probleem.
In dit voorbeeld worden image-set()
, -webkit-appearance
en ‑webkit-overflow-scrolling
gebruikt.
image-set()
Op dit moment moet je nog het volgende schrijven:
{-webkit-image-set(...); image-set(...);}
In de toekomst kun je volstaan met:
{image-set:(...);}
-webkit-appearance en -webkit-overflow-scrolling
Deze twee eigenschappen worden hier alleen maar gebruikt, om problemen op iOS en iPadOS op te lossen.
-webkit-overflow-scrolling
wordt alleen maar gebruikt om een probleem op iOS ouder dan versie 13 op te lossen. Alleen browsers op iOS ouder dan versie 13 ondersteunen deze eigenschap. Alle andere browsers en systemen (inclusief browsers op iPadOS) negeren dit, omdat ze de eigenschap domweg niet kennen. Daarom is in dit geval alleen de naam met -webkit-
voldoende:
{-webkit-overflow-scrolling: ...;}
-webkit-appearance
wordt hier alleen gebruikt om css te selecteren, die alleen door Safari en door browsers op iOS en iPadOS moet worden gebruikt. In dit geval hoeft daarom de standaardeigenschap appearance
niet gebruikt te worden.
Maar omdat in het verleden nogal wat aartsluie sitebouwers alleen -webkit-appearance
gebruikten, zonder daarachter appearance
, zijn sommige browsers ook -webkit-appearance
gaan ondersteunen. Daarom wordt -webkit-appearance
gebruikt in combinatie met de waarde apple-pay-button
. Die waarde wordt alleen door Safari en browsers op iOS en iPadOS ondersteund, waardoor alle andere browsers -webkit-appearance
alsnog negeren.
(In het algemeen is het een bijzonder slechte gewoonte om van een eigenschap alleen voor één bepaald systeem of browser 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 problemen op iOS en iPadOS en in Safari 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, en omdat ook Google Chrome in het verleden met -webkit-
werkte. Inmiddels is dat niet meer zo, maar deze gewoonte bestaat nog steeds. Waardoor 'n site alleen in op webkit georiënteerde browsers goed is te bekijken.
Dit is zo'n groot probleem dat andere browsers soms de variant met -webkit-
ook maar zijn gaan implementeren, naast de standaard. Want als 'n site het niet goed doet in 'n bepaalde browser, krijgt in de regel niet de site maar de browser de schuld.
Vanwege alle problemen met 'vendor prefixes' worden deze niet meer gebruikt. Nieuwe, experimentele css-eigenschappen zitten inmiddels achter een zogenaamde vlag: de gebruiker moet iets veranderen in de instellingen, waarna de eigenschap gebruikt (en getest) kan worden. Als alles werkt, zoals het hoort te werken, schakelt de browsermaker de vlag standaard in.
Voorlopig zijn we echter nog niet van deze voorvoegsels af. Als je ze gebruikt, gebruik dan álle varianten, en eindig met de variant zonder voorvoegsel, zoals die uiteindelijk ooit gebruikt gaat worden. Als je alleen de -webkit-
variant gebruikt, ben je in feite 'n onbetaalde reclamemaker voor Apple. (Dit geldt dus niet voor het hierboven genoemde -webkit-appearance
en -webkit-overflow-scrolling
, want die worden alleen gebruikt om een probleem 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 alleen <main> en <section> gebruikt. <main> en <section> gedragen zich als een gewone <div>, maar dan een <div> met een semantische betekenis. Hierdoor kunnen schermlezers, zoekmachines, en dergelijke beter zien, hoe de pagina is samengesteld. De meeste schermlezers kunnen dit soort elementen ook gebruiken om snel over de pagina te navigeren.
<main>
Hierbinnen staat de belangrijkste inhoud van de pagina (in dit voorbeeld is dat de hele pagina).
<section>
Min of meer samenhangend deel van een pagina, deel van een tekst, en dergelijke. In dit voorbeeld zitten de slideshow en de eronder staande tekst elk in een eigen <section>.
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.
WAI-ARIA-codes
WAI-ARIA wordt vaak ingekort tot ARIA. Voluit betekent het Web Accessibility Initiative - Accessible Rich Internet Applications.
Er worden in dit voorbeeld twee WAI-ARIA-codes gebruikt: aria-hidden
en aria-label
.
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 browservensters minimaal 760 px breed en minimaal 530 px hoog staan bij elke thumbnail vier knoppen, waarmee naar de volgende of vorige thumbnail of grote afbeelding kan worden gegaan:
<a class="vorige-picture" href="#picture-30" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-picture" href="#picture-2" tabindex="-1" aria-hidden="true">→</a>
<a class="vorige-div" href="#div-30" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-div" href="#div-2" tabindex="-1" aria-hidden="true">→</a>
Deze knoppen zijn in werkelijkheid gewone links. Met behulp van aria-hidden="true"
worden ze verborgen voor schermlezers.
Schermlezers kunnen van <img> naar <img> gaan, naar het volgende of vorige element waar iets in zit, de volgende of vorige kopregel, enzovoort. Als je deze vier links niet zou verbergen, zou een schermlezer tussen elke twee thumbnails vier keer uitgebreid een link aankondigen. Die geen enkel nut heeft voor een schermlezer. In plaats van rechtstreeks naar de volgende <img> te gaan met één beweging of toetsaanslag, zijn vijf bewegingen of toetsaanslagen nodig. Daarom worden de vier links verborgen.
Onder de bovenstaande vier links staat de naam van de afbeelding:
<span class="naam" aria-hidden="true">Aardbeien</span>
Ook deze naam wordt voor schermlezers verborgen.
In elke <picture> zit als laatste onderdeel een gewone <img>. Bij die <img> staat in de alt-tekst de naam van de afbeelding, en die naam wordt al voorgelezen. Als de naam in de <span> niet verborgen zou worden, zou de naam twee keer worden voorgelezen.
(In TalkBack op Android zit een of andere vreemde bug, waardoor deze de naam toch twee keer voorleest. Meer daarover bij Probleem: TalkBack op Android leest bij elke afbeelding de naam twee keer voor.)
In browservensters minimaal 730 px hoog en minimaal 530 px breed staat linksboven een <span> met een vraagteken:
<span id="vraagteken" aria-hidden="true">?</span>
Door aanraken of -klikken van het span#vraagteken
, wordt de hulptekst zichtbaar. (In werkelijkheid reageert niet de <span> op de aanraking of klik, maar div#uitleg
, de ouder van de <span>, maar voor het oog is het de <span> met het vraagteken.)
Met behulp van aria-hidden="true"
wordt de <span>, en daarmee het vraagteken, verborgen voor schermlezers. Als dat niet zou gebeuren, zou een schermlezer iets als 'vraagteken' voorlezen, terwijl volstrekt onduidelijk is, waarom dat opeens wordt voorgelezen.
Voor schermlezers is het ook helemaal niet nodig dat de hulp zichtbaar wordt. div#hulptekst
met de hulptekst staat weliswaar ver buiten het scherm, maar een schermlezer leest het gewoon voor, ook al zie je de tekst niet.
aria-label
Gelijk aan het begin van de tekst staat een link:
<a id="voor-sr" href="#begin-tekst" aria-label="Mogelijk is er nog een grote afbeelding geopend. Druk op Enter of dubbeltik op het scherm om deze te sluiten."></a>
In sommige schermlezers blijft een grote afbeelding geopend, als de schermlezer naar de onder de slideshow staande tekst gaat. De tekst wordt dan verborgen door de grote afbeelding.
Daarom wordt gelijk bovenaan de tekst een link gezet, die alleen wordt voorgelezen, als een grote afbeelding is geopend of geopend is geweest (dit ligt 'n beetje aan de gebruikte schermlezer). Door de link te volgen wordt de focus op het begin van de tekst gezet, waardoor een eventueel geopende afbeelding wordt gesloten.
Voor gebruikers van een muis, een touchscreen of de Tab-toets heeft deze link geen enkel nut. Gebruikers van muis of touchscreen kunnen gewoon het scherm aanraken of – klikken, en bij gebruik van de Tab-toets wordt een grote afbeelding automatisch gesloten, als de tekst wordt bereikt.
Daarom is deze link onzichtbaar. Schermlezers zullen echter de in aria-label
staande tekst gewoon voorlezen, zodat ook gebruikers van een schermlezer een eventueel nog openstaande grote afbeelding kunnen sluiten.
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 een element, dat normaal genomen door het gebruik van de Tab-toets de focus kan krijgen, volledig wordt genegeerd door de Tab-toets. Zelfs een link met een negatieve tabindex wordt volledig genegeerd.
Ook kan aan een element met een negatieve tabindex dat normaal genomen geen focus kan krijgen, toch de focus worden gegeven met behulp van JavaScript, een klik of een aanraking. Waarbij gebruikers van de Tab-toets daar geen last van hebben, omdat tabindex="-1"
wordt genegeerd door de Tab-toets.
In dit voorbeeld wordt tabindex="-1"
op een aantal plaatsen gebruikt.
Een grote afbeelding wordt onder andere geopend door het geven van de focus aan bepaalde elementen. Om de grote afbeelding weer te sluiten, moet die focus weer worden verwijderd of verplaatst. Het zou handig zijn, als dat ook zou kunnen door gewoon het scherm aan te raken of aan te klikken buiten de grote afbeelding.
Dat werkt overal, behalve op iOS en iPadOS. Daarop blijft bij aanraken van het scherm de focus gewoon staan, waar die staat. Waardoor de grote afbeelding geopend blijft. Daarom is de hele pagina gevoelig gemaakt voor een aanraking:
<html lang="nl" tabindex="-1" class="no-js">
Omdat <html> per definitie het hele browservenster vult, reageert nu het hele venster op een aanraking. Ook op iOS en iPadOS sluit een grote afbeelding nu, als het scherm ergens buiten de grote afbeelding wordt aangeraakt. Omdat de tabindex de waarde '-1' heeft, negeert de Tab-toets hem. Gebruikers van de Tab-toets hoeven dus niet extra te tabben.
Als de thumbs zijn gescrold, zetten bij heropenen van de pagina Safari en Firefox op OS X de eerste thumbnail niet helemaal links. De thumbs blijven gewoon staan, waar ze staan. Dit wordt met behulp van JavaScript opgelost. Hiervoor is het onder andere nodig dat <main> de focus kan krijgen. Wat normaal genomen niet kan, want <main> is geen link, <button>, of zoiets. Daarom krijgt <main> een tabindex:
<main tabindex="-1">
Nu kan het JavaScript toch de focus geven aan <main>. Omdat de tabindex de waarde '-1' heeft, negeert de Tab-toets hem. Gebruikers van de Tab-toets hoeven dus niet extra te tabben.
Als de Tab-toets wordt gebruikt, wordt bij elke Tab een thumbnail gemarkeerd en wordt de bijbehorende grote afbeelding geopend. Maar tussen elke thumbnail zitten vier knoppen om naar de vorige of volgende thumbnail of grote afbeelding te gaan:
<a class="vorige-picture" href="#picture-2" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-picture" href="#picture-4" tabindex="-1" aria-hidden="true">→</a>
<a class="vorige-div" href="#div-2" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-div" href="#div-4" tabindex="-1" aria-hidden="true">→</a>
Omdat die knoppen eigenlijk gewone links zijn, worden die normaal genomen ook bezocht bij gebruik van de Tab-toets. Daarom moet je vijf keer op Tab drukken om een nieuwe grote afbeelding te openen. Door aan de vier links tabindex="-1"
toe te voegen, worden ze genegeerd door de Tab-toets en volstaat één Tab om een nieuwe grote afbeelding te tonen.
Hetzelfde verhaal geldt, maar dan maar op één plaats aan het begin van de slideshow, voor de links naar de eerste en laatste thumbnail:
<a class="eerste" href="#picture-1" tabindex="-1" aria-hidden="true">|←</a>
<a class="laatste" href="#picture-30" tabindex="-1" aria-hidden="true">→|</a>
Als een grote afbeelding wordt gesloten, blijft de bijbehorende thumbnail geselecteerd. Ook als de hulp wordt gesloten, blijft de geselecteerde thumbnail geselecteerd. Hierdoor hoef je niet weer van voren af aan door alle thumbnails heen te lopen, als je 'n grote afbeelding sluit. Om dit voor elkaar te krijgen, wordt met behulp van JavaScript op verschillende momenten opgeslagen, welk element de focus heeft.
Je kunt onder andere door de thumbnails lopen met behulp van de zes knoppen rechtsboven. Twee van die knoppen linken naar de vorige of volgende <picture>. Dat werkt op zich goed, maar een <picture> kan niet in alle browsers een 'echte' focus krijgen, ook niet als ernaartoe wordt gelinkt. Daardoor kan JavaScript niet constateren, dan een <picture> de focus heeft. En kan dat dus ook niet worden opgeslagen.
Door het toevoegen van een tabindex lukt dat wel:
<picture id="picture-1" tabindex="-1">
Omdat de tabindex de waarde '-1' heeft, negeert de Tab-toets hem. Gebruikers van de Tab-toets hoeven dus niet extra te tabben.
In sommige schermlezers blijft een grote afbeelding geopend, als de onder de slideshow staande tekst wordt bereikt. Daarom wordt bovenaan de tekst met behulp van een link de mogelijkheid gegeven de grote afbeeldingen te sluiten. Als de link wordt gevolgd, krijgt de <h2> bovenaan de tekst de focus, waardoor een eventueel nog openstaande grote afbeelding wordt gesloten.
Die link gaat naar de <h2> aan het begin van de tekst:
<h2 id="begin-tekst" tabindex="-1">Opvultekst</h2>
<a id="voor-sr" href="#begin-tekst" (...) "></a>
De <h2> krijgt niet in alle schermlezers de focus, als link a#voor-sr
wordt gevolgd. Wel wordt altijd de grote afbeelding gesloten, maar soms gaat de schermlezer terug naar het begin van de pagina. Door een tabindex aan de <h2> te geven, gaan alle schermlezers naar de <h2> aan het begin van de tekst.
Omdat de tabindex de waarde '-1' heeft, negeert de Tab-toets hem. Gebruikers van de Tab-toets hoeven dus niet extra te tabben.
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 browser mag doen wat hij wil. 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.
(In de praktijk blijkt trouwens dat alle browsers de volgorde van de html aanhouden bij tabindex="0"
, nadat eerst alle tabindexen met een positief nummer zijn bezocht.)
In dit voorbeeld wordt tabindex="0"
op vier plaatsen gebruikt.
Het vraagteken en de bijbehorende uitleg staan samen in een <div>. Met behulp van :focus-within
wordt gekeken, of deze <div> of een van de nakomelingen ervan de focus heeft. Als dat zo is, wordt de hulp getoond.
Een <div> kan echter normaal genomen geen focus krijgen. Daarom krijgt de <div> een tabindex:
<div id="uitleg" tabindex="0">
Als de <div> nu wordt aangeraakt of -geklikt, krijgt deze de focus en kan de hulp worden getoond. Omdat als waarde '0' is gebruikt, wordt de hulp ook getoond, als de Tab-toets wordt ingedrukt.
De tekst in de uitleg staat zelf ook weer in een <div>. De uitleg is te lang om zonder scrollen te tonen. Als je echter de uitleg aanraakt om te scrollen, verliest div#uitleg
in sommige browsers de focus, waardoor de uitleg wordt gesloten. 'n Beetje sadistisch: je denkt, goh, wat 'n spannend verhaal, Agatha Christie is er niets bij, snel verder lezen om te zien hoe het afloopt, en floep: de ontknoping verdwijnt exact op het moment dat je naar beneden wilt scrollen. Daarom krijgt ook de <div> met de hulptekst een tabindex:
<div id="hulptekst" tabindex="0">
Nu krijgt de <div> met de hulptekst bij aanraken of -klikken gewoon de focus. div#hulptekst
is een kind van div#uitleg
. Met behulp van :focus-within
wordt de hulp getoond, als div#uitleg
of een van de nakomelingen ervan de focus heeft. Dat is hier het geval, dus de hulp wordt nu niet meer gesloten bij aanraken of -klikken.
Gebruikers van de Tab-toets kunnen ook niet de hele hulptekst lezen, omdat deze gewoon te lang is. Maar ook de Tab-toets zal deze <div> nu bezoeken, omdat als waarde bij de tabindex '0' is gebruikt. En vervolgens kan de hulp gewoon met behulp van de pijltjes worden gescrold.
(En je kunt nu eventueel ook de hulptekst kopiëren met behulp van selecteren en rechtsklikken. Bij selecteren zal de hulptekst ook de focus krijgen en netjes openblijven als tekst wordt gekopieerd. Deze hulptekst zal alleen iemand met een uitgegroeide tekstverzameldwangneurose willen kopiëren, maar je kunt je voorstellen dat een tekst wél interessant is.)
Elke thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke, zit in een <div>: div#nr-1
tot en met div#nr30
. Als een <div> de focus krijgt, wordt de bijbehorende grote afbeelding getoond. Maar een <div> kan normaal genomen geen focus krijgen. Daarom wordt een tabindex toegevoegd:
<div id="div-1" tabindex="0">
Nu kan de <div> de focus krijgen, waardoor de bijbehorende grote afbeelding kan worden getoond. Deze focus kan worden gekregen door de <div> aan te raken of aan te klikken, of doordat de knop (eigenlijk de link) naar de volgende of vorige grote afbeelding wordt gevolgd.
Omdat als waarde bij de tabindex '0' is gebruikt, kan de <div> ook de focus krijgen bij gebruik van de Tab-toets. Als dat gebeurt, opent de bijbehorende grote afbeelding.
De tekst onder de slideshow staat in een <section>, die een tabindex heeft:
<section tabindex="0">
Hierdoor kan de <section> de focus krijgen. Als dat gebeurt, krijgt de tekst een blauw kader, zodat gebruikers van de Tab-toets weten dat ze nu de tekst hebben bereikt. En dat ze eventueel kunnen scrollen door gebruik van de pijltjestoetsen.
tabindex="..."
Op de plaats van de puntjes moet een positief getal worden ingevuld: het volgnummer. Een element met een positieve tabindex wordt altijd bezocht bij gebruik van de Tab-toets, ook als dit element normaal genomen zou worden genegeerd. Elementen met een tabindex met een positief volgnummer worden altijd als eerste bezocht, voor elementen als een link of tekstveld zonder tabindex, en ook voor elementen met tabindex="0"
.
In dit voorbeeld wordt een positieve tabindex niet gebruikt.
Muis, toetsenbord, touchpad en touchscreen
Vroeger, toen het leven nog mooi was en alles beter, waren er alleen monitors. Omdat kinderen daar niet af konden blijven met hun tengels, besloten fabrikanten dan maar touchscreens te gaan maken, omdat je die mag aanraken. Het bleek makkelijker te zijn om volwassenen ook te leren hoe je 'n scherm vies maakt, dan om kinderen te leren met hun vingers van de monitor af te blijven.
Zo ontstonden touchscreens en kreeg het begrip ellende een geheel nieuwe lading. In de perfecte wereld van vroeger, waarin alleen desktops bestonden, werkten dingen als hoveren, klikken en slepen gewoon. Zonder dat je eerst 'n cursus hogere magie op Zweinstein hoefde te volgen. Zelfs in JavaScript was het nog wel te behappen, ook voor mensen zoals ik die toevallig niet Einstein heten.
Op dit moment kun je computerschermen ruwweg in twee soorten indelen: schermen die worden aangeraakt, en schermen die worden bediend met hulpmiddelen als een toetsenbord, muis of touchpad. Omdat ook computerschermen zich kennelijk vermengen, bestaan er inmiddels ook schermen die zowel van aanraken als van muizen houden.
Hieronder staat een lijstje met dingen die zijn aangepast voor de verschillende soorten schermen, zodat dit voorbeeld overal werkt. Een touchpad werkt ongeveer hetzelfde als een muis. Als hieronder iets over een muis staat, geldt dat ook voor een touchpad.
:hover
Omdat :hover
mogelijk niet werkt, als css uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :hover
tonen.
Je hovert over een element, als je met behulp van muis of touchpad de cursor boven dat element brengt. Hoveren kan over álle elementen. Het wordt veel gebruikt om iets van uiterlijk te laten veranderen, een pop-up te laten verschijnen, en dergelijke.
Op een touchscreen wordt hoveren vaak hetzelfde afgehandeld als een aanraking.
Bij gebruik van een muis is er een verschil tussen hoveren en klikken, maar op een touchscreen is dat verschil er niet: je raakt een touchscreen aan of niet. Dat levert vooral soms problemen op, als bij een element :hover
én klikken worden gebruikt, zoals bij een link die bij hoveren erover verkleurt. Omdat :hover
niet wordt gebruikt in dit voorbeeld, spelen deze problemen niet.
:focus
Omdat :focus
mogelijk niet werkt, als css uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :focus
tonen.
De meeste mensen gaan met een muis naar een link, invoerveld, en dergelijke. Waarna ze vervolgens klikken om de link te volgen, tekst in te voeren, of wat ze ook maar willen doen. (Dit geldt uiteraard niet voor touchscreens.)
Er is echter 'n tweede manier om naar links, invoervelden, en dergelijke te gaan: met behulp van de Tab-toets kun je naar links, invoervelden, en dergelijke 'springen'. (Over het gebruik van de Tab-toets staat meer bij Tabindex en Tab-toets.)
Waar je staat, wordt door alle browsers aangegeven met een of ander kadertje. De link en dergelijke met het kadertje eromheen 'heeft focus'. Dat wil zeggen dat je die link volgt, als je op de Enter-toets drukt, of dat je in dat veld tekst kunt gaan invoeren, enzovoort.
Het kadertje dat de focus aangeeft, moet nooit zonder meer worden weggehaald. Gebruikers van de Tab-toets hebben dan geen idee meer, waar ze zijn. In dit voorbeeld is het kadertje aangepast: het wordt een brede witte of donkerblauwe rand rondom het element, wat actief is.
Normaal genomen kan een link bij gebruik van de Tab-toets de focus krijgen. De zes knoppen rechtsboven zijn eigenlijk links. Door het toevoegen van tabindex="‑1"
aan elke link wordt dit voorkomen.
De grote afbeeldingen worden getoond, als bepaalde elementen de focus hebben. Om ze weer te sluiten, kun je onder andere het scherm buiten de grote afbeelding aanraken of ‑klikken . Het element met de focus verliest de focus dan. Om dit te laten werken op iOS en iPadOS, moet <html> een tabindex="-1"
krijgen.
<main> kan normaal genomen geen focus krijgen. Om een probleem in Safari en Firefox op OS X op te lossen, heeft <main> een tabindex="-1"
gekregen. Hierdoor kan het JavaScript toch de focus geven aan <main>.
Als een grote afbeelding wordt gesloten, blijft de bijbehorende thumbnail met behulp van JavaScript gemarkeerd. Om dit te kunnen doen, moet soms <picture> de focus kunnen krijgen. Normaal genomen kan dat niet. Door het toevoegen van tabindex="-1"
kan het JavaScript toch de focus aan <picture> geven.
Een uitgebreider verhaal over het hoe en waarom van tabindex="-1"
bij deze elementen is te vinden bij tabindex="-1".
De hulptekst wordt getoond, als div#uitleg
of een van de nakomelingen ervan de focus heeft. Normaal genomen kan een <div> geen focus krijgen. Door het toevoegen van tabindex="0"
aan div#uitleg
en de daarin zittende div#hulptekst
kan dat hier wel.
Elke thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke zit in een <div>. Als een <div> de focus heeft, wordt de bijbehorende grote afbeelding getoond. Normaal genomen kan een <div> geen focus krijgen. Door het toevoegen van tabindex="0"
kan dat hier wel.
Een uitgebreider verhaal over het hoe en waarom van tabindex="0"
bij deze elementen is te vinden bij tabindex="0".
:active
Omdat :active
mogelijk niet werkt, als css uitstaat, ontbreekt of onvolledig is geïmplementeerd, moet je nooit belangrijke informatie alleen met behulp van :active
tonen.
Een element is actief, als de muis wordt ingedrukt boven dat element. Op sommige touchscreens is het element actief, als het wordt aangeraakt.
:active
wordt niet gebruikt in dit voorbeeld.
Resolutie en afbeeldingen
Het verhaal hieronder is een stuk simpeler, dan het in werkelijkheid is. Dat heeft twee redenen: het moet niet te ingewikkeld worden, en schrijver dezes is geen expert op het gebied van afbeeldingen. Dingen als (lossless) comprimeren, anti-aliasing en subpixels bijvoorbeeld, worden buiten beschouwing gelaten. Kennis daarvan is ook niet echt nodig voor dit voorbeeld.
In dit voorbeeld wordt 'browservenster' of 'venster van de browser' gebruikt. Veel mensen noemen dit bij een mobiel apparaat gewoon 'scherm'. Op een mobiel apparaat is het venster inderdaad meestal even groot als het scherm, maar op de desktop is dat vaak niet het geval. Hier wordt daarom altijd de term 'browservenster' of 'venster van de browser' gebruikt, als het venster wordt bedoeld. 'Scherm' of 'beeldscherm' wordt gebruikt, als het fysieke scherm (wat je aan kunt raken) wordt bedoeld.
De afbeeldingen die op websites worden gebruikt, zijn in twee grote groepen te verdelen: vector en bitmap. Bitmap wordt ook wel raster genoemd. Een veel gebruikt formaat van een vector-afbeelding is SVG. Veel gebruikte bitmap-formaten zijn jpg, gif en png. Elke van deze formaten heeft z'n eigen voor‑ en nadelen.
Bij een vector-afbeelding is er eigenlijk helemaal geen afbeelding. Er zijn alleen allerlei formules aanwezig, zoals 'trek een rode lijn van dit punt naar dat punt' of 'teken een cirkel met een doorsnede van zoveel, een rode achtergrondkleur en een zwarte rand'. Daarom zijn vector-afbeeldingen heel geschikt voor dingen als logo's, waarin vaak voornamelijk lijnen en vlakken worden gebruikt. Lijnen, vlakken, en dergelijke zijn goed met formules te beschrijven.
Voor foto's is een vector-afbeelding meestal niet het meest geschikt, omdat foto's meestal niet voornamelijk uit lijnen en vlakken bestaan. Een menselijk gezicht zou je kunnen beschrijven met formules, maar dan zou je waanzinnig veel formules nodig hebben om alle kleurnuances, rimpels, elke wenkbrauwhaar, noem maar op, te omschrijven.
Voor foto's is daarom een bitmap-afbeelding meestal geschikter. Een gif-afbeelding kan maar maximaal 256 kleuren bevatten en is daarom meestal ook niet geschikt voor een foto. Blijven over png en jpg. png heeft een aantal voordelen boven jpg, maar daardoor zijn de bestanden meestal (veel) groter dan jpg. En omdat elke afbeelding moet worden gedownload, wordt meestal jpg gebruikt.
Zo was de situatie tot voor kort. Inmiddels is er een nieuw formaat: WebP, dat inmiddels redelijk goed door browsers wordt ondersteund. WebP verenigt de voordelen van jpg en png in één formaat.
In dit voorbeeld worden jpg-bestanden gebruikt, zoals op de hele site. Omdat de afbeeldingen hier allemaal vrij klein zijn en het heel veel werk is om alles om te bouwen, zal dat ook niet zo snel gaan gebeuren. Maar als je iets nieuws maakt, kun je zeker overwegen WebP te gebruiken.
Vanaf nu wordt de term 'bitmap-bestanden' of kortweg 'bitmap' gebruikt, omdat het hele verhaal niet alleen voor jpg, maar voor alle bitmap-bestanden geldt, inclusief WebP.
Een vector-bestand kan uitstekend worden vergroot of verkleind. Dat is een van de grootste voordelen van zo'n bestand. Als 'n formule 'n cirkel van 100 px doorsnede laat verschijnen, hoef je alleen maar 100 in 200 te veranderen en de cirkel is, zonder enig kwaliteitsverlies, twee keer zo groot.
Een bitmap-afbeelding is fundamenteel anders opgebouwd. Hierdoor is het vrijwel onmogelijk een bitmap te vergroten, zonder dat dat tot (groot) kwaliteitsverlies leidt. Als een bitmap op een hogeresolutiescherm wordt weergegeven, is dat enigszins vergelijkbaar met wat er gebeurt, als een bitmap-afbeelding wordt vergroot. Vandaar dat in dit verhaal vergroten en hogeresolutieschermen enigszins door elkaar heen lopen.

Ook een bitmap-afbeelding is in feite helemaal geen echte afbeelding. Wat voor het oog mooie, in elkaar overlopende kleuren en soepele rondingen zijn, bestaat in feite helemaal niet. In werkelijkheid bestaat de afbeelding uit kleine blokjes, die alleen maar zo soepel en vloeiend zijn, omdat mensenogen makkelijk voor de gek zijn te houden.
Op de afbeelding hiernaast staat een wesp. Omdat op deze foto kleine details aanwezig, zijn, is deze uitstekend geschikt om bij dit verhaal als illustratie te gebruiken.
Hieronder staat een stukje van een detail van de bovenste vleugel van de wesp, vijftien keer vergroot. Dat mooie ronde adertje (of hoe dat ook heet bij 'n insect) blijkt opgebouwd te zijn uit 'n verzameling rotsblokken, waar Stonehenge nog iets van kan leren.
Maar als je de foto niet vergroot, ziet het er op een beeldscherm prima uit. Omdat mensenogen gewoon niet zo goed zijn. Mensen zien de afzonderlijke blokjes niet, als die maar klein genoeg zijn.
Omdat een bitmap is opgebouwd uit losse blokjes, is vergroten een probleem. Bij een vector-afbeelding bouwt de browser de afbeelding op aan de hand van de formules. Als de grootte verandert, verandert de formule en maakt de browser gewoon 'n andere, nieuwe afbeelding, zonder kwaliteitsverlies.
Bij een bitmap-afbeelding kan dit niet. Als je 'n bitmap-afbeelding vergroot, worden in feite de afzonderlijke blokjes vergroot. Als je maar ver genoeg vergroot, zoals op de afbeelding hierboven is gebeurd, ga je die blokjes zien.
De blokjes in een bitmap-afbeelding worden pixel genoemd, met als afkorting px. Elke pixel in een bitmap is opgebouwd uit drie kleuren: rood, groen en blauw. Elk van die drie kleuren kan 256 mogelijke waarden hebben: van volledig ontbrekend tot volledig aanwezig (knalrood bijvoorbeeld). Samen kunnen deze drie kleuren op 256 x 256 x 256 = 16.777.216 manieren worden gecombineerd. Dat levert 16.777.216 mogelijke kleuren op, ruim voldoende voor het menselijk oog. Van volledig wit (drie keer nul) tot volledig zwart (drie keer 255. 255, want een computer begint meestal bij nul te tellen).
Voor elke pixel in de afbeelding zijn drie bytes nodig: eentje voor elke kleur. Een afbeelding van 100 x 100 px heeft daardoor een bestandsgrootte van 3 x 100 x 100 = 30.000 byte, oftewel 30 kB (kilobyte). Soms is dat nog wat meer, omdat sommige soorten bitmap-afbeeldingen ook nog een waarde voor de doorzichtigheid van de pixel hebben.
Een afbeelding van 200 x 200 px, twee keer zo breed en hoog als een afbeelding van 100 x 100 px, heeft een bestandsgrootte van 3 x 200 x 200 = 120.000 byte, 120 kB. Dat is vier keer zoveel als de afbeelding van 100 x 100 px, want niet alleen de breedte verdubbelt, maar ook de hoogte.
(Als iemand dit nou gaat narekenen met twee afbeeldingen, dan blijkt bovenstaande berekening niet te kloppen. Door slimme compressiemethoden is een bestand uiteindelijk (veel) kleiner, dan je zou verwachten. Ook de inhoud van de afbeelding heeft invloed. Grote vlakken bijvoorbeeld zijn veel beter te comprimeren dan iets met veel krullen.)
Bitmap-afbeeldingen zijn opgebouwd uit pixels. Beeldschermen zijn op een enigszins gelijke manier opgebouwd uit 'schermpixels' (in het Engels 'device pixel'). En elke schermpixel is weer opgebouwd uit een soort subpixel voor rood, groen en blauw. Er bestaat een gigantische spraakverwarring over wat de juiste namen voor deze onderdelen zijn. De een noemt een subpixel een dot, en de ander noemt een dot een pixel. Gelukkig maakt dat voor het verhaal hier weinig uit.
De grootte van een beeldscherm, de resolutie, wordt normaal genomen in pixels opgegeven, net zoals bij een afbeelding. Een beeldscherm van 1280 x 1024 px is 1280 pixel breed en 1024 pixel hoog.
Een pixel kan groot of klein zijn. Van oudsher wordt de grootte van pixels, de resolutiedichtheid, uitgedrukt in ppi. (Vaak wordt foutief de eenheid dpi gebruikt. Die eenheid is voor printers.)
Tot niet zolang geleden had elke monitor een ppi van 96: 96 pixels per inch. Van oudsher wordt helaas 'inch' gebruikt. Voor de theorie maakt het niets uit, maar het is wat lastiger als je niet aan 'n inch gewend bent. Eén inch is ongeveer 2,5 centimeter.
Apple had een iets afwijkende ppi, maar omdat 96 ppi het vaakst voorkwam, is dat gebruikt om de grootte van de pixel in css, html, en dergelijke vast te stellen. Vanaf nu noem ik een beeldscherm met een ppi van 96 een 'standaard' beeldscherm.
1 px in css, html, en dergelijke is even groot als 1 ppi. In iets andere woorden: 1 px in css is even groot als 1 px op een standaard beeldscherm met een ppi van 96. De in de css, html, en dergelijke gebruikte pixel heet een 'css-pixel'. Als je 96 css-pixels naast elkaar zet, heb je een breedte van 1 inch. Net zoals op een standaard beeldscherm met 96 ppi: 96 pixels per inch.
Als je nu een bitmap op een beeldscherm gaat weergeven, komt in elke pixel op het beeldscherm één pixel van de afbeelding te staan. En omdat je jarenlang alleen maar beeldschermen had met 96 ppi, waren alle afbeeldingen tot niet zolang geleden hierop afgestemd. Als je 'n afbeelding 2 inch breed wilde weergeven, had de afbeelding in de breedte niet meer dan 192 pixels nodig, want er zaten in die 2 inch op het beeldscherm ook maar 192 pixels.
Omdat elke pixel in de afbeelding 3 of 4 byte aan ruimte inneemt (om de hoeveelheid rood, groen en blauw en eventueel de doorzichtigheid in op te slaan), is het van belang afbeeldingen met zo weinig mogelijk pixels te hebben. Als een afbeelding twee inch breed moet worden en er zitten in het beeldscherm maar 192 pixels in die twee inch, heeft het geen nut een afbeelding met een breedte van 384 pixel te sturen. Daar maak je alleen het bestand maar groter mee, en de browser moet de helft van de pixels weggooien, omdat er te weinig pixels in het scherm zitten om alle pixels uit de afbeelding te kunnen gebruiken.
Omdat menselijke ogen niet zo goed zijn, zien afbeeldingen die met 96 ppi werden weergegeven er best nog wel goed uit. Maar echt heel fijne details kun je er niet mee weergeven, omdat de pixels gewoon te groot zijn. Een heel dun lijntje is dunner dan zo'n pixel, dus dat lijntje wordt of helemaal niet, of met een dikte van 1 px en dus te dik weergegeven.
Maar goed, het werkte, mensen waren nog niet zo verwend en iedereen was gelukkig en tevreden en leefde nog lang en gelukkig.
Tot Apple het nodig vond om met een hogeresolutiescherm te komen: een scherm waarin de pixels dichter op elkaar staan dan 96 ppi. Apple noemt zo'n scherm 'Retina', maar dat is gewoon de merknaam van Apple voor een hogeresolutiescherm. Het verhaal hier geldt voor alle hogeresolutieschermen, welke naam de Afdeling Marketing er ook voor heeft bedacht.
Stel dat je een afbeelding hebt van 96 bij 96 px. Als die op een standaardscherm wordt weergegeven, wordt elke px van de afbeelding op een px van het scherm gezet. De afbeelding is op het scherm ook 96 bij 96 px. En omdat het scherm een resolutiedichtheid van 96 ppi heeft, 96 pixels per inch, is de afbeelding op het scherm 1 x 1 inch. Omdat de afbeelding voor die grootte is gemaakt, is de afbeelding goed te zien.
Bij een hogeresolutiescherm zitten de pixels dichter op elkaar dan bij een standaard beeldscherm met 96 ppi. Zelfs meer dan vier keer zo dicht op elkaar is inmiddels geen uitzondering meer. Dat betekent dat er soms meer dan 384 pixels in elke inch zitten. Die pixels zijn uiteraard veel kleiner dan de pixels van 96 ppi, want er moeten er nu 384 naast elkaar in diezelfde ene inch passen.
Een van de voor het testen gebruikte iPads heeft een scherm met een resolutie, een grootte, van 2048 x 1536 px. Als die een ppi van 96 zou hebben, zou die 2048 gedeeld door 96 is meer dan 21 inch breed zijn, dat is meer dan 'n halve meter. Dat is geen tablet meer, dat is een slagwapen. Die tablet heeft dan ook geen ppi van 96, maar van 264, bijna drie keer zo hoog als die van een standaard beeldscherm. De dots zijn bijna negen keer zo klein als die op een standaard beeldscherm, waardoor de tablet hanteerbaar blijft. (Negen keer: drie keer in de breedte en drie keer in de hoogte.)
Maar nu ontstaat een probleem met de afbeelding van 96 x 96 px. Als elke pixel van de iPad één pixel van de afbeelding zou krijgen, worden nog steeds 96 px gebruikt. Maar omdat de pixels op de tablet bijna negen keer zo klein zijn, is de afbeelding nu opeens bijna negen keer zo klein. Bij een resolutiedichtheid van 384 ppi, vier keer zo hoog als van een standaard beeldscherm, is de afbeelding zestien keer zo klein. De afbeelding was goed zichtbaar bij een breedte van 1 inch, maar op een breedte (en hoogte) van 'n halve of 'n kwart inch heb je 'n vergrootglas nodig om 'm nog goed te kunnen bekijken.
Als een hogeresolutiescherm zo zou werken, zou elke bestaande website onbruikbaar worden, omdat alles veel te klein zou worden. Althans: bitmaps.
Vector-afbeeldingen zijn relatief makkelijk te vergroten, omdat je gewoon de formules opnieuw kunt berekenen.
Hogeresolutieschermen passen dezelfde truc toe bij bitmap-afbeeldingen: ze vergroten de afbeelding. Daarbij wordt ervan uitgegaan dat 96 px uit de afbeelding 1 inch breed moeten worden, net zoals op het standaard beeldscherm het geval was. Als de afbeelding 96 px breed is, en dus op een standaard beeldscherm 1 inch breed wordt weergegeven, wordt die op de iPad met een ppi van 264 ook 1 inch breed. Alleen worden daarvoor 264 pixels van het beeldscherm gebruikt.
De 'css-pixel' is een min of meer vaste maat. De schermpixels van het beeldscherm hebben geen vaste maat. Bij een standaard beeldscherm van 96 ppi zijn de schermpixels even groot als de css-pixels. Maar bij de iPad van hierboven zijn de schermpixels bijna negen keer zo klein als een css-pixel (drie keer in de breedte en drie keer in de hoogte).
Veel mensen moeten erg wennen aan het verschil tussen css-pixels en schermpixels. Je zou het ook zo kunnen zien: css-pixels, de pixels die in css, html, en dergelijke worden gebruikt, zijn 'n soort vaste maat, net als een centimeter. Schermpixels zijn de pixels, waarvoor je hebt betaald. Het scherm is in css-pixels even groot als een standaard beeldscherm van dezelfde maat, maar hoe duurder het is, hoe meer schermpixels er meestal in zitten. (Als dat niet zo is, ben je mogelijk genept...)
Door het splitsen in css-pixels en schermpixels wordt voorkomen dat alle bitmaps onbruikbaar klein worden. Maar dit brengt weer een nieuw probleem met zich mee.
Bij 264 ppi zijn er bijna negen schermpixels voor elke pixel uit de afbeelding nodig. Voor acht van de negen schermpixels is er dus geen informatie over kleur en eventuele doorzichtigheid. De browser lost dit op door de ontbrekende pixels zelf in te vullen. Dat is voor een behoorlijk deel raadwerk.
Er zit wel énige intelligentie in dit toevoegen van pixels. Tot op zekere hoogte kan de browser 'raden' wat een kleur moet zijn en zelf missende pixels invullen, bijvoorbeeld bij een geleidelijke overgang tussen twee kleuren. Maar hier zijn grenzen aan. Gespecialiseerde grafische programma's kunnen dit meestal (veel) beter dan een algemeen programma als een browser, maar ook die zijn niet perfect als het om het vergroten van een bitmap-afbeelding gaat.
De enige afdoende oplossing is het sturen van een grotere afbeelding naar een hogeresolutiescherm. Een afbeelding die op de iPad met 264 ppi, 264 pixels per inch, 1 inch breed moet worden, moet 264 pixel breed zijn. En op een tablet met 384 ppi moet diezelfde afbeelding 384 px breed zijn. Dan is er voor elke schermpixel een eigen pixel uit de afbeelding beschikbaar.
Dit heeft nog een bijkomend voordeel. Als de schermpixels kleiner zijn, kunnen (veel) kleinere details worden weergegeven.

Op de afbeelding hiernaast is weer een stukje vleugel van de wesp te zien. Het enige verschil tussen de afbeeldingen is de afmeting. De linkerafbeelding is 200 px breed, de rechterafbeelding is 400 px breed, maar is versmald tot 200 px. Er zijn nog steeds 400 px aanwezig in de rechterafbeelding, maar ze zijn twee keer zo klein gemaakt. Met andere woorden 96 ppi is veranderd in 192 ppi.
(Dit verhaal klopt niet helemaal, omdat het verschil tussen beide afbeeldingen ook op niet-hogeresolutieschermen te zien moet zijn. Daarom zijn beide afbeelding niet precies even groot. Nou ja, kuch, ook mede omdat er aan schrijver dezes geen groot grafisch talent verloren is gegaan...)
Het verschil in kwaliteit is duidelijk zichtbaar: op de linkerafbeelding zijn de fijnere lijntjes binnen de vleugel stomweg afwezig. En wat niet aanwezig is, kan ook niet worden weergegeven door de browser, ook al doet deze nog zo haar best. Als het hogeresolutiescherm de linkerafbeelding zou weergeven, missen de lijntjes uit de vleugel daar ook. Een browser is geen entomoloog, dus die lijntjes kan de browser er niet zelf bij fantaseren.
Als je inzoomt (vergroot) tot 20000%, kun je nog veel duidelijker de verschillen tussen de details in de afbeeldingen zien. (Zoomen, niet alleen de lettergrootte veranderen.)
Voor een 'gewoon' beeldscherm is de kwaliteit van de linkerafbeelding goed genoeg, want dat beeldscherm is toch niet in staat de fijne lijntjes weer te geven. Daar zijn de pixels te grof voor. En omdat menselijke ogen zo makkelijk te misleiden zijn, ziet het er toch nog redelijk goed uit.
Een hogeresolutiescherm, met z'n (veel) kleinere schermpixels, kan echter wel de fijne lijntjes uit de vleugel weergeven. Als de rechterafbeelding naar een hogeresolutiescherm wordt gestuurd, worden de lijntjes in de vleugel wel weergegeven. Er zitten genoeg pixels, genoeg informatie, in de afbeelding.
Bijkomend voordeel is dat de afbeelding bij vergroten niet zo snel blokkerig wordt, omdat het langer duurt voor de afzonderlijke pixels te zien zijn.
(Op papier is je oog trouwens minder goed voor de gek te houden, daarom zijn voor een afdruk ruwweg vier keer zoveel pixel nodig voor een enigszins acceptabele kwaliteit, als op een standaard beeldscherm. Daardoor zijn veel afbeeldingen op internet eigenlijk niet af te drukken: ze bevatten gewoon te weinig pixels voor een kwalitatief acceptabele papieren afdruk.)
Dat is dus opgelost: gewoon een grote afbeelding met lekker veel pixels gebruiken.
Maar helaas, daar maak je je niet populair mee. Als je zeker wilt weten dat de bitmap op élk scherm goed wordt weergegeven, moet je een afbeelding gebruiken die geschikt is voor een hogeresolutie-breedbeeldscherm met een breedte van 2500px of zo. Maar die afbeelding wordt dan ook naar dat goedkope mobieltje met z'n schermpje van 320 px breed en 'n ppi van 120 gestuurd. Voor die afbeelding is gedownload, heeft de gebruiker van dat mobieltje z'n ochtendgymnastiek gedaan, ontbeten en de hond uitgelaten. En als de gebruiker voor de bandbreedte moet betalen, is er 'n reëel gevaar voor spontaan volksoproer.
Kortom: dat is geen oplossing.
In het verleden is deze methode trouwens wel redelijk vaak toegepast: stuur overal dezelfde grote afbeelding naartoe. Meestal was dat dan wel 'n soort compromis, waardoor het op 'n hogeresolutiescherm niet de beste kwaliteit was, maar nog wel te downloaden voor een smartphone. Maar ideaal was dit beslist niet, want met dit compromis had helemaal niemand meer echt de beste afbeelding.
Inmiddels wordt internet meer mobiel gebruikt dan via de desktop, met meestal (veel) kleinere schermen dan op de desktop. Mede daardoor is het volledig achterhaald om dit op 'op te lossen' door naar mobieltjes en dergelijke te grote afbeeldingen te sturen.
Ook werd in het verleden wel gekozen voor gruwelijk ingewikkelde oplossingen, zoals het laten herkennen van het apparaat door de server, en aan de hand daarvan de juiste afbeelding sturen. Los van de ingewikkeldheid daarvan ging dit ook regelmatig gewoon mis, omdat lang niet elke browser correcte informatie over naam, versie, enzovoort verstuurt. Bovendien is het bijhouden van gegevens als resolutiedichtheid, resolutie (grootte), enzovoort van duizenden apparaten ook nogal tijdrovend.
Grootte zeg je? Ja, dat is nog helemaal niet aan de orde gekomen.
Stel dat een afbeelding schermvullend moet worden weergeven op een scherm met een ppi van 96 en een breedte van 600 px. (We gaan er even vanuit dat het browservenster even breed is als het scherm). In een perfecte wereld zou je daar een afbeelding met een breedte van 600 px naar sturen. Als het scherm 1200 px breed is, zou je 'n afbeelding van 1200 px breedte sturen. Het heeft geen nut om die afbeelding van 1200 px naar het scherm van 600 px te sturen, want op dat scherm kan de helft van de pixels niet worden gebruikt.
Nou zijn browsers relatief goed in het verwijderen van overbodige pixels (ze kunnen ook vrij goed verkleinen), maar er wordt wel 'n (veel) te groot bestand naar het kleinere scherm gestuurd.
Eigenlijk is dat hetzelfde probleem als bij de resolutiedichtheid: er moet een afbeelding met voldoende pixels, maar niet te veel, worden gebruikt.
In css3 kun je met behulp van media query's de breedte, hoogte, en nog veel meer van het browservenster en/of het scherm opvragen. Afhankelijk van breedte, hoogte, enzovoort kan dan de css worden aangepast voor dat bepaalde venster en/of scherm.
Die mogelijkheid is er met html5 ook specifiek voor afbeeldingen gekomen. Maar de oplossingen hieronder werken alleen, als in de <head> van de pagina <meta name="viewport" ... staat. Als dat niet zo is, wordt voor elke pixel van de afbeelding één schermpixel gebruikt en is er een grote kans dat de afbeelding veel te groot of veel te klein wordt weergegeven. Of de browser probeert er het beste van te maken, waarbij 'het beste' vaak niet hetzelfde is als 'goed'.
Met behulp van het attribuut srcset
kan een aantal afbeeldingen worden opgegeven, waarvan alleen de grootte verschilt. Met het attribuut sizes
kan worden opgegeven, hoe breed de afbeelding moet worden weergegeven. Daarbij kun je die weergavebreedte afhankelijk maken van breedte, hoogte, resolutiedichtheid, en dergelijke van het browservenster en/of het scherm. Het attribuut sizes
wordt in dit voorbeeld niet gebruikt. De breedte van de afbeelding wordt hier geregeld met behulp van css.
In dit voorbeeld word in browservensters smaller dan 760 px en/of lager dan 530 px de grote afbeelding volledig weergegeven, zonder gebruik te maken van thumbnails. De grootte van de afbeelding wordt aangepast aan de grootte van het venster, en aan de resolutiedichtheid van het venster. Eén van de hiervoor gebruikte <source>'s:
<source media="(min-width: 360px)"
srcset="055-pics/ananas-700.jpg,
055-pics/ananas-1400.jpg 2x,
055-pics/ananas-2000.jpg 3x">
De drie afbeeldingen 'ananas-...jpg' hierboven worden gebruikt, als het browservenster minstens 360 px breed is: media="min-width: 360px)"
.
Verder staat achter de twee laatste afbeeldingen nog een waarde: '2x' en '3x'. De afbeelding 'ananas-1400.jpg' wordt gebruikt, als de resolutiedichtheid van het scherm minstens twee keer de oude standaardwaarde van 96 ppi is. De afbeelding 'ananas-2000.jpg' wordt gebruikt, als de resolutiedichtheid minstens drie keer de oude standaardwaarde heeft.
Nu heeft de browser voldoende informatie om een afbeelding te kiezen, die het best bij de grootte en resolutiedichtheid van scherm en browservenster past.
De drie afbeeldingen zijn precies hetzelfde, alleen is de eerste 700 px breed, de tweede 1400 px en de derde 2000 px. In principe wordt nu in een browservenster van bijvoorbeeld 500 px breed met een resolutiedichtheid van 294 ppi (drie keer de standaardwaarde) de afbeelding met een breedte van 2000 px gebruikt, en in een venster met de standaardwaarde de afbeelding met een breedte van 700 px.
Ook dingen als de snelheid van de verbinding kunnen nog meespelen. srcset
geeft alleen maar een hint aan de browser, de browser besluit uiteindelijk welke afbeelding wordt gebruikt.
In browservensters minstens 760 breed en minstens 530 px hoog is de grote afbeelding een background-image
, een achtergrond-afbeelding. Tot voor kort kon je daarin geen verschillende groottes voor verschillende schermen opgeven. Inmiddels kan dat wel met behulp van image-set()
.
Uiteindelijk moet image-set()
net zo gaan werken als srcset
, maar op dit moment is de ondersteuning nog wat beperkt. Alleen de 'x' voor de resolutiedichtheid wordt voldoende ondersteund. Daarom wordt in de css ook een media query gebruikt om de grootte van de afbeelding aan te passen aan de breedte van het browservenster. Hierdoor moet je, op dit moment, de hele riedel met image-set()
dus helaas nog twee keer herhalen. En in feite zelfs vier keer, want een aantal browsers gebruikt nog -webkit-image-set
. (Over het voorvoegsel -webkit-
is meer te vinden bij De voorvoegsels -moz- en -webkit-.)
Gegenereerde code
Het onderstaande geldt alleen voor desktopbrowsers. In browsers op mobiele systemen is het vaak ook mogelijk gegenereerde code te bekijken, maar dat is veel ingewikkelder. Bovendien verandert de manier, waarop dat kan, nogal snel.
Als je html schrijft, kan dat (hopelijk) in de browser worden weergegeven. Vanuit de browser kun je die html bekijken, precies zoals je hem hebt ingevoerd. Alsof je het in een editor ziet. In Firefox bijvoorbeeld kan dat door in het menu te kiezen voor Extra → Browserhulpmiddelen → Paginabron. (Of door de veel snellere sneltoets Ctrl+U.) Elke browser heeft dit soort mogelijkheden.
Wat je dan te zien krijgt, is exact de code, zoals jij die hebt ingetypt. Inclusief alle fouten, hoofd- en kleine letters, noem maar op. Als je zo slordig bent om een <p> niet af te sluiten, zit er niet plotsklaps een afsluitende </p> in de code. Als er css wordt gebruikt, html wordt ingevoegd via JavaScript, noem maar op, zie je daar niets van, want dat heb jij niet ingetypt.
Daar heb je dus eigenlijk vrij weinig aan, want die code kende je al. Die heb je zelf fluitend of met bloed, zweet en tranen zitten intypen.
Wat de browser daadwerkelijk gebruikt, is iets totaal anders: de gegenereerde code. En die is veel interessanter, want die code blijkt (fors) af te wijken, van wat jij heb ingetypt. De browser gebruikt een tijdelijke kopie van de door jou geschreven code, die zo is aangepast dat er voor de browser mee is te werken.
Elke browser heeft inmiddels mogelijkheden om de gegenereerde code te bekijken. In Firefox bijvoorbeeld in het menu Extra → Browserhulpmiddelen → Webontwikkelaarshulpmiddelen. In Google Chrome in het menu onder Meer hulpprogramma's → Hulpprogramma's voor ontwikkelaars. In Edge open je dit door op F12 te drukken, en het kan vast ook via het menu.
Houdt er wel rekening mee dat elke browser de door jou ingetypte code iets zal aanpassen. In Firefox bijvoorbeeld wordt een <P> veranderd in een <p>. Als er 'n </p> mist, is die opeens wel aanwezig in de gegenereerde code. Wat elke browser precies aanpast, zal iets verschillen en kan ook nog veranderen. Oudere versies van Internet Explorer bijvoorbeeld veranderden een <p> juist in een <P>, nieuwere versies deden dat niet meer.
Als je met behulp van JavaScript elementen invoegt of verwijdert in de HTML (zoals in dit voorbeeld gebeurt), zie je dat alleen in deze gegenereerde code.
De code aanpassen aan je eigen ontwerp
- Als je dit voorbeeld gaat aanpassen voor je eigen site, houd het dan in eerste instantie zo eenvoudig mogelijk. Ga vooral geen details invullen.
-
Gebruik geen FrontPage, Publisher of Word (alle drie van Microsoft). Publisher en Word zijn niet bedoeld om websites mee te maken. FrontPage is zwaar verouderd en wordt al jaren niet meer onderhouden door Microsoft.
Ook OpenOffice en LibreOffice leveren een uiterst beroerd soort html af. Tekstverwerkers met al hun toeters en bellen zijn gewoon niet geschikt om websites mee te bouwen.
Je kunt beter een goed (gratis) programma gebruiken. Links naar dat soort programma's vind je op de pagina met links onder Gereedschap → wysiwyg-editor.
Maar het allerbeste is om gewoon zelf html, css, enzovoort te leren, omdat zelfs het allerbeste programma het nog steeds zwaar verliest van 'n op de juiste manier met de hand gemaakte pagina.
-
Als je in een desktopbrowser met behulp van zoomen het beeld vergroot, heeft dit hetzelfde effect, als wanneer de pagina in een kleiner browservenster wordt getoond. Je kunt hiermee dus kleinere apparaten zoals een tablet of een smartphone simuleren. Maar het blijft natuurlijk wel een simulatie: het is nooit hetzelfde als testen op een écht apparaat. Zo kun je bijvoorbeeld aanrakingen alleen echt testen op een echt touchscreen.
Inmiddels hebben veel browsers in de ontwikkelgereedschappen mogelijkheden voor het simuleren van weergave op een kleiner scherm ingebouwd. Ook dit blijft een simulatie, maar geeft vaak wel een beter beeld dan zoomen.
-
Als je 'n site maakt in Firefox, Opera, Safari, Google Chrome of Edge, is er 'n hele grote kans dat hij in alle browsers werkt. Ik geef de voorkeur aan Firefox, omdat het de enige grote browser is die niet bij een bedrijf hoort dat vooral op je centen of je data uit is.
Google Chrome wordt ook door veel mensen gebruikt, maar ik heb dus wat moeite met hoe Google je hele surfgedrag, je schoenmaat en de kleur van je onderbroek vastlegt. Daarom gebruik ik Google Chrome zelf alleen om in te testen.
-
Het allereerste dat je moet invoeren, is het doctype, vóór welke andere code dan ook. Een lay-out met een missend of onvolledig doctype ziet er totaal anders uit dan een lay-out met een geldig doctype. Wát er anders is, verschilt ook nog 'ns tussen de diverse browsers. Als je klaar bent en dan nog 'ns 'n doctype gaat invoeren, weet je vrijwel zeker dat je van voren af aan kunt beginnen met de lay-out.
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.
-
Gebruik een 'strict' doctype of (beter!) het doctype voor html5. Deze zijn bedoeld voor nieuwe sites. Het transitional doctype is bedoeld voor al bestaande sites, niet voor nieuwe.
Het transitional doctype staat talloze tags toe, die in html5 zijn verboden. Deze tags worden al zo'n tien jaar afgeraden. Het transitional doctype is echt alleen bedoeld om de puinhoop van vroeger, toen niet volgens standaarden werd gewerkt, enigszins te herstellen.
Het strict doctype staat verouderde tags niet toe. Daardoor kan met 'n strict doctype, of het nu html of xhtml is, probleemloos worden overgestapt naar html5. Met een transitional doctype en het gebruik van afgekeurde tags kun je niet overstappen naar html5. Je moet dan eerst alle verouderde tags verwijderen, wat echt ontzettend veel werk kan zijn.
Het doctype voor html5 is uiterst simpel:
<!doctype html>
. Omdat het doctype voor html5 in alle browsers werkt, zelfs in de gelukkig vrijwel uitgestorven nachtmerrie Internet Explorer 6, is er geen enkele reden dit uiterst simpele doctype niet te gebruiken. - De eerste regel binnen de <head> moet de charset zijn. Dit vertelt de browser, welke tekenset er gebruikt moet worden, zodat letters met accenten en dergelijke overal goed worden weergegeven. Het beste kun je utf-8 nemen. Als je later van charset verandert, loop je 'n grote kans dat je alle aparte tekens als letters met accenten weer opnieuw moet gaan invoeren. In html5 is het simpele
<meta charset="utf-8">
voldoende. - Test vanaf het allereerste begin in zoveel mogelijk verschillende browsers in 'n aantal resoluties (schermgroottes). Onder het kopje Getest in kun je in deze uitleg vinden, waar dit voorbeeld in is getest.
- Voor alle voorbeelden geldt: breng veranderingen stapsgewijs aan. Als je bijvoorbeeld foto's wilt laten weergeven, begin dan met het alleen veranderen van de namen van de foto's, zodat je eigen foto's worden weergegeven. Maakt niet uit als de maten niet kloppen en de teksten fout zijn. Als dat werkt, ga dan bijvoorbeeld de maten aanpassen. Dan de teksten. En controleer steeds, of alles nog goed werkt.
-
Als het om een lay-out of iets dergelijks gaat: zorg eerst dat header, kolommen, footer, menu, en dergelijke staan en bewegen, zoals je wilt. Ga daarna pas details binnen die blokken invullen. In eerste instantie gebruik je dus bijvoorbeeld 'n leeg blok op de plaats, waar uiteindelijk het menu komt te staan.
Als je begint met allerlei details, is er 'n heel grote kans dat die de werking van de blokken gaan verstoren. Bouw eerst het huis, en ga dan pas de kamers inrichten. Zorg eerst dat de blokken werken, zoals je wilt. Dan zul je het daarna gelijk merken, als 'n toegevoegd detail als tekst of 'n afbeelding iets gaat verstoren. Daarvoor moet je natuurlijk wel regelmatig controleren in verschillende browsers, of alles nog wel goed werkt.
e kunt de blokken tijdens het aanpassen opvullen met bijvoorbeeld <br>1<br>2<br>3 enzovoort, tot ze de juiste hoogte hebben. Het is handig om aan het einde even iets toe te voegen als 'laatste', zodat je zeker weet dat er niet ongemerkt drie regels onderaan naar 't virtuele walhalla zijn verhuisd.
Om de breedte te vullen, kun je het best 'n kort woord als 'huis' duizend keer of zo herhalen. Ook hier is het handig om aan 't eind (en hier ook aan 't begin) 'n herkenningsteken te maken, zodat je zeker weet dat je de hele tekst ziet.
- Zolang je in grotere dingen zoals 'n lay-out aan 't wijzigen bent, kan het helpen de verschillende delen een achtergrondkleur te geven. Je ziet dan goed, waar 'n deel precies staat. Een achtergrondkleur heeft - anders dan bijvoorbeeld een border - verder geen invloed op de lay-out, dus die is hier heel geschikt voor.
- Als je eigenschappen verandert in de css, verander er dan maar één, hooguit twee tegelijk. Als je er zeventien tegelijk verandert, is de kans groot dat je niet meer weet, wat je hebt gedaan. En dat je 't dus niet meer terug kunt draaien.
-
margin
,padding
enborder
worden bij de hoogte en breedte van het element opgeteld. Hier worden vaak fouten mee gemaakt. Als je bijvoorbeeld in een lay-out 'n border toevoegt aan een van de 'hoofdvakken' (header, footer, kolommen), dan wordt deze er bij opgeteld. Bij 'n border van 2 px rondom de linkerkolom wordt deze dus plotseling 4 px breder (2 px aan beide kanten), en 4 px hoger. Zoiets kan je hele lay-out verstoren, omdat iets net te breed of te hoog wordt. Je moet dan elders iets 4 px kleiner maken. Dat zal vaak zo zijn: als je één maat verandert, zul je vaak ook 'n andere moeten aanpassen.Css geeft de mogelijkheid om met behulp van
box-sizing
de padding en border bínnen de breedte en hoogte van de inhoud te zetten, als je dat handiger vindt.Met nieuwere css-eigenschappen als grid en flexbox, die speciaal zijn gemaakt om een lay-out mee te maken, spelen dit soort problemen veel minder. In alle browsers waarop hier nog wordt getest, werken flexbox en grid prima. Maar als je oudere browsers moet ondersteunen, kan dat wel problemen opleveren en moet je ook in die oudere browsers testen.
-
In plaats van een absolute eenheid als
px
kun je ook een relatieve eenheid gebruiken, met nameem
enrem
. Voordeel vanem
enrem
is dat een lettergrootte, regelhoogte, en dergelijke inem
enrem
in alle browsers kan worden veranderd. Nadeel is dat het de lay-out sneller kan verstoren dan bijvoorbeeldpx
. Dit moet je gewoon van geval tot geval bekijken. Voor weergave in mobiele apparaten zijn relatieve eenheden alsem
enrem
vrijwel altijd beter dan absolute eenheden alspx
.(De minder bekende
rem
is ongeveer hetzelfde als deem
. Alleen is de lettergrootte bijrem
gebaseerd op de lettergrootte van het <html>-element, waardoor derem
overal op de pagina precies even groot is. Bij deem
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij derem
niet.)Zoomen kan trouwens altijd, ongeacht welke eenheid je gebruikt.
-
Valideren, valideren, valideren en dan voor 't slapen gaan nog 'ns valideren.
Valiwie???
Valideren is het controleren van je html en css op 'n hele serie fouten. Computers zijn daar vaak veel beter in dan mensen. Als je 300 keer <h2> hebt gebruikt en 299 keer </h2> vindt 'n computer die ene missende </h2> zonder enig probleem. Jij ook wel, maar daarna ben je misschien wel aan vakantie toe.
Valideren kan helpen om gekmakende fouten te vinden. Valide code garandeert ook dat de weergave in verschillende browsers (vrijwel) hetzelfde is. En valide code is over twintig jaar ook nog te bekijken.
Valideren moet trouwens ook niet worden overdreven. Het is een hulpmiddel om echte fouten te vinden, meer niet. Het gaat erom dat je site goed werkt, niet dat je het braafste kind van de klas bent. Als de code niet valideert, maar daar is een goede reden voor, is daar niets op tegen. Zeker met nieuwere html en css wil de validator nog wel eens achterlopen, terwijl dat al prima is te gebruiken.
Op deze site is alle css en html gevalideerd. Als de code niet helemaal valide is (wat regelmatig voorkomt), staat daar onder Bekende problemen (en oplossingen) de reden van.
Je kunt je css en html valideren als 't online staat, maar ook als het nog in je computer staat.
Html kun je valideren op: validator.w3.org/nu.
Css kun je valideren op: jigsaw.w3.org/css-validator.
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:
-
Gebruik altijd een alt-beschrijving bij een afbeelding. De alt-tekst wordt gebruikt, als afbeeldingen niet kunnen worden getoond of gezien (dat geldt dus ook voor zoekmachines). Als je iets wilt laten zien, als je over de afbeelding hovert, gebruik daar dan het title-attribuut voor, niet de alt-beschrijving.
Als een afbeelding alleen maar voor de sier wordt gebruikt, zet je daarbij
alt=""
, om aan te geven dat de afbeelding niet belangrijk is voor het begrijpen van de tekst of zo. -
Gebruik in links een tekst die duidelijk aangeeft, waar de link naartoe gaat. Een tekst als 'pagina met externe links' is waarschijnlijk duidelijk genoeg, een tekst als alleen 'links' waarschijnlijk niet. Een duidelijke zwart-witregel is niet te geven, omdat dit ook van tekst en dergelijke in de omgeving van de link afhangt.
Schermlezers kunnen een lijst van alle links in de pagina weergeven, en een duidelijke tekst is daarbij belangrijk. Alleen 'volgende' zegt niets, als dat in 'n lijst met alleen links staat.
-
Accesskeys (sneltoetsen) kun je beter niet gebruiken, deze geven te veel problemen, omdat ze vaak dubbelop zijn met sneltoetsen voor de browser of andere al gebruikte sneltoetsen. Bovendien is voor de gebruiker meestal niet duidelijk, welke toetsen het zijn.
Op zichzelf zijn accesskeys een heel goed idee. Maar helaas zijn ze ook in html5 volstrekt onvoldoende gedefinieerd. Er is nog steeds geen standaard voor de meest gebruikelijke accesskeys, zoals Zoek of Home.
Er is nog steeds niet vastgelegd, hoe accesskeys zichtbaar gemaakt kunnen worden. Voor de makers van browsers zou dit 'n relatief kleine moeite zijn, voor de makers van 'n site is het bergen extra werk.
Hierdoor zijn accesskeys (vrijwel) niet te gebruiken. Misschien kunnen ze nog enig nut hebben op sites, die gericht zijn op 'n specifieke groep gebruikers. Maar voor algemene sites is het advies: normaal genomen niet gebruiken.
-
Met behulp van de Tab-toets (of op 'n soortgelijke manier) kun je in de meeste browsers door links, invoervelden, en dergelijke lopen. Elke tab brengt je één link, invoerveld, en dergelijke verder, Shift+Tab één plaats terug. Met behulp van het attribuut
tabindex
kun je de volgorde aangeven, waarin de Tab-toets werkt. Zondertabindex
wordt de volgorde van de html aangehouden bij gebruik van de Tab-toets, maar soms is een andere volgorde logischer.In principe is het beter, als
tabindex
niet nodig is, maar gewoon de volgorde van de html wordt aangehouden. Bij verkeerd gebruik kantabindex
heel verwarrend zijn. Het is niet bedoeld om van de pagina een hindernisbaan voor kangoeroes te maken, waarop van beneden via links over rechts naar boven wordt gesprongen. (Meer over de Tab-toets is te vinden bij Tabindex en Tab-toets.) - Als, zoals hierboven beschreven, een gebruiker van de Tab-toets bij een link, invoerveld, en dergelijke is aangekomen, heeft dit element 'focus'. Dit wordt aangegeven door de link, invoerveld, en dergelijke extra te markeren met een kadertje. Dat kadertje mag je alleen weghalen, als op een andere manier wordt duidelijk gemaakt, welk element focus heeft. Een gebruiker van de Tab-toets kan anders niet zien, waar zij of hij zit, en welk element gaat reageren op bijvoorbeeld een Enter. (Meer over focus is te vinden bij focus.)
- In het verleden werd vaak aangeraden de volgorde van de code aan te passen. Een menu bijvoorbeeld kon in de html onderaan worden gezet, terwijl het op het scherm met behulp van css bovenaan werd gezet. Inmiddels zijn schermlezers en dergelijke zo sterk verbeterd dat dit niet meer wordt aangeraden. De volgorde in de html kan tegenwoordig beter hetzelfde zijn als die op het scherm, omdat het anders juist verwarrend kan werken.
-
Een zogenaamde skip-link is vaak nog wel zinvol. Dat is een link die je buiten het scherm parkeert met behulp van css, zodat hij normaal genomen niet te zien is. Zo'n link is wel gewoon zichtbaar in speciale programma's zoals tekstbrowsers en schermlezers, want die kijken gewoon naar wat er in de broncode staat.
(Alleen in de schermlezer TalkBack op oudere versies van Android werkt zo'n buiten het scherm geplaatste link niet. TalkBack leest zo'n link wel voor, maar de link kan niet worden gevolgd, als deze buiten het scherm staat. Met ingang van versie 8.1 van Android is dit eindelijk opgelost en werkt een skip-link ook fatsoenlijk in TalkBack.)
Een skip-link staat bovenaan de pagina, nog boven menu, header, en dergelijke, en linkt naar de eigenlijke inhoud van de pagina. Hierdoor kunnen mensen met één toetsaanslag naar de eigenlijke inhoud van de pagina gaan.
Een skip-link is vooral nuttig voor gebruikers van de Tab-toets. Zodra de normaal genomen onzichtbare link door het indrukken van de Tab-toets focus krijgt, kun je hem op het scherm plaatsen, waardoor hij zichtbaar wordt. Bij een volgende tab wordt hij dan weer buiten het scherm geplaatst en is dus niet meer zichtbaar, zodat de lay-out niet wordt verstoord.
Op pagina's en in voorbeelden waar dat nuttig is, wordt op deze site een skip-link gebruikt. (Althans: nog niet in alle voorbeelden die daarvoor in aanmerking komen, zit een skip-link. Maar geleidelijk aan worden dat er steeds meer.)
-
Van oorsprong is html een taal om wetenschappelijke documenten weer te geven, pas later is hij gebruikt voor lay-out. Maar daar is hij dus eigenlijk nooit voor bedoeld geweest. Het gebruiken van html voor lay-out leidt tot enorme problemen voor gehandicapten en tot een lage plaats in zoekmachines.
De html hoort alleen inhoud te bevatten, lay-out doe je met behulp van css. Die css moet in een externe stylesheet staan of, als hij alleen voor één bepaalde pagina van toepassing is, in de <head> van die pagina.
-
Breng een logische structuur aan in je document. Gebruik een <h1> voor de belangrijkste kop, een <h2> voor een subkop, enzovoort. Schermlezers en dergelijke kunnen van kop naar kop springen. En een zoekmachine gaat ervan uit dat <h1> belangrijke tekst bevat.
Dit geldt voor al dit soort structuurbepalende tags.
Als een <h1> te grote letters geeft, maak daar dan met behulp van je css 'n kleinere letter van, maar blijf die <h1> gewoon gebruiken. Op dezelfde manier kun je al dit soort dingen oplossen.
- <table> is fantastisch, maar alleen als die wordt gebruikt om een echte tabel weer te geven, niet als hij voor opmaak wordt misbruikt. In het verleden is dat op grote schaal gebeurd bij gebrek aan andere mogelijkheden. Een tabel is, als je niet heel erg goed oplet, volstrekt ontoegankelijk voor gehandicapten en zoekmachines. Het lezen van een tabel is ongeveer te vergelijken met het lezen van een krant van links naar rechts: niet per kolom, maar per regel. Dat gaat dus alleen maar goed bij een echte tabel, zoals een spreadsheet. In alle andere gevallen garandeert 'n tabel volstrekte ontoegankelijkheid voor schermlezers en dergelijke en als extra bonus vaak 'n lagere plaats in een zoekmachine.
-
Frames horen bij een volstrekt verouderde techniek, die heel veel nadelen met zich meebrengt. <iframe>'s hebben voor een deel dezelfde nadelen. Eén van die nadelen is dat de verschillende frames voor zoekmachines, schermlezers, en dergelijke als los zand aan elkaar hangen, omdat ze los van elkaar worden weergegeven. Ze staan wel naast elkaar op het scherm, maar er zit intern geen verband tussen.
Als je 'n stuk code vaker wilt gebruiken, zoals 'n menu dat op elke pagina hetzelfde is, voeg dat dan in met PHP. Dan wordt de pagina niet pas in de browser, maar al op de server samengesteld. Hierdoor zien zoekmachines, schermlezers, en dergelijke één pagina, net zoals wanneer je maar één pagina met html zou hebben geschreven.
(Je kunt ook invoegen met behulp van SSI. Maar tegenwoordig kun je beter PHP dan SSI gebruiken, omdat SSI min of meer aan het uitsterven is en PHP veel meer mogelijkheden heeft. Op deze site wordt in enkele voorbeelden nog SSI gebruikt, maar zodra die worden bijgewerkt, gaat dat vervangen worden door PHP.)
-
Geef de taal van het document aan, en bij woorden en dergelijke die afwijken van die taal de afwijkende taal met behulp van
lang="..."
. Op deze site gebeurt dat maar af en toe, omdat de tekst (en vooral de code) een mengsel is van Engels, Nederlands en eigengemaakte namen. Dat soort teksten is gewoon niet goed in te delen in een taal. Maar bij enigszins 'normale' teksten hoor je een taalwisseling aan te geven.Op deze site wordt de lijst op woordenlijst.org gebruikt om te bepalen, of een woord inmiddels 'Nederlands' is. Als het woord in deze lijst voorkomt, wordt geen
lang
-attribuut gebruikt, ook niet als het woord oorspronkelijk uit een andere taal komt. - Gebruik de tag <abbr> bij afkortingen. Doe dat de eerste keer op een pagina samen met de title-eigenschap:
<abbr title="ten opzichte van">t.o.v.</abbr>
. Daarna kun je op dezelfde pagina volstaan met<abbr>t.o.v.</abbr>
. Doe je dit niet, dan is er 'n grote kans dat 'n schermlezer 't.o.v.' uit gaat spreken als 'tof', en 'n zoekmachine kan er ook geen chocola van maken. - Geef een verandering niet alleen door kleur aan. Een grote groep mensen heeft moeite met het onderscheiden van kleuren en/of het herkennen van kleuren. Verander bijvoorbeeld een ronde rode knop niet in een ronde groene knop, maar in een vierkante groene knop. Door ook de vorm te veranderen, is het herkennen van de verandering niet alleen van een kleur afhankelijk.
- Zorg voor voldoende contrast tussen achtergrond- en voorgrondkleur, tussen
background-color
encolor
. Soms zie je heel lichtgrijze tekst op een donkergrijze achtergrond, en dan ook nog in een mini-formaat. Dat is dus voor heel veel mensen stomweg volledig onleesbaar. Op de pagina met links staat onder het kopje Toegankelijkheid → Contrast en kleurenblindheid een hele serie sites, waar je kunt controleren of het contrast groot genoeg is. -
De spider van 'n zoekmachine, schermlezers, en dergelijke kunnen geen plaatjes 'lezen'. Het is soms verbazingwekkend om te zien hoe veel, of eigenlijk: hoe weinig tekst er overblijft op een pagina, als de plaatjes worden weggehaald.
Op Linux kun je met Lynx kijken, hoe je pagina eruitziet zonder plaatjes en dergelijke, als echt alleen de tekst overblijft. Een installatie-programma voor Lynx op Windows is te vinden op invisible-island.net/lynx.
Ook kun je in Windows het gratis programma WebbIE installeren. WebbIE laat de pagina zien, zoals een tekstbrowser en dergelijke hem ziet. WebbIE is te downloaden vanaf www.webbie.org.uk.
Ten slotte kun je je pagina nog online op toegankelijkheid laten controleren op 'n behoorlijk aantal sites, zoals:
lowvision.support: laat zien hoe een kleurenblinde de site ziet. Engelstalig.
wave.webaim.org: deze laat grafisch zien, hoe de toegankelijkheid is. Engelstalig. Deze tester is ook als extensie in Firefox en Google Chrome te installeren.
Op de pagina met links kun je onder Toegankelijkheid links naar meer tests en dergelijke vinden.
Getest in
Laatst gecontroleerd op 2 december 2022.
Onder dit kopje staat alleen maar, hoe en waarin is getest. Alle eventuele problemen, ook die met betrekking tot zoomen, lettergroottes, toegankelijkheid, uit staan van JavaScript en/of css, enzovoort staan iets hieronder bij Bekende problemen (en oplossingen). Het is belangrijk dat deel te lezen, want uit een test kan ook prima blijken dat iets totaal niet werkt!
Dit voorbeeld is getest op de volgende systemen:
Desktopcomputers
Windows 7 (1280 x 1024 px, resolution 96 ppi):
Firefox, Google Chrome en Edge, in grotere en kleinere browservensters.
Linux (Kubuntu 20.04 LTS, 'Focal Fossa') (2560 x 1080 px, resolution: 96 ppi):
Firefox en Google Chrome, in grotere en kleinere browservensters.
Laptops
Windows 8.1 (1366 x 768 px, resolution: 135 ppi):
Bureaublad-versie: Firefox, Google Chrome en Edge, in grotere en kleinere browservensters.
Windows 10 (1600 x 900 px, resolution: 106 ppi):
Firefox, Google Chrome en Edge, in grotere en kleinere browservensters.
OS X 11.7.1 ('Big Sur') (1440 x 900 px, resolution: 96 ppi, device-pixel-ratio: 1):
Firefox, Safari, Google Chrome en Microsoft Edge, in grotere en kleinere browservensters.
Tablets
iPad met iOS 12.5.6 (2048 x 1536 px, device-pixel-ratio: 2:
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).
iPad met iOS 13.3 (gesimuleerd in Xcode):
Safari (portret en landschap).
iPad met iPadOS 16.1.1 (2160 x 1620 px, 264 ppi):
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).
Android 6.0 ('Marshmallow') (1920 x 1200 px, resolution: 224 ppi):
Samsung Internet, Firefox en Chrome (alle portret en landschap).
Android 8.1 ('Oreo') (1920 x 1200 px, resolution: 218 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Android 12 (2000 x 1200 px, resolution: 225 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Smartphones
iPhone 7 met iOS 12.4 (gesimuleerd in Xcode):
Safari (portret en landschap).
iPhone 8 met iOS 13.3 (gesimuleerd in Xcode):
Safari (portret en landschap).
iPhone met iOS 15.7.1 (1334 x 750 px, 326 ppi):
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).
Android 7.0 ('Nougat') (1280 x 720 px, resolution: 294 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Android 9.0 ('Pie') (1920 x 1080 px, resolution: 424 ppi):
Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Er is op de aan het begin van dit hoofdstukje genoemde controledatum getest in de meest recente versie van de browser, die op het betreffende besturingssysteem kon draaien. Het aantal geteste browsers en systemen is al tamelijk fors, en als ook nog rekening gehouden moet worden met (zwaar) verouderde browsers, is het gewoon niet meer te doen. Surfen met een verouderde browser is trouwens vragen om ellende, want updates van browsers hebben heel vaak met beveiligingsproblemen te maken.
In- en uitzoomen en - voor zover de browser dat kan - een kleinere en grotere letter zijn ook getest. Er is ingezoomd en vergroot tot zover de browser kan, maar niet verder dan 200%.
Er is getest met behulp van muis en toetsenbord, behalve op iOS, iPadOs en Android, waar een touchscreen is gebruikt. Op Windows 8.1 en 10 is getest met touchscreen, touchpad, toetsenbord, muis, en - waar dat zinvol was - op een combinatie daarvan. Op OS X 11.7.1 is getest met (een combinatie van) toetsenbord, touchpad en muis.
Als in een voorbeeld JavaScript is gebruikt, is ook getest of het werkt zonder JavaScript. Dat is alleen gedaan in de browsers, waarin in de instellingen JavaScript kan worden uitgeschakeld.
Ook is getest zonder css en - als afbeeldingen worden gebruikt - zonder afbeeldingen.
Schermlezers en dergelijke
Naast deze 'gewone' browsers is ook getest in Lynx, WebbIE, NVDA, TalkBack, VoiceOver 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 6.0, 7.0, 8.1, 9 en 12
VoiceOver is een in iOS en OS X ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS 12.5.6 en 15.7.1, iPadOS 16.1.1 en OS X 11.7.1.
Verteller (Narrator) is een in Windows 10 ingebouwde schermlezer. Er is getest in combinatie met Edge.
(Voor de bovenstaande programma's zijn links naar sites met uitleg en dergelijke te vinden op de pagina met links onder Toegankelijkheid → Schermlezers, tekstbrowsers, en dergelijke.)
Als het voorbeeld in deze programma's toegankelijk is, zou het in principe toegankelijk moeten zijn in alle aangepaste browsers en dergelijke. En dus ook voor zoekmachines, want een zoekmachine is redelijk vergelijkbaar met een blinde.
Eventuele problemen in schermlezers (en eventuele aanpassingen om die te voorkomen) staan iets hieronder bij Bekende problemen (en oplossingen).
Alleen op de hierboven genoemde systemen en browsers is getest. Er is dus niet getest op bijvoorbeeld 'n Blackberry. Er is een kans dat dit voorbeeld niet (volledig) werkt op niet-geteste systemen en apparaten. Om het wel (volledig) werkend te krijgen, zul je soms (kleine) wijzigingen en/of (kleine) aanvullingen moeten aanbrengen, bijvoorbeeld met JavaScript.
Er is ook geen enkele garantie dat iets werkt in een andere tablet of smartphone dan hierboven genoemd, omdat fabrikanten in principe de software kunnen veranderen. Dit is anders dan op de desktop, waar browsers altijd (vrijwel) hetzelfde werken, zelfs op verschillende besturingssystemen. Iets wat in Samsung Internet op Android werkt, zal in de regel overal werken in die browser, maar een garantie is er niet. De enige garantie is het daadwerkelijk testen op een fysiek apparaat. En aangezien er duizenden mobiele apparaten zijn, is daar geen beginnen aan.
De html is gevalideerd met de html-validator, de css met de css-validator van w3c. Als om een of andere reden niet volledig gevalideerd kon worden, wordt dat bij Bekende problemen (en oplossingen) vermeld.
Nieuwe browsers worden pas getest, als ze uit het bèta-stadium zijn. Anders is er 'n redelijke kans dat je tegen 'n bug zit te vechten, die voor de uiteindelijke versie nog gerepareerd wordt.
Dit voorbeeld is alleen getest in de hierboven met name genoemde browsers. Vragen over niet-geteste browsers kunnen niet worden beantwoord, en het melden van fouten in niet-geteste browsers heeft ook geen enkel nut. (Melden van fouten, problemen, enzovoort in wel geteste browsers: graag! Dat kan op het forum.)
Bekende problemen (en oplossingen)
Waarop en hoe is getest, kun je gelijk hierboven vinden bij Getest in.
Als je hieronder geen oplossing vindt voor een probleem dat met dit voorbeeld te maken heeft, kun je op het forum proberen een oplossing te vinden voor je probleem. Om forumspam te voorkomen, moet je je helaas wel registreren, voordat je op het forum een probleem kunt aankaarten.
Bij toegankelijkheid is er vaak geen goed onderscheid te maken tussen oplossing en probleem. Zonder (heel simpele) aanpassingen heb je vaak 'n probleem, en omgekeerd. Daarom staan wat betreft toegankelijkheid aanpassingen en problemen hier bij elkaar in dit hoofdstukje.
Voor zover van toepassing wordt eerst het ontbreken van JavaScript, css en/of afbeeldingen besproken. Vervolgens problemen en aanpassingen met betrekking tot toegankelijkheid voor specifieke groepen bezoekers, zoals zoomen en andere lettergrootte, Tab-toets, tekstbrowsers en schermlezers. Als laatste volgen de overige problemen in één of meer specifieke browsers.
Als in een onderdeel geen problemen aanwezig zijn, staat in een smal groen kadertje 'Geen problemen'. Bij een onderwerp over toegankelijkheid zijn er soms geen problemen, maar alleen aanpassingen. Ook in dat geval staat bovenaan in een smal groen kadertje 'Geen problemen'. Daaronder staan dan de aanpassingen.
Als in een onderdeel één of meer problemen worden besproken, staat van elk probleem in een breed rood kadertje een korte samenvatting daarvan.
Als bij het probleem een oplossing wordt gegeven, staat de samenvatting in een rode stippellijn. Bij een onderwerp over toegankelijkheid zijn er soms, naast de opgeloste problemen, ook aanpassingen. In dat geval staan staan die aanpassingen boven de kadertjes met opgeloste problemen.
Als bij het probleem geen oplossing is gevonden, staat de samenvatting in een rode ononderbroken lijn. Bij een onderwerp over toegankelijkheid zijn er soms, naast de problemen, ook aanpassingen. In dat geval staan staan die aanpassingen boven de kadertjes met problemen.
Zonder JavaScript
Probleem: in grotere vensters wordt bij herladen van de pagina de eerste thumbnail niet altijd vooraan gezet.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters worden geen thumbnails gebruikt.
Als de thumbnails zijn gescrold en de eerste thumbnail dus niet meer helemaal links staat, wordt bij herladen van de pagina de eerste thumbnail niet altijd weer helemaal links neergezet. Soms blijven de thumbnails staan, waar ze stonden. Soms worden echter wel de bij de eerste thumbnail horende knoppen getoond, wat nogal verwarrend is.
Wanneer dit gebeurt is afhankelijk van browser, besturingssysteem en of er eventueel al iets is aangeraakt of -geklikt. Een volledig overzicht geven wanneer dit gebeurt is onbegonnen werk.
Met behulp van JavaScript wordt bij herladen van de pagina het adres van de pagina aangepast, zodat de eerste thumbnail altijd helemaal links komt te staan.
Probleem: in grotere vensters wordt elk gebruik van een knop opgeslagen in de geschiedenis van de browser.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters worden geen knoppen gebruikt.
De zes knoppen rechtsboven zijn eigenlijk gewone links. Als je 'n knop indrukt, volg je in feite een gewone link. Die link wordt opgeslagen in de geschiedenis van de browser. Als je dertig keer een knop hebt gebruikt, moet je 31 keer op de Terug-knop van de browser drukken, om naar de vorige pagina terug te gaan. Of 31 keer op Alt+←.
Met behulp van JavaScript wordt voorkomen, dat het volgen van de links onder de knoppen wordt opgeslagen in de geschiedenis. De adressen van de links onder de knoppen en dergelijke zijn nog steeds gewoon zichtbaar, maar de Terug-knop van de browser negeert ze.
Probleem: in grotere vensters sluit Escape de grote afbeelding niet.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters zijn de grote afbeeldingen altijd zichtbaar.
Mensen zijn gewend om een pop-up en dergelijke te kunnen sluiten door het indrukken van Escape. Zonder JavaScript gaat dat niet. De grote afbeeldingen worden getoond met behulp van selectors als :focus
en :focus-within
, en daar heeft Escape geen invloed op.
Door te luisteren welke toets wordt ingedrukt, kan met behulp van JavaScript een grote afbeelding toch worden gesloten door het indrukken van Escape.
Probleem: in grotere vensters sluit Escape de hulp niet.
Hiervoor geldt precies hetzelfde als voor het sluiten van een grote afbeelding gelijk hierboven.
Probleem: als in grotere vensters een thumbnail is geselecteerd, opent Enter de bijbehorende grote afbeelding niet.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters zijn de grote afbeeldingen altijd zichtbaar.
Als met behulp van de eerste, vierde, vijfde of zesde knop door de thumbnails wordt gelopen, geeft een wit kadertje rondom de thumbnail aan, waar je bent. De bij de thumbnail horende naam wordt ook zichtbaar. Het lijkt dan logisch dat het indrukken van Enter de bijbehorende grote afbeelding opent, maar dat gebeurt niet. Dat kadertje geeft alleen maar aan, waar je bent, het heeft niets met een link of zo te maken.
Door te luisteren welke toets wordt ingedrukt, wordt met behulp van JavaScript toch een grote afbeelding geopend bij de thumbnail met het witte kadertje, als je op Enter drukt.
Probleem: als in grotere vensters een grote afbeelding wordt gesloten, wordt altijd teruggegaan naar de eerste thumbnail.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters zijn de grote afbeeldingen altijd zichtbaar.
Een grote afbeelding kan worden gesloten door het scherm buiten de grote afbeelding aan te raken of klikken, of door het indrukken van Escape. Door de manier waarop de slideshow is opgebouwd, ga je dan altijd terug naar de eerste thumbnail, met de bij de eerste thumbnail horende knoppen.
Als je de 28e grote afbeelding sluit, en je wilt met behulp van de knoppen naar de 29e afbeelding, moet je weer 28 keer 'n knop indrukken. Of je moet weer 28 keer de Tab-toets indrukken.
Met behulp JavaScript wordt bijgehouden, waar je bent. Bij sluiten van een grote afbeelding, of bij sluiten van de hulp, blijft de laatste bezochte thumbnail gemarkeerd en staan de bij die thumbnail horende knoppen op het scherm.
Probleem: als in grotere vensters de hulp wordt gesloten, wordt altijd teruggegaan naar de eerste thumbnail.
Hiervoor geldt precies hetzelfde als voor het sluiten van een grote afbeelding gelijk hierboven.
Probleem: bij gebruik van de Tab-toets worden in grotere vensters de twee volgende grote afbeeldingen niet alvast gedownload.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters zijn de grote afbeeldingen altijd zichtbaar.
Bij gebruik van de Tab-toets worden de grote afbeeldingen een voor een getoond. Omdat de afbeeldingen pas worden gedownload, als ze worden getoond, kan dat een (korte) vertraging opleveren als de volgende afbeelding moet worden getoond. Want deze moet eerst nog worden gedownload.
Met behulp van JavaScript worden, bij gebruik van de Tab-toets, de twee afbeeldingen die volgen op de getoonde afbeelding alvast gedownload, zodat ze snel getoond kunnen worden.
Probleem: loading="lazy"
werkt niet.
Het attribuut loading="lazy"
bij een <img> voorkomt dat afbeeldingen worden geladen, terwijl ze misschien helemaal nooit worden bekeken. Dit attribuut werkt met behulp van JavaScript. Volgens de specificatie mag het niet werken, als JavaScript is uitgeschakeld, omdat het dan gebruikt zou kunnen worden om de activiteit van de bezoeker te volgen. Dit heeft dus niets met dit voorbeeld te maken, het is gewoon volgens de specificatie.
Zonder css
Probleem: zonder css zie je in grotere vensters alleen de thumbnails.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters zijn de grote afbeeldingen altijd zichtbaar.
Omdat de grote afbeeldingen achtergrond-afbeeldingen zijn, zijn ze gewoon volledig onzichtbaar en onbereikbaar zonder css. Je kunt ze wel bekijken (en downloaden) met behulp van het ontwikkelgereedschap, maar gewoon normaal openen kan niet.
Dit zou je kunnen opvangen door links te maken die alleen zichtbaar zijn, als de css uitstaat. Maar eerlijk gezegd: hoeveel mensen zouden een slideshow gaan bekijken met de css uit?
(Wat trouwens ook werkt: sterk inzoomen. Als je maar genoeg vergroot, gaat de tablet of desktop zich gedragen als een smartphone en zie je dus gewoon alle grote afbeeldingen. Misschien niet in de juiste resolutie, maar je ziet ze in ieder geval wel.)
Zonder afbeeldingen
Probleem: zonder afbeeldingen zie je geen afbeeldingen.
Ja, en zonder zon schijnt de zon niet, en als het niet regent, regent het niet.
Iemand die een slideshow bezoekt en geen afbeeldingen wil zien, zou zich moeten inzetten voor het verkorten van de wachtlijsten voor mensen met ernstige psychische problemen.
(Komt toch opeens de vraag op: zouden er ook mensen zijn die proberen droog te blijven onder de douche?)
Gebruikers Tab-toets
Geen problemen.
Bij een van de eerste tabs wordt een skip-link zichtbaar, waarmee de slideshow met één Enter gepasseerd kan worden en rechtstreeks naar de eronder staande tekst wordt gegaan.
Bij de volgende tab wordt de hulp geopend. De tab daarna gaat naar de eigenlijke hulptekst, zodat deze (desgewenst met de pijltjes) gescrold kan worden. Daarna opent elke volgende tab een grote afbeelding. Dit wordt mogelijk gemaakt door het gebruik van tabindex="0", waar een uitgebreider verhaal staat.
De zes knoppen rechtsboven zijn met behulp van tabindex="-1" uitgeschakeld, omdat anders vijf keer de Tab-toets moet worden ingedrukt om een grote afbeelding te openen.
Tekstbrowsers
Probleem: er zijn geen afbeeldingen.
Ja, daarom heten het dus tekstbrowsers. Lynx en WebbIE tonen de volledige tekst, inclusief de alt-teksten bij de afbeeldingen. Maar uiteraard geen afbeeldingen, want daarom worden die browsers nou juist gebruikt.
Schermlezers
Probleem: TalkBack op Android leest bij elke afbeelding de naam twee keer voor.
In Android zit kennelijk een hoogst eigenaardige bug. De thumbnails en de afbeeldingen voor browservensters smaller dan 760 px of lager dan 530 px zitten in een <picture>, die weer binnen een <div> zit. <picture> en <div> hebben beide het attribuut tabindex, zodat ook gebruikers van de Tab-toets de grote afbeeldingen kunnen zien.
Het gebruik van tabindex zou geen enkele invloed moeten hebben op het voorlezen van de alt-tekst bij de <img> in de <picture>, maar dat heeft het wel: de alt-tekst wordt twee keer voorgelezen. Als je een van de twee tabindexen verwijdert, wordt de alt-tekst nog maar één keer voorgelezen.
Zelfs op een pagina zonder css, zonder JavaScript en met de minimale html (één <div>, waarin zelfs helemaal geen afbeeldingen worden geladen), doet deze bug zich voor. In alle andere geteste schermlezers gaat het wel goed.
(Eerst stond er nog 'n tabindex meer, en werd de naam zelfs drie keer voorgelezen.)
Bug gemeld en nou maar hopen dat ze er iets aan doen.
Probleem: in sommige schermlezers kan een grote afbeelding over de tekst onder de slideshow staan.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters staan de zijn de grote afbeeldingen altijd zichtbaar en staan gewoon boven de tekst.
Als een grote afbeelding is geopend en de schermlezer gaat naar de onder de slideshow staande tekst, blijft in sommige schermlezers de grote afbeelding geopend. Die dekt dan de tekst af.
De grote afbeelding wordt met behulp van :focus getoond. Sommige schermlezers zetten de focus op de tekst, als deze wordt bereikt. Waardoor een eventueel geopende grote afbeelding sluit. Maar niet alle schermlezers doen dat, daarom is aan het begin van de tekst een link aangebracht:
<a id="voor-sr" href="#begin-tekst" aria-label="Mogelijk is er nog een grote afbeelding geopend. Druk op Enter of dubbeltik op het scherm om deze te sluiten."></a>
Deze link wordt verborgen, zodra de focus binnen de <section> met de tekst staat. Als dat niet het geval is, is er een grote kans dat een grote afbeelding is geopend. In dat geval wordt de link getoond en de erin zittende WAI-ARIA-code aria-label
voorgelezen. Of eigenlijk wordt de link niet getoond, want deze is onzichtbaar. De tekst binnen aria-label
wordt alleen door een schermlezer herkend.
Vrijwel altijd werkt dit goed, een enkele keer wordt de link ook voorgelezen als geen grote afbeelding is geopend.
Door de link te volgen wordt de focus op de kopregel boven de tekst gezet, waardoor de focus binnen de <section> met tekst staat, en de link wordt verborgen.
Veel gebruikers van een schermlezer navigeren over een pagina door van kopregel naar kopregel te springen. Daarom is de link gelijk onder de <h2> boven de tekst gezet. Zo wordt de link – als dat nodig is – ook bij die manier van navigeren gevonden.
Probleem: in TalkBack op Android 6 en eerder en in iOS 12 en eerder werkt de skip-link niet.
Als een schermlezer de pagina voorleest, worden eerst alle onderdelen van de slideshow voorgelezen. Dat kan behoorlijk irritant zijn, als je gelijk naar de tekst onder de slideshow wilt. Daarom is voor de slideshow een skip-link neergezet, waarmee het menu in één keer gepasseerd kan worden.
Deze skip-link is normaal genomen onzichtbaar, maar dat zou voor schermlezers geen probleem horen te zijn: ook als een link buiten het browservenster staat, zou deze horen te werken. En inmiddels is dat ook zo in de meeste schermlezers.
In TalkBack, de schermlezer van Android, werkt een link buiten het browservenster pas in Android versie 7 en later.
In VoiceOver werkt op iOS 12 en eerder een link binnen de pagina gewoon helemaal nooit. Hij wordt wel netjes afgehandeld door VoiceOver, maar vervolgens gaat het voorlezen gewoon op dezelfde plaats verder. In iOS 13 (en iPadOS 13) en later is dit eindelijk opgelost.
Verder geen problemen.
Afgezien van bovenstaande zijn er verder geen problemen. De thumbnails zijn gewone afbeeldingen, dus de schermlezer kan daar op de gebruikelijke manier naartoe. Afhankelijk van instellingen en schermlezer opent de grote afbeelding automatisch, door dubbeltikken of door het indrukken van Enter.
De zes knoppen rechtsboven zijn met behulp van aria-hidden="true"
verborgen, omdat schermlezers anders tussen elke grote afbeelding vier keer uitgebreid een link aankondigen.
Naam en vraagteken (waarmee de hulp wordt geopend) worden ook verborgen. De naam is hier precies hetzelfde als de alt-tekst bij de afbeelding, en die wordt al voorgelezen. De hulptekst wordt altijd voorgelezen. Dat die buiten het scherm staat, is geen enkel probleem voor een schermlezer.
Een uitgebreider verhaal is te vinden bij WAI-ARIA-codes.
Zoomen en andere lettergrootte
Probleem: in Samsung Internet komen bij een grotere letter de symbolen in de knoppen op de thumbnails te staan.

Dit probleem speelt alleen in Samsung Internet, en alleen bij een grotere letter en in browservensters minimaal 760 px breed en minimaal 530 px hoog. In kleinere vensters zijn geen knoppen aanwezig.
Zoomen gaat prima, maar als de letters worden vergroot, zakken de pijltjes in de zes knoppen rechts naar beneden. De knoppen horen dan gelijktijdig hoger te worden, en de balk met thumbnails hoort omlaag te zakken. In Samsung Internet veranderen alleen lettergrootte en regelhoogte, waardoor de pijltjes over de balk met thumbnails komen te staan.
Bovenaan op de afbeelding is te zien, hoe het hoort te gaan. Onderaan is te zien, wat Samsung Internet ervan bakt: een met vlag en wimpel geslaagd klassiek misbaksel.
Hier is niets aan te doen. Het is een probleem dat veel vaker speelt in deze browser: als je de letters vergroot, veranderen alleen lettergrootte en regelhoogte. Andere maten zoals hoogte en breedte veranderen niet, ook niet als je die in em
of rem
opgeeft. Dit speelt al vanaf het begin in deze browser.
(Nou, mogelijk is dit op te lossen met 'n tamelijk ingewikkeld stuk JavaScript, maar er zijn grenzen in het meegaan in de grappen en grollen van browsermakers.)
Probleem: op iOS voor versie 13 kan de hulptekst niet worden gescrold, als wordt ingezoomd (vergroot).
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters is geen hulp aanwezig.
Normaal genomen kun je tekst en dergelijke vergroten door inzoomen. Dat kan bij de hulptekst ook, maar je kunt dan de tekst niet meer scrollen. Je kunt wel eerst de tekst scrollen en dan inzoomen, maar dat is nogal omslachtig.
Dit komt door het gebruik van de eigenschap -webkit-overflow-scrolling
, die weer nodig is om een ander probleem in dit superieure besturingssysteem op te lossen. Meer daarover is te vinden bij -webkit-overflow-scrolling: touch;.
Overige problemen
Probleem: imageset()
valideert nog niet.
Het in de css bij de achtergrond-afbeelding gebruikte imageset()
levert nog een foutmelding op bij valideren in de validator van w3c. Deze eigenschap is betrekkelijk nieuw, en de validator loopt altijd (een heel stuk) achter.
Omdat het gewoon werkt, kan deze melding genegeerd worden.
Probleem: op tablets met iOS en iPadOS, en in Safari op OS X, staat de horizontale scrollbalk onderaan de pagina.
Dit speelt alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog, want in kleinere vensters is geen horizontale scrollbalk aanwezig.
De scrollbalk zou eigenlijk gelijk onder de thumbnails moeten staan, zoals in alle andere browsers het geval is. Maar een echt probleem is dit eigenlijk niet.
Op tablets is de scrollbalk nauwelijks of niet zichtbaar, dus daar is het al helemaal geen probleem.
In OS X lijkt het ook heel onwaarschijnlijk, dat iemand hierdoor niet zou opmerken dat de thumbnails gescrold kunnen worden.
Wijzigingen
Alleen grotere wijzigingen worden hier vermeld, geen dingen als een link die is geüpdatet.
:
Nieuw opgenomen.
24 maart 2009:
-
Tekst aangepast voor de nieuw verschenen Internet Explorer 8.
-
meta-tag toegevoegd zodat het in Internet Explorer 8 wordt weergegeven op dezelfde manier als in Internet Explorer 7.
-
Tekst van het hulpschermpje voor Internet Explorer 7 aangepast, omdat dit nu ook door Internet Explorer 8 wordt gebruikt. Die in compatibiliteits-modus dus niet volledig hetzelfde werkt als Internet Explorer 7, ondanks de beweringen van Microsoft.
19 april 2009:
-
Hierboven genoemde meta-tag weer weggehaald, omdat Internet Explorer 8 het nu ook in standaardmodus goed weergeeft.
-
Hulpschermpje voor Internet Explorer 7 gesplitst in eentje voor Internet Explorer 7 en eentje voor Internet Explorer 8.
-
Speciale css voor Internet Explorer 8 toegevoegd, zodat het nu ook goed werkt in de standaardmodus van deze browser.
Internet Explorer 8 handelt de
z-index
niet goed af, waardoor de < en > - 1 thumbnail terug of verder gaan - onzichtbaar waren: ze waren gewoon niet boven de navigatiebalk te krijgen, zelfs niet als deze volledig doorzichtig was en de balk met thumbnails 'nposition: relative
had met hogez-index
.Dit is opgelost door de balk met thumbnails 'n absolute positie te geven. Nu werd het mogelijk om 'n
z-index
te gebruiken, waardoor < en > eindelijk zichtbaar werden. Maar nu moesten wel wat andere onderdelenposition: fixed
krijgen en 'n anderez-index
.Omdat het hier om 'n werkend voorbeeld gaat, heb ik ervoor gekozen de aanpassingen voor Internet Explorer 8 apart te zetten. Anders zou dit hele (ingewikkelde) voorbeeld weer helemaal opnieuw gemaakt moeten worden. En 't gaat om 'n echte fout in Internet Explorer 8, dus het is nog maar de vraag of er niet hoe dan ook aparte css nodig zou zijn geweest.
Overigens: ik mag graag mopperen op de troep van Microsoft, maar in dit geval gaat het om 'n bug in 'n behoorlijk ingewikkelde constructie, en dit is de soort bug zoals elke browser die wel heeft.
8 januari 2010:
-
Doctype veranderd naar html5.
-
Bij < en > (vorige en volgende) aan de link 'n tabindex van -1 toegevoegd. Hierdoor worden deze links helemaal niet meer bezocht bij gebruik van de Tab-toets.
-
Verhaal over bug weggehaald bij Opera. Bij snel achter elkaar klikken opent een contextueel menu. Dit blijkt geen bug te zijn, maar 'n extra. Althans: dat vindt Opera kennelijk. Je kunt 't uitvinken onder Extra → Voorkeuren → Geavanceerd → Werkbalken. Of aanzetten natuurlijk, als je 'n beetje masochistisch bent...
6 januari 2011:
- Charset veranderd naar die van html5.
- Waarschuwing over gebruik doctype html5 weggehaald. Dit kan inmiddels veilig worden gebruikt.
- Hoe het hoort te werken en hoe het blijkt te werken aangepast. Belangrijkste verandering is dat Opera nu bij gebruik van > ook over de volle lengte van de scrollbalk verplaatst.
- Diverse kleinere veranderingen in de tekst.
20 december 2011:
-
Code en tekst aangepast voor de inmiddels verschenen Internet Explorer 9. Internet Explorer 9 bleek exact dezelfde wijzigingen als Internet Explorer 8 nodig te hebben, dus dit bleef feitelijk beperkt tot het her en der toevoegen van 'en Internet Explorer 9'.
Vreemd trouwens, want Internet Explorer 9 werkt vrijwel altijd net zoals alle andere browsers. Kennelijk nog 'n restant van de absolute bagger die Microsoft in het verleden uitbracht onder de foutieve naam 'browser'.
- Stukje toegevoegd bij Bekende problemen (en oplossingen) over kleine bug in Internet Explorer 9.
- Bij
div#thumbs a:focus
css veranderd vanoutline: none;
naaroutline: solid transparent 0;
. Hierdoor zie je nu in Opera geen lelijk blauw kader meer als 'n thumb focus heeft.
25 november 2022:
Er zijn eigenlijk te veel wijzigingen om ze allemaal op te noemen. De volledige code is herschreven. Daarom worden hier niet alle wijzigingen in de code gemeld, zoals eerder wel gebeurde. Alleen de grote lijnen worden weergegeven.
- Alle aanpassingen voor Opera en Internet Explorer zijn verwijderd.
- Er is een aantal nieuwe hoofdstukken toegevoegd, die voornamelijk met toegankelijkheid en mobiele apparatuur te maken hebben.
-
De gebruikte afbeeldingen zijn niet meer afkomstig van public-domain-photos.com, maar van unsplash.com. (De maat van de foto's is aangepast, om meerdere maten te kunnen gebruiken in dit voorbeeld. Ook is een breedte aangebracht op de foto's, zodat je kunt zien, welke grootte wordt gebruikt.)
Ondanks de naam 'public-domain-photos.com' was de licentie veranderd in een nogal commerciële licentie, en inmiddels bestaat de site helemaal niet meer.
- De grootte van de afbeelding wordt nu aangepast aan de grootte en/of resolutiedichtheid van het scherm.
- Werkt nu ook op touchscreens.
- Knoppen vergroot, zodat ze ook met vingers zijn te gebruiken.
- Aan de knoppen kleine voorvertoningen toegevoegd.
- Bij de afbeeldingen is het attribuut 'title' verwijderd. Dit was bedoeld om de toegankelijkheid te verbeteren, maar werkte averechts.
- :active en :hover worden niet meer gebruikt.
- Alle tabindexen met een positief nummer verwijderd.
- Selectors zo kort mogelijk gemaakt.
- De extra css en html om de grote afbeeldingen te kunnen kopiëren en downloaden is verwijderd. Een iets uitgebreider verhaal hierover staat bij In browservensters smaller...
2 december 2022:
-
Oeps. Door een briljante aanpassing in de html die nodig was om het JavaScript goed te laten werken, openden in grotere browservensters de grote afbeeldingen helemaal niet meer, als een thumbnail werd aangeraakt of -geklikt. Althans: als JavaScript aanstond, werkte het prima. Maar als JavaScript uitstond, werkte het niet meer.
Terwijl nou juist de bedoeling was dat de slideshow ook (min of meer onhandig, dat wel) ook werkt zónder JavaScript. Totaal gemist bij het testen na deze aanpassing van het JavaScript. Een briljante miskleun. Vandaar een spoedreparatie.
Aan <html>
class="no-js"
toegevoegd.Die class wordt door het JavaScript verwijderd. Als er geen JavaScript is, blijft de class gewoon staan.
Als de class blijft staan, wordt de grote afbeelding ook getoond met de nieuwe selector
.no-js #thumbs picture:focus:not(:target) + .groot
.Op alle relevante plaatsen in de uitleg is de tekst aangepast.
- Firefox blijkt geen probleem te hebben met de Tab-toets op OS X. Bij een update was een instelling in OS X veranderd, waardoor dit probleem ontstond. Gecorrigeerd in de tekst.
Inhoud van de download en licenties
De inhoud van deze download kan vrij worden gebruikt, met drie beperkingen:
* Sommige onderdelen die van 'n andere site of zo afkomstig zijn, vallen mogelijk onder een of andere licentie. Dat is hieronder bij het betreffende onderdeel te vinden.
* Je gebruikt het materiaal uit deze download volledig op eigen risico. Het kan prima zijn dat er fouten in de hier verstrekte code en dergelijke zitten. Voor eventuele schade die door gebruik van materiaal uit deze download ontstaat, in welke vorm dan ook, zijn www.css-voorbeelden.nl en medewerkers daarvan op geen enkele manier verantwoordelijk.
* Dit voorbeeld (en de bijbehorende uitleg en dergelijke) wordt min of meer regelmatig bijgewerkt. Het is daarom niet toegestaan dit voorbeeld (en de bijbehorende uitleg en dergelijke) op welke manier dan ook te verspreiden, zonder daarbij duidelijk te vermelden dat voorbeeld, uitleg, en dergelijke afkomstig zijn van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie is te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.
Een link naar www.css-voorbeelden.nl wordt trouwens altijd op prijs gesteld.
afbeelding-055-dl.html: de pagina met het voorbeeld.
afbeelding-055.pdf: deze uitleg (aangepast aan de inhoud van de download).
afbeelding-055-inhoud-download-en-licenties.txt: een kopie van de tekst onder dit kopje (Inhoud van de download en licenties).
055-css-dl:
afbeelding-055-dl.css: stylesheet voor afbeelding-055-dl.html.
055-pics:
Dertig afbeeldingen. Omdat elke afbeelding in negen verschillende groottes voorkomt, zijn er in totaal 270 afbeeldingen.
De afbeeldingen zijn oorspronkelijk afkomstig van unsplash.com en kunnen vrij worden gebruikt. De volledige licentie is te vinden op unsplash.com/license.
De verschillende groottes zijn op deze site gemaakt. Ook is hier in de rechterbovenhoek van de afbeelding de breedte in pixels aangegeven, zodat je kunt zien, welke afbeelding wordt gebruikt.
055-js-dl:
afbeelding-055-dl.js: javascript voor de slideshow.
HTML
De code is geschreven in een afwijkende
lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code), is in de hele uitleg onderstippeld blauw
. Alle niet-essentiële code is bruin
. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)
In de html hieronder wordt alleen de html besproken, waarover iets meer is te vertellen. Een <h1> bijvoorbeeld wordt in de regel niet genoemd, omdat daarover weinig interessants valt te melden. (Als bijvoorbeeld het uiterlijk van de <h1> wordt aangepast met behulp van css, staat dat verderop bij de bespreking van de css.)
Zaken als een doctype
en charset
hebben soms wat voor veel mensen onbekende effecten, dus daarover wordt hieronder wel een en ander geschreven.
<!doctype html>
Een document moet met een doctype beginnen om weergaveverschillen tussen browsers te voorkomen. Zonder doctype is de kans op verschillende (en soms volkomen verkeerde) weergave tussen verschillende browsers heel erg groot.
Geldige doctypes vind je op www.w3.org/QA/2002/04/valid-dtd-list.
Gebruik het volledige doctype, inclusief de eventuele url, anders werkt het niet goed.
Het hier gebruikte doctype is dat van html5. Dit kan zonder enig probleem worden gebruikt: het werkt zelfs in Internet Explorer 6.
<html lang="nl" tabindex="-1" class="no-js">
Het attribuut lang="nl"
bij <html> geeft aan dat de pagina in het Nederlands is. De taal is van belang voor schermlezers, automatisch afbreken, automatisch genereren van aanhalingstekens, juist gebruik van decimale punt of komma, en dergelijke.
Het attribuut tabindex="-1"
nodig om op iOS en iPadOS de grote afbeelding te kunnen sluiten door het scherm aan te raken. Meer hierover is te vinden bij tabindex="-1".
De class 'no-js' wordt verwijderd door het JavaScript. Als JavaScript uitstaat (of niet kan worden gedownload, of zoiets), blijft deze class staan. Daardoor blijven selectors die met .no-js
beginnen werken. Meer hierover is te vinden bij .no-js #thumbs picture:focus:not(:target) + .groot.
<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 (ä
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 grootte. En dat is een probleem. Sites waren, in ieder geval tot enkele jaren geleden, gemaakt voor desktopbrowsers. En die hebben, in vergelijking met bijvoorbeeld een smartphone, heel brede browservensters. Hoe moet je op 'n smartphone een pagina weergeven, die is gemaakt voor de breedte van een desktop? Je kunt natuurlijk wachten tot álle sites zijn omgebouwd voor smartphones, tablets, enzovoort, maar dan moet je waarschijnlijk heel erg lang wachten.
Mobiele browsers gokken erop dat een pagina een bepaalde breedte heeft. Safari voor mobiel bijvoorbeeld gaat ervan uit dat een pagina 980 px breed is. De pagina wordt vervolgens zoveel versmald dat 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 deze wil zien.
Dit betekent ook dat bij het openen van de pagina de tekst meestal heel erg klein wordt weergegeven. (Meestal, want niet alle browsers en apparaten doen het op dezelfde manier.) Niet erg fraai, maar bedenk maar 'ns 'n betere oplossing voor bestaande sites.
Nieuwe sites of pagina's kunnen echter wel rekening houden met de veel kleinere vensters van mobiele apparaten. In dit voorbeeld bijvoorbeeld worden in kleinere vensters gelijk de grote afbeeldingen getoond, zonder gebruik te maken van thumbnails.
Maar die stomme mobiele browser weet dat niet, dus die gaat ervan uit dat ook deze pagina 980 px breed is, en verkleint die dan. Dat is ongeveer even behulpzaam als de gedienstige kelner die behulpzaam de stoel naar achteren trekt, net als jij wilt gaan zitten.
Om de door de browser aangeboden hulp vriendelijk maar beslist te weigeren, wordt deze tag gebruikt. Hiermee geef je aan dat de pagina is geoptimaliseerd voor mobiele apparaten.
De kreet width=device-width
zegt tegen de mobiele browser dat de breedte van de weer te geven pagina gelijk is aan de breedte van het apparaat. Als een iPad in portretstand bijvoorbeeld 768 px breed is, wordt de pagina ook 768 px breed.
Er staat nog een tweede deel in de tag: initial-scale=1
. Sommige mobiele apparaten zoomen een pagina gelijk in of uit. Ook weer in een poging behulpzaam te zijn. Ook dat is hier niet nodig. Er is ook een instructie om zoomen helemaal onmogelijk te maken, maar die wordt niet gebruikt. De bezoeker kan zelf nog gewoon zoomen, wat belangrijk is voor mensen die wat slechter zien.
<link rel="stylesheet" href="055-css-dl/afbeelding-055-dl.css">
Dit is een koppeling naar een externe stylesheet (stijlbestand), waarin de css staat. In html5 is de toevoeging type="text/css"
niet meer nodig, omdat dit standaard al zo staat ingesteld. Je moet uiteraard de naam van en het pad naar de stylesheet aanpassen aan de naam en plaats, waar je eigen stylesheet staat.
Voordeel van een externe stylesheet is onder andere dat deze geldig is voor alle pagina's, waaraan deze is gelinkt. 'n Verandering in de lay-out hoef je dan maar in één enkele stylesheet aan te brengen, in plaats van in elke pagina apart. Op een grotere site kan dit ontzettend veel werk schelen. Bovendien hoeft de browser zo'n externe stylesheet maar één keer te downloaden, ongeacht hoeveel pagina's er gebruik van maken. Zou je de css in elke pagina opnieuw aanbrengen, dan worden de te downloaden bestanden veel groter.
In dit voorbeeld heeft een extern stylesheet eigenlijk geen nut, omdat er maar één pagina is die dit stylesheet gebruikt. In dit geval kun je de css beter in de <head> van de html-pagina zelf zetten. Voor de omvang maakt het hier niets uit, want de css wordt hoe dan ook altijd precies één keer gedownload, en nooit vaker. Voor het onderhoud maakt het ook geen verschil, want ook hier hoef je de css maar op één plaats te wijzigen. Maar het scheelt wel een extra aanroep naar de server, omdat geen apart stylesheet hoeft te worden gedownload.
Dat opnemen in de <head> gaat heel simpel: je kopieert gewoon het hele stylesheet en zet die bovenin de <head>, tussen <style> en </style>:
<style>
body {color: black;}
(...) rest van de css (...)
div {color: red;}
</style>
Maar zodra een stylesheet op meerdere pagina's wordt gebruikt, wat meestal het geval zal zijn, is een extern stylesheet beter.
(De reden dat er toch een extern stylesheet is, terwijl hierboven omstandig wordt beweerd dat dat in dit voorbeeld eigenlijk geen nut heeft: overzichtelijkheid. Nu kun je html en css los van elkaar bekijken.)
<main tabindex="-1">
Binnen <main> staat de belangrijkste inhoud van de pagina. Dat is hier de hele pagina.
<main> kan normaal genomen niet de focus krijgen. Door het toevoegen van tabindex="-1"
kan JavaScript toch de focus aan <main> geven. Meer hierover is te vinden bij tabindex="-1".
<a id="skippy" href="#begin-tekst">Skip slideshow</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.
Als een gebruiker van de Tab-toets in dit voorbeeld gelijk naar de tekst onder de slideshow zou willen gaan, moet die eerst ruim dertig keer de Tab-toets indrukken, voordat die tekst wordt bereikt. Daarom is helemaal bovenaan de pagina een zogenaamde 'skip-link' geplaatst.
Omdat het een gewone link is, wordt deze bij gebruik van de Tab-toets bezocht, krijgt focus. Als de gebruiker dan op Enter drukt, wordt de link gevolgd. In dit geval is het een link naar het begin van de tekst onder de slideshow. Op deze manier kan in één keer de hele slideshow 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 delen van de pagina te passeren. In welke schermlezers een skip-link wel en niet werkt, is te vinden bij Bekende problemen (en oplossingen) onder het kopje Schermlezers.
<div id="uitleg" tabindex="0">
(div#uitleg
is alleen zichtbaar in browservensters minimaal 760 px breed en minimaal 530 px hoog.)
In div#uitleg
staan het vraagteken en de uitleg. De uitleg wordt met behulp van #uitleg:focus-within
getoond, als div#uitleg
of een van de nakomelingen ervan de focus heeft. Normaal genomen kan een <div> geen focus krijgen, maar door het toevoegen van tabindex="0"
kan dat bij div#uitleg
wel. Meer hierover is te vinden bij tabindex="0".
<span id="vraagteken" aria-hidden="true">?</span>
(span#vraagteken
is alleen zichtbaar in browservensters minimaal 760 px breed en minimaal 530 px hoog.)
In span#vraagteken
staat het vraagteken, dat bij aanraken of -klikken de hulptekst laat verschijnen.
Met behulp van aria-hidden="true"
wordt deze <span>, en daarmee het erin zittende vraagteken, verborgen voor schermlezers>. Als dat niet zou gebeuren, zou alleen iets als 'vraagteken' worden voorgelezen, zonder dat duidelijk is, wat dat betekent. Meer hierover is te vinden bij WAI-ARIA-codes
<div id="hulptekst" tabindex="0">
In div#hulptekst
staat de uitleg. De ouder van div#hulptekst
is div#uitleg
. De uitleg wordt met behulp van #uitleg:focus-within
getoond, als div#uitleg
of een van de nakomelingen ervan de focus heeft. Als kind van div#uitleg
moet div#hulptekst
daarom de focus kunnen krijgen.
Normaal genomen kan een <div> geen focus krijgen, maar door het toevoegen van tabindex="0"
kan dat bij div#hulptekst wel
. Meer hierover is te vinden bij tabindex="0".
<a class="eerste" href="#picture-1" tabindex="-1" aria-hidden="true">|←</a>
<a class="laatste" href="#picture-30" tabindex="-1" aria-hidden="true">→|</a>
(Deze twee links zijn alleen zichtbaar in browservensters minimaal 760 px breed en minimaal 530 px hoog.)
Helemaal bovenin div#thumbs
met de slideshow staan twee links naar de eerste en laatste thumbnail, vermomd als knop: a.eerste
en a.laatste
.
Voor gebruikers van de Tab-toets zijn de knoppen rechtsboven naar vorige en volgende thumbnail en grote afbeelding uitgeschakeld, omdat ze anders tussen elke grote afbeelding vier keer op de Tab-toets zouden moeten drukken. Nu opent elke tab gelijk de volgende (of vorige) grote afbeelding.
Hetzelfde geldt ongeveer voor gebruikers van een schermlezer, maar dan nog wat meer. Een schermlezer zou alle vier de links naar vorige en volgende thumbnail en grote afbeelding aankondigen als zijnde een link, en pas dan zou de volgende echte thumbnail worden voorgelezen.
Daarom worden voor gebruikers van de Tab-toets en schermlezers de tussen de grote afbeeldingen zittende links verborgen.
tabindex="-1"
zorgt ervoor dat de links worden genegeerd door de Tab-toets. Er is meer over te vinden bij tabindex="-1".
aria-hidden="true"
voorkomt voorlezen door een schermlezer. Er is meer over te vinden bij WAI-ARIA-codes.
Een heel verhaal, maar eigenlijk heeft dit helemaal niets te maken met de twee links hierboven. Want die staan niet tussen elke grote afbeelding, maar staan slechts één keer aan het begin van de slideshow. Het lijkt echter wat onlogisch om alleen die twee knoppen te laten werken en de rest niet. Daarom worden ook deze twee knoppen verborgen.
<div>
<span class="vorige-picture"></span>
<a class="volgende-picture" href="#picture-1" tabindex="-1" aria-hidden="true">→</a>
<span class="vorige-div"></span>
<a class="volgende-div" href="#div-1" tabindex="-1" aria-hidden="true">→</a>
</div>
(Deze div met de erin zittende <span>'s en <a>'s is alleen zichtbaar in browservensters minimaal 760 px breed en minimaal 530 px hoog.)
Dit zijn vier van de gelijk hierboven genoemde knoppen naar vorige of volgende thumbnail en grote afbeelding. Maar ook weer afwijkend van de overige vier knoppen, die tussen elke grote afbeelding zitten.
Net als de twee links gelijk hierboven komen ook deze vier knoppen maar één keer voor, en ook weer gelijk aan het begin van de slideshow.
Bij openen van de pagina zijn het deze vier knoppen, die zichtbaar zijn. Als je de knoppen bij de eerste thumbnail zou laten zien, zou dat wat verwarrend zijn, want die verwijzen gelijk naar de vorige of volgende thumbnail en grote afbeelding. Zou je die aanraken of -klikken, dan zou je de eerste thumbnail gelijk passeren, terwijl die nog helemaal niet is bezocht.
Er zijn hier maar twee echte links naar de volgende thumbnail en grote afbeelding, wat hier de eerste thumbnail en grote afbeelding is (de aardbeien). Hierdoor wordt, bij aanraken of -klikken van de knop, de eerste thumbnail of grote afbeelding bezocht, en niet overgeslagen.
(In sommige browsers zijn deze twee links niet nodig, omdat die altijd de links naar de eerste thumbnail en grote afbeelding tonen bij openen van de pagina. Maar kwaad doen ze daar niet.)
De twee <span>'s zijn helemaal nep: het zijn lege elementen met alleen een witte achtergrond, die bij openen van de pagina links naar de vorige thumbnail en grote afbeelding afdekken, en daarmee verbergen.
De twee links naar de eerste thumbnail en grote afbeelding worden met behulp van tabindex="-1"
en aria-hidden="true"
verborgen voor gebruikers van Tab-toets en schermlezers. Het uitgebreidere verhaal hierover is iets hierboven bij <a class="eerste" href="#picture-1" ... te vinden, alleen geldt het nu voor deze twee links.
<div id="div-1" tabindex="0">
Elke thumbnail met bijbehorende knoppen, grote afbeelding, enzovoort staat in een eigen <div>: div#div-1
tot en met div#div-30
.
In browservensters minimaal 760 px breed en minimaal 530 px hoog zijn de grote afbeeldingen en de bijbehorende naam standaard verborgen. Ze worden pas getoond, als de <div> waar ze in zitten de focus krijgt. De bijbehorende naam wordt pas getoond, als de <div> of een van de nakomelingen de focus krijgt.
Een <div> kan normaal genomen geen focus krijgen. Door het toevoegen van tabindex="0"
kan dit toch. Meer hierover is te vinden bij tabindex="0".
<picture id="picture-1" tabindex="-1">
Met behulp van JavaScript wordt bijgehouden, welk element de focus heeft. Hiervoor is het onder andere nodig dat ook <picture> de focus kan krijgen, als een van de knoppen rechtsboven naar een <picture> wordt aangeraakt of -geklikt. Dan wordt een link naar een <picture> gevolgd. Niet in alle browsers krijgt de <picture> dan echter de focus. Door het toevoegen van een tabindex lukt dat wel. Meer hierover is te vinden bij tabindex="-1".
Door gebruik te maken van <picture> kun je naar schermen met een verschillende resolutiedichtheid en naar browservensters met een verschillende grootte de afbeelding sturen, die het best bij die resolutiedichtheid en bij die venstergrootte past. Dat gebeurt met behulp van de in de picture zittende <source>'s en <img>, die gelijk hieronder worden beschreven.
<source media="(min-width: 760px) and (min-height: 530px)
srcset="055-pics/aardbeien-60.jpg,
055-pics/aardbeien-120.jpg 2x,
055-pics/aardbeien-180.jpg 3x">
<source media="(min-width: 360px)"
srcset="055-pics/aardbeien-700.jpg,
055-pics/aardbeien-1400.jpg 2x,
055-pics/aardbeien-2000.jpg 3x">
<source srcset="055-pics/aardbeien-350.jpg,
055-pics/aardbeien-700.jpg 2x,
055-pics/aardbeien-1000.jpg 3x">
<img loading="lazy" src="055-pics/aardbeien-700.jpg" alt="Aardbeien">
In elke <picture> zitten drie <source>'s en één <img>. Met behulp van <source> en de erin zittende srcset
kan de browser de afbeelding vinden, die het best bij de grootte van het browservenster, de resolutiedichtheid van het scherm, en dergelijke van de bezoeker past. Zonder dat hele kleine vensters een joekel van een afbeelding moeten downloaden, en zonder dat grote vensters een postzegel te zien krijgen.
Stel dat je een browservenster hebt met een breedte van 600 px. Binnen dat venster wordt de grote afbeelding met een breedte van 500 px weergegeven. Een afbeelding met een breedte van 500 px heeft dan in de breedte 500 px naast elkaar staan en wordt netjes 500 px breed. Ook als je geen css gebruikt, want dit is de échte breedte van de afbeelding.
Maar een afbeelding met een breedte van 1000 px zou uit zichzelf 1000 px breed worden, want dat is de échte breedte van die afbeelding. Je zou die afbeelding zelfs moeten scrollen om hem helemaal te zien.
Daarom wordt in dat geval in principe naar een browservenster met een breedte van 600 px een afbeelding met een breedte van 500 px gestuurd: die past netjes binnen dat venster.
Een scherm met een hogere resolutie heeft kleinere pixels, die dichter op elkaar staan. Bij een scherm heten die pixels 'schermpixels'. Daardoor kan een afbeelding, ronde lijn, enzovoort met kleinere details worden weergegeven. Als de schermpixels twee keer zo klein zijn als op een 'gewoon' scherm, is op dat scherm wél ruimte voor de afbeelding die 1000 px breed is. Want binnen dat scherm passen die 1000 px van de afbeelding netjes binnen de 1000 schermpixels van het scherm. Hierdoor is de weergave (veel) gedetailleerder. In dat geval is het dus zinvol om een bredere afbeelding te sturen.
De afbeelding wordt nog steeds op een breedte van 500 px weergegeven, want de 1000 schermpixels zijn twee keer zo klein als op een 'gewoon' scherm. Maar de kwaliteit van de weergave zal (veel) beter zijn.
Op een scherm met een drie keer zo hoge resolutie, met schermpixels die drie keer zo klein zijn als de 'gewone', kan een afbeelding met een breedte van 1500 px worden weergeven: nog gedetailleerder. Terwijl die afbeelding nog steeds wordt weergegeven met een breedte van 500 px, want de schermpixels zijn drie keer zo klein.
(Het gaat hier steeds over de breedte van de afbeelding, want de hoogte past zich normaal genomen automatisch aan de breedte aan. Als dat niet zo zou zijn, zou je een lachspiegel-effect krijgen.)
Met behulp van <source> en de daarin zittende srcset
kan worden gezorgd dat een kleiner browservenster en/of een scherm met een lagere resolutiedichtheid een kleinere afbeelding krijgt dan een groter venster en/of een scherm met een hogere resolutiedichtheid.
Schermen met een lagere resolutie en/of kleinere browservensters krijgen minder brede afbeeldingen, die dus (veel) kleiner zijn dan de brede afbeeldingen. Daardoor gebruiken ze (veel) minder bandbreedte en downloaden (veel) sneller. Een veel uitgebreider verhaal over pixels, schermpixels, resolutie, enzovoort is te vinden bij Resolutie en afbeeldingen. Hier gaat het verder alleen over de werking van <source> en srcset
.
Met behulp van <source> kun je voorwaarden geven, waaraan scherm en/of browservenster moeten voldoen. Je kunt bijvoorbeeld voorwaarden stellen aan de resolutiedichtheid van het scherm en/of de grootte van het venster. En je kunt eventueel in smallere vensters afbeeldingen smaller weergeven.
De verschillende <source>'s kijken in dit voorbeeld alleen, hoe breed en eventueel hoe hoog het browservenster is. Binnen elke <source> kijkt een srcset
dan vervolgens, hoe groot de resolutiedichtheid van het scherm is, en dat bepaalt dan, welke afbeelding uiteindelijk wordt gebruikt.
De eerste <source>:
<source media="(min-width: 760px) and (min-height: 530px)"
Dit werkt precies hetzelfde als bij een media query: het browservenster moet minstens 760 px breed zijn én het moet minstens 530 px hoog zijn. Is dat niet zo, dan wordt de srcset
binnen deze <source> (en daarmee de daarin zittende afbeeldingen) genegeerd door de browser.
(Een veel uitgebreider verhaal over hoe een media query werkt (en dus ook media
in <source>) is te vinden bij @media screen and (orientation: portrait) and (max-width: 759px).)
Als het venster van de browser aan deze twee voorwaarden voldoet, wordt naar de srcset
binnen deze <source> gekeken:
srcset="055-pics/aardbeien-60.jpg,
055-pics/aardbeien-120.jpg 2x,
055-pics/aardbeien-180.jpg 3x">
(De inspringingen hierboven zijn voor de browser niet nodig. Ze maken het alleen (hopelijk) wat leesbaarder. In de html zelf staat alle code gelijk achter elkaar, en dan maken dit soort inspringingen duidelijk, wat bij elkaar hoort. Hoe je dit precies doet, is eigenlijk vooral een kwestie van persoonlijke voorkeur.)
Binnen de srcset
zit een serie bij elkaar horende afbeeldingen. Eén van deze gaat dus gebruikt worden binnen browservensters met een minimale breedte van 760 px én een minimale hoogte van 530 px. De afbeeldingen binnen een srcset
staan allemaal tussen dezelfde aanhalingstekens, en ze worden gescheiden door een komma.
Het hieronder staande verhaal gaat over de afbeeldingen die beginnen met 'aardbeien', maar het geldt voor elke groep afbeeldingen die begint met dezelfde naam.
Alle afbeeldingen die beginnen met 'aardbeien' zijn exact hetzelfde, maar met verschillende breedtes (en om een lachspiegel-effect te voorkomen uiteraard ook met een bijpassende andere hoogte: de verhouding tussen breedte en hoogte is bij elke afbeelding hetzelfde).
Om de afbeeldingen makkelijk van elkaar te kunnen onderscheiden, staat achter de naam steeds een koppelteken, gevolgd door een getal. Dat getal is de breedte van de afbeelding in px. Er zijn in totaal negen 'aardbeien'.
Hoe je de afbeeldingen herkenbaar maakt, is weer een kwestie van persoonlijke voorkeur. Hier is gewoon de breedte in pixels verwerkt in de naam. Ook over zevenhonderd jaar is heel snel duidelijk, wat dat getal betekent: '...-60.jpg' is 60 px breed.
Je kunt ook 'aardbeien-a', 'aardbeien-b', of 'aardbeien-1', 'aardbeien-2', of desnoods 'aardbeien-piet', 'aardbeien-truus' als naam gebruiken. Maar van dat soort coderingen worden vermoedelijk alleen fabrikanten van zware kalmerende middelen en dwangbuizen vrolijk.
(Vóór de naam staat overal '055-pics/', maar dat is alleen het pad naar de map met afbeeldingen en speelt verder geen rol.)
Achter de eerste 'aardbeien-60.jpg' staat verder niets. In principe wordt, in browservensters minstens 760 px breed én minstens 530 px hoog, deze afbeelding gebruikt. Maar er staat nog meer in de srcset
, dus de browser kijkt verder.
Achter de tweede 'aardbeien-120.jpg' staat '2x'. Dat betekent in een srcset
dat de resolutiedichtheid van het scherm minstens twee keer de resolutie moet zijn van wat ooit een standaardscherm op de desktop was. Die standaardresolutie was 96 ppi, dus het scherm moet minstens een resolutiedichtheid van 2 x 96 = 192 ppi hebben. Als dat zo is, wordt deze tweede afbeelding uit srcset
gebruikt: 'aardbeien-120.jpg'. Deze afbeelding is 120 px breed, en het scherm heeft genoeg pixels om die allemaal weer te kunnen geven. Op een breedte van 60 px, want elke schermpixel is twee keer zo klein als een pixel van de afbeelding.
Er staat echter nóg een afbeelding: 'aardbeien-180.jpg'. En daarachter staat '3x'. Je hoeft geen Sherlock Holmes te heten om te veronderstellen, dat dit zou kunnen betekenen dat de resolutie minstens drie keer de oude standaardresolutie moet zijn: 3 x 96 = 288 ppi. Als dat zo is, wordt deze afbeelding gebruikt.
De afbeelding is 180 px breed, maar omdat de schermpixels drie keer zo klein zijn, wordt de afbeelding weer op een breedte van 60 px weergegeven: even groot als de andere afbeeldingen uit deze srcset
.
(De laatste >
achter '3x' is de afsluitende >
van <source
.)
Je kunt deze serie desgewenst uitbreiden tot desnoods 100x (als zo'n scherm zou bestaan), het is net hoe belangrijk je de meest optimale weergave vindt.
In dit geval wordt de eerste <source> gebruikt om in browservensters minimaal 760 px breed en minimaal 530 px hoog de thumbnails weer te geven. En binnen die <source> bepaalt de srcset
, afhankelijk van de resolutiedichtheid, welke afbeelding het uiteindelijk wordt. Voor de grootte van de weergave maakt het niet uit of de afbeelding 60, 120 of 180 px breed is: met behulp van css worden ze allemaal op een breedte van 60 px weergegeven. Alleen de kwaliteit verschilt.
Je kunt met behulp van srcset
ook nog de breedte van de weergave regelen, maar dat gebeurt hier niet. De breedte van de weergave wordt in dit voorbeeld volledig met css geregeld.
(Een veel uitgebreider verhaal over pixels, schermpixels, resolutie, enzovoort is te vinden bij Resolutie en afbeeldingen. Hier gaat het vooral over de werking van <source> en srcset
.)
De afbeeldingen uit deze srcset
uit de eerste <source> zijn heel klein, omdat ze alleen als thumbnail worden gebruikt. Dat maakt voor het verhaal verder niets uit. De afbeeldingen uit de srcset
's hieronder zijn veel groter, want die worden in kleinere browservensters als grote afbeelding gebruikt.
De tweede <source> is:
<source media="(min-width: 360px)"
Nu is de enige voorwaarde dat het browservenster 360 px breed moet zijn. De hoogte is hier niet belangrijk. In deze vensters worden helemaal geen thumbnails gebruikt, want daar zijn de vensters (veel) te klein voor. Hier worden gelijk de grote afbeeldingen getoond.
Binnen deze <source> staat de volgende srcset
:
srcset="055-pics/aardbeien-700.jpg,
055-pics/aardbeien-1400.jpg 2x,
055-pics/aardbeien-2000.jpg 3x">
Deze srcset
werkt weer op precies dezelfde manier als bij de srcset
uit de eerste <source>. Standaard wordt 'aardbeien-700.jpg' gebruikt, bij een resolutiedichtheid van minstens twee keer de oude standaardwaarde van 96 ppi wordt 'aardbeien-1400.jpg' gebruikt, en bij een resolutiedichtheid van minstens drie keer de oude standaardwaarde wordt 'aardbeien-2000.jpg' gebruikt.
Bij de derde <source> staat helemaal geen voorwaarde. Als het browservenster niet aan de voorwaarde van de eerste of tweede <source> voldoet, wordt deze derde <source> gebruikt. Dat is als het venster minder dan 360 px breed is. Er is dus voor elke grootte venster altijd een <source> die gebruikt kan worden.
De srcset
binnen deze <source>:
srcset="055-pics/aardbeien-350.jpg,
055-pics/aardbeien-700.jpg 2x,
055-pics/aardbeien-1000.jpg 3x">
Deze werkt ook weer precies hetzelfde als de eerdere. Alleen zijn, voor deze smalle browservensters, de afbeeldingen veel kleiner dan in de tweede <source>
In een heel klein browservenster op een heel simpel scherm wordt dus uiteindelijk 'aardbeien-350.jpg' gebruikt, want aan het gebruik daarvan is geen enkele voorwaarde gesteld.
De opgegeven afbeeldingen zijn geen absolute verplichting voor de browser, het zijn hints. Als bijvoorbeeld de verbinding erg traag is, zou de browser toch een afbeelding kunnen gebruiken, die eigenlijk voor een lagere resolutie of een kleiner browservenster is bedoeld.
Rekenwonders hebben misschien gemerkt dat de tweede en derde afbeelding uit de eerste <source> precies twee en drie keer zo groot zijn als de eerste afbeelding. En dat dit bij de afbeeldingen uit de tweede <source> niet zo is. Dat heeft te maken met een ordinaire rekenfout. Drie keer zevenhonderd is, o oneindige verbazing, geen tweeduizend.
Tja. Vooral tja als je daarachter komt, als je alle afbeeldingen al hebt gemaakt en de breedte er al op hebt gezet. Vervolgens grijpt dan gelukkig de aangeboren ongeneeslijke luiheid in: geen zin dat te gaan corrigeren.
Bij de srcset
uit de derde <source> speelde iets anders. Daar is de breedte 350, 700 en 1000 px. Als de derde afbeelding precies drie keer zo breed als de eerste zou zijn, had dat 1050 px moeten zijn. De afbeeldingen worden echter nog op andere plaatsen gebruikt, en 1000 px kwam daar beter. uit. Dat verschil van 50 px is niet echt te zien, en dit spaarde een serie van nog 'ns dertig afbeeldingen met een breedte van 1050 px uit.
Deze 'afwijkingen' maken voor de weergave niets uit, want je hebt de vreemdste resolutiedichtheden. Die zijn bepaald ook niet allemaal een, twee, drie of vier keer de oude standaardwaarde van 96 ppi. De browser zorgt er zelf voor dat een afbeelding met pixels, die niet volledig aansluiten op de schermpixels, toch goed wordt weergegeven.
Blijft er nog één element uit de <picture> over:
<img loading="lazy" src="055-pics/aardbeien-700.jpg" alt="Aardbeien">
Dit is een gewone simpele <img>, zoals die al vanaf het begin bestaat. (Wat opgeleukt met wat nieuwere toeters en bellen, waarover later meer.)
Deze <img> heeft twee functies.
Als een oudere browser <source> niet kent, negeert die de in de <source>'s zittende srcset
. De browser gebruikt dan gewoon de <img>, want die kennen ze allemaal. Er kan dus altijd een afbeelding worden weergegeven.
De tweede functie heeft te maken met het eigenlijke weergeven van de afbeelding, die de browser heeft uitgekozen. Wat de browser eigenlijk doet, is het vervangen van de src uit de <img> door de src van de door de browser uitgekozen afbeelding. Als de browser bijvoorbeeld uit de tweede <source> de tweede afbeelding 'aardbeien-1400.jpg' heeft gekozen, staat hier wat de browser betreft
<img loading="lazy" src="055-pics/aardbeien-1400.jpg" alt="Aardbeien">
Hierdoor werken attributen als loading en alt gewoon, want uiteindelijk is het eigenlijk een doodgewone <img>, die wordt weergegeven.
Door bij elke <img> gebruik te maken van loading="lazy"
, worden niet alle afbeeldingen bij openen van de pagina al gedownload. Slechts een aantal afbeeldingen wordt gedownload, en pas bij scrollen worden er meer gedownload. Hiermee wordt voorkomen dat er afbeeldingen worden gedownload, die nooit worden bekeken. Wat een verspilling van bandbreedte zou zijn.
Omdat er altijd 'vooraf' al wat afbeeldingen worden gedownload, worden de afbeeldingen nog steeds snel getoond. Hoeveel afbeeldingen er 'vooraf' worden gedownload, verschilt per browser en is niet in te stellen. Oudere versies van iOS en iPadOS ondersteuning loading
niet, die negeren dit attribuut gewoon.
<div id="div-2" tabindex="0">
(...)
<a class="vorige-picture" href="#picture-1" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-picture" href="#picture-3" tabindex="-1" aria-hidden="true">→</a>
<a class="vorige-div" href="#div-1" tabindex="-1" aria-hidden="true">←</a>
<a class="volgende-div" href="#div-3" tabindex="-1" aria-hidden="true">→</a>
</div>
(Deze vier links zijn alleen zichtbaar in browservensters minimaal 760 px breed en minimaal 530 px hoog.)
Bij elke thumbnail staan vier knoppen naar de vorige en volgende thumbnail en grote afbeelding. Alleen de href
van deze links is bij elke thumbnail anders. (Bij de eerste thumbnail is de 'vorige' de laatste thumbnail of grote afbeelding, bij de laatste thumbnail is de 'volgende' de eerste thumbnail of grote afbeelding. De hierboven staande links horen bij de tweede thumbnail, de ananas.)
Gebruikers van de Tab-toets of van een schermlezers zouden hierdoor tussen elke grote afbeelding vier links tegenkomen, wat uiterst vervelend zou zijn. Daarom worden de vier links met behulp van tabindex="-1"
en aria-hidden="true"
verborgen voor gebruikers van Tab-toets en schermlezers. Het uitgebreidere verhaal hierover is iets hierboven bij <a class="eerste" href="#picture-1" ... te vinden, alleen geldt het nu voor deze vier links.
<span class="naam" aria-hidden="true">Aardbeien</span>
In deze <span> zit de naam, die bij thumbnail en bijbehorende grote afbeelding hoort. In browservensters minimaal 760 px breed en minimaal 530 px hoog is deze naam niet altijd zichtbaar (in kleinere vensters wel).
Zodra een thumbnail wordt geselecteerd door aanraken of -klikken, of door op een van de knoppen te drukken, krijgt de thumbnail een witte rand. Gelijktijdig verschijnt boven de rij met thumbnails de bijbehorende naam.
In kleinere browservensters is de grote afbeelding altijd zichtbaar, in grotere vensters is de thumbnail altijd zichtbaar. Die grote afbeeldingen voor kleinere vensters en de thumbnails voor grotere vensters komen uit dezelfde <picture>. Het laatste element in die <picture> is een <img>, die een alt-tekst heeft: de naam van de thumbnail (wat dus ook de naam van de grote afbeelding is).
Die alt-tekst wordt door schermlezers voorgelezen. Als nu deze naam ook zou worden voorgelezen, zou de naam twee keer worden voorgelezen. Met behulp van de WAI-ARIA-code aria-hidden="true"
wordt deze naam daarom voor schermlezers verborgen.
Hoe een <img> binnen een <picture> precies werkt, is te lezen bij Blijft er nog één element uit de <picture> over.
(TalkBack op Android leest de naam twee keer voor, zowel in kleine als in grotere browservensters. Dat heeft hier niets mee te maken, het gaat om een bug in TalkBack. Meer daarover is te vinden bij Probleem: TalkBack op Android leest bij elke afbeelding de naam twee keer voor.)
<section tabindex="0">
De tekst onder de slideshow staat binnen een <section>. Een <section> kan normaal genomen geen focus krijgen, maar door het gebruik van tabindex="0" kan dat hier wel. Als de <section> de focus krijgt, krijgt de tekst een blauw kader, zodat gebruikers van de Tab-toets weten dat ze nu de tekst hebben bereikt (en met pijltjes en dergelijke de tekst kunnen scrollen).
<h2 id="begin-tekst" tabindex="-1">Opvultekst</h2>
<a id="voor-sr" href="#begin-tekst" aria-label="Mogelijk is er nog een grote afbeelding geopend. Druk op Enter of dubbeltik op het scherm om deze te sluiten."></a>
In sommige schermlezers blijft een grote afbeelding geopend, als de tekst onder de slideshow wordt bereikt. Daardoor is de tekst grotendeels onzichtbaar. Daarom wordt bovenaan de tekst de mogelijkheid gegeven die afbeelding met behulp van link a#voor-sr
te sluiten. Die link gaat naar h2#begin-tekst
, die gelijk boven de tekst staat.
De grote afbeelding sluit altijd door het volgen van de link, maar sommige schermlezers gaan vervolgens terug naar het begin van de pagina in plaats van naar de <h2> boven de tekst. Door aan h2#begin-tekst
, het doel van de link, een tabindex="-1" te geven, gaat het altijd goed. Omdat de waarde van de tabindex '-1' is, hebben gebruikers van de Tab-toets hier geen last van.
De tekst binnen link a#voor-sr
staat binnen de WAI-ARIA-code aria-label
. Deze tekst is niet zichtbaar op het scherm, maar wordt wel voorgelezen door schermlezers.
De link werkt alleen, als er mogelijk een grote afbeelding is geopend. Als dat niet zo is, wordt de link verborgen.
Veel gebruikers van schermlezers navigeren door de pagina met behulp van de kopregels, de <h>'s. Daarom staat de link gelijk na de <h2>, die boven de tekst staat. Als een gebruiker met een schermlezer 'n grote afbeelding heeft geopend, en dan besluit verder te navigeren met behulp van de <h>'s, zal deze toch de link tegenkomen.
<p lang="la">
De Latijnse opvultekst onder de slideshow staat in <p>'s. De taal is van belang voor schermlezers, automatisch afbreken, automatisch genereren van aanhalingstekens, juist gebruik van decimale punt of komma, en dergelijke. Daarom wordt met lang="la"
aangegeven dat de tekst binnen deze <p> Latijn is. (En tot mijn niet geringe verbazing blijkt een schermlezer als NVDA dat dan, voor zover ik dat kan beoordelen, op de juiste manier voor te lezen.)
<script src="055-js-dl/afbeelding-055-dl.js"></script>
Vrijwel onderaan de pagina staat een koppeling naar een extern JavaScript.
Dit script gebruikt een groot aantal onderdelen van de pagina, zoals div#thumbs
en alle a#volgende-div
's. Om deze onderdelen te kunnen gebruiken, moeten ze eerst door de browser worden gemaakt. Daarom staat het script helemaal onderaan de html: je weet dan zeker, dat alle elementen al zijn aangemaakt.
Bovendien stopt de opbouw van de pagina, zodra de browser een script ontmoet, omdat het script eerst wordt uitgevoerd. Het zou immers kunnen dat het script (heel) belangrijk is voor de weergave. Dat is hier niet het geval. Door het script onderaan de pagina te zetten, voorkom je ook die in dit geval overbodige vertraging.
Als het script op slechts één pagina wordt gebruikt, zoals hier het geval is, kun je het hele script ook tussen <script>
en </script>
zetten, onderaan de pagina zelf. Als het op meer pagina's wordt gebruikt, is het beter om het in een eigen bestand te zetten. Het hoeft dan maar één keer te worden gedownload.
In dit geval had het script dus gewoon binnen de pagina kunnen worden gezet. Maar het is vanwege de overzichtelijkheid toch in een apart bestand gezet: nu kun je het apart bekijken.
CSS
De code is geschreven in een afwijkende
lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code) is in de hele uitleg onderstippeld blauw
. Alle niet-essentiële code is bruin
. (In de inhoudsopgave staat alles in een gewone letter vanwege de leesbaarheid.)
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 het kopje Gereedschap → Snelheid, testen, gzip, comprimeren (inclusief theorie) 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
/* afbeelding-055-dl.css */
Om vergissingen te voorkomen is het een goede gewoonte bovenaan het stijlbestand even de naam neer te zetten. Voor je het weet, zit je anders in het verkeerde bestand te werken.
body
Het element waarbinnen de hele pagina staat. Veel instellingen die hier worden opgegeven, worden geërfd door de nakomelingen van <body>. Ze gelden voor de hele pagina, tenzij ze later worden gewijzigd. Dit geldt bijvoorbeeld voor de lettersoort, de lettergrootte en de voorgrondkleur.
background: #ff9;
Achtergrondkleurtje.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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;
Slim om te doen vanwege verschillen tussen browsers.
#skippy
Het element met id="skippy". Dit is een link die normaal genomen onzichtbaar buiten het scherm staat. Hij is bedoeld voor gebruikers van de Tab-toets en – in mindere mate – voor gebruikers van schermlezers. Die kunnen met behulp van deze link in één keer de hele slideshow passeren. Een uitgebreider verhaal over een skip-link is te vinden bij <a id="skippy" href="begin-tekst">Skip slideshow</a>.
background: white;
Witte achtergrond.
width: 10em;
Breedte.
Als eenheid wordt de relatieve eenheid em
gebruikt, omdat bij een vaste eenheid als px
de breedte niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht de eenheid die je gebruikt.
font-size: 1.5em;
Grotere letter dan normaal.
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: 5em;
Regelhoogte. Omdat geen gewone hoogte is opgegeven, is dit tevens de hoogte van de 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. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.
text-align: center;
Tekst horizontaal centreren.
border: red solid 3px;
Opvallende rode rand.
position: absolute;
Om de <a> op de juiste plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als die er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het browservenster.
Een <a> is van zichzelf een inline-element, waardoor eigenschappen als breedte niet gebruikt kunnen worden. Door de <a> absoluut te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.
top: 8 rem;
8 rem vanaf de bovenkant neerzetten.
Als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de afstand tot de bovenkant van het browservenster niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
(Normaal genomen kun je een skip-link gewoon op een vaste afstand van de bovenkant neerzetten met iets als top: 20px;
. Hier levert het echter in sommige schermlezers een probleem op, als de skip-link over de slideshow komt te staan. Bij een grotere letter zou dat kunnen gebeuren, als je een absolute eenheid als px
gebruikt. Nu zakt de skip-link bij een grotere letter omlaag, waardoor deze onder de slideshow blijft staan.)
left: -20000px;
Ver links buiten het scherm parkeren, waardoor de link onzichtbaar is. Als de link door gebruik van de Tab-toets de focus krijgt, wordt deze bij #skippy:focus binnen het scherm gezet en daarmee zichtbaar.
z-index: 30;
Normaal genomen worden elementen in de volgorde van de html op het scherm gezet. Wat later in de html staat, wordt over eerdere elementen gezet. Om te zorgen dat de skip-link altijd bovenaan staat, krijgt deze een hogere z-index.
Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een absolute positie. Die is iets hierboven aan a#skippy
gegeven, dus dat is geregeld.
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div, .volgende-div, #voor-sr
Het element met id="uitleg" (de <div> waar de uitleg in staat); alle elementen met class="eerste", "laatste", "vorige-picture", "volgende-picture", "vorige-div" en "volgende-div" (de zes knoppen rechts bovenin het browservenster van grotere vensters); en het element met id="voor-sr" (een link speciaal voor schermlezers).
Al deze elementen worden alleen in browservensters minimaal 760 px breed en minimaal 530 px hoog gebruikt.
display: none;
Verbergen. Voor grotere browservensters worden deze elementen later weer zichtbaar gemaakt.
h1
Alle <h1>'s. Dat is er maar één: de belangrijkste kop van de pagina staat erin.
Eigenlijk is een kop hier 'n beetje overbodig, omdat ook zonder kop wel duidelijk is, wat dit is. En als dat niet zo is, is er nog het vraagteken met de daaronder zittende uitleg.
Deze kop is voor schermlezers bedoeld. Voor een gebruiker van een schermlezer is mogelijk niet gelijk duidelijk, wat dit is. En veel schermlezers navigeren door een pagina met behulp van de kopregels, de <h>'s. Die horen dan gelijk 'Slideshow'.
position: absolute;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als zo'n voorouder er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.
left: -20000px;
Ver links buiten het scherm parkeren. Deze kopregel is alleen voor schermlezers bedoeld, en die lezen hem gewoon voor, ook al staat hij onzichtbaar buiten het scherm.
#thumbs
Het element met id="thumbs". De <div> waar de eigenlijke slideshow in z'n geheel in zit.
display: flex;
De waarde flex
is onderdeel van een hele reeks eigenschappen en waarden, die bij elkaar 'flexbox' worden genoemd. Met display: flex;
wordt div#thumbs
in een zogenaamde 'flex container' veranderd. Dit maakt het veel makkelijker om de directe kinderen van dit element, de 'flex items', op een bepaalde plaats neer te zetten. De directe kinderen van #thumbs
zijn hier dertig <div>'s, met in elke <div> een thumbnail met bijbehorende grote afbeelding, knoppen, enzovoort: div#div-1
tot en met div#div-30
. (Naast deze dertig <div>'s zijn er nog 'n paar speciale elementen, die hier verder geen rol spelen.)
In dit geval komen de vier <div>'s naast elkaar te staan, omdat dat de standaardinstelling van flex-direction
is. Zou je hier flex-direction: column;
gebruiken, dan zouden flex items onder elkaar komen te staan.
De waarde van flex-direction
is van belang voor een aantal andere eigenschappen van flexbox, zoals het hieronder gebruikte align-items
.
align-items: start;
De directe kinderen van div#thumbs
, de dertig <div>'s met thumbnails en dergelijke, worden allemaal tegen de bovenkant van flex container div#thumbs
gezet, op gelijke hoogte. Hier is dat eigenlijk niet nodig, omdat de <div>'s allemaal even hoog zijn.
Omdat de standaardwaarde flex-direction: row;
niet is gewijzigd, staat 'start' in dit geval voor de bovenkant van div#thumbs
, de 'start' van de flex container.
overflow: auto;
Als geen breedte of zo is opgegeven, wordt een blok-element standaard automatisch even breed als de ouder ervan. Hier is die ouder <section>, ook een blok-element. De ouder van <section> is <main>, alweer een blok-element. Waarvan de ouder weer het blok-element <body> is. Met als laatste ouder het blok-element <html>.
Bij geen van deze blok-elementen is een breedte opgegeven, ze worden dus allemaal even breed als hun ouder. De laatste ouder is <html>, en die wordt normaal genomen automatisch even breed als het venster van de browser.
Uiteindelijk wordt div#thumbs
dus ook even breed als het browservenster.
De eigenschap overflow
regelt, wat er gebeurt als de inhoud van een blok-element groter is dan dat element zelf. Standaard is de waarde 'visible': ook al past de inhoud niet in het blok-element, geef het toch weer. Mogelijk wordt dan de lay-out verstoord, maar er verdwijnt in ieder geval geen tekst of zo.
De hier gebruikte waarde 'auto' wil zeggen dat er, als de inhoud niet in het blok-element past, gescrold kan worden. Deze css is bedoeld voor browservensters smaller dan 760 px en/of lager dan 529 px in landschapsstand (voor andere vensters wordt de css later aangepast).
Dit zullen meestal mobieltjes zijn in landschapsstand. Daarin zitten de grote afbeeldingen in één lange rij naast elkaar, dus veel te breed voor div#thumbs
: er kan horizontaal gescrold worden. In sommige browsers verschijnt hierbij ook een scrollbalk.
Hier is nog een andere reden voor overflow: auto;
: de iets hieronder staande eigenschap scroll-snap-type
kan alleen worden gebruikt in combinatie met overflow: auto;
of overflow: scroll;
.
margin-top: 5px;
Kleine afstand tot de bovenkant van het browservenster.
-webkit-overflow-scrolling: touch;
Normaal genomen zou hier ook nog de eigenschap overflow-scrolling
op volgen, zonder -webkit-
, maar dat is hier niet nodig. Meer hierover is te vinden bij De voorvoegsels -moz- en -webkit-.
Op iOS voor versie 13 werkt 'scroll to snap' niet, zonder de hierboven staande eigenschap. 'scroll to snap' is een verzameling eigenschappen die ervoor zorgen dat scrollen altijd zo stopt, dat een element helemaal zichtbaar is. Onder andere de gelijk hieronder gebruikte eigenschap scroll-snap-type
is onderdeel van deze verzameling.
scroll-snap-type: x mandatory;
Als de grote afbeeldingen horizontaal worden gescrold, zullen er heel vaak bij het stoppen met scrollen afbeeldingen slechts gedeeltelijk zichtbaar zijn. Deze eigenschap regelt dat een direct kind van div#thumbs
altijd volledig zichtbaar is, samen met de iets hieronder bij #thumbs > div:nth-of-type(n + 2) gebruikte eigenschap scroll-snap-align
.
De directe kinderen van div#thumbs
zijn voornamelijk de <div>'s met onder andere de grote afbeeldingen. Als die <div> volledig zichtbaar is, is ook de erin zittende grote afbeelding met bijbehorende naam volledig zichtbaar.
(Wat een direct kind is, is te vinden bij Elke thumbnail...)
'x' geeft de richting aan: horizontaal.
'mandatory' komt er min of meer op neer, dat áltijd op het juiste punt wordt gestopt met scrollen.
#thumbs h2
Alle <h2>'s binnen het element met id="thumbs". Dat is er hier maar één, bij het eigenlijke begin van de slideshow.
position: absolute;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als zo'n voorouder er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.
left: -20000px;
Ver links buiten het scherm neerzetten.
Hierdoor is de <h2> onzichtbaar. Maar schermlezers lezen de <h2> gewoon voor. En daar is deze <h2> ook voor bedoeld. Als de tekst wordt voorgelezen, is niet direct duidelijk, wanneer de uitleg eindigt en de slideshow begint. Deze <h2> geeft dat duidelijk aan.
Bovendien navigeren veel gebruikers van schermlezers met behulp van de <h>'in een pagina. Zonder 'n <h> aan het begin van de slideshow, zouden de de hele slideshow kunnen missen.
#thumbs > div:nth-of-type(n + 2)
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van div#thumbs-1
zijn. Onderstaande <div> is een direct kind van div#thumbs
:
<div id="thumbs">
<div>
</div>
De <img> hieronder is geen direct kind van div#thumbs
, omdat er een <div> tussen div#thumbs
en de <img> zit:
<div id="thumbs">
<div>
<img>
</div>
</div>
In elke <div> die een direct kind van div#thumbs
is, zit een thumbnail met bijbehorende knoppen, grote afbeelding, en dergelijke. Met één uitzondering: in de eerste <div> zitten alleen de knoppen naar vorige en volgende thumbnail en grote afbeelding, die worden getoond als de pagina wordt geopend of ververst.
div:nth-of-type(n + 2)
div
: alle <div>'s.
:nth-of-type
: kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de in deze selector zittende pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld.
(n + 2)
: tussen de haakjes staat aangegeven, om welke <div> of <div>'s het gaat.
Het getal '2' is gewoon een getal, daar is verder weinig geheimzinnigs aan.
De 'n' is een soort teller, die begint bij 0, en steeds met 1 wordt verhoogd. Bij de eerste keer is de 'n' 0, bij de tweede keer 1, bij de derde keer 2, enzovoort.
De eerste keer is het resultaat 0 + 2 = 2. Oftewel: de tweede <div>.
De tweede keer is het resultaat 1 + 2. Oftewel: de derde <div>.
De derde keer is het resultaat 2 + 2 = 4. Oftewel: de vierde <div>.
Bij de laatste berekening is het resultaat 29 + 2 = 31. Hoger is niet zinvol, want bij de volgende berekening zou de uitkomst 30 + 2 = 32 zijn, en er zijn maar twintig <div>'s.
In dit geval gaat het om de tweede en latere <div>. Dit zijn de <div>'s, waarin steeds een thumbnail met toebehoren zit.
Omdat voor :nth-of-type(n + 2)
een div
staat, worden alleen <div>'s geteld. Als binnen div#thumbs
327 <p>'s zitten, tellen die niet mee. Hadden ze maar 'n <div> moeten zijn.
De hele selector in gewone taal: doe iets met de tweede tot en met de laatste <div> die een direct kind van div#thumbs
zijn.
line-height: 0;
In deze <div>'s zit onder andere een <img>. Een <img> is een gewoon inline-element, met alleen wat bijzondere eigenschappen. Daarom krijgt het, net zoals gewone tekst, een kleine ruimte onder de afbeelding voor letters als de 'g' en de 'j', die iets onder de regel uitsteken. Die ruimte wordt hier weggehaald.
Omdat regelhoogte wordt geërfd, geldt dit voor alle nakomelingen van de <div>'s, tenzij dat wordt aangepast.
margin: 0 5px;
Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 0 5px 0 5px
in de volgorde boven – rechts – onder – links. Boven en onder geen marge, links en rechts 5 px, zodat er wat ruimte tussen de <div>'s zit.
outline: black solid 2px;
Zwart randje. Aan de boven- en onderkant valt dat randje weg, aan de bovenkant wordt dat later bij picture img geregeld, aan de onderkant bij .naam.
scroll-snap-align: center;
Bij #thumbs iets hierboven is met scroll-snap-type
opgegeven dat kinderen van div#thumbs
altijd helemaal zichtbaar moeten zijn. Hier wordt geregeld, op welke plaats dat precies is. 'center' zet de <div>'s precies in het midden van div#thumbs
. En daarmee ook de in deze <div>'s zittende grote afbeelding. Omdat div#thumbs
even breed is als het venster van de browser, staat hierdoor de grote afbeelding altijd horizontaal in het midden van het venster.
Je móét hier een waarde opgeven, want de standaardwaarde is 'none', waardoor het eerder gebruikte scroll-snap-type
ook nutteloos is geworden.
#thumbs > div:last-of-type
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.
#thumbs > div:nth-of-type(n + 2) {line-height: 0; margin: 0 5px; outline: black solid 2px; scroll-snap-align: center;}
Deze selector lijkt erg op #thumbs > div:nth-of-type(n + 2) iets hierboven. Alleen staat hier :last-of-type
in plaats van :nth-of-type(n + 2)
.
Bij die eerdere selector ging het om de tweede en verdere <div>'s die een direct kind van div#thumbs
zijn. Hier gaat het alleen om div:last-of-type
: de laatste <div> die een direct kind is van div#thumbs
.
margin-right: 50px;
Bredere marge rechts. Hierdoor is gelijk duidelijk, dat dit de laatste grote afbeelding is.
picture
Alle <picture>'s. In elke <picture> zit een serie grote afbeeldingen om te gebruiken in kleinere browservensters (daar is geen ruimte voor de thumbnails), én de thumbnail die wordt gebruikt in grotere vensters.
display: block;
Een <picture> is een inline-element. Daardoor kunnen eigenschappen als breedte en hoogte niet worden gebruikt. Door het te veranderen in een blok-element, kan dit wel.
height: 525px;
De hoogte van een blok-element wordt normaal genomen bepaald door de inhoud ervan. Standaard wordt een blok-element precies hoog genoeg om de inhoud ervan weer te kunnen geven. Die inhoud is hier de gebruikte grote afbeelding (voor grotere browservensters wordt dit later aangepast, want die gebruiken de thumbnail uit de <picture>).
Welke afbeelding er precies wordt gebruikt, hangt mede af van de resolutiedichtheid van het scherm. Bij deze hoogte worden alle afbeeldingen goed weergegeven. En omdat iets hieronder de afbeelding een hoogte van 100% van deze <picture> krijgt, zijn ze ook allemaal even groot.
Die hoogte bepalen is 'n beetje nattevingerwerk, je zou ook 'n andere hoogte kunnen nemen. Maar deze css is voornamelijk bedoeld voor mobieltjes in landschapsstand, en daar is deze hoogte ruim voldoende voor.
max-height: calc(90vh – 1.4rem);
Hier gelijk boven is een hoogte van 525 px aan de <picture> gegeven, en daarmee ook aan de erin zittende afbeelding. In browservensters die lager zijn dan 525 px zou hierdoor een deel van de afbeelding onder het venster komen te staan. Onder de afbeelding staat ook nog een naam, die ook binnen het venster moet passen. Daarom wordt hier een maximumhoogte opgegeven.
Met behulp van calc()
kunnen berekeningen worden gemaakt. In dit geval wordt de maximumhoogte berekend.
90vh
: de eenheid vh
is gebaseerd op de hoogte van het venster van de browser. 1 vh is 1% van de hoogte van het venster, en 90 vh is 90% van de hoogte.
1.4rem
: als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de hoogte van <picture> niet mee verandert met de lettergrootte. Als de lettergrootte van de onder de afbeelding staande naam wordt vergroot, is er minder ruimte voor de afbeelding. De hoogte van <picture> moet daaraan worden aangepast. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
De berekening wordt hier gemaakt met twee verschillende eenheden: vh en rem. Iedereen die zich weleens knollen voor citroenen heeft laten verkopen, weet dat van twee verschillende eenheden ellende komt. Dat is hier ook zo. Daarom rekent de browser de eenheden vh
en rem
om naar de eenheid px
.
Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet hoe hoog het browservenster is en wat de lettergrootte is. Maar op het moment van weergave weet de browser dat wel.
De berekening wordt dan 90% van de hoogte van het browservenster in px, min 1,4 keer de lettergrootte in px. Dat levert genoeg ruimte op om de grote afbeelding, de naam en eventueel een scrollbalk onder de grote afbeeldingen weer te geven.
picture img
Alle <img>'s binnen een <picture>.
In elke <picture> zitten drie sets met afbeeldingen: twee sets met grote afbeeldingen voor kleinere browservensters en één set met thumbnails voor grotere vensters. Welke afbeelding uiteindelijk gebruikt gaat worden, bepaalt de browser aan de hand van de grootte van het venster en de resolutiedichtheid van het scherm. Hoe de browser dit doet, is te vinden bij <picture "id=picture-1" tabindex="-1">.
max-height: 100%;
Een maximumhoogte in procenten is ten opzichte van de ouder van het element. Die ouder is hier <picture>, die gelijk hierboven een hoogte van 525 px heeft gekregen. En een maximumhoogte, voor het geval het browservenster lager is, zodat <picture> (en de naam onder de afbeelding) altijd binnen het venster past.
Omdat de <img> nooit hoger dan <picture> mag worden, zal de afbeelding ook altijd binnen het browservenster passen, en nooit hoger dan 525 px worden.
Deze css is bedoeld voor landschapsstand. Zelfs het allerkleinste mobieltje zal in landschapsstand breder dan 360 px zijn. Zoals beschreven bij <picture "id=picture-1" tabindex="-1"> is de kleinste afbeelding die dan gebruikt kan worden een afbeelding met een breedte van 700 px. En die hebben een hoogte van 525 px.
De gebruikte afbeelding zal dus altijd minimaal een hoogte van 525 px hebben, zodat de afmeting altijd de <picture> volledig zal vullen. (Als de afbeelding lager dan 525 px zou zijn, zou de afbeelding lager dan 525 px worden weergegeven, want hier is alleen een maximumhoogte voor de afbeelding opgegeven.)
Je zou de browser een kleinere afbeelding ook kunnen laten vergroten, maar dat levert vaak geen erg mooi resultaat op. Omdat alle afbeeldingen hier echter dezelfde grootte hebben, worden ze allemaal even groot weergegeven.
border-top: black solid 2px;
Bij #thumbs > div:nth-of-type(n + 2) is met behulp van outline
een zwart randje aan elke <div> met thumbnail, bijbehorende knoppen, en dergelijke gegeven. Aan de boven- en onderkant valt de outline weg, omdat die niet binnen div#thumbs
past. Daarom wordt hier aan de <img> aan de bovenkant een border gegeven. Die sluit precies aan op de outline van de <div>.
(Je kunt ook gaan pielen met extra marges en dergelijke, maar de ervaring leert dat dat al snel tot kleine verschillen tussen browsers leidt. En juist bij randjes is dat foeilelijk. Er is trouwens niet echt uitgeprobeerd of dat hier ook zo is, want deze oplossing is gewoon veel simpeler. Bovendien moet de rand aan de onderkant dunner worden, en dat kan nu ook heel simpel gelijk hieronder bij .naam worden geregeld.)
.naam
Alle elementen met class="naam". In span.naam
zit de naam van de afbeelding.
background: white;
Witte achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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;
Een <span> is van zichzelf een inline-element. Hierdoor kunnen eigenschappen als breedte niet worden gebruikt. Door de <span> weer te geven als een blok-element, kan dit wel.
width: 100%;
Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier de <div> met afbeeldingen, knoppen, en dergelijke, waar deze naam bij hoort. De <span> met de naam wordt dus precies even breed als de <div> met de grote afbeelding.
line-height: 1.4rem;
Regelhoogte. Omdat geen gewone hoogte is opgegeven, is dit gelijk de hoogte van de <span>.
Als eenheid wordt de relatieve eenheid em
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de regelhoogte niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.
text-align: center;
Tekst horizontaal in het midden zetten.
border-bottom: black solid 1px;
Bij #thumbs > div:nth-of-type(n + 2) is met behulp van outline
een zwart randje aan elke <div> met thumbnail, bijbehorende knoppen, naam en dergelijke gegeven. Aan de boven- en onderkant valt de outline weg, omdat die niet binnen div#thumbs
past. Daarom wordt hier aan span.naam
aan de onderkant een border gegeven. Die sluit precies aan op de outline van de <div>.
Door die border hier apart op te geven, kan de border ook een andere breedte krijgen dan de rest van de rand rondom <div> en afbeelding.
section:last-of-type
Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld. De pseudo-class :nth-last-of-type
, onderdeel van deze selector, werkt hetzelfde, maar telt van achter naar voren. Omdat het vaak voorkomt dat je het laatste element van 'n bepaald type wilt hebben, heeft dat een aparte pseudo-class: :last-of-type
.
section
: alle <section>'s.
:last-of-type
: het element met een bepaald volgnummer, geteld van achter naar voren.
In dit geval wordt geen volgnummer gebruikt, maar een speciaal voor het laatste element bedoelde selector: :last-of-type
. Voor alle eerdere elementen gebruik je 'n soortgelijke selector, maar dan met een volgnummer: :nth-last-of-type()
. Tussen de haakjes komt het volgnummer. :nth-last-of-type(1)
is precies hetzelfde als :last-of-type
, maar de laatste is wat mensvriendelijker.
Omdat voor :last-of-type
(of voor eerdere elementen :nth-last-of-type()
) een section
staat, worden alleen <section>'s geteld. Als binnen <main> 327 <p>'s zitten, tellen die niet mee. Hadden ze maar 'n <section> moeten zijn.
De hele selector in gewone taal: elke laatste <section> die de laatste <section> binnen z'n ouder is. Hier zijn maar twee <section>'s aanwezig, die beide een direct kind van <main> zijn. De laatste <section> is hier dus de tweede <section>, de <section> met de onder de slideshow staande tekst.
De pseudo-class :last-of-type
(of :nth-last-of-type()
) kan onverwachte bijwerkingen hebben. In dit geval is er maar één serie <section>'s aanwezig, alleen in <main>. Maar als binnen de pagina nog 'n serie <section>'s met gemeenschappelijke ouder aanwezig is, wat vaak zo zal zijn, zou deze selector ook voor die <section>'s gelden. (Dit zou je kunnen oplossen door het toevoegen van de ouder aan de selector, iets als main > section:last-of-type
. Dan geldt de selector alleen voor de <section> die de laatste <section> binnen <main> is.)
background: white;
Witte achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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: 90vw;
Breedte.
De eenheid vw
is gebaseerd op de breedte van het venster van de browser. 1 vw is 1% van de breedte van het venster, en 90 vw is 90% van de breedte. De <section> met de tekst wordt hierdoor nooit breder dan 90% van de breedte van het venster, ongeacht de breedte van het venster.
max-width: 600px;
Maximumbreedte.
Hier gelijk boven is een breedte aan de <section> gegeven van 90% van de breedte van het browservenster. Dat is daarmee ook de lengte van de regels met tekst. In bredere vensters is dat veel te lang om nog goed leesbaar te zijn. Daarom wordt hier een maximumbreedte opgegeven.
margin: 20px auto 0;
Omdat voor links geen waarde is opgegeven, staat krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 20px auto 0 auto
in de volgorde boven – rechts – onder links.
Aan de bovenkant een kleine afstand tot de slideshow. Onder geen marge.
Links en rechts auto
, wat hier hetzelfde betekent als evenveel. Hierdoor komt de <section> altijd horizontaal gecentreerd binnen z'n ouder <main> te staan. <main> is een blok-element en wordt daardoor normaal genomen automatisch even breed als z'n ouder <body>. Ook <body> is weer een blok-element dat dus normaal genomen even breed wordt als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser.
Hierdoor staat uiteindelijk de <section> met de tekst altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht de breedte van het venster. En daarmee ook de in de <section> zittende tekst.
border: black solid 1px;
Zwart randje rondom de <section>
padding: 5px;
Kleine afstand tussen de buitenkant van de <section> en de erin zittende tekst.
section:last-of-type h2
Alle <h2>'s binnen de laatste <section> van een serie. Een uitgebreidere uitleg over section:last-of-type
staat gelijk hierboven bij section:last-of-type. Het is de <section> met de onder de slideshow staande tekst.
font-size: 1.2em;
Standaard heeft een <h2> een vrij grote lettergrootte. Die wordt hier iets verkleind.
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: 0;
Standaard heeft een <h2> een marge aan boven- en onderkant. Die wordt hier weggehaald.
css voor vensters in portretstand en maximaal 759 px breed
@media screen and (orientation: portrait) and (max-width: 759px)
De css die hier tot nu toe staat, geldt voor alle browservensters. De css die hieronder staat, geldt alleen voor vensters in portretstand maximaal 759 px breed. In deze vensters worden geen thumbnails gebruikt en komen de grote afbeeldingen niet naast, maar onder elkaar te staan.
@media
: geeft aan dat het om css gaat, die alleen van toepassing is, als aan bepaalde voorwaarden wordt voldaan. Al langer bestond de mogelijkheid om met behulp van zo'n @media-regel css voor bijvoorbeeld printers op te geven. css3 heeft dat uitgebreid tot veel meer eigenschappen, zoals de breedte en hoogte van het venster van de browser.
screen
: deze regel geldt alleen voor schermweergave. Een slideshow printen is wat zinloos.
and
: er komt nog een voorwaarde, waaraan moet worden voldaan.
(orientation: portrait)
: het browservenster moet in landschapsstand zijn.
Een venster kent twee standen: landscape (landschap, liggend) en portrait (portret, staand). (Allerlei andere standen zoals bijvoorbeeld lezend in bed tellen hier niet mee. Liggend of staand, dat is het, al hang je met je tablet ondersteboven heen en weer te zwaaien aan de trapeze.)
and
: er komt nog een voorwaarde, waaraan moet worden voldaan.
(max-width: 759px)
: het browservenster mag niet breder zijn dan 759px. 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 (orientation: portrait) and (max-width: 759x; {
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 }
.
Als je nou 'n mobieltje hebt met een resolutie van – ik roep maar wat – 1024 x 768 px, dan geldt deze media mogelijk toch voor dat mobieltje. Terwijl dat toch echt meer dan 759 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 ppi. Als dat mobieltje een resolutie van 192 ppi heeft, 192 pixels per inch, zijn de pixels ervan twee keer zo klein als op een origineel beeldscherm. Er zijn per inch twee keer zoveel schermpixels aanwezig.
Om nu te voorkomen dat alles op dat mobieltje twee keer zo klein wordt, geeft het mobieltje niet het echte aantal schermpixels (1024 x 768), maar een lager aantal css-pixels door bij een media query. De 192 ppi van het mobieltje is twee keer zo veel als de 96 ppi van een normaal beeldscherm. Het aantal css-pixels is dan het aantal schermpixels gedeeld door 2. 1024 x 768 gedeeld door 2 is 512 x 384 px. Het aantal css-pixels is 512 x 384 px en zit daarmee onder de grens van deze media query.
Je bent dus niet opgelicht, of in ieder geval niet wat betreft het aantal pixel.
Door deze truc is een lijn van 1 px breed op een normaal beeldscherm ook op het mobieltje nog steeds 1 px breed, alleen wordt die ene (css‑)pixel opgebouwd uit twee schermpixels (feitelijk vier, want het verhaal geldt voor breedte én hoogte). De dikte van het lijntje is hetzelfde, maar het is veel fijner opgebouwd. Bij lijntjes is dat verschil bijvoorbeeld in bochten goed te zien.
Hetzelfde verhaal geldt voor hogere resoluties, Een tablet met een breedte van 4096 schermpixels en een ppi van 384 (vier keer de originele dichtheid) geeft 4096 gedeeld door 4 = 1024 css-pixel door. Het lijntje van 1 px breedte op de originele monitor is nog steeds 1 css-pixel breed op de tablet, maar die ene css-pixel is nu opgebouwd uit zestien 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.
#thumbs
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.
#thumbs {display: flex; align-items: start; overflow: auto; margin-top: 5px; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory;}
Het element met id="thumbs". De <div> waar de eigenlijke slideshow in z'n geheel in zit.
display: block;
Eerder is div#thumbs
met display: flex;
veranderd in een 'flex container'. Dat was handig in browservensters in landschapsstand, maar in portretstand mag #thumbs
weer een gewoon blok-element zijn. De grote afbeeldingen staan hier niet naast, maar onder elkaar.
height: 90hv;
Hoogte.
Standaard wordt een <div> precies hoog genoeg om de inhoud ervan weer te geven. In dit geval zijn dat de dertig grote afbeeldingen met bijbehorende naam. Als div#thumbs
even hoog wordt als de inhoud ervan, heeft het eerder bij #thumbs opgegeven overflow: auto;
geen effect meer, want er is helemaal geen overflow meer: de inhoud past gewoon.
Zonder overflow werkt de gelijk hieronder gebruikte eigenschap scroll-snap-type
niet. Daarom wordt hier een – tamelijk willekeurig gekozen – hoogte aan div#thumbs
gegeven.
De eenheid vh
is gebaseerd op de hoogte van het venster van de browser. 1 vh is 1% van de hoogte van het venster, en 90 vh is 90% van de hoogte. De <div> met de grote afbeeldingen wordt hierdoor nooit hoger dan 90% van de hoogte van het venster, ongeacht de hoogte van het venster.
scroll-snap-type: y proximity;
Als de grote afbeeldingen verticaal worden gescrold, zullen er heel vaak bij het stoppen met scrollen afbeeldingen slechts gedeeltelijk zichtbaar zijn. Deze eigenschap regelt dat een direct kind van div#thumbs
altijd volledig zichtbaar is, samen met de iets hieronder bij #thumbs > div:nth-of-type(n + 2) gebruikte eigenschappen scroll-margin-top
en scroll-snap-align
.
De directe kinderen van div#thumbs
zijn voornamelijk de <div>'s met onder andere de grote afbeeldingen. Als die <div> volledig zichtbaar is, is ook de erin zittende grote afbeelding met bijbehorende naam volledig zichtbaar.
(Wat een direct kind is, is te vinden bij Elke thumbnail...)
'y' geeft de richting aan: verticaal.
'proximity' komt er min of meer op neer, dat in principe op het juiste punt wordt gestopt met scrollen. Maar de browser zou hiervan kunnen afwijken, als dat beter uitkomt. In de praktijk is er nauwelijks verschil met 'mandatory' (verplicht altijd op het juiste punt stoppen met scrollen).
Maar als iemand een idioot hoog mobieltje heeft, zou de browser kunnen denken 'o, er zijn al drie volledige kinderen van div#thumbs
zichtbaar, dus stop maar waar je zin hebt. In Firefox bijvoorbeeld wordt in een tamelijk hoog browservenster bij snel scrollen netjes op de juiste plaats gestopt. Maar als je heel langzaam met de hand scrolt, kun je overal stoppen.
#thumbs > div:nth-of-type(n + 2)
Deze selector werkt alleen binnen vensters maximaal 759 breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#thumbs > div:nth-of-type(n + 2) {line-height: 0; margin: 0 5px; outline: black solid 2px; scroll-snap-align: center;}
#thumbs > div:last-of-type {margin-right: 50px;}
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter de >
moeten een direct kind van het element voor de >
zijn.
div:nth-of-type(n + 2)
: de tweede en latere <div> binnen ouder #thumbs
.
Binnen elk van deze <div>'s zit een thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke. Een uitgebreidere uitleg over wat een direct kind is en over :nth-of-type(n + 2)
, is te vinden bij #thumbs > div:nth-of-type(n + 2).
width: 700px;
Breedte.
css is bedoeld voor kleinere browservensters in portretstand. Hier komen de afbeeldingen onder elkaar te staan. Om zoveel mogelijk van het venster te gebruiken, wordt hier een tamelijk forse breedte opgegeven. Dit wordt ook de breedte van de grote afbeeldingen.
max-width: 96vw;
Hier gelijk boven is een breedte van 700 px opgegeven. Als het venster van de browser smaller is dan die 700 px, moet je horizontaal scrollen om alles te zien. Daarom wordt hier een maximumbreedte opgegeven.
De eenheid vw
is gebaseerd op de breedte van het venster van de browser. 1 vw is 1% van de breedte van het venster, en 96 vw is 96% van de breedte. De <div>, en dus de erin zittende grote afbeelding, wordt hierdoor nooit breder dan 96% van de breedte van het venster, ongeacht de breedte van het venster.
margin: 0 auto 20px;
Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 0 auto 20px auto
in de volgorde boven – rechts – onder – links.
Boven geen marge, aan de onderkant een kleine marge voor wat afstand tussen de grote afbeeldingen.
Links en rechts auto
, wat hier hetzelfde betekent als evenveel. Hierdoor komt de <div> altijd horizontaal gecentreerd binnen z'n ouder div#thumbs
te staan. div#thumbs
is een blok-element en wordt daardoor normaal genomen automatisch even breed als z'n ouder <section>. Ook een blok-element, dat dus normaal genomen ook weer even breed wordt als z'n ouder <main>. Ook <main> is een blok-element en wordt daardoor normaal genomen even breed als z'n ouder <body>. Het wordt eentonig: ook weer een blok-element dat dus normaal genomen even breed wordt als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser.
Hierdoor staan uiteindelijk ook deze <div>'s altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht de breedte van het venster. En daarmee ook de in de <div> zittende grote afbeelding.
scroll-snap-margin-top: 15px; scroll-margin-top: 15px;
Hier staat eigenlijk twee keer precies hetzelfde. In een eerdere ontwerpversie van de specificatie heette deze eigenschap scroll-snap-margin-top
. In de uiteindelijke versie is dit scroll-margin-top
geworden. Oudere versies van iOS en Safari op OS X gebruiken de oude naam nog.
Als een browser een eigenschap niet kent, wordt deze genegeerd. Oudere versies van iOS en Safari op OS X kennen de eerste naam en gebruiken die. De tweede naam kennen ze niet en negeren ze dus. Alle andere browsers kennen de tweede naam en gebruiken dus die, ook als ze ook de oudere naam nog kennen, want de tweede naam staat lager in de css en 'wint' daardoor van de eerste naam.
Hier gelijk onder wordt geregeld dat een grote afbeelding altijd bovenaan het browservenster komt te staan. Maar dat is wel heel erg tegen de bovenkant aan. Daarom wordt hier gezorgd voor een kleine afstand tussen de afbeelding en de bovenkant van het venster.
scroll-snap-align: start;
Bij #thumbs iets hierboven is met scroll-snap-type
opgegeven dat kinderen van div#thumbs
altijd helemaal zichtbaar moeten zijn. Hier wordt geregeld, op welke plaats dat is. 'start' zet de <div>'s precies bovenaan div#thumbs
. En daarmee ook de in deze <div>'s zittende grote afbeelding.
picture
Deze selector werkt alleen binnen vensters maximaal 759 breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
picture {display: block; height: 525px; max-height: calc(90vh – 1.4rem);}
Alle <picture>'s. In elke <picture> zit een serie grote afbeeldingen om te gebruiken in kleinere browservensters (daar is geen ruimte voor de thumbnails), én de thumbnail die wordt gebruikt in grotere vensters.
height: auto;
Voor weergave in kleinere browservensters in landschapsstand is eerder een hoogte van 525 px aan <picture> gegeven, zodat er voldoende ruimte was voor de naam onder de grote afbeelding.
In portretstand staan de grote afbeeldingen niet naast, maar onder elkaar. Daarom is er hier min of meer automatisch voldoende ruimte voor de naam, en daarom wordt hier de eerder opgegeven hoogte weggehaald.
(Waarschijnlijk kan die hoogte helemaal geen kwaad, maar misschien geeft het problemen in een of ander exotisch formaat van een browservenster. Simpeler dan testen in 346.468 groottes is het gewoon verwijderen van de hoogte. De ook eerder opgegeven maximumhoogte kan gewoon blijven staan, want in portretstand is er geen enkele <picture> die zelfs maar in de buurt komt van een hoogte van calc(90vh – 1.4rem)
.)
picture img
Deze selector werkt alleen binnen vensters maximaal 759 breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
picture img {max-height: 100%; border-top: black solid 2px;}
Alle <img>'s binnen een <picture>.
In elke <picture> zitten drie sets met afbeeldingen: twee sets met grote afbeeldingen voor kleinere browservensters en één set met thumbnails voor grotere vensters. Welke afbeelding uiteindelijke gebruikt gaat worden, bepaalt de browser aan de hand van de grootte van het venster en de resolutiedichtheid van het scherm. Hoe de browser dit doet, is te vinden bij <picture "id=picture-1" tabindex="-1">.
max-width: 100%;
Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier de <picture>, waar de <img> in zit.
Een <picture> is een inline-element, maar bij picture is het met display: block;
veranderd in een blok-element. Een blok-element wordt normaal genomen even breed als z'n ouder. De ouder van <picture> is de <div>, waar de thumbnail met bijbehorende knoppen, grote afbeelding, en dergelijke in zit. Die <div> heeft bij #thumbs > div:nth-of-type(n + 2) een breedte van 700 px gekregen, maar een maximumbreedte belet de <div> breder te worden dan het venster van de browser.
Uiteindelijk krijgt de <img> ook deze breedte: 700 px, maar nooit breder dan het venster van de browser.
In browservensters die minimaal 360 px breed zijn is de kleinste afbeelding, die de browser kan gebruiken, een afbeelding van 700 px breed. In smallere vensters is de afbeelding minimaal 350 px breed. De afbeelding is dus altijd breed genoeg om de hele <div> te vullen, zonder dat de browser de afbeelding moet vergroten. (Hoe de browser een afbeelding uitpikt, is te vinden bij <picture "id=picture-1" tabindex="-1">.)
border-top: none;
Eerder is aan de bovenkant van de afbeelding een border opgegeven. Die was in landschapsstand nodig, maar leidt in portretstand tot een dubbele border aan de bovenkant van de grote afbeelding. Daarom wordt die border hier weggehaald.
#thumbs > div:nth-of-type(2) img
Deze selector werkt alleen binnen vensters maximaal 759 breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
picture img {max-height: 100%; border-top: black solid 2px;}
picture img {max-width: 100%; border-top: none;}
#thumbs
: het element met id = "thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: alle <div>'s binnen #thumbs
, die een direct kind van #thumbs
zijn.
:nth-of-type(2)
: het element met een bepaald volgnummer. Het volgnummer staat tussen de haakjes. In dit geval is het volgnummer twee. Het gaat hier dus om de tweede <div> binnen de #thumbs
.
Omdat voor :nth-of-type(2)
een div
staat, worden alleen <div>'s geteld. In deze <div> zit de eerste thumbnail met bijbehorende knoppen, grote afbeelding, en dergelijke.
img
: de <img>'s binnen die tweede <div>. Dat is er maar één. In een kleiner browserscherm heeft de browser in deze <img> een grote afbeelding gezet.
(De browser kiest uit een aantal mogelijkheden de juiste afbeelding voor de grootte van het browservenster en de resolutiedichtheid van het scherm. Hoe dat gebeurt, is te vinden bij <picture "id=picture-1" tabindex="-1">.)
De hele selector in gewone taal: de <img> binnen de tweede <div> binnen de #thumbs
.
border-top: black solid 2px;
Het zwarte randje rondom grote afbeelding en naam is eigenlijk geen border, maar een outline bij elke <div>, waar een afbeelding, naam, enzovoort in zit. Deze outline valt aan de bovenkant van de eerste <div> weg. Daarom krijgt de eerste grote afbeelding toch weer een zwart randje aan de bovenkant.
.naam
Deze selector werkt alleen binnen vensters maximaal 759 breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
.naam {background: white; color: black; display: block; width: 100%; line-height: 1.4rem; text-align: center; border-bottom: black solid 1px;}
Alle elementen met class is 'naam'. In span.naam
zit de naam van de afbeelding.
border-bottom: none;
Eerder is aan de onderkant van span.naam
een border gegeven. Die was in landschapsstand nodig, maar leidt in portretstand tot een dubbele border aan de onderkant van de naam. Daarom wordt die border hier weggehaald.
css voor vensters minimaal 760 px breed en minimaal 530 px hoog
@media screen and (min-width: 760px) and (min-height: 530px)
De opbouw van deze regel staat beschreven bij @media screen and (orientation: portrait) and (max-width: 759px). Er zijn drie verschillen:
(orientation: portrait)
vervalt: het maakt niet uit of het browservenster in landschapsstand of portretstand staat;
(max-width: 759px)
(maximaal 759 px breed) is veranderd in (min-width: 760px)
(minimaal 760 px breed);
(min-height: 530px)
(minimaal 530 px hoog) is een nieuwe voorwaarde.
De css binnen deze mediaregel geldt voor browservensters minimaal 760 px breed en minimaal 530 px hoog. Voor alle andere vensters heeft de css geen enkel effect.
In deze grotere browservensters zijn bij openen van de pagina alleen thumbnails te zien. Grote afbeeldingen worden alleen op verzoek getoond.
body
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
body {background: #ff9; color: black; font-family: Arial, Helvetica, sans-serif; margin: 0;}
font-size: 110%;
Iets groter dan standaard in deze grotere browservensters. '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.
main:focus
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Normaal genomen kunnen alleen links, knoppen, en dergelijke de focus krijgen, en een element als <main> niet. Omdat aan <main> het attribuut tabindex="-1" is toegevoegd, kan het JavaScript toch aan <main> de focus geven.
outline: none;
Als een element de focus krijgt, krijgt het een kadertje dat dat aangeeft. Dat is hier overbodig en foeilelijk. Die focus is hier alleen nodig om een probleem in Safari op OS X en Firefox op te lossen. Daarom is het hier niet nodig aan te geven dat <main> de focus heeft. Normaal genomen moet je dat kadertje nooit zonder meer verwijderen, want gebruikers van de Tab-toets hebben dan geen idee meer, waar op de pagina ze zijn.
#skippy:focus
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#skippy {background: white; width: 10em; font-size: 1.5em; line-height: 5em; text-align: center; border: red solid 3px; position: absolute; top: 8rem; left: -20000px; z-index: 30;}
Het element met id="skippy", maar alleen als dit de focus heeft.
Dit is een link die normaal genomen onzichtbaar buiten het scherm staat. Hij is bedoeld voor gebruikers van de Tab-toets en – in mindere mate – voor gebruikers van schermlezers. Die kunnen met behulp van deze link in één keer de hele slideshow passeren. Een uitgebreider verhaal over een skip-link is te vinden bij <a id="skippy" href="begin-tekst">Skip slideshow</a>.
left: 100px;
Link binnen het venster van de browser zetten, waardoor deze zichtbaar wordt. Zodra #skippy
bij een volgende Tab de focus weer verliest, verdwijnt de link weer, zodat de lay-out niet wordt verstoord.
#uitleg
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
Het element met id="uitleg". Binnen div#uitleg
staan het vraagteken en de korte uitleg. En omdat deze <div> er toch is, wordt de <div> ook gebruikt voor de grijze achtergrond tussen vraagteken en de andere knoppen.
background: #ddd;
Grijze achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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;
De eerder met display: none;
verborgen <div> zichtbaar maken.
height: 3rem;
Hoogte.
Als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de hoogte van de <div> niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
border: black solid 1px;
Zwart randje.
position: fixed;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is bij een fixed positie normaal genomen het venster van de browser. Hierdoor scrolt de <div> niet mee met de pagina.
De in div#uitleg
zittende span#vraagteken
en div#hulptekst
worden gepositioneerd ten opzichte van deze div#uitleg
. Om nakomelingen van div#uitleg
te kunnen positioneren ten opzichte van de <div>, moet de <div> zelf een andere positie dan statisch hebben.
Ltop: 0;
Helemaal bovenaan het browservenster zetten.
right: 294px;
Op 294 px vanaf de rechterkant van het browservenster zetten. De rechterkant van de <div> staat nu precies tegen de zes knoppen rechtsboven aan. Omdat iets hierboven een grijze achtergrondkleur aan de <div> is gegeven, vult de grijze achtergrondkleur alle lege ruimte tussen de knoppen.
Helemaal rechts neerzetten kan niet, want dan zouden sommige knoppen onder de grijze achtergrond verdwijnen, afhankelijk van z-index
en dergelijke bij die knoppen.
left: 0;
Helemaal links neerzetten.
Omdat de knop met het vraagteken binnen deze div#uitleg
zit, levert helemaal links neerzetten geen probleem met de grijze achtergrond van de <div> op.
z-index: 10;
Normaal genomen worden elementen in de volgorde van de html op het scherm gezet. Wat later in de html staat, wordt over eerdere elementen gezet. In dit geval zou de hulptekst gedeeltelijk verdwijnen achter de tekst, die onder de thumbnails staat.
Door div#uitleg
een hogere z-index te geven, wordt dat voorkomen. div#uitleg
, en daarmee ook de nakomelingen ervan, staat nu boven de tekst.
Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een fixed positie. Die is iets hierboven aan de <div> gegeven, dus dat is geregeld.
#vraagteken
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Het element met id="vraagteken". De <span> linksboven met het vraagteken, waaronder de hulp zit.
background: white;
Witte achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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.
cursor: help;
Bij hoveren over het vraagteken verandert de cursor in het symbool voor hulp. Dit wordt alleen zichtbaar, als je met muis of touchpad over het vraagteken hovert.
width: 48px;
Breedte.
Als eenheid is de absolute eenheid px genomen, omdat deze niet mee verandert met de lettergrootte. Als de knop met het vraagteken (en de zes knoppen rechts) breder zouden worden bij een grotere letter, is er op een gegeven moment te weinig ruimte tussen de knoppen om de naam van de afbeelding weer te geven.
font-size: 2rem;
Lettergrootte.
Dit is twee keer de lettergrootte van de rest van de pagina, zodat het vraagteken opvalt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
line-height: 3rem;
Regelhoogte. Omdat voor de gewone hoogte niets is opgegeven, is dit tevens de hoogte van de <span>.
Als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de regelhoogte niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De hoogte van de <span> mag, in tegenstelling tot de breedte, wel mee veranderen met de lettergrootte. Omdat bij een grotere letter de rij met thumbnails ook lager komt te staan, levert dat geen probleem op.
De eenheid rem
wordt gelijk hierboven bij font-size
beschreven.
text-align: center;
Vraagteken horizontaal centreren.
border-right: black solid 1px;
Randje rechts.
position: absolute;
Om de <span> op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier div#uitleg
, die bij #uitleg fixed is gepositioneerd.
top: 0;
Helemaal bovenin div#uitleg
zetten, en daarmee ook helemaal bovenaan het venster van de browser.
#hulptekst
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Het element met id="hulptekst". De <div> met de uitleg.
background: white;
Witte achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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: 400px;
Breedte.
max-height: 60vh; overflow: auto;
Een <div> wordt normaal genomen automatisch precies hoog genoeg om de inhoud ervan weer te kunnen geven. Dat is hier ook zo. Alleen wordt de <div> daardoor hoger dan het venster van de browser. Door de combinatie van ouder div#thumbs
met een fixed positie en kind div#hulptekst
met een absolute positie, verdwijnt wat niet binnen het venster past aan de onderkant. En is daardoor niet te lezen. Je kunt natuurlijk je monitor kantelen of voor je verjaardag een superhoog apparaat vragen, maar dat is toch allemaal wat omslachtig en zo.
Daarom wordt aan de <div> met de uitleg een maximumhoogte gegeven, in combinatie met overflow: auto;
. Als de inhoud van de <div>, de hulptekst, hoger is dan de maximumhoogte, zorgt overflow: auto;
ervoor dat je kunt scrollen voor de rest. Op sommige browsers verschijnt een verticale scrollbalk.
De eenheid vh
is gebaseerd op de hoogte van het venster van de browser. 1 vh is 1% van de hoogte van het venster, en 60 vh is 60% van de hoogte. De <div> met de uitleg wordt hierdoor nooit hoger dan 60% van de hoogte van het venster, ongeacht de hoogte van het venster.
-webkit-overflow-scrolling: touch;
eze eigenschap is nodig om een probleem op iOS ouder dan versie 13 op te lossen. Daarom is het hier geen probleem dat alleen de eigenschap met het voorvoegsel ‑webkit-
wordt gebruikt, want deze eigenschap wordt alleen door browsers op iOS ouder dan versie 13 herkend. Meer over voorvoegsels is te vinden bij De voorvoegsels -moz- en -webkit-.
De ouder van div#hulptekst
is div#uitleg
, die bij #uitleg fixed is gepositioneerd. Zodra een element of een voorouder daarvan fixed is gepositioneerd, kan er vaak nauwelijks nog gescrold worden op iOS. Dit doet zich niet altijd voor, maar hier wel bij de <div> met de hulptekst.
Als je de hulptekst wilt scrollen, gaat dat ongelooflijk houterig. Je kunt uiteindelijk wel overal komen, maar vraag niet hoe. Een jichtige olifant die de chachacha danst, met als partner een giraf met een nekhernia, is een toonbeeld van soepelheid en elegantie, vergeleken met het scrollen van een fixed element op iOS voor versie 13.
Om een of andere reden lost deze eigenschap het probleem met het scrollen op.
Maar deze eigenschap heeft de meest wilde bijwerkingen. Heel vaak verdwijnen er gewoon delen van de pagina bij gebruik van deze eigenschap, of een link werkt niet meer, of... Het regende hier klachten over, maar zoals gewoonlijk reageerde Apple nergens op. Allerlei mensen bedachten hier zelf oplossingen voor, maar die werkten nooit in alle gevallen.
Als je deze eigenschap gebruikt, moet je dus echt heel goed testen, of er geen dingen verdwijnen en op andere bijwerkingen. Een vrijwel altijd voorkomende bijwerking is dat het element, waarbij deze eigenschap gebruikt wordt, niet meer ingezoomd én gescrold kan worden. Dat is hier ook het geval: als je inzoomt, kun je de hulptekst niet meer scrollen. Je kunt wel eerst scrollen, dan inzoomen, lezen, uitzoomen, weer 'n stuk scrollen, weer inzoomen, enzovoort, maar dat is nogal omslachtig.
Ook hier regende het klachten over, maar omdat Apple gewoon nooit ergens op reageert, was zelfs onduidelijk of dit zo hoorde, of het een bug was, wat dan ook. Applefans noemen dit soort dingen trouwens meestal een feature. Hoe dan ook: in versie 13 van iOS is het gelukkig eindelijk opgelost. Daarin is deze eigenschap niet meer nodig en wordt zelfs volledig genegeerd.
border: black solid 2px;
Zwart randje.
padding: 5px 5px 200px 5px;
Boven, rechts en links kleine afstand tussen de buitenkant van de <div> en de tekst erin. Onderaan een grotere padding, zodat duidelijk is dat dit het einde van de tekst is.
position: absolute;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Dat is hier div#uitleg
, die bij #uitleg fixed is gepositioneerd.
top: -10000px; left: -20000px;
Ver boven en links buiten het scherm zetten.
Normaal genomen is het voldoende om iets alleen links buiten het scherm neer te zetten, als je het wilt verbergen. div#hulptekst
kan echter door het gebruik van tabindex="0" de focus krijgen. Als de <div> alleen links buiten het scherm zet en je raakt het scherm aan op de hoogte van div#uitleg
, reageren sommige mobiele browsers daar toch op, ook al staat div#hulptekst
buiten het scherm. Een link op die hoogte werkt dan bijvoorbeeld niet meer. Daarom wordt de <div> niet alleen links buiten, maar ook boven buiten het scherm gezet.
#hulptekst h2
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Alle <h2>'s binnen het element met id="hulptekst". Hierin zit de kop boven de hulptekst.
font-size: 1.2em;
Standaard heeft een <h2> een vrij grote letter. Hier wordt de lettergrootte iets verkleind.
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: 0;
Standaard heeft een <h2> een marge aan boven- en onderkant. Die wordt hier weggehaald.
#hulptekst p
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
<p>'s binnen het element met id="hulptekst". De paragrafen met de hulptekst.
text-indent: 15px;
Elke eerste regel van de <p> 15 px laten inspringen. Je zou dit ook met behulp van een <span> kunnen doen, maar dan moet je precies weten hoelang die eerste regel is. En dat weet je niet, want bij een grotere of kleinere letter wordt de eerste regel langer of korter. Nu regelt de browser het en wordt het automatisch aan de lettergrootte aangepast.
margin: 0;
Standaard heeft een <p> een marge aan boven- en onderkant. Die wordt hier weggehaald.
#hulptekst h3
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Alle <h3>'s binnen het element met id="hulptekst". Hierin zitten de subkopjes boven de hulptekst.
font-size: 1em;
Standaard heeft een <h3> een iets grotere letter. Hier wordt de lettergrootte iets verkleind.
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: 0;
Standaard heeft een <h3> een marge aan boven- en onderkant. Die wordt hier weggehaald.
#uitleg:focus-visible #vraagteken, #hulptekst:focus-visible
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#vraagteken {background: white; color: black; cursor: help; width: 48px; font-size: 2rem; text-align: center; border-right: black solid 1px; line-height: 3rem; position: absolute; top: 0;}
#hulptekst {background: white; color: black; width: 400px; max-height: 60vh; overflow: auto; -webkit-overflow-scrolling: touch; border: black solid 2px; padding: 5px 5px 200px 5px; position: absolute; top: -10000px; left: -20000px;}
Dit zijn twee selectors, gescheiden door een komma.
De eerste selector #uitleg:focus-visible #vraagteken
:
#uitleg
: het element met id="uitleg". De <div> waar vraagteken en uitleg in staan.
:focus-visible
: maar alleen als dit element de focus heeft, én als zichtbaar moet zijn dat dit element de focus heeft.
Voor gebruikers van de Tab-toets is uiterst belangrijk dat ze kunnen zien, waar op de pagina ze zijn. Links, knoppen, en dergelijke kunnen door gebruik van de Tab-toets de focus krijgen. Als een link de focus heeft, wordt die link gevolgd door het indrukken van Enter. Als een keuzevakje (checkbox) de focus heeft, kan dit worden aan- of uitgevinkt met de Spatiebalk. Enzovoort.
Welk element de focus heeft, wordt aangegeven door een kadertjes. Als je dat kadertje weghaalt, kan een gebruiker van de Tab-toets niet meer zien, waar op de pagina die zich bevindt. Daarom mag je dat kadertje nooit zonder meer weghalen.
Maar een gebruiker van de muis of van een touchscreen heeft dat kadertje helemaal niet nodig, want die weet, waar met de muis is geklikt, of waar het scherm is aangeraakt. En voor het mooie hoef je dat kadertje niet te tonen. Het moet opvallen en is daardoor in de regel knap storend, als het overbodig is.
:focus-visible
lost dit op. Het kadertje wordt alleen weergegeven, als dat zinvol is. Op touchscreens en bij gebruik van de muis wordt het niet wordt weergegeven, en bij gebruik van de Tab-toets wel.
#vraagteken
: het element met id="vraagteken". De <span> met het vraagteken.
De eerste selector samengevat in gewone taal: doe iets met de in div#uitleg
zittende span#vraagteken
, maar alleen als div#uitleg
de focus heeft én als die focus zichtbaar moet zijn.
De tweede selector #hulptekst:focus-visible
:
#hulptekst
: het element met id="hulptekst". De <div> met de uitleg.
:focus-visible
: maar alleen als dit element de focus heeft, én als zichtbaar moet zijn dat dit element de focus heeft. (Alleen als de bezoeker de Tab-toets gebruikt, meer over :focus-visible is iets hierboven te vinden bij :focus-visible.)
De tweede selector samengevat in gewone taal: doe iets met div#hulptekst
, maar alleen als div#hulptekst
de focus heeft én als die focus zichtbaar moet zijn.
outline: blue solid 3px;
Blauwe outline.
De uitleg is te lang om in z'n geheel te zien, daarom moet deze gescrold kunnen worden. Door de <div> met de uitleg een outline te geven, is voor gebruikers van de Tab-toets duidelijk dat dit element de focus heeft en met behulp van de pijltjes gescrold kan worden.
outline-offset: -3px;
Outline iets naar binnen plaatsen. Omdat de uitleg helemaal aan de linkerkant van het browservenster staat, valt de outline aan de linkerkant weg. Door de outline iets naar binnen te verplaatsen, is deze overal zichtbaar.
#uitleg:focus-within #hulptekst
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#hulptekst {background: white; color: black; width: 400px; max-height: 60vh; overflow: auto; -webkit-overflow-scrolling: touch; border: black solid 2px; padding: 5px 5px 200px 5px; position: absolute; top: -10000px; left: -20000px;}
#uitleg:focus-visible #vraagteken, #hulptekst:focus-visible {outline: blue solid 3px; outline-offset: -3px;}
#uitleg
: het element met id="uitleg". De <div> met het vraagteken en de uitleg.
:focus-within
: als dit element of een van de nakomelingen ervan de focus heeft.
#hulptekst
: het element met id="hulptekst". De <div> met de uitleg.
De selector in gewone taal: doe iets met de in div#uitleg
zittende div#hulptekst
, maar alleen als div#uitleg
of een van de nakomelingen ervan de focus heeft.
Een <div> kan normaal genomen geen focus krijgen, maar door gebruik van het attribuut tabindex="-1" bij div#uitleg
kan dat hier wel. In div#uitleg
zit span#vraagteken
met het vraagteken. Door het aanraken of -klikken van het vraagteken, of door gebruik van de Tab-toets, krijgt div#uitleg
de focus en moet de uitleg worden getoond.
Omdat de uitleg te lang is om in z'n geheel te kunnen zien, moet deze gescrold kunnen worden. Dat kan scrollen kan met de vinger, met de muis, en met behulp van de pijltjestoetsen (nadat er met de Tab-toets naartoe is gegaan). Maar op dat moment krijgt div#hulptekst
de focus en verliest div#uitleg
de focus. Dus de uitleg sluit. Ietwat sadistisch: net als je enthousiast wilt gaan scrollen, verdwijnt de uitleg.
Daarom wordt de uitleg getoond, als div#uitleg
zelf óf een van de nakomelingen van div#uitleg
de focus heeft. Nu blijft de uitleg ook geopend als div#uitleg
de focus heeft, als wanneer div#hulptekst
de focus heeft.
Bijkomend voordeel: de tekst van de uitleg kan nu ook worden geselecteerd en gekopieerd. Nou is deze tekst natuurlijk geen literatuur van wereldklasse, dus de kans dat iemand dit zou willen kopiëren, is vrij klein. Maar als het geschreven zou zijn door een reïncarnatie van Shakespeare, zou het ook kunnen. En daar zou misschien wel belangstelling voor zijn.
top: calc(3rem + 67px); left: -1px;
Bij #hulptekst is de tekst boven en links buiten het scherm neergezet. Normaal genomen is het voldoende om iets alleen links buiten het scherm neer te zetten, als je het wilt verbergen. div#hulptekst
kan echter door het gebruik van tabindex="0" de focus krijgen. Als je de <div> alleen links buiten het scherm zet en je raakt het scherm aan op de hoogte van div#uitleg
, reageren sommige mobiele browsers daar toch op, ook al staat div#hulptekst
buiten het scherm. Een link op die hoogte werkt dan bijvoorbeeld niet meer. Daarom wordt de <div> niet alleen links buiten, maar ook boven buiten het scherm gezet. Om de <div> te tonen moet deze daarom zowel in de hoogte als in de breedte worden verplaatst.
top
: met behulp van calc()
wordt de juiste hoogte berekend. De berekening wordt hier gemaakt met twee verschillende eenheden: rem
en px
. Dat kan niet, daarom worden beide eenheden eerst omgerekend naar px
. Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet hoe groot de lettergrootte (de rem
) is. Maar op het moment van weergave weet de browser dat wel.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
De berekening wordt dan drie keer de door de bezoeker ingestelde lettergrootte, plus 67 px. Op deze hoogte komt de uitleg (ongeveer) gelijk onder de thumbnails te staan. Ongeveer, want er zijn kleine afwijkingen tussen systemen en browsers.
left
: deze is simpel: gewoon links in het browservenster neerzetten. Links, dat is toch gewoon left: 0;
? En hier staat left: -1px;
. Ja, sorry, het woord 'simpel' is misplaatst.
div#hulptekst
is absoluut gepositioneerd. Dat gebeurt ten opzichte van – in dit geval – div#uitleg
, want dat is de eerste voorouder die fixed is gepositioneerd. En div#uitleg
heeft bij #uitleg een border van 1 px breed gekregen. Bij positioneren telt de border niet mee: div#hulptekst
wordt tegen de border van voorouder div#uitleg
aangezet, en staat daarmee 1 px te veel naar rechts.
Je ziet die border van div#uitleg
niet, omdat div#uitleg
heel laag is. Voor het positioneren maakt dat echter niets uit. Dit levert een lelijk kiertje van 1px op tussen linkerkant van de uitleg en linkerkant van het browservenster. Door als waarde -1px
te gebruiken, wordt dit gecorrigeerd.
En bij deze is plechtig beloofd dat het woord 'simpel' nooit meer gebruikt wordt, als het om css gaat.
#thumbs
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#thumbs {display: flex; align-items: start; overflow: auto; margin-top: 5px; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory;}
Het element met id="thumbs". De <div> waar de eigenlijke slideshow in z'n geheel in zit.
background: #444;
Donkergrijze achtergrond.
color: black;
Voorgrondkleur zwart. Dit is onder andere de kleur van de tekst.
Hoewel dit de standaardkleur is, wordt deze toch specifiek opgegeven. Hierboven is een achtergrondkleur opgegeven. Sommige mensen hebben zelf de voorgrond‑ en/of achtergrondkleur veranderd, bijvoorbeeld omdat ze slecht kleuren kunnen onderscheiden. Als nu de achtergrondkleur wordt veranderd, maar niet de voorgrondkleur, loop je het risico dat tekstkleur en achtergrondkleur te veel op elkaar gaan lijken.
Door beide op te geven, is redelijk zeker dat achtergrond- en tekstkleur genoeg van elkaar blijven verschillen. Als de gebruiker !important
heeft gebruikt in een eigen 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.
margin-top: 3rem;
Afstand tot de bovenkant van het venster van de browser.
In div#thumbs
zit onder andere de rij thumbnails. Deze moet onder de knoppen en de eventuele naam komen te staan. De elementen met de knoppen en de naam zijn bij #uitleg en .eerste .laatste .vorige-picture, ... allemaal fixed gepositioneerd.
Voor de rest van de pagina bestaan fixed elementen niet. Als je div#thumbs
gewoon bovenaan neerzet, komt die daardoor op dezelfde plaats als de eventuele naam en de knoppen te staan.
div#thumbs
is zelf niet gepositioneerd. Elementen met een fixed positie worden altijd boven niet-gepositioneerde elementen gezet, ongeacht waar ze in de html staan.

Het tot grote droefenis stemmende resultaat is op de afbeelding te zien: de thumbnails verdwijnen ondanks heldhaftig verzet vrijwel volledig onder hun asociale bovenburen.
Daarom wordt div#thumbs
op een afstand van 3 rem vanaf de bovenkant van het venster van de browser neergezet. 3 rem is dezelfde hoogte als de knoppen en dergelijke hebben gekregen. Daardoor blijft div#thumbs
, en daarmee de thumbnails, altijd netjes onder de knoppen en dergelijke staan, ook bij een andere lettergrootte.
Als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de hoogte van de marge niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
border-bottom: black solid 1px.
Zwart randje.
Onder div#thumbs
staat in sommige browsers een horizontale scrollbalk. Niet elke browser geeft de onderkant van die scrollbalk duidelijk aan. Vandaar het randje aan de onderkant.
padding: 5px 0 3px;
Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 5px 0 3px 0
in de volgorde boven – rechts – onder – links.
Aan boven- en onderkant wat ruimte tussen de buitenkant van div#thumbs
en de inhoud ervan, waaronder de rij thumbnails.
scroll-snap-type: none;
Eerder is voor kleinere browservensters opgegeven dat een grote afbeelding na scrollen altijd volledig zichtbaar moet zijn. Hier worden de grote afbeeldingen niet gescrold, ze staan altijd midden in het venster.
De thumbnails kunnen wel worden gescrold, maar die zijn zo klein, dat het niet uitmaakt waar ze tot stilstand komen. Daarom wordt die eerdere eigenschap hier uitgeschakeld.
#thumbs > div:nth-of-type(n + 2)
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#thumbs > div:nth-of-type(n + 2) {line-height: 0; margin: 0 5px; outline: black solid 2px; scroll-snap-align: center;}
#thumbs > div:last-of-type {margin-right: 50px;}
De tweede en latere <div> die een direct kind van het element met id="thumbs" zijn. Dit zijn de <div>'s waarbinnen een thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke zit.
Een uitgebreidere verklaring van deze selector is te vinden bij #thumbs > div:nth-of-type(n + 2).
margin: 0 3px 2px;
Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 0 3px 2px 3px
in de volgorde boven – rechts – links – onder.
Boven geen marge. Links en rechts een marge van 3 px voor wat afstand tussen de <div>'s, en daarmee tussen de erin zittende thumbnails. Onderaan een kleine afstand tot de onderkant van ouder div#thumbs
.
picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
picture {display: block; height: 525px; max-height: calc(90vh - 1.4rem);}
Alle <picture>'s. In elke <picture> zit een serie grote afbeeldingen om te gebruiken in kleinere browservensters (daar is geen ruimte voor de thumbnails), én de thumbnail die wordt gebruikt in grotere vensters. Hier wordt alleen de thumbnail uit de <picture> gebruikt. Hoe de browser weet, welke afbeelding gebruikt moet worden, staat bij <picture "id=picture-1" tabindex="-1">.
height: 47px;
Eerder is voor kleinere browservensters de hoogte van <picture> 525 px gemaakt. Hier worden alleen de thumbnails altijd getoond, en die zijn allemaal 45 px hoog. Bij picture img hebben de <img>'s, waarin de thumbnails zitten, een border van 2 px aan de bovenkant gekregen. Daarom moet de hoogte 45 + 2 = 47 px worden, anders valt er 2 px aan de onderkant van de thumbnail weg.
.eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div, .volgende-div
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
Alle elementen met class="eerste", "laatste", "vorige-picture", "volgende-picture", "vorige-div" en "volgende-div": de zes knoppen rechts bovenin het browservenster van grotere vensters.
Omdat een groot deel van de lay-out voor deze knoppen hetzelfde is, wordt die hier in één keer voor allemaal gegeven.
Dit zijn allemaal links, en ze staan allemaal binnen div#thumbs
. Buiten deze elementen staan er geen andere links binnen div#thumbs
. Je zou deze selector dus ook kunnen schrijven als #thumbs a
, en dat werkt hier prima. Maar dan wordt het heel onoverzichtelijk, als je later voor de verschillende knoppen aparte aanpassingen gaat maken. Een simpele selector als .vorige-picture
is dan bijvoorbeeld niet meer te gebruiken.
background: white;
Witte achtergrond.
color: black;
Links hebben standaard een afwijkende kleur. Dat is hier niet nodig en bovendien niet mooi. Daarom wordt de kleur veranderd in zwart.
display: block;
De eerder met display: none;
voor kleinere browservensters verborgen knoppen worden hier weer zichtbaar gemaakt.
Alle knoppen zijn <a>'s, dat zijSn inline-elementen. Een inline-element kan geen eigenschappen als breedte en hoogte krijgen. Door ze als blok-element weer te geven, kan dat wel.
width: 48px;
Breedte.
De breedte wordt in de absolute eenheid px
opgegeven.
Bij inzoomen verandert de breedte gewoon mee met de rest van de pagina: de knoppen worden breder. Dat levert geen problemen op, want bij inzoomen boven ongeveer 150% verandert de lay-out in de lay-out voor kleinere browservensters.
De absolute eenheid px
verandert niet mee met de lettergrootte: bij een grotere letter, blijft de breedte gewoon 48 px. Als je alleen de lettergrootte vergroot, verandert de lay-out niet. Als je voor de knoppen voor de breedte een eenheid als em
of rem
gebruikt, verandert de breedte van de knoppen mee met de lettergrootte. Bij een grotere letter komen de knoppen dan op 'n gegeven moment over de ernaast staande naam te staan. Door de eenheid px
te gebruiken, voorkom je dat.
Omdat in de knoppen alleen pijltjes en hele kleine afbeeldingen staan, levert het geen problemen op, als de breedte niet mee verandert met de lettergrootte.
height: 3rem;
Hoogte.
Hier wordt, anders dan bij de breedte hierboven, als eenheid juist de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de hoogte van de knoppen niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
Nu verandert de hoogte van de knop mee met de lettergrootte, waardoor de witte achtergrond ook hoger wordt, zoals op de afbeelding is te zien. En dat ziet er gewoon wat netter uit.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
font-size: 1.2em;
Iets grotere letter. Deze maat is voor de pijltjes het beste.
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: 3.4;
Regelhoogte 3,4 keer de grootte van de lettergrootte. Op deze hoogte komen de pijltjes in alle browsers net onder de kleine afbeeldingen te staan.
De regelhoogte verandert mee met de lettergrootte en blijft dus altijd 3,4 keer de lettergrootte.
text-align: center;
Tekst – hier de pijltjes – horizontaal centreren.
text-decoration: none;
De knoppen zijn links. Normaal genomen worden die onderstreept, maar hier is dat foeilelijk.
border: black solid 1px;
Zwart randje.
position: fixed;

Om de knoppen op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is bij een fixed positie normaal genomen het venster van de browser.
Als je position: fixed;
weghaalt, zie je dat alle knoppen eigenlijk altijd aanwezig zijn. Door ze fixed te positioneren, komen ze op de juiste plaats te staan. Met behulp van dingen als :focus
en z‑index
wordt verderop geregeld, dat de juiste knop bovenaan staat en daardoor – als enige – zichtbaar is.
Een <span> is van zichzelf een inline-element, waardoor eigenschappen als breedte niet gebruikt kunnen worden. Door de <span> fixed te positioneren verandert deze in een soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.
top: 0;
Helemaal bovenaan neerzetten.
right: 245px;
Op 245 px vanaf rechts neerzetten.
Hierdoor komen alle zes de knoppen op dezelfde plaats over elkaar heen te staan. Dit wordt opgelost door vijf van de knoppen later steeds iets meer naar rechts te zetten, zodat ze uiteindelijk netjes naast elkaar komen te staan.
.eerste, .laatste
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
Alle elementen met class="eerste" en class="laatste". Dat zijn er maar twee: de links naar de eerste en laatste thumbnail. Deze twee knoppen zijn altijd zichtbaar en zijn daarom een wat apart geval.
De css voor beide knoppen is deels hetzelfde, daarom wordt die hier in één keer opgegeven. Later worden dan wat aanpassingen gedaan voor de link naar de laatste thumbnail.
background: url("../055-pics/aardbeien-60.jpg") no-repeat white center top;

De eerste thumbnail is de aardbei, daarom wordt in de knop naar de eerste thumbnail de aardbei getoond: 'aardbeien-60.jpg'. Op resolutiedichtheid van het scherm wordt hierbij niet gelet: de afbeelding wordt gelijk hieronder verkleind en is daardoor zo klein dat de resolutie geen enkel verschil maakt. (Meer over resolutiedichtheid en dergelijke is te vinden bij <picture "id=picture-1" tabindex="-1">.)
url("../055-pics/aardbeien-60.jpg")
: pad naar en naam van de afbeelding.
no-repeat
: standaard wordt een achtergrond-afbeelding herhaald, tot de hele achtergrond is gevuld. Nu wordt de afbeelding maar één keer weergegeven.
white
: eerder is bij .eerste .laatste, ... met background: white;
een witte achtergrondkleur opgegeven. Hier wordt opnieuw background
gebruikt. background
is een zogenaamde 'shorthand': eigenlijk is het een aantal eigenschappen, die met één gemeenschappelijke naam worden opgegeven. Hier zou je background
kunnen vervangen door de afzonderlijke eigenschappen background-image
, background-repeat
, background-color
en background-position
. Als je 'n eigenschap overslaat bij gebruik van 'n shorthand, gaat die terug naar z'n standaardwaarde.
Eerder is background: white;
opgegeven. Als die kleur hier niet wordt herhaald, wordt teruggegaan naar de standaardwaarde transparent
, waardoor je de beige achtergrondkleur van de pagina krijgt te zien.
Daarom wordt de kleur hier herhaald.
center top
: in het midden aan de bovenkant neerzetten.
Ik vind een shorthand vaak duidelijker dan een hele rits afzonderlijke eigenschappen, maar veel mensen krijgen eigenaardige rode vlekken bij het horen van 'shorthand'. De meningen verschillen dus. Feitelijk is het een kwestie van persoonlijke voorkeur (en je consequent aan die voorkeur houden, anders wordt het een chaos).
background-size: 12px;
De breedte van de achtergrond-afbeelding. Als je maar één waarde opgeeft, wordt de hoogte 'auto'. Hierdoor blijft de verhouding tussen breedte en hoogte bewaard.
letter-spacing: -3px;
In de knoppen naar eerste en laatste thumbnail staan een verticaal streepje en een pijltje naast elkaar. Door de letterafstand iets te verkleinen, komt het pijltje tegen het streepje aan te staan.
z-index: 30;
Normaal genomen worden elementen in de volgorde van de html op het scherm gezet. Wat later in de html staat, wordt over eerdere elementen gezet. Bovendien wordt in dit voorbeeld ook uitbundig met z-indexen gewerkt. Hierdoor valt af en toe het randje aan de onderkant van de knoppen weg. Nu blijven deze randjes altijd zichtbaar.
Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een fixed positie. Die is iets hierboven aan de <a> gegeven, dus dat is geregeld.
.laatste
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.eerste, .laatste {background: url("../055-pics/aardbeien-60.jpg") no-repeat white center top; background-size: 12px; letter-spacing: -3px; z-index: 30;}
Alle elementen met class="laatste". Dat is er maar eentje: de link naar de laatste thumbnail.
background-image: url("../055-pics/worteltjes-60.jpg");
Hierboven bij .eerste, .laatste heeft de <a> een achtergrond-afbeelding gekregen. Maar die hoort bij de eerste thumbnail, en deze knop gaat naar de laatste thumbnail. Daarom wordt die afbeelding hier aangepast.
Anders dan hierboven bij .eerste, .laatste wordt hier geen 'shorthand' gebruikt, want alleen de achtergrond-afbeelding moet worden veranderd, verder niets. Meer over wat een shorthand is, is te vinden bij background.
right: 0;
Helemaal rechts neerzetten.
.vorige-picture, .volgende-picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
Alle elementen met class="vorige-picture" en class="volgende-picture". Dit zijn de links naar de vorige en volgende thumbnail.
Omdat elke thumbnail een andere vorige en volgende thumbnail heeft, komen in elke knop andere kleine achtergrond-afbeeldingen te staan. Maar op die achtergrond-afbeelding na zijn alle knoppen hetzelfde. Daarom wordt hier voor alle knoppen in één keer de css opgegeven.
background: no-repeat white center top;
Achtergrond-afbeelding niet herhalen, witte achtergrond en achtergrond-afbeelding midden boven neerzetten.
background
is een zogenaamde 'shorthand': één gemeenschappelijke naam voor een aantal eigenschappen. Hier zijn die afzonderlijke eigenschappen background-repeat
, background-color
en background-position
.
background-image
is ook zo'n afzonderlijke eigenschap, maar die wordt hier niet gebruikt. Bij een niet gebruikte eigenschap vult de shorthand de standaardwaarde in: background-image: none;
.
Om aan elke knop de juiste achtergrond-afbeelding te geven, wordt later juist wel background-image
gebruikt. Dan verandert alleen background-image
, maar de hier bij background
opgegeven waarden veranderen niet.
Zou je bij het opgeven van de background-image ook de shorthand background gebruiken:
background: url("aardbeien-60.jpg");
dan zou de shorthand de ontbrekende eigenschappen terugzetten naar hun standaardwaarde en staat er eigenlijk:
background: repeat transparent 0% 0% url("aardbeien-60.jpg");
background-size: 12px;
De breedte van de achtergrond-afbeelding. Als je maar één waarde opgeeft, wordt de hoogte 'auto'. Hierdoor blijft de verhouding tussen breedte en hoogte bewaard.
right: 98px;
Op 98 px vanaf rechts neerzetten. Op deze plaats komen de knoppen op de derde plaats vanaf rechts te staan.
.volgende-picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.vorige-picture, .volgende-picture {background: no-repeat white center top; background-size: 12px; right: 98px;}
Alle elementen met class="volgende-picture". Dit zijn de links naar de volgende thumbnail.
right: 49px;
Op 49 px vanaf rechts neerzetten. Op deze plaats komen de knoppen op de tweede plaats vanaf rechts te staan.
.vorige-div, .volgende-div
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
Alle elementen met class="vorige-div" class="volgende-div". Dit zijn de links naar de vorige en volgende <div>, waarin een thumbnail met bijbehorende knoppen en dergelijke zit. Als deze links worden gevolgd, wordt ook de grote afbeelding getoond.
Omdat elke thumbnail een andere vorige en volgende thumbnail heeft, komen in elke knop andere achtergrond-afbeeldingen te staan. Maar op die achtergrond-afbeelding na zijn alle knoppen hetzelfde. Daarom wordt hier voor alle knoppen in één keer de css opgegeven.
background: no-repeat white;
Achtergrond-afbeelding niet herhalen en witte achtergrondkleur.
background-size: 48px 30px;
De achtergrond-afbeelding wordt 48 px breed en 30 px hoog.
Eigenlijk zou de hoogte 36 px moeten zijn, want de verhouding van de achtergrond-afbeelding tussen breedte en hoogte is 4:3. Maar dan wordt de achtergrond-afbeelding te hoog en is er te weinig ruimte voor de onder de afbeelding staande pijltjes. Omdat de afbeelding in z'n geheel wordt weergegeven, vervormt deze dus een klein beetje. Omdat het om kleine voorbeelden gaat, lijkt dat niet zo'n probleem.
Als je dit wilt oplossen, moet je nóg 'n set afbeeldingen maken, en er zijn er al 270, dus...
right: 196px;
Op deze plaats komen de knoppen op de vijfde plaats van rechts te staan.
.volgende-div
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.vorige-div, .volgende-div {background: no-repeat white; background-size: 48px 30px; right: 196px;}
Alle elementen met class="volgende-div". Dit zijn de links naar de volgende <div>, waarin een thumbnail met bijbehorende knoppen en dergelijke zit. Als deze links worden gevolgd, wordt ook de grote afbeelding getoond.
right: 147px;
Op deze plaats komen de knoppen op de vierde plaats van rechts te staan.
#thumbs > div:first-of-type .volgende-div, #thumbs > div:first-of-type .volgende-picture, #div-2 .vorige-div, #div-2 .vorige-picture, #div-30 .volgende-div, #div-30 .volgende-picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.vorige-picture, .volgende-picture {background: no-repeat white center top; background-size: 12px; right: 98px;}
.volgende-picture {right: 49px;}
.vorige-div, .volgende-div {background: no-repeat white; background-size: 48px 30px; right: 196px;}
.volgende-div {right: 147px;}
Dit is de eerste groep selectors van een hele serie selectors, die de juiste kleine achtergrond-afbeelding in de juiste knop zetten. De eerste twee groepen van deze serie zijn wat anders dan de rest, omdat hier ook het openen van de pagina en de eerste en laatste thumbnail worden afgehandeld.
Deze groep selectors zorgt dat op de juiste momenten naar de eerste thumbnail en grote afbeelding (de aardbeien) wordt gelinkt: bij openen van de pagina, van de tweede thumbnail terug naar de eerste thumbnail en grote afbeelding, en van de laatste thumbnail weer terug naar de eerste thumbnail en grote afbeelding.
Vandaar dat dit er wat ingewikkelder uitziet dan de hierop volgende selectors van deze serie. Maar als je het in stukjes hakt, is het eigenlijk niet heel erg ingewikkeld. Dit zijn zes selectors, gescheiden door een komma.
De eerste selector #thumbs > div:first-of-type .volgende-div
:
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: elke <div> die een direct kind van #thumbs
is.
:first-of-type
: het element met een bepaald volgnummer. In dit geval wordt geen volgnummer gebruikt, maar een speciaal voor het eerste element bedoelde pseudo-class, het in deze selector gebruikte: :first-of-type
. Voor alle latere elementen gebruik je 'n soortgelijke selector, maar dan met een volgnummer: :nth-of-type()
. Tussen de haakjes komt het volgnummer. :nth-of-type(1)
is precies hetzelfde als :first-of-type
, maar de laatste is wat mensvriendelijker.
Omdat voor :first-of-type
een div
staat, worden alleen <div>'s geteld. Als binnen div#thumbs
327 <spans>'s zitten, tellen die niet mee. Hadden ze maar 'n <div> moeten zijn.
.volgende-div
: de elementen met class="volgende-div". Dat is er maar één: de link naar de volgende grote afbeelding.
De eerste selector in gewone taal: doe iets met de a.volgende-div
die in de eerste <div> zit die een direct kind van div#thumbs
is.
Die eerste <div> is een apart geval. Bij openen van de pagina is het wat verwarrend, als je naar de vorige thumbnail of grote afbeelding kunt gaan, terwijl je nog helemaal niets hebt gedaan. Daarom worden die twee knoppen bij openen van de pagina afgedekt met een wit blokje. (Dat gebeurt door bij #uitleg:not(:focus-within) + #thumbs > div:first-of-type de z-index te verhogen.)
Om dezelfde reden moeten bij openen van de pagina de knop naar de volgende thumbnail en de knop naar de volgende grote afbeelding linken naar de eerste thumbnail, met bijbehorende kleine afbeelding (hier de aardbei).
Daarom zitten in deze eerste groep selectors twee extra selectors: de links naar de volgende thumbnail en de volgende grote afbeelding, die bij openen van de pagina worden getoond.
In a.volgende-div
zit de link naar de <div>, waarin de thumbnail, knoppen, en dergelijke die bij de aardbei horen zitten. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De tweede selector #thumbs > div:first-of-type .volgende-picture
:
Deze is precies hetzelfde als de eerste selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de aardbei hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
De derde selector #div-2 .vorige-div
:
Alle elementen met class="vorige-div" die binnen het element met id="div-2" zitten. Dat is er maar één: de link naar de vorige <div> vanuit #div-2
.
In div#div-2
zit de thumbnail met de ananas en de daarbij horende knoppen, grote afbeelding, en dergelijke.
a.vorige-div
linkt naar de vorige <div>: de <div> waar de thumbnail met de aardbei en bijbehorende knoppen en dergelijke in zit. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De vierde selector #div-2 .vorige-picture
:
Deze is precies hetzelfde als de derde selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de aardbei hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
De vijfde selector #div-30 .volgende-div
:
Alle elementen met class="volgende-div" die binnen het element met id="div-30" zitten. Dat is er maar één: de link naar #div-1
, de <div> met de aardbei en de bijbehorende knoppen en grote afbeelding en dergelijke.
In #div-30
zit de laatste thumbnail met de worteltjes met bijbehorende knoppen en dergelijke. Als je bij die laatste thumbnail op de knop naar de volgende thumbnail klikt, ga je weer terug naar de eerste thumbnail: de aardbei.
Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De zesde selector #div-30 .volgende-picture
:
Deze is precies hetzelfde als de vijfde selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de aardbei hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de aardbei gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
background-image: url("../055-pics/aardbeien-60.jpg");
Toon in alle zes de hierboven genoemde links de kleine achtergrond-afbeelding met de aardbei.
Omdat de achtergrond-afbeeldingen verkleind worden weergegeven, wordt niet gelet op de resolutiedichtheid van het scherm. (Meer over resolutiedichtheid en dergelijke is te vinden bij <picture "id=picture-1" tabindex="-1">.)
De grootte van de weergave van de achtergrond-afbeelding en dergelijke zijn eerder al opgegeven met behulp van de 'shorthand' background
. Daarom wordt hier niet de shorthand background
, maar de meer expliciete eigenschap background-image
gebruikt. Zou je dat niet doen, dan zouden de eerdere bij de shorthand opgegeven eigenschappen worden teruggezet naar hun standaardwaarde. Iets meer daarover is te lezen bij .vorige-picture, .volgende-picture.
#div-1 .vorige-div, #div-1 .vorige-picture, #div-29 .volgende-div, #div-29 .volgende-picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.vorige-picture, .volgende-picture {background: no-repeat white center top; background-size: 12px; right: 98px;}
.volgende-picture {right: 49px;}
.vorige-div, .volgende-div {background: no-repeat white; background-size: 48px 30px; right: 196px;}
.volgende-div {right: 147px;}
Dit is de tweede groep selectors van een hele serie selectors, die de juiste kleine achtergrond-afbeelding in de juiste knop zetten. Ook deze tweede groep wijkt nog af van de rest, maar al veel minder dan de hierboven staande eerste groep.
Deze groep selectors zorgt dat de knoppen naar de vorige thumbnail en grote afbeelding bij de eerste thumbnail weer terug naar de laatste thumbnail linken, en dat de knoppen naar de volgende thumbnail en grote afbeelding bij de 29e thumbnail terug naar de 30e thumbnail linken.
Dit zijn vier selectors, gescheiden door een komma.
De eerste selector #div-1 .vorige-div
:
Alle elementen met class="vorige-div" die binnen het element met id="div-1" zitten. Dat is er maar één: de link naar de vorige <div> vanuit #div-1
.
In div#div-1
zit de thumbnail met de aardbeien en de daarbij horende knoppen, grote afbeelding, en dergelijke. Dit is de eerste thumbnail. Daardoor is er uiteraard geen vorige thumbnail. Daarom linkt deze knop niet naar de vorige thumbnail, maar juist terug naar de laatste, de worteltjes.
Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de laatste thumbnail, die met de worteltjes, gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De tweede selector #div-1 .vorige-picture
:
Deze is precies hetzelfde als de eerste selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de worteltjes hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de laatste thumbnail, die met de worteltjes, gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
De derde selector #div-29 .volgende-div
:
Alle elementen met class="volgende-div" die binnen het element met id="div-29" zitten. Dat is er maar één: de link naar #div-30
, de <div> met de worteltjes en de bijbehorende knoppen en grote afbeelding en dergelijke.
Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de worteltjes gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De vierde selector #div-29 .volgende-picture
:
Deze is precies hetzelfde als de derde selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de worteltjes hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de worteltjes gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
background-image: url("../055-pics/worteltjes-60.jpg");
Toon in alle vier de hierboven genoemde links de kleine achtergrond-afbeelding met de worteltjes.
Omdat de achtergrond-afbeeldingen verkleind worden weergegeven, wordt niet gelet op de resolutiedichtheid van het scherm. (Meer over resolutiedichtheid en dergelijke is te vinden bij <picture "id=picture-1" tabindex="-1">.)
De grootte van de weergave van de achtergrond-afbeelding en dergelijke zijn eerder al opgegeven met behulp van de 'shorthand' background
. Daarom wordt hier niet de shorthand background
, maar de meer expliciete eigenschap background-image
gebruikt. Zou je dat niet doen, dan zouden de eerdere bij de shorthand opgegeven eigenschappen worden teruggezet naar hun standaardwaarde. Iets meer daarover is te lezen bij .vorige-picture, .volgende-picture.
#div-1 .volgende-div, #div-1 .volgende-picture, #div-3 .vorige-div, #div-3 .vorige-picture
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
.eerste .laatste .vorige-picture, .volgende-picture, .vorige-div, .volgende-div {background: white; color: black; display: block; width: 48px; height: 3rem; font-size: 1.2em; line-height: 3.4; text-align: center; text-decoration: none; border: black solid 1px; position: fixed; top: 0; right: 245px;}
.vorige-picture, .volgende-picture {background: no-repeat white center top; background-size: 12px; right: 98px;}
.volgende-picture {right: 49px;}
.vorige-div, .volgende-div {background: no-repeat white; background-size: 48px 30px; right: 196px;}
.volgende-div {right: 147px;}
Dit is de derde groep selectors van een hele serie selectors, die de juiste kleine achtergrond-afbeelding in de juiste knop zetten. Deze groep, en de andere uit deze serie die hierop volgen, zijn vrijwel hetzelfde.
Deze groep selectors zorgt dat de knoppen naar de volgende thumbnail en grote afbeelding bij de eerste thumbnail naar de tweede thumbnail linken, en dat de knoppen naar de vorige thumbnail en grote afbeelding bij de derde thumbnail terug naar de tweede thumbnail linken.
Dit zijn vier selectors, gescheiden door een komma.
De eerste selector #div-1 .volgende-div
:
Alle elementen met class="volgende-div" die binnen het element met id="div-1" zitten. Dat is er maar één: de link naar de volgende <div> vanuit #div-1
.
In div#div-1
zit de thumbnail met de aardbeien en de daarbij horende knoppen, grote afbeelding, en dergelijke. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de tweede thumbnail, die met de ananassen, gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De tweede selector #div-1 .volgende-picture
:
Deze is precies hetzelfde als de eerste selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de ananassen hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de tweede thumbnail, die met de ananassen, gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
De derde selector #div-3 .vorige-div
:
Alle elementen met class="vorige-div" die binnen het element met id="div-3" zitten. Dat is er maar één: de link naar #div-2
, de <div> met de ananassen en de bijbehorende knoppen en grote afbeelding en dergelijke.
Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de ananassen gemarkeerd en de bij de thumbnail horende knoppen en grote afbeelding worden getoond.
De vierde selector #div-3 .vorige-picture
:
Deze is precies hetzelfde als de derde selector gelijk hierboven, alleen linkt deze niet naar de <div> die bij de ananassen hoort, maar naar de <picture> in die <div>. Als deze link wordt gevolgd, wordt door gebruik van dingen als :focus
en z-index
de thumbnail met de ananassen gemarkeerd en de bij de thumbnail horende knoppen worden getoond. De grote afbeelding wordt echter niet getoond (en ook niet gedownload, wat bandbreedte uitspaart).
background-image: url("../055-pics/ananas-60.jpg");}
Toon in alle vier de hierboven genoemde links de kleine achtergrond-afbeelding met de ananassen.
Omdat de achtergrond-afbeeldingen verkleind worden weergegeven, wordt niet gelet op de resolutiedichtheid van het scherm. (Meer over resolutiedichtheid en dergelijke is te vinden bij <picture "id=picture-1" tabindex="-1">.)
De grootte van de weergave van de achtergrond-afbeelding en dergelijke zijn eerder al opgegeven met behulp van de 'shorthand' background
. Daarom wordt hier niet de shorthand background
, maar de meer expliciete eigenschap background-image
gebruikt. Zou je dat niet doen, dan zouden de eerdere bij de shorthand opgegeven eigenschappen worden teruggezet naar hun standaardwaarde. Iets meer daarover is te lezen bij .vorige-picture, .volgende-picture.
#div-2 .volgende-div, #div-2 .volgende-picture, #div-4 .vorige-div, #div-4 .vorige-picture {background-image: url("../055-pics/appels-60.jpg");}
tot en met
#div-28 .volgende-div, #div-28 .volgende-picture, #div-30 .vorige-div, #div-30 .vorige-picture {background-image: url("../055-pics/walnoten-60.jpg");}
De selectors #div-2 ...
tot en met #div-28 ...
werken precies hetzelfde als die gelijk hierboven bij #div-1 .volgende-div, #div-1 .volgende-picture, #div-3 .vorige-div, #div-3 .vorige-picture. Alleen is het nummer in de id van de <div> steeds eentje hoger. En omdat er in die <div> steeds een volgende, andere thumbnail zit, linken ook de links steeds één <div> verder. De bijbehorende achtergrond-afbeelding wordt ook steeds aangepast.
.groot
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Alle elementen met class="groot">. De <span>'s die worden gebruikt om de grote afbeelding te tonen.
background-repeat: no-repeat;
Standaard wordt een achtergrond-afbeelding herhaald, tot de achtergrond van het element volledig is opgevuld.
Als het goed is, heeft de <span> steeds dezelfde verhouding tussen breedte en hoogte als de afbeelding. Maar door afrondingsverschillen of zo zou er toch aan een van de randen een klein kiertje kunnen verschijnen. In dat kiertje zou de achtergrond-afbeelding dan worden herhaald, wat zichtbaar zou kunnen zijn als een vreemd lijntje. Daarom wordt, voor de zekerheid, dat herhalen hier uitgeschakeld.
background-size: contain;
Geef de achtergrond-afbeelding zo groot mogelijk weer, zonder deze te vergroten of delen weg te laten. Als het goed is, heeft de <span> dezelfde verhouding tussen breedte en hoogte als de afbeelding. Hierdoor vult de afbeelding de volledige <span>. Daardoor geldt de aan de <span> gegeven grootte of positie ook voor de afbeelding.
display: none;
Standaard de <span>, en dus de daarin zittende afbeelding, verbergen. Alleen op verzoek wordt de grote afbeelding getoond.
Ook achtergrond-afbeeldingen worden alvast gedownload door de browser, maar niet als het element waar ze in zitten met display: none;
wordt verborgen. Dit spaart dus bandbreedte uit.
width: 700px; height: 525px;
Breedte en hoogte.
Omdat alle weer te geven grote afbeeldingen op deze grootte moeten worden weergegeven, kan hier veilig één grootte worden opgegeven.
max-width: calc(1.3333 * (100vh - 3rem – 86px));
max-height: calc(100vh - 3rem – 86px);
Deze css geldt voor browservensters met een minimale breedte van 760 px en een minimale hoogte van 530 px. Een afbeelding die wordt weergegeven met een breedte van 760 px en een hoogte van 525 px, zou daarin dus altijd moeten passen. Dat is ook zo, maar er staat nog meer op het scherm dan alleen de grote afbeelding.
Boven de afbeelding staan nog naam en knoppen. Bij de hoogte van de afbeelding moet daar rekening mee worden houden. Daarom wordt een maximumhoogte opgegeven, die rekening houdt met de aanwezigheid van deze dingen.
Maar als je alleen de maximumhoogte aanpast en niet de maximumbreedte, krijg je een lachspiegel-effect, omdat de verhoudingen van de afbeelding worden verstoord. Dat valt hier nog mee, omdat iets hierboven background-size: contain;
is gebruikt: geef de achtergrond-afbeelding zo groot mogelijk weer, zonder deze te vergroten of delen weg te laten.
Op de afbeelding hierboven is te zien, wat er dan gebeurt. De hele afbeelding wordt inderdaad weergegeven, dus er valt niets weg. Maar de <span>, waarin de achtergrond-afbeelding staat, is nu breder dan de afbeelding. Daardoor blijft er aan de rechterkant van de afbeelding een stuk leeg.
De border rondom de <span> loopt vrolijk door, want die heeft met deze ruzie helemaal niets te maken. De <span> staat nog steeds horizontaal gecentreerd, wat te zien is aan de zwarte border, maar de afbeelding niet meer.
Als iets eerder niet background-repeat: no-repeat;
was gebruikt, zou de achtergrond-afbeelding worden herhaald om het lege stuk te vullen.
U begrijpt: het is handig om ook een maximumbreedte op te geven. Omdat de hoogte de uiteindelijke grootte van de <span> (en daarmee van de erin zittende achtergrond-afbeelding) moet bepalen, wordt de hoogte van het browservenster als uitgangspunt genomen. De breedte wordt vervolgens aangepast aan de gevonden hoogte.
Met behulp van calc()
kunnen berekeningen worden gemaakt. Omdat de maximumhoogte hier ook bepalend is voor de maximumbreedte, wordt eerst de maximumhoogte besproken: max-height: calc(100vh - 3rem – 86px);
100vh
: de eenheid vh
is gebaseerd op de hoogte van het venster van de browser. 1 vh is 1% van de hoogte van het venster, en 100 vh is 100% van de hoogte.
3rem
: als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de hoogte van <picture> niet mee verandert met de lettergrootte. Als de lettergrootte van de boven de afbeelding staande naam, knoppen, en dergelijke wordt vergroot, is er minder ruimte voor de afbeelding. De hoogte van de <span>, en daarmee de hoogte van de erin zittende achtergrond-afbeelding, moet daaraan worden aangepast.
Alle elementen van de slideshow die boven de thumbnails staan, hebben een hoogte van 3 rem
. Daarom wordt die hoogte van de 100 vh afgetrokken.
Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
86px
: na enig uitproberen blijken alle browsers het er uiteindelijk over eens dat dit een redelijke hoogte is voor de thumbnails en de daarbij horende borders en dergelijke, en de eventueel eronder staande horizontale scrollbalk. Daarom wordt van de 100 vh ook nog 86 px afgetrokken.
De berekening wordt hier gemaakt met drie verschillende eenheden: vh
, rem
en px
. Dat kan niet, eerst moeten alle eenheden worden gerekend naar dezelfde eenheid. Daarom rekent de browser de eenheden vh
en rem
om naar de eenheid px
.
Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet hoe hoog het browservenster is en wat de lettergrootte is. Maar op het moment van weergave weet de browser dat wel.
De berekening wordt dan 100% van de hoogte van het browservenster in px, min drie keer de lettergrootte in px, min 86 px. Dat levert genoeg ruimte op om de grote afbeelding, de naam en eventueel een scrollbalk onder de grote afbeeldingen weer te geven. En daarmee wordt dit de maximumhoogte.
Het verhaal is vrijwel hetzelfde voor de maximumbreedte max-width: calc(1.3333 * (100vh – 3rem -86px));
:
(100vh – 3rem – 86px)
: dit deel is precies hetzelfde als hier gelijk boven voor de maximumhoogte. Het staat tussen haakjes om, net als bij gewoon rekenen, aan te geven dat dit deel eerst moet worden uitgerekend. Ervoor staat nog een vermenigvuldiging, en volgens de regels van het rekenen hoort die eerst te worden uitgevoerd, waardoor je een volkomen andere uitkomst zou krijgen. Nu wordt eerst het deel tussen de haakjes uitgevoerd, en met de uitkomst daarvan pas de vermenigvuldiging.
1.3333 *
: het sterretje staat voor een vermenigvuldiging: de hierboven gevonden uitkomst moet met 1,3333 worden vermenigvuldigd (css gebruikt komma's en punten zoals in de VS, vandaar dat er een punt staat in de eigenlijke css).
De verhouding tussen hoogte en breedte is 4 : 3: als de breedte 4 centimeter is, is de hoogte drie centimeter cm. De breedte, en dus ook de maximumbreedte, moet dus een derde meer zijn dan de hoogte en de maximumhoogte: 1⅓. ⅓ is in decimalen niet exact weer te geven, maar 1,3333 komt dicht genoeg in de buurt. Degene die deze afwijking meent te zien, moet direct een andere opticien nemen.
Omdat de maximumbreedte op dezelfde manier als de hoogte wordt berekend, is deze nu altijd nu 1⅓ maal de maximumhoogte. Wat overeenkomt met de verhouding van de grote afbeeldingen: 4 : 3.
border: black solid 2px;
Randje rondom de <span>, en daarmee ook rondom de achtergrond-afbeelding in de <span>
position: fixed;
Om de <span>, en daarmee de erin zittende achtergrond-afbeelding, op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is bij een fixed positie normaal genomen het venster van de browser. Hierdoor staat de <span>, en daarmee de erin zittende achtergrond-afbeelding, altijd op dezelfde plaats.
left: 50%;
Halverwege het venster van de browser neerzetten.
transform: translateX(-50%);
Hier gelijk boven is de linkerkant van de <span> precies in het midden van het venster van de browser gezet. Als je nou de <span> de helft van z'n eigen breedte terug naar links zet, staat de <span> horizontaal precies gecentreerd binnen het venster.
Met de bij transform
horende functie translateX()
kun je 'n element ten opzichte van zichzelf verplaatsen. En als je die verplaatsing in procenten opgeeft, gelden die procenten ten opzichte van het element zelf. Omdat de browser bij het maken van de pagina alle maten weet, kan de browser altijd uitrekenen, hoeveel 50% is.
translateX()
verplaatst in horizontale richting. Omdat de waarde negatief is, wordt naar links verplaatst. Met 50%, precies de helft van de breedte van de <span>. Oftewel: de helft van de <span> staat nu links van het midden van het browservenster, en de andere helft staat rechts daarvan: horizontaal gecentreerd. En daarmee ook de in de <span> zittende achtergrond-afbeelding, want die is even groot als de <span>
top: -20000px;
Ver boven het scherm neerzetten, en daarmee onzichtbaar.
De <span>'s zijn hierboven met display: none;
verborgen. Hierdoor worden de erin zittende afbeeldingen pas gedownload, als dat echt nodig is: als de <span> op het scherm wordt gezet. Dit voorkomt onnodig downloaden van de grote afbeeldingen.
Als je de Tab-toets of de knop naar de volgende grote afbeelding gebruikt, wordt een grote afbeelding getoond. Met de Tab-toets kun je alleen opeenvolgend door álle grote afbeeldingen bladeren. Als je de knop naar de volgende grote afbeelding gebruikt, ben je mogelijk ook door álle grote afbeeldingen aan het bladeren (anders zou je de knop naar de volgende thumbnail gebruiken, of je zou één grote afbeeldingen tonen door 'n thumbnail aan te raken of aan te klikken.)
Als de grote afbeelding nog gedownload moet worden, geeft dat een kleine vertraging op bij het weergeven. Dat kan bij bladeren irritant zijn, zeker bij een iets tragere verbinding. Daarom wordt later bij bladeren ook alvast bij de twee volgende <span>'s display: none;
veranderd in display: block;
. Waardoor de daarin zittende achtergrond-afbeeldingen alvast worden gedownload.
Maar die twee volgende afbeeldingen mogen nog niet worden getoond. Dat gebeurt echter soms wel (afhankelijk van de z-index en dergelijke), want ze zijn er nou eenmaal al. Daarom wordt de <span>, en daarmee de erin zittende achtergrond-afbeelding, ook nog 'ns verborgen door deze boven het scherm te zetten. Alleen de grote afbeelding die echt bij de geselecteerde thumbnail hoort, wordt dan later met behulp van :focus
echt getoond.
#thumbs > div:nth-of-type(2) .groot
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
.groot {background-repeat: no-repeat; background-size: contain; display: none; height: 525px; width: 700px; max-width: calc(1.3333 * (100vh - 3rem – 86px)); max-height: calc(100vh - 3rem – 86px); border: black solid 2px; position: fixed; left: 50%; transform: translateX(-50%); top: -20000px;}
Deze selector is de eerste van een serie van dertig, die regelen welke grote afbeelding er wordt getoond. Deze selectors zijn allemaal vrijwel hetzelfde. Alleen het volgnummer van de <div> en de gebruikte afbeelding verschillen.
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: alle <div>'s binnen #thumbs
die een direct kind van #thumbs
zijn.
:nth-of-type(2)
: Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
, onderdeel van deze selector, worden alleen elementen van een bepaald soort geteld. Het volgnummer staat tussen de haakjes. In dit geval is het volgnummer twee. Het gaat hier dus om de tweede <div> binnen #thumbs
, die een direct kind van #thumbs
is. (De eerste <div> is een apart geval, die doet niet mee in de serie die wordt gebruikt om de grote afbeelding te tonen.)
Omdat voor :nth-of-type(2)
een div
staat, worden alleen <div>'s geteld.
.groot
: alle elementen met class="groot", die binnen deze tweede <div> zitten. Dat is er maar één: de <span> die wordt gebruikt om de grote afbeelding als achtergrond-afbeelding in de <span> te tonen.
De hele selector in gewone taal: span.groot
binnen de tweede <div> binnen #thumbs
, die een direct kind van #thumbs
is.
background-image: url("../055-pics/aardbeien-700.jpg");
Dit is een gewone achtergrond-afbeelding. Deze is bedoeld voor oudere browsers, die het gelijk hieronder gebruikte image-set()
niet kennen. Die browsers negeren het voor hen onbekende image-set()
en gebruiken deze regel.
Dit levert mogelijk niet de beste afbeelding op, en misschien staat de afbeelding niet helemaal in het midden van de <span> te staan, maar in ieder geval wordt er een afbeelding getoond.
Alle andere eigenschappen voor de achtergrond-afbeelding (en voor span.groot
, waar de afbeelding in komt te staan) zijn al eerder opgegeven bij .groot.
background-image: -webkit-image-set(
url("../055-pics/aardbeien-700.jpg") 1x,
url("../055-pics/aardbeien-1400.jpg") 2x,
url("../055-pics/aardbeien-2000.jpg") 3x);
background-image: image-set(
url("../055-pics/aardbeien-700.jpg") 1x,
url("../055-pics/aardbeien-1400.jpg") 2x,
url("../055-pics/aardbeien-2000.jpg") 3x);
Hier staat twee keer precies dezelfde css, alleen wordt in de eerste regel -webkit-image-set(...)
gebruikt, en in de tweede regel image-set(...)
. Waarom dat zo is, is te vinden bij De voorvoegsels -moz- en -webkit-.
In de vorige regel is ook een background-image
opgegeven, maar dan met één simpele url
zonder image-set()
. Browsers die image-set()
ondersteunen, negeren die eerdere regel, omdat deze tweede regel voor een background-image
lager staat en daardoor 'wint' van de eerdere regel.
image-set()
werkt ongeveer hetzelfde als <source> met de daarin zittende srcset
: het geeft de browser een hint, welke afbeelding het best gebruikt kan worden.
image-set()
bestaat nog niet zolang en heeft daardoor nog weinig volledige ondersteuning. Alleen voor het bepalen van de resolutiedichtheid van het scherm is voldoende ondersteuning. Uiteindelijk zou dat er ook moeten zijn voor dingen als de grootte van het browservenster, maar nu is dat nog niet zo. Daarom moeten voor de grootte van het venster al deze dertig regels nog eens volledig worden herhaald binnen een media query voor grotere vensters. Hopelijk kan zo'n media query met een volledige herhaling van de css die hier ook al staat ooit vervallen en kan alles binnen één image-set()
worden geregeld.
background-image
: dit is gewoon de al heel lang bestaande eigenschap, waarmee je een achtergrond-afbeelding aan een element geeft.
image-set()
: binnen image-set()
staat een serie afbeeldingen, waaruit de browser de browser kan kiezen. In dit geval zijn dat er drie. De drie afbeeldingen zijn precies hetzelfde, alleen de grootte verschilt, en daarmee ook de grootte van het bestand.
url("../055-pics/aardbeien-700.jpg")
: pad naar en naam van de achtergrond-afbeelding. Het getal in de naam geeft de breedte van de afbeelding in px aan. Dat is verder niet nodig, het is gewoon een handige code om te weten om welke afbeelding het gaat.
1x
: deze afbeelding is bedoeld voor schermen, die een lage resolutiedichtheid hebben. Die hebben niets aan supergedetailleerde afbeeldingen, omdat ze stomweg de schermpixels missen om die weer te kunnen geven. Die gedetailleerdere afbeeldingen hebben een (veel) grotere bestandsgrootte, waardoor je bandbreedte verspeelt: je downloadt een groot bestand, waar de browser vervolgens maar een klein deel van kan gebruiken.
,
: met de komma worden de verschillende achtergrond-afbeeldingen, waaruit de browser kan kiezen, van elkaar gescheiden.
url("../055-pics/aardbeien-1400.jpg") 2x,
: precies dezelfde afbeelding als hierboven, maar dan twee keer zo breed. Deze is bedoeld voor schermen met een hogere resolutiedichtheid, die meer details kunnen weergeven. Daarom staat er '2x' achter: een resolutiedichtheid die twee keer zo hoog is als wat ooit de standaard was.
url("../055-pics/aardbeien-2000.jpg") 3x
: weer hetzelfde verhaal, maar nu voor schermen die minimaal drie keer de resolutiedichtheid hebben van wat ooit standaard was.
)
: het afsluitende haakje van image-set()
.
Wat een resolutiedichtheid en dergelijke zijn, is te vinden bij <source>. Daar gaat het om een gewone afbeelding, hier om een achtergrond-afbeelding, maar het verhaal is voor beide op precies hetzelfde.
De aanwijzingen voor de resolutiedichtheid zijn geen starre bevelen, het zijn hints. De browser kan hiervan afwijken, bijvoorbeeld als de internetverbinding traag is.
Op deze manier krijgen hogeresolutieschermen een betere kwaliteit, terwijl voor eenvoudiger schermen geen onnodig grote bestanden worden gedownload.
#thumbs > div:nth-of-type(3) .groot
{background-image: url("../055-pics/ananas-700.jpg");
background-image: -webkit-image-set(
url("../055-pics/ananas-700.jpg") 1x,
url("../055-pics/ananas-1400.jpg") 2x,
url("../055-pics/ananas-2000.jpg") 3x);
background-image: image-set
url("../055-pics/ananas-700.jpg") 1x,
url("../055-pics/ananas-1400.jpg") 2x,
url("../055-pics/ananas-2000.jpg") 3x);
}
tot en met
#thumbs > div:nth-of-type(31) .groot {
background-image: url("../055-pics/worteltjes-700.jpg");
background-image: -webkit-image-set(
url("../055-pics/worteltjes-700.jpg") 1x,
url("../055-pics/worteltjes-1400.jpg") 2x,
url("../055-pics/worteltjes-2000.jpg") 3x);
background-image: image-set(
url("../055-pics/worteltjes-700.jpg") 1x,
url("../055-pics/worteltjes-1400.jpg") 2x,
url("../055-pics/worteltjes-2000.jpg") 3x);
}
Dit zijn de regels die voor de grote afbeelding bij de tweede tot en met dertigste thumbnail zorgen (dat de nummering begint bij 3 en eindigt bij 31, komt omdat de eerste <div> een apart geval is en niet mee doet.)
Selectors en css zijn precies hetzelfde als die hierboven bij #thumbs > div:nth-of-type(2) .groot. De enige twee verschillen: het volgnummer van de <div> in nth-of-type()
en de gebruikte achtergrond-afbeeldingen zijn anders.
.naam
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
.naam {background: white; color: black; display: block; width: 100%; line-height: 1.4rem; text-align: center; border-bottom: black solid 1px;}
Alle elementen met class="naam". In span.naam
zit de naam van de afbeelding.
background: transparent;
Geen achtergrondkleur. Omdat span.naam
boven div#uitleg
wordt gepositioneerd, zie je nu de grijze achtergrond van div#uitleg
achter de naam in plaats van de eerder aan .naam
gegeven witte achtergrond.
display: none;
Verbergen. De naam moet pas worden getoond als een thumbnail is geselecteerd of als de grote afbeelding wordt getoond.
width: auto;
Eerder is voor kleinere browservensters een breedte van 100% opgegeven. In bredere vensters wordt iets hieronder de <span> met de naam absoluut gepositioneerd tussen de knoppen. De <span> links van het vraagteken positioneren én rechts van de zes knoppen naar volgende en vorige én een breedte geven kan onmogelijk worden gecombineerd. Daarom wordt de opgegeven breedte hier verwijderd.
(Als je toch onmogelijke combinatie met de eerder opgegeven breedte van 100% laat staan, wordt de iets hieronder opgegeven waarde voor right
gewoon genegeerd.)
font-size: 1.8em;
Iets grotere letter.
Als eenheid wordt de relatieve eenheid em
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
niet alle browsers de lettergrootte kunnen veranderen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.
line-height: 3rem;
Regelhoogte.
Omdat geen gewone hoogte is opgegeven, is dit ook de hoogte van span.naam
. De naam komt boven div#uitleg
te staan, die een hoogte heeft van 3 rem. Omdat tekst altijd halverwege de regelhoogte wordt neergezet, staat de naam nu netjes verticaal gecentreerd boven de grijze achtergrond van div#uitleg
.
Als eenheid wordt de relatieve eenheid em
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de regelhoogte niet mee verandert met de lettergrootte. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.
position: absolute;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als zo'n voorouder er niet is, zoals hier het geval is, wordt gepositioneerd ten opzichte van het venster van de browser.
top: 1px;
Eerder heeft span.naam
een zwarte rand aan de onderkant gekregen, die gelijk met de naam zichtbaar wordt. Door de <span> 1 px omlaag te zetten, komt die zwarte rand precies gelijk met de randen van andere elementen.
(Je zou dit ook op 'n andere manier kunnen oplossen, maar dan moet je enorm gaan goochelen met allerlei borders en posities. Dit werkt en is simpel.)
right: 295px; left: 50px;
Op 295 px vanaf rechts en 50 px vanaf links neerzetten. Hiermee komt de naam precies tussen de zes knoppen rechts en het vraagteken links te staan. In browservensters minimaal 1004 px breed wordt de naam later midden boven de grote afbeelding gezet, maar in smallere vensters ziet het er beter uit, als de naam in het midden van de knoppen staat.
section:last-of-type
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
section:last-of-type {background: white; color: black; width: 90vw; max-width: 600px; margin: 20px auto 0; border: black solid 1px; padding: 5px;}
Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld. De pseudo-class :nth-last-of-type()
, onderdeel van deze selector, werkt hetzelfde, maar telt van achter naar voren. Omdat het vaak voorkomt dat je het laatste element van 'n bepaald type wilt hebben, heeft dat een aparte pseudo-class: :last-of-type
.
section
: alle <section>'s.
:last-of-type
: het element met een bepaald volgnummer, geteld van achter naar voren.
In dit geval wordt geen volgnummer gebruikt, maar een speciaal voor het laatste element bedoelde selector: :last-of-type
. Voor alle eerdere elementen gebruik je 'n soortgelijke selector, maar dan met een volgnummer: :nth-last-of-type()
. Tussen de haakjes komt het volgnummer. :nth-last-of-type(1)
is precies hetzelfde als :last-of-type
, maar de laatste is wat mensvriendelijker.
Omdat voor :last-of-type
(of voor eerdere elementen :nth-last-of-type()
) een section
staat, worden alleen <section>'s geteld.
De hele selector in gewone taal: elke laatste <section> die de laatste <section> binnen z'n ouder is. Hier zijn maar twee <section>'s aanwezig, die beide een direct kind van <main> zijn. De laatste <section> is hier dus de tweede <section>, de <section> met de onder de slideshow staande tekst.
De pseudo-class :last-of-type
(of :nth-last-of-type()
) kan onverwachte bijwerkingen hebben. In dit geval is er maar één serie <section>'s aanwezig, alleen in <main>. Maar als binnen de pagina nog 'n serie <section>'s met gemeenschappelijke ouder aanwezig is, wat vaak zo zal zijn, zou deze selector ook voor die <section>'s gelden. (Dit zou je kunnen oplossen door het toevoegen van de ouder aan de selector, iets als main > section:last-of-type
. Dan geldt de selector alleen voor de <section> die de laatste <section> binnen <main> is.)
overflow: auto;
Iets hieronder wordt de <section> fixed gepositioneerd. Omdat de tekst hoger is dan het browservenster, zou hierdoor een deel van de tekst wegvallen onder het venster. Deze eigenschap zorgt ervoor dat gescrold kan worden, zodat alle tekst kan worden gezien. In sommige browsers verschijnt rechts een verticale scrollbalk.
-webkit-overflow-scrolling: touch;
Deze eigenschap is nodig om een probleem in scrollen op iOS voor versie 13 op te lossen. Zonder deze eigenschap is het bijzonder lastig om te scrollen. Meer hierover is te vinden bij -webkit-overflow-scrolling: touch;.
margin: 0 auto;
Omdat voor onder en links geen waarde is ingevuld, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 0 auto 0 auto
in de volgorde boven – rechts – onder – links.
Boven en onder geen marge. Links en rechts auto
, wat hier hetzelfde betekent als evenveel. Hierdoor komt de <div> altijd horizontaal gecentreerd binnen z'n ouder <main> te staan. <main> is een blok-element en wordt daardoor normaal genomen even breed als z'n ouder <body>. Ook <body> is een blok-element en wordt dus normaal genomen even breed als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser.
Hierdoor staat uiteindelijk de <section> altijd horizontaal gecentreerd binnen het venster, ongeacht de breedte van het venster. En daarmee ook de in de <section> zittende tekst.
Deze manier van horizontaal centreren van een blok-element werkt alleen, als het blok-element een breedte heeft, want anders zou het normaal genomen even breed worden als de ouder ervan. Dat is geregeld, want bij section:last-of-type heeft de <section> zowel een breedte van 90 vw als een maximumbreedte van 600 px gekregen.
Maar hier doet zich een eigenaardig verschijnsel voor. De <section> wordt gelijk hieronder fixed gepositioneerd, en bij een fixed 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 de <section> van de linker- tot de rechterkant van het browservenster moet lopen. Eerder is echter een maximumbreedte van 600 px opgegeven.
Deze css is voor browservensters minimaal 760 px breed. Daarom is deze combinatie onmogelijk. Als het browservenster bijvoorbeeld 1000 px breed is en #wrapper
moet van left: 0;
tot right: 0;
lopen, én mag vanwege de max-width: 600px;
niet breder dan 600 px zijn, dan kan aan één van die drie eigenschappen onmogelijk worden voldaan.
De browser raakt hier dermate opgewonden van, dat right: 0;
en left: 0;
gewoon worden genegeerd en margin: 0 auto;
toch werkt. Oftewel: bij een fixed positie werkt margin: 0 auto;
niet, tenzij je ook left: 0;
en right: 0;
opgeeft.
(Deze weinig bekende truc werkt trouwens ook bij position: absolute;
. 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 opgewonden raakt. Dit staat gewoon in de specificatie. Alleen is het zo weerzinwekkend technisch opgeschreven dat volgens geruchten zelfs Einstein het pas na 38 keer lezen begreep. Inmiddels is er een nieuwe ontwerp-specificatie met een andere benadering, maar het resultaat is hetzelfde.)
position: fixed;
Om de <section> op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is bij een fixed positie normaal genomen het venster van de browser. Hierdoor scrolt de <section> niet mee met de pagina.
Dit is een wat slordige oplossing, die alleen wordt gebruikt omdat de tekst hier eigenlijk geen enkele functie heeft. Feitelijk staat die tekst er alleen maar, om te laten zien hoe je die eventueel kunt bereiken.
Als de <section> niet fixed wordt gepositioneerd, scrollen de thumbnails mee naar boven als de tekst wordt gescrold. En op tablets met iOS en iPadOS, en in Safari op OS X, scrolt de tekst ook nog mee naar links, als de thumbnails worden gescrold.
Je zou dat ook op andere, nettere, manieren kunnen voorkomen, maar dat is hier niet gedaan.
top: calc(3rem + 85px);
Met behulp van calc()
kunnen berekeningen worden gemaakt. In dit geval wordt berekend, waar de bovenkant van de <section> moet komen te staan.
3rem
: als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de plaats van de bovenkant niet mee verandert met de lettergrootte. Als de naam van de afbeelding en van de knoppen wordt vergroot, moet de bovenkant van de <section> met de tekst lager komen te staan. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
3 rem is dezelfde hoogte als de knoppen boven de thumbnails hebben.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
85px
: dit is iets meer dan de hoogte van de thumbnails, met de eventueel daaronder staande horizontale scrollbalk.
De berekening wordt hier gemaakt met twee verschillende eenheden: rem
en px
.
Dat kan niet, eerst moeten allen eenheden worden omgerekend naar dezelfde eenheid. Daarom rekent de browser de eenheid rem
om naar de eenheid px
.
Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet, wat de lettergrootte is. Maar op het moment van weergave weet de browser dat wel.
De berekening wordt dan 3 keer de lettergrootte + 85 px. Hiermee komt de <section> met de tekst iets onder de thumbnails te staan, en bij een eventueel grotere letter zakt de <section> omlaag.
right: 0; bottom: 0; left: 0;
right
en left
worden iets hierboven bij margin: 0 auto; besproken. Hun enige functie hier is helpen bij het horizontaal centreren van de <section> en de daarin zittende tekst.
bottom: 0;
is nodig om scrollen van de tekst mogelijk te maken. Eerder is met overflow: auto;
opgegeven, dat de tekst te scrollen moet zijn. Anders verdwijnt een deel van de tekst onder het browservenster vanwege de fixed positie. Om te kunnen scrollen, moet de <section> een hoogte hebben. Omdat iets hierboven top
is opgegeven en hier bottom
, is aan deze voorwaarde voldaan.
#voor-sr
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
Het element met id="voor-sr". Een link a#voor-sr
speciaal voor schermlezers. Als er mogelijk nog een grote afbeelding is geopend, terwijl de tekst onder de thumbnails al wordt voorgelezen, kan hiermee de grote afbeelding worden gesloten. Meer hierover is te vinden bij aria-label.
display: block;
De eerder verborgen link zichtbaar maken.
Van zichzelf is een <a> een inline-element. Daardoor kunnen eigenschappen als breedte en hoogte niet worden gebruikt. Door de <a> weer te geven als blok-element kan dat wel.
visibility: hidden;
De link moet alleen worden getoond, als dat nodig is. Anders zouden schermlezers te vaak de tekst in de link voorlezen, terwijl dat niet nodig is. Daarom wordt deze standaard verborgen. Vaak gebeurt dat met display: none;
, maar dat kan hier niet. (De reden dat display: none;
hier niet gebruikt kan worden, staat iets hieronder bij transition-delay: 0.4s;
.)
Daarom wordt hier visibility: hidden;
gebruikt. Dat verbergt de link ook, alleen blijft de ruimte nu in gebruik (bij display: none;
wordt de ruimte vrijgegeven). Maar omdat de link maar 1 x 1 px groot is en op een plaats staat, waar verder niets aanwezig is, is dat geen probleem.
width: 1px; height: 1px;
De link zo klein mogelijk maken. Met deze grootte werkt de link niet, als deze per ongeluk wordt aangeraakt op een touchscreen. Het mooiste zou zijn als de link buiten het scherm kon worden geplaatst. Maar in Android 6 (en eerder) en in iOS 12 (en eerder) werkt een buiten het scherm geplaatste link niet, ook niet in schermlezers. Bovendien mag de link niet worden afgedekt door andere elementen, want ook dan werkt deze niet altijd.
position: fixed;
Om het element op een bepaalde plaats neer te kunnen zetten.
Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is bij een fixed positie normaal genomen het venster van de browser. Hierdoor scrolt de link niet mee met de pagina.
bottom: 10px; right: 20px;
Op 10 px vanaf de onderkant en 20 px vanaf rechts staat de link altijd bovenaan, zonder de kans te lopen afgedekt te worden door andere elementen. Als de link wordt afgedekt door een ander element, werkt deze niet in alle schermlezers.
transition-delay: 0.4s;
Iets hierboven is met visibility: hidden;
de link verborgen. Als je nu bij :hover
, :focus
, :target
, en dergelijke een andere waarde bij visibility
opgeeft, zorgt transition-delay
ervoor dat visibility
niet onmiddellijk naar die nieuwe waarde wordt veranderd bij :hover
en dergelijke, maar pas na een vertraging. Als de zichtbaarheid van 'hidden' wordt veranderd naar 'visible', vindt die verandering pas na een vertraging plaats. De hier opgegeven vertraging is 0,4 seconde. Deze vertraging is zo kort, dat je het nauwelijks merkt.
Als deze link wordt aangeraakt of -geklikt, wordt de focus op het begin van de onder de thumbnails staande tekst gezet, waardoor een eventueel geopende grote afbeelding wordt gesloten. Gelijk daarna is de link niet meer nodig. Daarom wordt deze weer verborgen, zodra de link de focus verliest.
Maar als je die link weer verbergt zonder vertraging, verdwijnt de link, voordat deze gevolgd kan worden. De vertraging van 0,4 seconde zorgt ervoor dat de link eerst wordt gevolgd en dan pas verdwijnt.
Meestal wordt transition-delay
alleen bij de verandering bij :hover
en dergelijke opgegeven. De vertraging van beginstand naar verandering, en weer terug van verandering naar beginstand, is dan in beide richtingen hetzelfde. Maar dat hoeft niet. Door verschillende tijden op te geven, kun je een link bijvoorbeeld met enige vertraging verbergen, maar zonder vertraging tonen. Dat is wat hier gebeurt.
Als de link getoond moeten worden, wordt deze bij section:last-of-type:not(:focus-within) #voor-sr met visibility: visible;
zichtbaar gemaakt. Bij die verandering van onzichtbaar naar zichtbaar is transition-delay: 0s;
opgegeven. Daardoor wordt de link zonder enige vertraging getoond.
Die benodigde vertraging is ook de reden dat display: none;
hier niet gebruikt kan worden om de link te verbergen: een verandering van de waarde bij display
is altijd ogenblikkelijk, je kunt die niet vertragen. Bij visibility
kan dat wel.
transition-delay
is onderdeel van een serie bij elkaar horende eigenschappen, die allemaal met het vertragen, geleidelijk aan uitvoeren, en dergelijke van een verandering te maken hebben. Meestal worden die samengevat als transition
: een zogenaamde 'shorthand', een manier om álle bij elkaar horende eigenschappen in één regel op te schrijven.
transition
, en daarmee ook transition-delay
, brengt een geheel eigen risico met zich mee. In de specificatie staat, welke eigenschappen met behulp van transition
kunnen veranderen. Je kunt eventueel bij transition
opgeven, voor welke eigenschappen het geldt. Als je bijvoorbeeld top
en left
beide wilt veranderen bij :checked
, kun je bijvoorbeeld aangeven dat de geleidelijke verandering met behulp van transition
alleen voor top
geldt. left
verandert dan gewoon in één keer.
Als je alleen transition-delay
gebruikt, kun je niet opgeven, voor welke eigenschappen het geldt. Elke eigenschap die bij :hover
, :focus
, :target
, en dergelijke wordt veranderd, valt onder transition-delay
. In dit geval is dat geen probleem, omdat alleen visibility
verderop met :focus-within
wordt veranderd.
Als er meerdere eigenschappen veranderen, zou daar ook een eigenschap bij kunnen zitten die op dit moment niet door transition
kan worden vertraagd. Maar als de specificatie of een van de browsermakers de geest krijgt en plotsklaps die eigenschap ook onder transition
laat vallen, kan dat tot heel onverwachte resultaten leiden.
Stel dat je een bepaalde eigenschap heel traag wilt veranderen bij :hover
en een andere eigenschap flitsend snel. Mogelijk wordt dat flitsend snel dan opeens ook heel traag, als transition
in de toekomst die eigenschap ook gaat ondersteunen.
Als er ook maar de kleinste kans is op dit soort problemen, of als niet alle eigenschappen die veranderen onder transition moeten vallen, moet je aangeven, welke eigenschap(pen) transition
mag aansturen.
Je kunt dat doen door transition
te gebruiken en daarbij aan te geven, voor welke eigenschappen het geldt. Of je kunt eventueel alleen transition-property
gebruiken en daarmee de eigenschappen aangeven, waarvoor transition-delay
geldt.
section:last-of-type:not(:focus-within) #voor-sr
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#uitleg, .eerste, .laatste, .vorige-picture, .volgende-picture, .vorige-div. .volgende-div, #voor-sr {display: none;}
#voor-sr {display: block; visibility: hidden; width: 1px; height: 1px; position: fixed; bottom: 10px; right: 20px; transition-delay: 0.4s;}
Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld. De pseudo-class :nth-last-of-type()
, onderdeel van deze selector, werkt hetzelfde, maar telt van achter naar voren. Omdat het vaak voorkomt dat je het laatste element van 'n bepaald type wilt hebben, heeft dat een aparte pseudo-class: :last-of-type
.
section
: alle <section>'s.
:last-of-type
: het element met een bepaald volgnummer, geteld van achter naar voren.
In dit geval wordt geen volgnummer gebruikt, maar een speciaal voor het laatste element bedoelde selector: :last-of-type
. Voor alle eerdere elementen gebruik je 'n soortgelijke selector, maar dan met een volgnummer: :nth-last-of-type()
. Tussen de haakjes komt het volgnummer. :nth-last-of-type(1)
is precies hetzelfde als :last-of-type
, maar de laatste is wat mensvriendelijker.
Omdat voor :last-of-type
(of voor eerdere elementen :nth-last-of-type()
) een section
staat, worden alleen <section>'s geteld. Als binnen <main> 327 <p>'s zitten, tellen die niet mee. Hadden ze maar 'n <section> moeten zijn.
:focus-within
: alleen als het element (hier de <section>) of een van de nakomelingen ervan de focus heeft.
:not(:focus-within)
: maar dan omgekeerd. Omdat :focus-within
binnen :not()
staat, geldt dit nu juist als <section> of een van de nakomelingen ervan níét de focus heeft. De pseudo-class :not()
geeft aan dat wat tussen de haakjes staat, juist níét zo moet zijn.
De hele selector in gewone taal: als de laatste <section> of een van de nakomelingen ervan niet de focus heeft, doe dan iets met de in <section> zittende link #voor-sr
.
visibility: visible;
Maak de link zichtbaar.
Als de focus niet binnen de <section> met tekst zit, is er een grote kans dat er nog een grote afbeelding wordt, want dat tonen gebeurt met behulp van :focus
. Als de focus binnen deze <section> zit, kan er onmogelijk nog een grote afbeelding zijn geopend. Daarom wordt de link alleen getoond, als de <section> met tekst of een van de nakomelingen ervan niet de focus heeft.
(Geprobeerd is ook om de link alleen te tonen, als de focus binnen de eerste <section> met de slideshow zit. Dat zou beter werken, want nu wordt de link soms ook getoond, als dat niet nodig is. Helaas gebruiken niet alle schermlezers :focus om een afbeelding te tonen, daardoor werkt dat niet. Maar in het overgrote deel van de gevallen wordt de link terecht getoond, omdat er nog een grote afbeelding geopend is.)
transition-delay: 0s;
Het tonen van de link gebeurt zonder vertraging. Hoe transition-delay: 0s;
werkt is iets hierboven te vinden bij transition-delay: 0.4s;.
section:last-of-type:focus
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
section:last-of-type {background: white; color: black; width: 90vw; max-width: 600px; margin: 20px auto 0; border: black solid 1px; padding: 5px;}
section:last-of-type {overflow: auto; -webkit-overflow-scrolling: touch; margin: 0 auto; position: fixed; top: calc(3rem + 85px); right: 0; bottom: 0; left: 0;}
Kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld. De pseudo-class :nth-last-of-type()
, onderdeel van deze selector, werkt hetzelfde, maar telt van achter naar voren. Omdat het vaak voorkomt dat je het laatste element van 'n bepaald type wilt hebben, heeft dat een aparte pseudo-class, het in deze selector gebruikte :last-of-type
.
section
: alle <section>'s.
:last-of-type
: het element met een bepaald volgnummer, geteld van achter naar voren.
In dit geval wordt geen volgnummer gebruikt, maar een speciaal voor het laatste element bedoelde selector: :last-of-type
. Voor alle eerdere elementen gebruik je 'n soortgelijke selector, maar dan met een volgnummer: :nth-last-of-type()
. Tussen de haakjes komt het volgnummer. :nth-last-of-type(1)
is precies hetzelfde als :last-of-type
, maar de laatste is wat mensvriendelijker.
Omdat voor :last-of-type
(of voor eerdere elementen :nth-last-of-type()
) een section
staat, worden alleen <section>'s geteld. Als binnen <main> 327 <p>'s zitten, tellen die niet mee. Hadden ze maar 'n <section> moeten zijn.
:focus-visible
: maar alleen als dit element de focus heeft, én als zichtbaar moet zijn dat dit element de focus heeft. (Alleen als de bezoeker de Tab-toets gebruikt, meer over :focus-visible is te vinden bij :focus-visible.)
De hele selector in gewone taal: doe iets met de laatste <section>, maar alleen als die de focus heeft én als die focus zichtbaar moet zijn.
outline: blue solid 3px;
Blauw kadertje rondom de <section> met de tekst. Nu weten gebruikers van de Tab-toets dat ze de tekst hebben bereikt (en desgewenst kunnen scrollen met behulp van de pijltjes).
#uitleg:not(:focus-within) + #thumbs > div:first-of-type
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
#uitleg
: het element met id="uitleg". De <div> waarin het vraagteken en de uitleg zitten.
:focus-within
: alleen als het element (hier div#uitleg
) of een van de nakomelingen ervan de focus heeft.
:not(:focus-within)
: maar dan omgekeerd. Omdat :focus-within
binnen :not()
staat, geldt dit nu juist als div#uitleg
of een van de nakomelingen ervan níét de focus heeft. De pseudo-class :not()
geeft aan dat wat tussen de haakjes staat, juist níét zo moet zijn.
+
: het element achter de +
moet in de html direct volgen op het element voor de +
. In dit geval gaat het om div#thumbs
die gelijk op div#uitleg
volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder de eerste <section>.
+
moet hier worden gebruikt, omdat het de enige manier is om een eerder element invloed te laten hebben op een later element met dezelfde ouder (samen met het soortgelijke ~
). Zonder de +
kun je #uitleg:not(:focus-within)
geen enkele invloed laten hebben op de op div#uitleg
volgende div#thumbs
. (Dat is aan het veranderen met de nieuwe pseudo-class :has()
, maar die heeft nog onvoldoende ondersteuning.)
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: <div>'s die een direct kind van div#thumbs
zijn.
:first-of-type
: kinderen van dezelfde ouder kunnen worden geteld, net zoals dat bij kinderen uit een gezin het geval is: eerste kind, tweede kind, derde kind, enzovoort. Je kunt ook alleen 'n bepaald soort element tellen, net zoals je alleen kinderen van jonger of ouder dan zes jaar kunt tellen. Met de pseudo-class :nth-of-type()
worden alleen elementen van een bepaald soort geteld. Omdat het vaak voorkomt dat je het eerste element van 'n bepaald type wilt hebben, heeft dat een aparte pseudo-class, het in deze selector gebruikte :first-of-type
.
Omdat voor :first-of-type
een div
staat, worden alleen <div>'s geteld. Als binnen div#thumbs
327 <p>'s zitten, tellen die niet mee. Hadden ze maar 'n <div> moeten zijn.
De hele selector in gewone taal: als div#thumbs
of een van de nakomelingen ervan níét de focus heeft, doe dan iets met de eerste <div> die in de gelijk op div#uitleg
volgende div#thumbs
zit.
Een heel verhaal. Waar het om gaat: bij openen of herladen van de pagina moeten rechtsboven alleen de knoppen naar de eerste thumbnail en de eerste grote afbeelding (de aardbei), en naar de laatste thumbnail (de worteltjes) zichtbaar zijn. Als er andere knoppen zichtbaar zouden zijn, is dat heel verwarrend: als er nog helemaal niets is gedaan, is 'vorige' wat vreemd.
Daarom wordt de eerste <div> binnen div#thumbs
gebruikt om de knoppen naar de vorige thumbnail en de vorige grote afbeelding te verbergen achter een wit vlakje, en om te zorgen dat de bij de eerste thumbnail horende knoppen altijd zichtbaar zijn. Dat gebeurt met behulp van een aantal extra knoppen binnen deze eerste <div>
Die eerste <div> is altijd aanwezig, maar maar niet altijd zichtbaar. Dat geldt ook voor de erin zittende knoppen.
Als de pagina wordt geopend of herladen, heeft geen enkel element de focus. Dus ook div#uitleg
en de nakomelingen daarvan niet. Als je in die situatie de z-index van de eerste <div> verhoogt (wat gelijk hieronder gebeurt), staat die bij openen of herladen van de pagina altijd bovenaan: de juiste knoppen worden getoond. Zonder z-index staan normaal genomen elementen die lager in de html staan boven eerdere elementen en zie je deze <div> en wat erin zit niet.
Als later een ander element binnen div#thumbs
de focus krijgt, wordt de z-index van dat element verhoogd, waardoor de bij dat element horende knoppen boven de knoppen uit deze eerste <div> worden neergezet. En dus de juiste knoppen bij de juiste thumbnail worden getoond.
z-index: 5;
Hogere z-index. De reden daarvan staat gelijk hierboven onder de selector.
Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is, als het element een 'flex item' is: een direct kind van een 'flex container'. De ouder van deze eerste <div> is div#thumbs
, die bij #thumbs is veranderd in een flex container, dus dat is geregeld.
#thumbs > div:nth-of-type(n + 2):focus-within
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#thumbs > div:nth-of-type(n + 2) {line-height: 0; margin: 0 5px; outline: black solid 2px; scroll-snap-align: center;}
#thumbs > div:last-of-type {margin-right: 50px;}
#thumbs > div:nth-of-type(n + 2) {margin: 0 3px 2px;}
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div:nth-of-type(n + 2)
: de tweede en latere <div> binnen ouder #thumbs
. Binnen elk van deze <div>'s zit een thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke. Een uitgebreidere uitleg over :nth-of-type(n + 2)
, is te vinden bij #thumbs > div:nth-of-type(n + 2).
:focus-within
: maar alleen als dit element of een van de nakomelingen ervan de focus heeft.
De hele selector in gewone taal: doe iets met de tweede en verdere <div> die een direct kind van div#thumbs
zijn, maar alleen als die <div> of een van de nakomelingen ervan de focus heeft.
De enige elementen binnen deze div#thumbs
die de focus kunnen krijgen zijn <picture> (waarin de thumbnail zit) en <div> (als die de focus heeft, wordt de grote afbeelding getoond). In beide gevallen moet de thumbnail gemarkeerd worden om aan te geven, waar in de slideshow je zit.
outline: white solid 4px;
Witte outline.
In tegenstelling tot een border neemt een outline geen ruimte in. Hierdoor wordt de <div> niet plotsklaps 8 px hoger en breder, waardoor andere elementen gedwongen worden ruimte te maken en dus verspringen.
z-index: 10;
Door deze <div> een hogere z-index te geven, wordt komen deze <div> en de inhoud ervan boven andere elementen te staan, ook als die lager in de html staan. Hierdoor worden altijd de knoppen getoond, die bij de thumbnail in deze <div> horen.
Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is, als het element een 'flex item' is: een direct kind van een 'flex container'. De ouder van deze <div>'s is div#thumbs
, die bij #thumbs is veranderd in een flex container, dus dat is geregeld.
#thumbs > div:nth-of-type(n + 2):focus-within .naam, #thumbs > div:focus .groot, #thumbs > div:nth-of-type(n + 2):target + div .groot, #thumbs > div:nth-of-type(n + 2):target + div + div .groot, #thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus + div .groot, #thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus + div + div .groot, .no-js #thumbs picture:focus:not(:target) + .groot
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het nogal veel is. Hier worden slechts twee al eerder opgemaakte elementen zichtbaar gemaakt.
Zeven selectors, gescheiden door een komma. De eerste selector zorgt ervoor dat de naam wordt getoond. De andere vijf selectors zorgen ervoor dat (afhankelijk van de gekozen thumbnail of knop) één of drie grote afbeeldingen worden gedownload (door het veranderen van display: none;
bij span.groot
te veranderen in display: block;
, het echte tonen wordt iets hieronder bij #thumbs > div:focus .groot geregeld).
#thumbs > div:nth-of-type(n + 2):focus-within .naam
, de eerste selector:
#thumbs > div:nth-of-type(2)
: de tweede en latere <div> die een direct kind van het element met id="thumbs" zijn. Dit zijn de <div>'s waarbinnen een thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke zit.
Een uitgebreidere verklaring van dit deel van de selector is te vinden bij #thumbs > div:nth-of-type(n + 2).
:focus-within
: maar alleen als dit element of een van de nakomelingen ervan de focus heeft.
.naam
: de elementen met class="naam" binnen deze <div>. Dat is er maar één in elke <div>: span.naam
met daarin de naam die bij de thumbnail hoort.
De hele eerste selector in gewone taal: doe iets met het element met class="naam" in de tweede en latere <div> die een direct kind van #thumbs
zijn, maar alleen als de <div> of een nakomeling ervan de focus heeft.
De css die hier bij deze selector wordt uitgevoerd is display: block;
. Voor de <span> met de naam betekent dat, dat de eerder bij .naam met display: none;
verborgen naam zichtbaar wordt.
De <div> of een van de nakomelingen ervan kan op een aantal manieren de focus krijgen: door rechtstreeks aanraken of -klikken, door gebruik van de Tab-toets, en door een van de zes knoppen rechtsboven te gebruiken. In alle gevallen moet de bij de thumbnail horende naam worden getoond.
#thumbs > div:focus .groot
, de tweede selector:
#thumbs
: het element met id="thumbs". In deze <div> zit de hele slideshow.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: alle <div>'s die een direct kind van #thumbs
zijn.
:focus
: maar alleen als deze <div> de focus heeft.
.groot
: de elementen met class="groot" binnen deze <div>. Dat is er maar één in elke <div>: span.groot
. In deze <span> komt, als achtergrond-afbeelding, de grote afbeelding te staan. De grote afbeelding vult de volledige <span>. Grootte, positie, en dergelijke van de <span> zijn eerder al bij .groot opgegeven.
De hele tweede selector in gewone taal: doe iets met het element met class="groot" in de <div>'s die een direct kind van #thumbs
zijn, maar alleen als de <div> de focus heeft.
De css die hier bij deze selector wordt uitgevoerd is display: block;
. Voor de <span> met de grote afbeelding betekent dat, dat de eerder bij .groot met display: none;
verborgen <span> zichtbaar wordt. Of zichtbaar: eigenlijk nog niet, want span.groot
is ook ver boven het venster van de browser gezet. Het eigenlijke zichtbaar worden gebeurt pas iets hieronder bij #thumbs > div:focus .groot, waar de <span> binnen het venster wordt gepositioneerd.
Omdat de <span> eerder met display: none;
was verborgen, werd de achtergrond-afbeelding niet gedownload. Dat gebeurt pas hier, waar de <span> met display: block;
tot leven wordt gewekt. Door de grote afbeelding pas nu te downloaden, wordt onnodig downloaden van niet-getoonde afbeeldingen voorkomen, wat bandbreedte bespaart.
De <div> kan op verschillende manieren de focus krijgen: door gebruik van de Tab-toets, door een rechtstreekse aanraking van of klik op een thumbnail, of door gebruik van de tweede of derde knop rechtsboven (die met de iets grotere kleine afbeelding).
In al deze gevallen moet de grote afbeelding worden getoond, en dus worden gedownload.
#thumbs > div:nth-of-type(n + 2):target + div .groot
, de derde selector:
#thumbs > div:nth-of-type(2)
: de tweede en latere <div> die een direct kind van het element met id="thumbs" zijn. Dit zijn de <div>'s waarbinnen een thumbnail met bijbehorende grote afbeelding, knoppen, en dergelijke zit.
Een uitgebreidere verklaring van deze selector is te vinden bij #thumbs > div:nth-of-type(n + 2).
:target
: maar alleen als deze <div> de 'target' is. Een 'target' is het element, dat het doel is van een link. In de hieronder staande link
<a class="volgende-div" href="#div-1" tabindex="-1" aria-hidden="true">→</a>
is #div-1
de 'target':
<div id="div-1" tabindex="0">
+
: het element achter de +
moet in de html direct volgen op het element voor de +
. In dit geval gaat het om de <div> die gelijk op de <div> die 'target' is volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder div#thumbs
.
div
: de <div> die op de <div> die 'target' is volgt.
.groot
: de elementen met class="groot" binnen deze <div>. Dat is er maar één in elke <div>: span.groot
. In deze <span> komt, als achtergrond-afbeelding, de grote afbeelding te staan. De grote afbeelding vult de volledige <span>. Grootte, positie, en dergelijke van de <span> zijn eerder al bij .groot opgegeven.
De hele derde selector in gewone taal: als de tweede of latere <div> die een direct kind van #thumbs
is de 'target' is, doe dan iets met de in de direct op volgende <div> zittende span.groot
.
De enige manier waarop deze <div>'s 'target' kunnen worden, is door het aanraken of -klikken van de tweede of derde knop rechtsboven: die met de iets grotere kleine afbeelding. Deze zijn bedoeld om door de slideshow te bladeren, terwijl álle grote afbeeldingen worden getoond.
Om een afbeelding te kunnen tonen, moet deze eerst worden gedownload. Dat levert een (kleine) vertraging op, voordat de afbeelding de eerste keer kan worden getoond. (Bij een herhaling gaat het sneller, omdat de browser de afbeelding dan heeft opgeslagen in de cache.) Dertig keer (in deze slideshow) die kleine vertraging kan knap irritant worden, zeker bij een iets tragere verbinding.
De css die hier bij deze selector wordt uitgevoerd is display: block;
. Eerder is span.groot bij .groot met display: none;
verborgen. Daardoor is de achtergrond-afbeelding van span.groot
, de grote afbeelding, nog niet gedownload. Dat gebeurt pas hier, waar de <span> met display: block;
tot leven wordt gewekt. Door de grote afbeelding pas nu te downloaden, wordt onnodig downloaden van niet-getoonde afbeeldingen voorkomen, wat bandbreedte bespaart.
Nu is de afbeelding alvast gedownload, voordat deze moet worden getoond, waardoor deze veel sneller kan worden getoond.
Er is hier gekozen voor :target
en niet voor :focus
, omdat de <div> ook de focus krijgt, als de thumbnail wordt aangeraakt of -geklikt. En dan moet alleen de bij die thumbnail horende grote afbeelding worden getoond.
Deze truc werkt alleen bij vooruit bladeren met behulp van de +
in de selector, waarbij je iets met het volgende element kunt doen. Er bestaat niet zoiets als een –
, waarmee je iets met een vorig element zou kunnen doen. Daarvoor is JavaScript nodig. Omdat de meeste mensen vooraan zullen beginnen en dan vooruit zullen gaan, is daar verder niet naar gekeken.
#thumbs > div:nth-of-type(n + 2):target + div + div .groot
, de vierde selector:
De vierde selector is vrijwel hetzelfde als de derde selector, alleen gaat het nu om de tweede <div> die volgt op de <div> die de 'target' is. Ook de grote afbeelding binnen deze <div> wordt alvast gedownload. Bij bladeren door de slideshow staan er dus altijd al twee volgende grote afbeeldingen klaar om getoond te worden.
#thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus + div .groot
, de vijfde selector:
Deze selector lijkt erg op de derde selector, wat goed te zien is als je ze boven elkaar zet:
#thumbs > div:nth-of-type(n + 2):target + div .groot
#thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus + div .groot
Er zijn twee verschillen.
Bij de derde selector staat vooraan #thumbs
, bij de vijfde selector #thumbs.gebruikt-tab
. De vijfde selector geldt alleen voor een element dat id="thumbs" én class="gebruikt-tab" heeft.
Als binnen div#thumbs
de Tab-toets wordt ingedrukt, wordt met behulp van JavaScript class 'gebruikt-tab' aan div#thumbs
toegevoegd:
<div id="thumbs" class="gebruikt-tab">
Kennelijk gebruikt de bezoeker het toetsenbord om door de slideshow te lopen. Zodra ergens een andere toets wordt ingedrukt, wordt door het JavaScript de class 'gebruikt-tab' weer verwijderd. (Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
De vijfde selector begint met #thumbs.gebruikt-tab
en werkt hierdoor alleen, als de bezoeker de Tab-toets gebruikt om door de slideshow te lopen.
Bij de derde en vierde selector wordt :target
gebruikt: alleen als de tweede of derde knop rechtsboven worden aangeraakt of -geklikt, kan een <div> 'target' worden. En alleen dan wordt kennelijk door alle grote afbeeldingen van de slideshow gebladerd en worden de twee volgende grote afbeeldingen alvast gedownload, om vertraging bij het tonen te voorkomen.
Als een gebruiker de Tab-toets gebruikt om door de slideshow te bladeren, wordt achtereenvolgens elke grote afbeelding getoond, omdat bij elke Tab een volgende <div> de focus krijgt. Om vertraging bij het weergeven te voorkomen, worden dan ook de volgende twee grote afbeeldingen alvast gedownload, net als bij bladeren door de slideshow met behulp van de tweede en derde knop rechtsboven.
Omdat de <div> ook de focus krijgt, als de thumbnail wordt aangeraakt of -geklikt, kon bij de derde selector :focus
niet worden gebruikt. Want dan zouden ook als je gewoon één grote afbeelding wilt zien de volgende twee al worden gedownload.
Hier speelt dat probleem niet, want deze selector werkt alleen als de Tab-toets wordt gebruikt. Hier kan dus gewoon :focus
worden gebruikt. (Bovendien werkt :target
alleen als een link wordt gevolgd, en dat is bij gebruik van de Tab-toets niet het geval).
Het downloaden gebeurt weer op precies dezelfde manier als bij de derde selector: de met display: none;
verborgen span.groot
wordt met display: block;
getoond, waardoor de erin zittende achtergrond-afbeelding (de grote afbeelding) wordt gedownload.
De vijfde selector samengevat: als div#thumbs
ook class 'gebruikt-tab' heeft (als de Tab-toets wordt gebruikt), downloadt dan ook alvast de volgende grote afbeelding.
#thumbs.gebruikt-tab > div:nth-of-type(n + 2):focus + div + div .groot
, de zesde selector:
Deze selector is precies hetzelfde als de vijfde selector, alleen geldt deze voor de tweede grote afbeelding na de grote afbeelding die wordt getoond.
.no-js #thumbs picture:focus:not(:target) + .groot
, de zevende selector:
.no-js
: alle elementen met class="no-js". Dat is er maar één: <html>.
#thumbs
: het element met id="thumbs". In deze <div> zit de hele slideshow.
picture
: alle <picture>'s binnen #thumbs
.
:focus
: maar alleen als de <picture> de focus heeft.
:target
: alleen als het element (hier de <picture>) de 'target' is. Een element is de target, als het het doel van een link is. Bij de <picture>'s kan dat alleen het geval zijn, als de tweede of derde knop vanaf rechts is aangeraakt of -geklikt. Alleen deze twee knoppen linken naar een van de <picture>'s.
:not(:target)
: maar dan omgekeerd. Omdat :target
binnen :not()
staat, geldt dit nu juist als de <picture> géén target is. De pseudo-class :not()
geeft aan dat wat tussen de haakjes staat, juist níét zo moet zijn.
:focus:not(:target)
bij elkaar: de <picture> moet wél de focus hebben, maar mag géén target zijn.
Als een thumbnail rechtstreeks wordt aangeraakt of -geklikt, wordt in feite de <picture> aangeraakt of -geklikt. Daardoor krijgt de <picture> de focus. Maar omdat de <picture> geen target is, is de selector dan geldig.
Als een <picture> de focus krijgt doordat een van de knoppen met een link naar een <picture> is aangeraakt of -geklikt, is de <picture> ook target, en is de selector niet geldig.
+
: het element achter de +
moet in de html direct volgen op het element voor de +
. In dit geval gaat het om span.groot
die op <picture> volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder dezelfde <div>.
.groot
: de elementen met class="groot". Dit zijn de <span>'s, waarbinnen de grote afbeelding als achtergrond-afbeelding wordt getoond.
De zevende selector zorgt er dus voor dat er iets met span.groot
gebeurt, maar alleen als de <picture> de focus heeft gekregen door het rechtstreeks aanraken of ‑klikken van een thumbnail. Bovendien moet in een van de voorouders van span.groot
class 'no-js' aanwezig zijn, want daar begint de selector mee.
Bij het openen van de pagina is deze class in <html> aanwezig:
<html lang="nl" tabindex="-1" class="no-js">
Een van de eerste dingen die het JavaScript doet, is het verwijderen van deze class. Hierdoor is deze selector niet meer geldig. Als, om wat voor reden dan ook, het script niet werkt, blijft de class gewoon aanwezig.
Als het JavaScript werkt, wordt door het script soms de focus teruggezet op <picture> bijvoorbeeld als de hulp wordt geopend en weer gesloten. Zonder JavaScript zou dat niet kunnen en moet je weer vooraan de slideshow beginnen.
Maar als JavaScript op deze manier de focus terugzet, moet de grote afbeelding niet worden getoond. Dat gebeurt echter wel, omdat de focus op <picture> wordt teruggezet, zonder dat een link wordt gevolgd. Oftewel: <picture> heeft wel de focus, maar is geen target. Hierdoor kan, als het JavaScript werkt, de grote afbeelding niet meer worden gesloten.
Door het script class 'no-js' weg te laten halen bij <html>, werkt de selector niet meer. Nu kan, ook als het script werkt, de grote afbeelding gewoon worden gesloten.
Omgekeerd kan, als het script om wat voor reden dan ook niet werkt, de grote afbeelding niet worden geopend door aanraken of -klikken van de thumbnail. Daar is deze selector voor nodig. Maar als het script niet werkt, blijft class 'no-js' gewoon aanwezig, en blijft deze selector gewoon werken.
display: block;
Toon het eerder met display: none;
verborgen element.
Omdat deze regel voor de verschillende selectors een nogal ander effect heeft, wordt de werking van display: block;
hierboven bij elk van de zeven selectors zelf beschreven.
#thumbs > div:focus .groot, .no-js #thumbs picture:focus:not(:target) + .groot
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het nogal veel is. Hier wordt slechts een al eerder opgemaakt element zichtbaar gemaakt, door het binnen het venster te plaatsen.
Twee selectors, gescheiden door een komma.
#thumbs div:focus .groot
, de eerste selector:
#thumbs
: het element met id="thumbs". De <div> waar de hele slideshow in zit.
>
: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <div> moet een direct kind van #thumbs
zijn. Een uitgebreidere uitleg over wat een direct kind is, is te vinden bij #thumbs > div:nth-of-type(n + 2).
div
: alle <div>'s binnen #thumbs
, die een direct kind van #thumbs
zijn.
:focus
: maar alleen als de <div> de focus heeft.
.groot
: de elementen met class="groot" binnen deze <div>. Dat is er maar één in elke <div>: span.groot
. In deze <span> komt, als achtergrond-afbeelding, de grote afbeelding te staan. De grote afbeelding vult de volledige <span>. Grootte, positie, en dergelijke van de <span> zijn eerder al bij .groot opgegeven.
.no-js #thumbs picture:focus:not(:target) + .groot
, de tweede selector:
Deze selector wordt iets hierboven bij .no-js #thumbs picture:focus:not(:target) + .groot uitgebreid beschreven. Hij werkt hetzelfde als de eerste selector hier, maar alleen als JavaScript uitstaat of om een andere reden niet werkt.
top: calc(3rem + 80px);
span.groot
wordt gebruikt om de grote afbeelding als achtergrond-afbeelding van span.groot
te tonen. Alle instellingen voor de grootte en dergelijke zijn eerder al opgegeven bij .groot. Het downloaden van de grote afbeelding is gelijk hierboven bij #thumbs > div:nth-of-type(n + 2):focus-within .naam ... geregeld. Kortom: alles is klaar voor de grote show.
Bij .groot is span.groot
niet alleen met display: none;
verborgen (om voortijdig downloaden te voorkomen), maar ook boven het scherm gezet met top: -20000px;
(om voortijdig tonen te voorkomen).
display: none;
is in de selector hier gelijk boven al veranderd in display: block;
. Maar de <span>, en daarmee de grote afbeelding, staat nog steeds boven het scherm. Hier wordt de <span> binnen het scherm gezet en daarmee zichtbaar, en dus ook de erin zittende achtergrond-afbeelding.
Omdat deze selector alleen geldt voor de <div> die de focus heeft, wordt alleen de in die <div> zittende <span> met de grote afbeelding getoond.
Met behulp van calc()
kunnen berekeningen worden gemaakt. In dit geval wordt berekend, waar de bovenkant van de <span>, en daarmee ook de grote afbeelding, moet komen te staan.
3rem
: als eenheid wordt de relatieve eenheid rem
gebruikt, omdat bij gebruik van een absolute eenheid zoals px
de plaats van de bovenkant niet mee verandert met de lettergrootte. Als de naam van de afbeelding en van de knoppen wordt vergroot, moet de bovenkant van de <span> met de grote afbeelding lager komen te staan. Zoomen kan wel altijd, ongeacht welke eenheid wordt gebruikt.
3 rem is dezelfde hoogte als de knoppen boven de thumbnails hebben.
De minder bekende rem
is ongeveer hetzelfde als de em
. Alleen is de lettergrootte bij rem
gebaseerd op de lettergrootte van het <html>-element, waardoor de rem
overal op de pagina precies even groot is, ook als de bezoeker de lettergrootte heeft veranderd. Bij de em
kan de lettergrootte worden beïnvloed door de voorouders van het element, bij de rem
niet.
80px
: dit is iets meer dan de hoogte van de thumbnails, met de eventueel daaronder staande horizontale scrollbalk.
De berekening wordt hier gemaakt met twee verschillende eenheden: rem
en px
. Dat kan niet, eerst moeten allen eenheden worden gerekend naar dezelfde eenheid. Daarom rekent de browser de eenheid rem
om naar de eenheid px
.
Bij het schrijven van de code kan dat omrekenen niet, omdat je niet weet, wat de lettergrootte is. Maar op het moment van weergave weet de browser dat wel.
De berekening wordt dan 3 keer de lettergrootte + 80 px. Hiermee komt de <span> met de grote afbeelding iets onder de thumbnails te staan, en bij een eventueel grotere letter zakt de <span> omlaag.
css voor iOS, iPadOS en Safari op OS X
@supports (-webkit-appearance: -apple-pay-button)
De css die hier tot nu toe staat, geldt voor alle browsers.
De css die binnen deze 'feature query' staat, geldt alleen voor browsers die webkit‑appearance: -apple-pay-button;
ondersteunen. (Er bestaat geen goede Nederlandse vertaling voor 'feature query'. Het is een vraag of een bepaalde eigenschap en waarde worden ondersteund: iets als 'eigenschap vraag'.)
@supports
: hierachter komt tussen haakjes de te onderzoeken eigenschap met een bepaalde waarde te staan.
-webkit-appearance: -apple-pay-button
: de eigenschap waarop wordt getest. Dit ziet er misschien wat onbekend uit, maar de test bestaat uit precies dezelfde css die wordt gebruikt bij de selector. Meestal zal dat iets zijn als mix-blend-mode: normal
, wat iets gangbaarder is.
Het gaat binnen deze query om css die alleen voor iOS, iPadOS en Safari op OS X van belang is. (Omdat alle browsers op het superieure iOS en iPadOS verplicht werken met de superieure weergave-machine van het superieure Safari, werken de superieure idioterieën van Safari ook in alle andere browsers op het superieure iOS en iPadOS.)
In principe zou -webkit-appearance
door het voorvoegsel -webkit-
alleen horen te werken in de hierboven genoemde browsers. Maar omdat in het verleden appearance
zonder voorvoegsel vaak werkt weggelaten, zijn andere browsers ‑webkit-
vaak ook gaan ondersteunen (meer hierover is te vinden bij De voorvoegsels -moz- en -webkit-).
Daarom is de waarde -apple-pay-button
gebruikt: deze wordt alleen door Safari en op iOS en iPadOS ondersteund. Hierdoor werkt de css binnen deze query wel voor bijvoorbeeld Safari op OS X, maar niet voor Firefox op OS X. Precies de bedoeling.
Gelijk na deze regel komt een {
te staan, en aan het einde van de css die binnen deze query staat een bijbehorende afsluitende }
. Die zijn in de regel hierboven weggevallen, maar het geheel ziet er zo uit:
@supports (–webkit-appearance:-apple-pay-button) {
body {color: silver;}
(...) rest van de css voor deze @supports-regel (...)
footer {color: gold;}
}
Voor de eerste css binnen deze @supports
-regel staat dus een extra {
, en aan het eind staat een extra }
.
Je kunt ook als voorwaarde opgeven dat meerdere eigenschappen (en/of waarden) moeten worden ondersteund, of dat een eigenschap juist niet mag worden ondersteund, maar dat gebeurt hier allemaal niet. Hier is de enige voorwaarde dat -webkit-apperance: ‑apple-pay-button;
wordt ondersteund.
#thumbs
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog, en alleen in browsers die -webkit-apperance: -apple-pay-button
ondersteunen. Voor andere vensters en browsers is de uitleg hieronder niet van belang.
Voor dit element is eerder css opgegeven. Deze wordt binnen dit blokje herhaald in de volgorde, waarin deze in de stylesheet staat, zodat alles hier overzichtelijk bij elkaar staat. (Alleen wat binnen deze media query geldig is, wordt binnen dit blokje herhaald.)
#thumbs {display: flex; align-items: start; overflow: auto; margin-top: 5px; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory;}
#thumbs {background: #444; color: black; margin-top: 3rem; border-bottom: black solid 1px; padding: 5px 0 3px; scroll-snap-type: none;}
Het element met id="thumbs". De <div> waar de hele slideshow in zit.
overflow: visible;
Eerder is overflow: auto;
opgegeven: als de inhoud niet in #thumbs
past, maak dan scrollen mogelijk en toon eventueel een scrollbalk. Die inhoud past niet, want dat zijn de dertig thumbnails naast elkaar.
Op iOS en iPadOS wordt de grote afbeelding niet getoond bij elke andere waarde dan 'visible' bij #thumbs
.
float: left;
Deze regel is nodig vanwege overflow: visible;
hierboven. Zonder deze regel verdwijnen in Safari op OS X en op iOS knoppen rechtsboven en loopt de border rondom de thumbnails niet over de volle lengte door.
.naam
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog, en alleen in browsers die -webkit-apperance: -apple-pay-button
ondersteunen. Voor andere vensters en browsers is de uitleg hieronder niet van belang.
Voor dit elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het een enorme hoeveelheid is. Hier wordt slechts een al eerder opgemaakt element fixed gepositioneerd. Een tamelijk kleine correctie, die alleen voor iOS, iPadOS en Safari op OS X nodig is.
Alle elementen met class="naam". In span.naam
zit de naam van de afbeelding.
position: fixed;
Eerder is bij .naam position: absolute;
opgegeven om span.naam
op de juiste plaats neer te zetten. In Safari op OS X, op iOS en op iPadOS scrolt de <span> met de naam dan echter mee met de thumbnails. Dit voorkomt dat meescrollen.
#thumbs > div:focus .groot, .no-js #thumbs picture:focus:not(:target) + .groot {top: calc(5.2rem + 70px);}
Deze selector werkt alleen binnen vensters minimaal 760 breed en minimaal 530 px hoog, en alleen in browsers die -webkit-apperance: -apple-pay-button
ondersteunen. Voor andere vensters en browsers is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het een enorme hoeveelheid is. En het gaat hier slechts om een correctie, die alleen voor iOS, iPadOS en Safari op OS X nodig is.
Hier wordt alleen span.groot
, die als achtergrond-afbeelding de grote afbeelding heeft, iets lager neergezet, omdat de afbeelding anders te hoog staat in de genoemde browsers. De selectors en de css werken precies hetzelfde als bij #thumbs > div:focus .groot, ..., alleen wordt hier bij de berekening 5,2 rem gebruikt in plaats van 3,2 rem.
css voor vensters minimaal 1004 px breed en minimaal 530 px hoog
@media screen and (min-width: 1004px) and (min-height: 530px)
De opbouw van deze regel staat beschreven bij @media screen and (orientation: portrait) and (max-width: 759px). Er zijn drie verschillen:
(orientation: portrait)
vervalt: het maakt niet uit of het browservenster in landschapsstand of portretstand staat;
(max-width: 759px)
(maximaal 759 px breed) is veranderd in (min-width: 1004px)
(minimaal 1004 px breed);
(min-height: 530px)
(minimaal 530 px hoog) is een nieuwe voorwaarde.
De css binnen deze mediaregel geldt voor browservensters minimaal 1004 px breed en minimaal 530 px hoog. Voor alle andere vensters heeft de css geen enkel effect.
In deze bredere browservensters komt de naam niet midden tussen de knoppen, maar midden boven de grote afbeelding te staan.
Als dat past binnen het venster van de browser, wordt de afbeelding 1000 px breed weergegeven. Omdat om de afbeelding nog een border van 2 px staat, moet het venster daarom minimaal 1004 px breed zijn. Vandaar deze ietwat vreemde minimumbreedte.
.naam
Deze selector werkt alleen binnen vensters minimaal 1000 px breed en minimaal 530 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het nogal veel is. En het gaat hier slechts om een kleine aanpassing van de positie.
Alle elementen met class is 'naam'. In span.naam
zit de naam van de afbeelding.
left: 295px;
Eerder is bij .naam de <span> met de naam horizontaal gecentreerd tussen de knoppen. In deze bredere browservensters is het mooier, als de naam midden boven de grote afbeelding staat. (Hoewel 'mooier' natuurlijk tamelijk subjectief is...)
css voor vensters minimaal 1004 px breed en minimaal 830 px hoog
@media screen and (min-width: 1004px) and (min-height: 830px)
De opbouw van deze regel staat beschreven bij @media screen and (orientation: portrait) and (max-width: 759px). Er zijn drie verschillen:
(orientation: portrait)
vervalt: het maakt niet uit of het browservenster in landschapsstand of portretstand staat;
(max-width: 759px)
(maximaal 759 px breed) is veranderd in (min-width: 1004px)
(minimaal 1004 px breed);
(min-height: 830px)
(minimaal 830 px hoog) is een nieuwe voorwaarde.
De css binnen deze mediaregel geldt voor browservensters minimaal 1004 px breed en minimaal 830 px hoog. Voor alle andere vensters heeft de css geen enkel effect.
Als dat past binnen het venster van de browser, wordt de afbeelding 1000 px breed weergegeven. Omdat om de afbeelding nog een border van 2 px staat, moet het venster daarom minimaal 1004 px breed zijn. Vandaar deze ietwat vreemde minimumbreedte.
.groot
Deze selector werkt alleen binnen vensters minimaal 1004 px breed en minimaal 830 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, omdat het nogal veel is. En het gaat hier slechts om een kleine aanpassing van de breedte bij al eerder volledig opgemaakte elementen.
Alle elementen met class="groot">. De <span>'s die worden gebruikt om de grote afbeelding te tonen.
width: 1000px; height: 750px;
Breedte en grootte van span.groot
. De grote afbeelding zit hierin als achtergrond-afbeelding. Eerder is bij .groot de weergave, positie, en dergelijke van <span> en de erin zittende achtergrond-afbeelding geregeld. Hier wordt alleen de grootte van de <span> aangepast aan de grotere vensters van de browser.
Als het browservenster groot genoeg is, wordt de <span> weergegeven op een grootte van 1000 px breed en 750 px hoog. Als het venster niet groot genoeg , gelden nog steeds de eerder opgegeven maximumbreedte en -hoogte.
Voor de grootte van de <span> geldt in deze bredere browservensters dus:
width: 1000px;
max-width: calc(1.3333 * (100vh - 3rem – 86px));
height: 750px;
max-height: calc(100vh - 3rem - 86px);
Hoe deze maximumbreedte en -hoogte precies worden berekend, is te vinden bij width: 700px; height: 525px;.
#thumbs > div:nth-of-type(2) .groot {
background-image: url("../055-pics/aardbeien-1000.jpg");
background-image: -webkit-image-set(
url("../055-pics/aardbeien-1000.jpg") 1x,
url("../055-pics/aardbeien-2000.jpg") 2x,
url("../055-pics/aardbeien-3000.jpg") 3x);
background-image: image-set
url("../055-pics/aardbeien-1000.jpg") 1x,
url("../055-pics/aardbeien-2000.jpg") 2x,
url("../055-pics/aardbeien-3000.jpg") 3x);
}
tot en met
#thumbs > div:nth-of-type(31) .groot {
background-image: url("../055-pics/worteltjes-1000.jpg");
background-image: -webkit-image-set
url("../055-pics/worteltjes-1000.jpg")1x,
url("../055-pics/worteltjes-2000.jpg") 2x,
url("../055-pics/worteltjes-3000.jpg") 3x);
background-image: image-set
url("../055-pics/worteltjes-1000.jpg") 1x,
url("../055-pics/worteltjes-2000.jpg") 2x,
url("../055-pics/worteltjes-3000.jpg") 3x);
}
Deze selectors werken alleen binnen vensters minimaal 1004 px breed en minimaal 830 px hoog. Voor andere vensters is de uitleg hieronder niet van belang.
Voor deze elementen is eerder css opgegeven. Deze wordt hier niet allemaal herhaald, want het is nogal veel. Hier wordt alleen voor deze grotere vensters een grotere background-image
opgegeven.
Hier wordt opgegeven, welke achtergrond-afbeelding moet worden gebruikt. Dat is afhankelijk van de resolutiedichtheid van het scherm. Dit werkt op precies dezelfde manier als in kleinere browservensters.
De volledige uitleg van hoe dit werkt is te vinden bij #thumbs > div:nth-of-type(2) .groot. Het enige verschil: hier worden grotere afbeeldingen gebruikt. Het getal gelijk voor '.jpg' in de naam van de afbeelding geeft, net als bij de kleinere browservensters, weer de breedte van de afbeelding in px aan.
JavaScript
De code is geschreven in een afwijkende
lettersoort. De code die te maken heeft met de basis van dit voorbeeld (essentiële code) is in de hele uitleg onderstippeld blauw
. 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.
/* afbeelding-055-dl.js */
(function () {
"use strict";
if (window.innerWidth < 760 || window.innerHeight < 530) { return; }
document.querySelector("html").classList.remove("no-js");
let heeftFocus;
const path = location.pathname + "#";
const thumbs = document.querySelector("#thumbs");
window.addEventListener("load", ververs);
document.querySelector("html").addEventListener("click", sluitGrote);
document.querySelector("html").addEventListener("keydown", sluitGrote);
document.querySelectorAll("#thumbs img").forEach(item => {
item.addEventListener("click", function(e) {
e.target.parentElement.parentElement.focus();
});
});
thumbs.addEventListener("click", verwerkLinks);
thumbs.addEventListener("keydown", focusBijTabEnter);
function ververs() {
let url = location.toString();
document.querySelector("#picture-1").focus();
document.querySelector("main").focus();
if (url.indexOf("#") > -1) {
url = url.substring(0, url.indexOf("#"));
location.assign(url);
}
}
function sluitGrote(e) {
if (e.key !== "Tab") { thumbs.classList.remove("gebruikt-tab"); }
if ( heeftFocus && e.key === "Escape" && document.querySelector("#uitleg").contains(document.activeElement) ) {
heeftFocus.focus();
return;
} else if ( ( (! thumbs.contains (e.target) && (e.target.id !== "vraagteken") ) || e.key === "Escape") && heeftFocus && heeftFocus.id.startsWith("div-") ) {
document.querySelector("#" + heeftFocus.id + "> picture").focus();
heeftFocus = document.activeElement;
} else if (e.target.id !== "vraagteken" && heeftFocus && heeftFocus.id.startsWith("picture-") ) {
document.querySelector("#" + heeftFocus.id).focus();
}
}
function verwerkLinks(e) {
if (e.target.href && (e.target.href.indexOf(path) > -1) {
location.replace(e.target.href);
}
heeftFocus = document.activeElement;
}
function focusBijTabEnter(e) {
if (e.key === "Enter" && e.target.id.startsWith("picture-")) {
e.target.parentElement.focus();
heeftFocus = document.activeElement;
} else if (e.key === "Tab") {
thumbs.classList.add("gebruikt-tab");
setTimeout(function() {
if (document.activeElement.id.startsWith("div-")) {
heeftFocus = document.activeElement;
} else {
heeftFocus = "";
}
}, 50 );
}
}
}) ();
Als je bovenstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.
In principe werkt de slideshow ook zonder JavaScript, maar dat werkt niet echt heel lekker. Dit script zorgt ervoor dat een aantal zaken beter werken. Een overzicht van de dingen die door dit script worden geregeld staat bij Kort overzicht van problemen die met JavaScript worden opgelost.
Omdat het om een iets groter script gaat, is dit in een apart bestand gezet: afbeelding-055.js in de map 055-js-dl. Het script wordt met behulp van <script src="055-js-dl/afbeelding-055-dl.js"></script> aan de pagina gekoppeld.
/* afbeelding-055-dl.js */
Om vergissingen te voorkomen is het een goede gewoonte bovenaan het script even de naam neer te zetten. Voor je het weet, zit je anders in het verkeerde bestand te werken.
(function () {
function
: het sleutelwoord waarmee het begin van een functie wordt aangegeven. Een functie is een stukje bij elkaar horende code. (Het haakje helemaal aan het begin wordt iets verderop beschreven.)
()
: achter een functie moeten twee haakjes staan. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie, maar bij deze functie gebeurt dat niet.
{
: geeft het begin van de code binnen de functie aan. Aan het eind van de functie, dat is in dit geval helemaal onderaan in de laatste regel van het script, staat een bijbehorende }
.
In die laatste regel staat in dit geval nog meer dan alleen de }
die het einde van de code aangeeft: }) ();
:
}
: dit is de eerder genoemde afsluitende }
, waarmee het einde van de code in de functie wordt aangegeven.
)
: dit afsluitende haakje is de tegenhanger van het haakje dat voor de functie staat. Samen zorgen ze ervoor dat de hele functie, met alles erop en eraan, tussen haakjes staat. De reden van deze haakjes wordt iets hieronder besproken.
()
: dit is weer gewoon een taalregel van JavaScript. In dit geval moeten er achter de })
nog twee haakjes staan. Een computer is gewoon niet zo slim, anders weet de ziel niet wat er moet gebeuren.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
We hebben hier een script, waarin alle code binnen een functie is geplaatst. Alle code staat tussen {
en }
, zodat de computer het begin en einde van de functie kan herkennen.
Een functie is een stukje bij elkaar horende code dat je, zo vaak als je wilt, kunt uitvoeren. Als je de tafel van twaalf op het scherm wilt zetten, hoef je niet twaalf vermenigvuldigingen in JavaScript te schrijven, maar schrijf je 'n functie voor één vermenigvuldiging, die je vervolgens tien keer (op steeds iets andere wijze) uitvoert.
Een functie heeft echter één probleem: de code in de functie wordt pas uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen kan op allerlei manieren gebeuren, bijvoorbeeld als de gebruiker 'n toets indrukt, als de pagina is geladen, als het 13:13 is op vrijdag de dertiende, noem maar op. Maar zonder te worden aangeroepen doet een functie helemaal niets.
In dit script is dat ook zo. Er zit bijvoorbeeld een functie in die reageert op het indrukken van een toets. Om die functie goed te laten werken, moet de computer eerst wat voorbereidend werk verrichten. Daarvoor moet het script worden gelezen. Maar dat hele script zit in een functie, en die functie doet dus pas wat, als die wordt aangeroepen.
Om te zorgen dat de buitenste functie, die waar alle code in zit, toch vanzelf wordt uitgevoerd, zet je er een (
voor. En helemaal aan het eind, achter de afsluitende }
, zet je de tegenhanger )
. Om te zorgen dat de functie echt vanzelf wordt uitgevoerd, moeten hierachter nog twee haakjes ()
worden gezet. Nu wordt de functie automatisch uitgevoerd, zonder dat deze hoeft te worden aangeroepen. En kan de code in het script worden gelezen, waardoor het benodigde voorbereidende werk kan worden uitgevoerd.
(Je kunt de code ook in een niet alles omvattende functie zetten. Dan wordt de code gelijk uitgevoerd. Maar dat brengt belangrijke risico's met zich mee. In dit script worden namen voor variabelen gebruikt als 'path' en 'thumbs'. 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.
if (window.innerWidth < 760 || window.innerHeight < 530) { return;
Als het venster van de browser smaller is dan 760 px of lager dan 530 px, wordt het script niet uitgevoerd. (In die vensters worden de grote afbeeldingen gelijk getoond en zijn geen thumbnails aanwezig.)
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
.
window.innerWidth < 760
: eerst het stuk voor de twee staande streepjes.
window
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door onder andere JavaScript gebruikt worden. De informatie is overzichtelijk opgeslagen in allerlei afdelingen en onderafdelingen, die in JavaScript 'object' heten.
Een van die objecten is het 'window' object. Daarin zit allerlei meer algemene informatie, die betrekking heeft op de pagina en op het browservenster. window
geeft alleen maar aan, dat wat achter de punt staat te vinden is in het window
-object.
innerWidth
: zo'n stukje informatie is innerWidth
: de breedte van het browservenster in px, inclusief een eventuele scrollbalk.
<
: minder dan. Wat links van dit teken staat, moet minder zijn dan wat rechts van het teken staat.
760
: gewoon een getal. De eenheid px wordt in JavaScript weggelaten, alleen de waarde is genoeg.
Voor de streepjes staat in gewone taal: als het venster van de browser minder dan 760 px breed is.
||
: de twee staande streepjes is JavaScript voor 'of'. Voor de streepjes staat een voorwaarde, en achter de streepjes staat nog een andere voorwaarde.
Als aan de voorwaarde voor ||
is voldaan, wordt niet eens meer gekeken naar de voorwaarde achter de ||
. Als aan één van de voorwaarden is voldaan, is dat bij ||
(of) voldoende.
window.innerHeight < 530
: het stuk achter de twee staande streepjes.
Deze voorwaarde is ongeveer hetzelfde als de voorwaarde die voor de streepjes staat. Alleen is innerWidth
hier vervangen door innerHeight
de hoogte van het venster van de browser in px, inclusief een eventuele scrollbalk.
Het getal 760 is nu 530: het venster moet minimaal 530 px hoog zijn.
Als je het hele deel tussen de haakjes in gewone taal samenvat, staat hier: als het venster van de browser minder dan 760 px breed is of als het venster lager dan 530 px hoog is. Als dat zo is, is aan de voorwaarde voldaan.
Omdat hier ||
(of) is gebruikt, is het voldoende als aan één van de voorwaarden is voldaan. Als aan de eerste voorwaarde is voldaan (minder dan 760 px breed), wordt zelfs niet eens meer gekeken naar de tweede voorwaarde.
{
: de code die wordt uitgevoerd, als aan deze if
-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if
hoort. Aan het eind van de code bij deze if
staat de afsluitende }
.
De spaties na en voor de {}
zijn niet nodig, die staan er alleen voor de leesbaarheid.
return;
: omdat het hier maar om heel weinig code gaat (één woord, om precies te zijn), staat alles op één regel. Bij meer code is het gebruikelijk om dat op meerdere regels te zetten, omdat het anders heel snel een onleesbare brij wordt.
De hier uit te voeren code is return
: ga onmiddellijk terug naar waar je vandaan kwam en voer de rest van de code hier niet uit.
Omdat dit helemaal aan het begin van het script staat, betekent teruggaan in dit geval: het hele script verlaten en daar verder helemaal niets van uitvoeren.
Achter return
staat nog een ;
. Daarmee geef je in JavaScript het eind van een regel aan. Het is vergelijkbaar met een punt in gewone taal.
De hele regel in gewone taal: als het browservenster smaller dan 760 px of lager dan 530 px is, voer het script dan niet uit.
let heeftFocus;
In dit deel van de code worden een paar dingen voorbereid. Het écht uitvoerende deel van het script, waarin bijvoorbeeld een toetsaanslag daadwerkelijk wordt herkend, volgt later.
Het is gebruikelijk dit soort voorbereidende zaken bovenin het script te zetten.
let
: met het sleutelwoord let
wordt aangegeven dat het erop volgende woord de naam van een 'variabele' is. Een variabele is een soort portemonnee: er kan van alles in zitten, en de inhoud kan veranderen.
Gelijk na let
volgt de naam van één of meer variabelen. Hier staat maar één variabele achter let
: 'heeftFocus'.
In heeftFocus
wordt dus iets opgeborgen. Omdat de variabele een naam heeft, kan de rest van het script de variabele aanroepen bij deze naam. Net zoals je iemand die 'Marie' heet kunt aanroepen met 'Marie', ongeacht of Marie aardig, onaardig, muzikaal of arrogant is, ongeacht de 'inhoud' van Marie.
Wat er precies in die variabele wordt opgeslagen, kun je hier opgeven of elders in het script. Als het om iets simpels gaat, wordt het hier opgegeven. Als het om iets meer ingewikkelds gaat, of als nog niet bekend is, wat moet worden opgeslagen, gebeurt het later. In dit geval wordt opgeslagen, welk element de focus heeft. Bij openen van de pagina heeft geen enkel element nog de focus, dus dat wordt pas later in de variabele heeftFocus
opgeslagen.
De variabele wordt hier 'gedeclareerd' of 'aangemaakt', dat wil zeggen dat er een naam aan wordt gegeven. Dat declareren gebeurt in de volgorde, waarin ze in het script worden gebruikt. Die volgorde hoeft niet, dat is een kwestie van voorkeur. Je hoeft ook niet alle variabelen aan het begin van het script aan te maken, maar dat is wel overzichtelijker.
Een gebruikelijke manier om de namen van variabelen weer te geven is 'camelCase'. Hierbij staat aan het begin geen hoofdletter, maar bij elk nieuw woord in de naam wel: heeftFocus
. In css zou je dit kunnen schrijven als 'heeft-focus', maar koppeltekens mogen in JavaScript niet worden gebruikt in namen van variabelen. De hoofdletters zorgen dat de namen toch leesbaar zijn.
Dingen als het gebruik van hoofdletters zijn geen absolute eisen, maar het zijn wel 'n soort afspraken, waar vrijwel elke programmeur zich aan houdt. Door je aan dit soort afspraken te houden, is je code ook leesbaarder voor anderen (en voor jezelf over acht jaar...).
Een met let
aangemaakte variabele is alleen te gebruiken binnen het blok, waarbinnen de variabele is aangemaakt. Zo'n blok is bijvoorbeeld een functie, of de iets hierboven tussen de {}
achter de if(...) zittende code.
Omdat heeftFocus
helemaal bovenaan de bij (function () { beschreven buitenste functie al wordt aangemaakt, kan heeftFocus
overal binnen die functie worden gebruikt. En omdat dit hele script binnen die buitenste functie staat, kan heeftFocus
overal in het script worden gebruikt. (Later worden ook nog variabelen aangemaakt, die maar in delen van het script zijn te gebruiken.)
Tot een aantal jaren geleden kon je maar op één manier variabelen aanmaken: met var
. Die manier wordt vrijwel niet meer gebruikt, maar je komt var
nog heel vaak tegen in iets oudere code. Het grootste nadeel van var
het was uitermate makkelijk om per ongeluk de inhoud van een met var
aangemaakte variabele te veranderen. En die fout was vaak bijzonder moeilijk op te sporen.
In dit geval moet de inhoud van heeftFocus
kunnen veranderen. Als dat niet zo is, kun je in plaats van let
beter const
gebruiken: const heeftFocus;
. De inhoud van const
kan niet worden veranderd, dus dat is nog veiliger. Als je const
gebruikt, moet je gelijk bij het aanmaken van de variabele een waarde aan de variabele geven, bijvoorbeeld const heeftFocus = "Blijf hier met je tengels vanaf, grrr";
. Maar hier moet de inhoud van heeftFocus
veranderd kunnen worden, dus hier wordt let
gebruikt.
heeftFocus
: de naam van de variabele. In deze variabele wordt later op een aantal plaatsen in het script opgeslagen, welk element de focus heeft. Die focus kan dan worden teruggezet, nadat bijvoorbeeld de hulp is gesloten. Voorlopig blijft de variabele leeg, omdat bij openen van de pagina geen enkel element al de focus heeft.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
document.querySelector("html").classList.remove("no-js");
document.querySelector("html")
: eerst het eerste deel van deze regel. Het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelector("html")
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar het element <html> gezocht, net zoals je in css in een selector html {...}
zou gebruiken.
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
Het hele deel document.querySelector("html")
: zoek het element <html> op in de huidige pagina. Je zou kunnen zeggen dat dit eerste deel dus eigenlijk gewoon <html> betekent: er moet iets gebeuren met het <html>-element van de pagina.
Bij het opzoeken van <html> is ook een enorme berg informatie van <html> opgeslagen, waaronder alle html, de css, de nakomelingen van <html>, noem maar op. Feitelijk is <html> in de vorm van een object opgeslagen: een bij elkaar horende verzameling van functies en andere code. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
.classList.remove("no-js");
, het tweede deel van de regel:
In het eerste deel van de regel is met document.querySelector("html")
het element <html> gevonden en in de vorm van een object opgeslagen. Omdat dit tweede deel van de regel volgt op het eerste deel (gekoppeld door een punt), werkt de code van het tweede deel van de regel op het eerste deel van de regel: op het element <html>.
classList
: dit is zo'n stukje in het object opgeslagen informatie. Alle eventueel aanwezige classes bij het element <html> zitten hierin.
remove("no-js")
: dit is zo'n iets hierboven genoemde methode. Met remove()
kun je een class verwijderen bij het object, bij <html>. Als de class niet aanwezig is, gebeurt er gewoon niets.
De class die wordt verwijderd, zet je tussen de haakjes. En tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst (een 'string') gaat.
(Als je wilt kijken, of deze class inderdaad door het script wordt verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel in gewone taal: verwijder bij <html> de class 'no-js'.
Door het verwijderen van deze class werken selectors die beginnen met .no-js
niet meer. Dat is nodig, omdat die de werking van JavaScript storen. Een uitgebreide uitleg over het hoe en waarom hiervan staat bij .no-js #thumbs picture:focus:not(:target) + .groot.
const path = location.pathname + "#";
const
: met het sleutelwoord const
wordt aangegeven dat het erop volgende woord woord de naam van een 'variabele' is. Een variabele is een soort portemonnee: er kan van alles in zitten. Over wat een variabele is, is hierboven bij let heeftFocus; meer te vinden.
Hierboven werd echter het sleutelwoord let
gebruikt om de variabele aan te maken. Hier wordt het sleutelwoord const
gebruikt om variabele path aan te maken. Het verschil: de inhoud van const
kan niet worden veranderd, die blijft constant. (Daar zijn 'n paar uitzonderingen op, maar die spelen hier niet.)
Bij het gebruik van const
is het daardoor vrijwel onmogelijk om de inhoud van de variabele per ongeluk te veranderen.
Als de inhoud van een variabele niet verandert, is het daarom in het algemeen het beste om een variabele met const
aan te maken en let
te reserveren voor variabelen, waarvan de inhoud wel moet kunnen veranderen.
path
: de naam van de variabele.
=
: 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.
Dat kun je ook later doen, maar omdat de inhoud van path
niet verandert, en omdat die inhoud eenvoudig te vinden is, wordt het gelijk hier afgehandeld.
location
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door 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 en op het browservenster. 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' het allerbovenste object is, mag je dat weglaten: de browser vult dat automatisch aan.)
pathname
: van het 'location' object wordt de informatie in het onderdeel 'pathname' gebruikt. Hierin zit het volledige pad naar en de naam van de pagina. Als je de pagina met het voorbeeld op de site bekijkt, is het volledige adres 'https://www.css-voorbeelden.nl/
Het is dit pad dat in de variabele path
wordt opgeslagen. Je kunt nu overal in dit script de variabele path
gebruiken, zodat je niet steeds overal '/afbeelding/slideshow/afbeelding-055.html' hoeft in te typen.
Maar het belangrijkste voordeel van een variabele: als je dit script op een pagina op een andere site dan www.css-voorbeelden.nl gebruikt, wordt het pad van die andere site gebruikt. Zonder dat je iets aan het script hoeft te veranderen.
+
: aan het pad, dat gelijk hierboven is gevonden, wordt nog iets toegevoegd.
"#"
: dit is, wat wordt toegevoegd. Het staat tussen aanhalingstekens, omdat de computer anders niet weet dat het 'n letterteken is. (Computers zijn (nog) niet zo slim. Ze kunnen wel in een ongelooflijk tempo dom zijn, en dat líjkt slim. Zoiets als de tweets van Trump.)
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Op de site zou dit alles bij elkaar opleveren: '/afbeelding/slideshow/afbeelding-055.html#'. (Op een andere site zou het eerste deel, het pad, uiteraard iets anders zijn.) Deze string (in JavaScript heet een stuk tekst een 'string') geeft een mogelijkheid om links naar een anker binnen de pagina te onderscheiden van links naar buiten de pagina. Elke link binnen de pagina móét dit stukje binnen z'n adres hebben staan. En het adres van een link naar een andere pagina of site, kan dit stukje tekst nooit bevatten.
Mogelijk zou alleen het pad zonder de "#" al voldoende zijn. Maar het zou kunnen dat iemand een of andere exotische link heeft gemaakt, waarin dat pad voorkomt, en die toch geen link binnen de pagina is. Of mogelijk wordt zoiets exotisch in de toekomst ooit bedacht. De combinatie pad + "#" is gereserveerd voor links binnen een pagina, dus hiermee sluit je elk (toekomstig) risico uit.
In plaats van het gebruik van variabele path
zou je in de rest van het script ook elke keer location.pathname + "#"
kunnen gebruiken. maar dat maakt code al snel onleesbaar. Bovendien hoeft het nu maar één keer opgezocht te worden. Maar dat laatste speelt hier eigenlijk geen rol, omdat het opzoeken snel gaat en de variabele maar twee keer wordt gebruikt.
const thumbs = document.querySelector("#thumbs");
Als je in de html een andere id dan 'thumbs' hebt gebruikt bij div#thumbs
, moet je die id ook in bovenstaande regel aanpassen.
const
: met het sleutelwoord const wordt aangegeven dat het erop volgende woord woord de naam van een 'variabele' is, waarin iets kan worden opgeslagen. Meer over const
staat gelijk hierboven bij const path = location.pathname + "#";.
thumbs
: de naam van de variabele.
=
: 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.
Dat kun je ook later doen, maar omdat de inhoud van thumbs
niet verandert, en omdat die inhoud eenvoudig te vinden is, wordt het gelijk hier afgehandeld.
document.querySelector("#thumbs")
: het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelector("#thumbs")
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar het element met id="thumbs" gezocht, net zoals je in css in een selector #thumbs {...}
zou gebruiken.
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
;
: helemaal aan het eind staat nog een puntkomma. Deze geeft het einde van de regel aan. In gewone taal zou je hier een punt gebruiken.
De hele declaratie const thumbs = document.querySelector("#thumbs")
: sla het element met id="thumbs" op in thumbs
. Dat wil zeggen dat in variabele thumbs
een ongelooflijke hoeveelheid informatie over div#thumbs
wordt gestopt, waar het script later gebruik van kan maken. Zo zit bijvoorbeeld alle css, die aan div#thumbs
is gegeven, ook in thumbs
. Maar niet alleen de in de stylesheet opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd. Alle nakomelingen van thumbs
en hun css, attributen, enzovoort, zitten ook in thumbs
.
Van al deze informatie in thumbs
kan het script gebruik maken. En ook kunnen veel van deze dingen worden veranderd door het script, zoals een kleur veranderen, of een element verwijderen, of juist toevoegen.
Feitelijk is div#thumbs
in de vorm van een object in thumbs
opgeslagen. Naast allerlei informatie die in dat object zelf in thumbs
wordt opgeslagen, kun je daardoor ook gebruik maken van allerlei methoden, die JavaScript gratis en voor niets toevoegt aan het object in thumbs
.
Het script gebruikt het object in variabele thumbs
, div#thumbs
, onder andere om te kijken waar er wordt geklikt en welke toets wordt ingedrukt. En om een class bij div#thumbs
aan te brengen, als de Tab-toets wordt gebruikt.
Eerder werd bij const path = location.pathname + "#"; beweerd dat een met const
aangemaakte variabele niet veranderd kan worden. Dat is ook zo voor de eerder in path
opgeslagen locatie, want dat is, wat JavaScript betreft, gewone tekst (een 'string', in JavaScriptiaans).
In thumbs
is echter div#thumbs
in de vorm van een object opgeslagen. Die div#thumbs
kan nog steeds niet gewijzigd worden. Als je iets als thumbs = "Joechei, het is in de mei";
in het script zou zetten, krijg je een foutmelding en stopt het script. Je kunt echter wel onderdelen van dat object veranderen.
Een van de eigenschappen van het object is classList
: een lijst met alle classes van div#thumbs
. En die classList
kun je wel wijzigen (wat later ook bovenin function function sluitGrote(e) { gebeurt).
In plaats van het gebruik van variabele thumbs
zou je in de rest van het script ook elke keer document.querySelector("#thumbs")
kunnen gebruiken. maar dat maakt code al snel onleesbaar. Bovendien hoeft het nu maar één keer opgezocht te worden. Maar dat laatste speelt hier eigenlijk geen rol, omdat het opzoeken snel gaat en de variabele maar vijf keer wordt gebruikt.
window.addEventListener("load", ververs);
window
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door onder andere JavaScript gebruikt worden. De informatie is overzichtelijk opgeslagen in allerlei afdelingen en onderafdelingen, die in JavaScript 'object' heten.
Een van die objecten is het 'window' object. Daarin zit allerlei meer algemene informatie, die betrekking heeft op de pagina en op het browservenster. window
geeft alleen maar aan, dat wat achter de punt staat te vinden is in het window
-object.
Los van die informatie zitten er ook allerlei functies in het object. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
addEventListener
: één van die hierboven genoemde methodes is 'addEventListener'. Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de 'event', kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener()
staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar 'n soort rampenplan: áls gebeurtenis is 'doodsmak', dán handeling is 'bel 112'.
"load"
: tussen aanhalingstekens, zodat het script weet dat dit een letterlijke naam is (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: 'load'. Deze gebeurtenis doet zich voor, als de pagina volledig is geladen, inclusief alle afbeeldingen, scripts, enzovoort.
ververs
: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. De naam verwijst naar een 'functie', iets wat moet gebeuren. Die functie staat iets hieronder bij function ververs() { en zorgt ervoor dat bij herladen van de pagina de eerste thumbnail altijd weer helemaal links komt te staan.
;
: de puntkomma aan het eind geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel in gewone taal: als de pagina volledig is geladen, voer dan functie 'ververs' uit.
document.querySelector("html").addEventListener("click", sluitGrote);
document.querySelector("html")
: eerst het eerste deel van deze regel. Het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelector("html")
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar het element <html> gezocht, net zoals je in css in een selector html {...}
zou gebruiken.
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
Het hele deel document.querySelector("html")
: zoek het element <html> op in de huidige pagina. Je zou kunnen zeggen dat dit eerste deel dus eigenlijk gewoon <html> betekent: er moet iets gebeuren met het <html>-element van de pagina.
addEventListener
: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande document.querySelector("html")
, dus eigenlijk aan <html>. Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de 'event', kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener()
staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar 'n soort rampenplan: áls gebeurtenis is 'doodsmak', dán handeling is 'bel 112'.
Omdat deze eventlistener aan <html> is gekoppeld, werkt deze eventlistener overal op de pagina, want <html> beslaat de hele pagina.
"click"
: tussen aanhalingstekens, zodat het script weet dat dit een letterlijke naam is (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: 'click': als de muis wordt ingedrukt, of als een touchscreen wordt aangeraakt (toen 'click' werd bedacht, bestonden er nog geen touchscreens. Je kon alleen maar 'click' doen met de muis, vandaar de wat achterhaalde naam.)
Deze regel roept de functie aan, die het eigenlijke werk gaat doen.
sluitGrote
: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. De naam verwijst naar een 'functie', iets wat moet gebeuren. Die functie staat iets hieronder bij function sluitGrote(e) { en handelt voornamelijk het sluiten van de grote afbeelding af.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel in gewone taal: als het scherm ergens binnen de pagina wordt aangeraakt of ‑geklikt, voer dan de functie 'sluitGrote' uit.
In plaats van het koppelen van de eventlisteners gelijk hier helemaal af te handelen, zou je <html> ook eerst in een variabele kunnen opslaan, en die vervolgens in een functie of zo gebruiken om de eventlistener te koppelen. Maar omdat <html> alleen hier en in de regel gelijk hieronder wordt gebruikt, en omdat het om iets eenvoudigs gaat, is het makkelijker om het hele gebeuren hier in één regel af te handelen.
document.querySelector("html").addEventListener("keydown", sluitGrote);
Deze regel is vrijwel hetzelfde als die gelijk hierboven bij document.querySelector("html").addEventListener("click", sluitGrote);.
Het enige verschil: in de eerdere regel werd naar 'click' geluisterd en hier naar 'keydown': het indrukken van een toets.
De hele regel in gewone taal: als ergens binnen de pagina een toets wordt ingedrukt, voer dan de functie 'sluitGrote' uit.
document.querySelectorAll("#thumbs img").forEach(item => {
item.addEventListener("click", function(e) {
e.target.parentElement.parentElement.focus();
});
});
Als je in de html een andere id dan 'thumbs' hebt gebruikt bij div#thumbs
, moet je die id ook in bovenstaande regel aanpassen.
Dit is een wat ingewikkelder regel. Net als bij de twee regels gelijk hierboven wordt ook hier weer een eventlistener ergens aan gekoppeld. Maar hier is de functie die uitgevoerd moet worden heel eenvoudig. Daarom wordt die functie gelijk hier afgehandeld, in plaats van een verderop staande functie aan te roepen. Dat maakt ook dat het er wat ingewikkeld uitziet: om dit goed te laten werken, is een hele reeks haakjes, puntkomma's en accolades nodig.
Eerst even een algemene opmerking: in deze regel wordt geluisterd naar een klik met de muis of een aanraking van een touchscreen. Er wordt echter in de rest van de beschrijving steeds gesproken over het indrukken van Enter. Deze regel is bedoeld voor schermlezer NVDA, en schermlezers behandelen een Enter hetzelfde als een klik of aanraking. Iets meer hierover staat verderop bij In de beschrijving van deze regel...
Allereerst wordt de regel opgesplitst in de vier onderdelen die elk een specifieke functie hebben. Dat maakt het al wat overzichtelijker:
document.querySelectorAll("#thumbs img")
: zoek alle <img>'s op die binnen #thumbs
zitten (dit zijn in deze grotere browservensters de <img>'s met de thumbnails);
forEach(item =>
: doe iets met elk van die gevonden <img>'s ('items');
{ item.addEventListener("click",
: dat 'iets' is het toevoegen van een eventlistener, die luistert naar het indrukken van Enter.
('click' is eigenlijk bedoeld voor een klik of aanraking. Waarom dat hier wordt gebruikt, staat iets verderop bij In de beschrijving van deze regel...);
function(e) { e.target.parentElement.parentElement.focus() }
: voer deze functie uit bij het indrukken van Enter.
Aan het eind komt dan nog een reeks afsluitende tekens, maar die zijn hier even weggelaten.
Hieronder wordt elk van deze vier onderdelen beschreven.
document.querySelectorAll("#thumbs img")
: het opzoeken van de <img>'s.
Het middelste stukje querySelectorAll
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelectorAll()
uit document
kan JavaScript alle elementen opzoeken, die aan een bepaalde voorwaarde voldoen, zoals alle <div>'s, alle elementen met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelectorAll("#thumbs img")
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar alle <img>'s binnen een element met id="thumbs" gezocht. Net zoals je in css in een selector #thumbs img {...}
zou gebruiken.
Zou je naar alle <span>'s zoeken die een eerste kind zijn, dan zou je de volgende regel gebruiken:
querySelectorAll("span:first-child")
Precies zoals selectors in css werken.
Er zijn dertig <img>'s die door deze selector worden gevonden. De gevonden <img>'s worden in een zogenaamde 'nodelist' gestopt: een soort adressenlijst met 30 ingangen, voor elke <img> één. Met de <img>'s in zo'n nodelist kun je van alles doen: css toevoegen, de afbeelding veranderen, noem maar op. Maar dat gebeurt hier allemaal niet.
Goed, we hebben dus een lijst met dertig <img>'s. Die zou je in 'n variabele kunnen stoppen, om daarna met die variabele verder te werken. Dat wordt hier niet gedaan: de lijst met <img>'s wordt niet opgeborgen in een variabele, maar gelijk hier afgehandeld.
forEach(item =>
: doe iets met elk van die gevonden <img>'s ('items');
forEach()
: dit volgt op de hierboven gevonden lijst met de dertig <img>'s. forEach
zorgt ervoor dat elk van die <img>'s wordt verwerkt. Van de eerste tot en met de laatste. Steeds als een <img> in de rest van de regel is verwerkt, wordt met de volgende <img> verder gegaan, tot de hele lijst met <img>'s is verwerkt.
item
: om de <img> die aan de beurt is te kunnen verwerken, moet het beestje 'n naam hebben: item
. Dit is een veel gebruikte naam in dit soort bewerkingen. Ook element
wordt vaak gebruikt. Maar je kunt desnoods ook stopMetHetOmhakkenVanKerstbomen
gebruiken als naam, alleen maak je je dan niet populair bij degene die ooit je code moet bekijken (en bij je toekomstige zelf van over twintig jaar, want zelf begrijp je dit dan ook niet meer).
=>
: een vereenvoudigde manier om te zeggen dat de hierop volgende code tussen de {}
uitgevoerd moet worden.
Samengevoegd betekent document.querySelectorAll("#thumbs img").forEach(item =>
: zoek alle <img>'s die in een element met id="thumbs" zitten op en voer de rest van de regel op die <img>'s uit, van de eerste tot en met de laatste <img>.
item.addEventListener("click",
:
item
: om iets te kunnen doen met achtereenvolgens elke gevonden <img>, is hierboven een naam gegeven aan de <img> die aan de beurt is: 'item'. Die naam wordt hier herhaald, zodat duidelijk is, waarmee iets moet worden gedaan. item
verwijst steeds naar een andere <img>, van de eerste tot en met de laatste, tot ze allemaal aan de beurt zijn geweest.
addEventListener
: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de item, aan de <img> die aan de beurt is.
Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de 'event', kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener()
staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar 'n soort rampenplan: áls gebeurtenis is 'doodsmak', dán handeling is 'bel 112'.
"click",
: aanhalingstekens, zodat het script weet dat dit een letterlijke naam is (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: 'click': als de muis wordt ingedrukt, of als een touchscreen wordt aangeraakt (toen 'click' werd bedacht, bestonden er nog geen touchscreens. Je kon alleen maar 'click' doen met de muis, vandaar de wat achterhaalde naam.)
Deze functie is bedoeld om schermlezer NVDA bij het indrukken van Enter bij een geselecteerde <img> (thumbnail) de grote afbeelding te laten tonen.
Samengevoegd betekent document.querySelectorAll("#thumbs > div").forEach(item => { item.addEventListener("click",
: zoek alle <img>'s die in een element met id="thumbs" zitten op en koppel aan elk van die <img>'s een eventlistener, die luistert naar een Enter.
(Deze functie wordt eigenlijk aangeroepen door een klik of aanraking. Maar deze regel is bedoeld voor schermlezer NVDA, die een Enter afhandelt, alsof het een klik of aanraking is. Meer daarover staat iets verderop bij In de beschrijving van deze regel...)
function(e) { e.target.parentElement.parentElement.focus(); }
:
function
: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. Binnen deze 'functie' staat tussen {}
de code, die uitgevoerd moet gaan worden.
In dit geval staat de code gelijk op deze regel, omdat die vrij eenvoudig is. Maar heel vaak staat die code ergens anders, in een aparte functie. In dat geval staat hier de naam van die functie, en wordt de code uitgevoerd die bij die functie tussen {}
staat.
(e)
: die haakjes horen nou eenmaal zo na het sleutelwoord function. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie. Zoals de plaats waar het scherm is aangeraakt of -geklikt. In dit geval wordt e
doorgegeven.
e
is een zogenaamd object. In een object zitten allerlei gegevens over hoe de functie is aangeroepen. In dit geval wordt deze functie aangeroepen door het indrukken van een toets. In e
zit bijvoorbeeld, welke toets is ingedrukt, of de toets blíjft ingedrukt (repeteert), en de taal waarvoor het toetsenbord is geconfigureerd.
Los hiervan voegt JavaScript aan e
allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.
Heel formeel is e
eigenlijk geen object, maar is e
een parameter, iets dat wordt doorgegeven aan de functie, zodat het binnen die functie gebruikt kan worden. e
is de naam van het object, en de inhoud van e
is een object. Om het object iets te kunnen vragen, of het iets te laten doen, moet het beestje 'n naam hebben: e
.
De naam e
voor het object is niet verplicht, maar 'n soort afspraak, zodat code makkelijker door anderen is te begrijpen. Maar als je het object niet e
, maar 'hetIsStervenskoud' wilt noemen, is daar technisch geen enkel bezwaar tegen. Het is dan wel verstandig een cursus zelfverdediging te volgen, voor het geval iemand anders ooit je code moet bekijken.
(e
is een afkorting van 'event', gebeurtenis. De functie reageert op een gebeurtenis, in dit geval het indrukken van een toets. In e
zit het object dat bij díé gebeurtenis hoort. Bij bijvoorbeeld een muisklik krijg je een ander object met andere informatie dan bij het indrukken van een toets.)
{
: tussen de { hier aan het begin en de } aan het eind, staat de code die de hierboven beschreven function(e)
aanroept. Door de {}
weet het script, waar de bij de functie horende code begint en eindigt.
e
: dit is de e
die tussen de haakjes bij function(e)
hierboven. e
is een object met een hele berg aan informatie over de <img>, waarop de Enter is ingedrukt. 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.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target
: dit is zon stukje informatie uit het hierboven genoemde object: hierin zit het element, wat de functie heeft aangeroepen. Hier is dat de <img>, waarbij de Enter is ingedrukt.
Feitelijk is target
ook weer een object. Hierdoor zit er allerlei informatie in over de <img> die aan de beurt is (en zou je ook van alles met die <img> kunnen doen, maar dat gebeurt hier niet.)
parentElement
: dit is de ouder van het element uit target
, de ouder van de <img>. Dat is hier de <picture> waar de <img> in zit.
parentElement
: dit is weer de ouder van die <picture>: de <div> waar de <picture> in zit. Feitelijk dus de grootouder.
focus()
: zet de focus op dit element, op die <div>. Dit is een van de dertig <div>'s, waarbinnen een thumbnail met bijbehorende knoppen, grote afbeelding, en dergelijke zit. Als een van deze <div>'s de focus heeft, wordt met behulp van css de bijbehorende grote afbeelding getoond.
;
: De puntkomma geeft het eind van dit deel van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Samengevoegd betekent document.querySelectorAll("#thumbs img").forEach(item => { item.addEventListener("click", function(e) { e.target.parentElement.parentElement.focus(); }
: zoek alle <img>'s die in een element met id="thumbs" zitten op en koppel aan elk van die <img>'s een eventlistener, die luistert naar het indrukken van Enter, en voer als dat gebeurt een functie uit, die de focus op de <div> zet, waarbinnen de <img> zit.
); });
: de laatste twee regels zijn wat haakjes en andere ongein die nodig zijn om de browser gelukkig en jou ongelukkig te maken. Diep ongelukkig, want de volledige reeks aan het eind van dit stukje code is eigenlijk (); }); });
en probeer dat maar 'ns in één keer goed te krijgen. Daarom is de hele reeks haakjes en andere ellende ook over meerdere regels verdeeld: het maakt het duidelijker, waar iets bij hoort.
Goed, die (); }
aan het begin van de reeks hierboven zijn hierboven al afgehandeld. Blijven nog over ); });
.
Deze hele reeks is nodig om eerdere (
en {
weer te sluiten, en om het eind van een stukje van de regel aan te geven.
De eerste )
is de tegenhanger van de (
gelijk na addEventListener
.
De ;
daarachter geeft het eind van de regel aan.
De }
is de afsluiter voor de {
gelijk achter =>
.
De daarop volgende )
is de afsluiter van de (
gelijk achter forEach
.
En de laatste ;
geeft weer het eind van de regel aan.
(Om te bepalen welk haakje bij welk haakje hoort en dergelijke, hoeft je geen cursus helderziendheid te volgen. Elke goede editor geeft de bij elkaar horende haakjes, accolades, en dergelijke aan. Wat bepaald helpt bij het voorkomen van een acute existentiële woedekoliek bij dit soort haakjeskerstbomen.)
In de beschrijving van deze regel is steeds gesproken over het indrukken van de Enter-toets. Terwijl een eventlistener voor 'click' luistert naar een klik of aanraking. Normaal genomen zou je iets als 'keydown' gebruiken voor de eventlistener (luisteren of een toets is ingedrukt), en dan vervolgens kijken of die toets toevallig de Enter-toets is.
Schermlezers behandelen een Enter echter hetzelfde als een klik of een aanraking. Dat is gedaan, omdat veel makers van sites geen rekening houden met schermlezers. Zonder deze noodgreep zouden allerlei dingen in een schermlezer niet werken.
Deze functie is bedoeld om schermlezer NVDA bij het indrukken van Enter bij een geselecteerde <img> (thumbnail) de grote afbeelding te laten tonen.
Er wordt trouwens door gewone browsers ook gereageerd op een gewone klik of aanraking, want daar is deze eventlistener nou eenmaal voor. Maar dat verstoort op geen enkele manier de werking van die browsers.
Hier is geen variabele gebruikt om de <img>'s in op te slaan en is geen aparte functie gemaakt, maar is alles op één regel afgehandeld. Er is best wat voor te zeggen, omdat niet zo te doen. Deze regel is wel lekker beknopt, maar het zou leesbaarder zijn als de regel in stukjes was gehakt. Eerst de <img>'s opzoeken en in een variabele als imgs
stoppen en die gebruiken in plaats van document.querySelectorAll("#thumbs img")
. En eventueel een aparte functie aanroepen om de eigenlijke code uit te voeren.
Wanneer je waarvoor kiest, is grotendeels een kwestie van voorkeur. Dit is geen cursus JavaScript, maar het is evengoed leuk om verschillende manieren te laten zien, hoe je iets aan kunt pakken.
thumbs.addEventListener("click", verwerkLinks);
thumbs
: in variabele thumbs
is bij const thumbs = document.querySelector("#thumbs"); div#thumbs
, de <div> met de slideshow, opgeslagen in de vorm van een object. In dat object is een een enorme hoeveelheid informatie over div#thumbs
opgeslagen (en over de nakomelingen van div#thumbs
). Naast die in thumbs
opgeslagen informatie kun je ook gebruik maken van allerlei functies, die JavaScript gratis en voor niets toevoegt aan het object thumbs
.
Een functie bij een object werkt iets anders dan een gewone functie, daarmee heet een functie bij een object een 'methode'.
addEventListener
: dit is zo'n methode. Er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande object thumbs
, dus eigenlijk aan div#thumbs
. Een eventlistener luistert naar een gebeurtenis. Die gebeurtenis, de 'event', kan van alles zijn: het indrukken van een toets, klikken, scrollen, de video is afgespeeld, van alles. Tussen de haakjes van addEventListener()
staat, naar welke soort gebeurtenis moet worden geluisterd, en wat er moet gebeuren, als die gebeurtenis zich voordoet. Zeg maar 'n soort rampenplan: áls gebeurtenis is 'doodsmak', dán handeling is 'bel 112'.
Omdat deze eventlistener aan div#thumbs
is gekoppeld, werkt deze eventlistener overal binnen div#thumbs
en de nakomelingen ervan. En dat is niet onbelangrijk, want deze eventlistener is eigenlijk bedoeld om het volgen van een link binnen div#thumbs
af te handelen. In totaal staan er 122 links binnen div#thumbs
, die eigenlijk allemaal een eigen eventlistener zouden moeten krijgen. Maar omdat een 'event' (hier een klik of aanraking) naar boven bubbelt, kun je ook met één eventlistener op een gemeenschappelijke voorouder volstaan.
Je volgt wel gewoon een link, maar de klik of aanraking bubbelt door naar boven tot deze bij div#thumbs
aankomt en daar wordt afgehandeld. In div#thumbs
kun je kijken, op welke link precies is geklikt. Daardoor kan de reactie op het volgen van een link worden aangepast aan welke link precies is gevolgd. Hiermee is het aantal benodigde eventlisteners teruggebracht van 122 naar 1.
"click"
: tussen aanhalingstekens, zodat het script weet dat dit een letterlijke naam is (dit is gewoon een van de taalkundige regels van JavaScript). Dit is de naam van de gebeurtenis, waarnaar wordt geluisterd, waarop wordt gewacht: 'click': als de muis wordt ingedrukt, of als een touchscreen wordt aangeraakt (toen 'click' werd bedacht, bestonden er nog geen touchscreens. Je kon alleen maar 'click' doen met de muis, vandaar de wat achterhaalde naam.)
verwerkLinks
: deze naam staat niet tussen aanhalingstekens, omdat het hier niet om een letterlijke naam of zo gaat. De naam verwijst naar een 'functie', iets wat moet gebeuren. Die functie staat iets hieronder bij function verwerkLinks(e) { en voorkomt dat elke gevolgde link naar een vorige of volgende thumbnail of grote afbeelding wordt opgeslagen in de geschiedenis van de browser.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Deze regel roept de functie aan, die het eigenlijke werk gaat doen: elke keer als ergens binnen div#thumbs
het scherm wordt aangeraakt of -geklikt, wordt functie verwerkLinks() uitgevoerd.
thumbs.addEventListener("keydown", focusBijTabEnter);
Deze regel is grotendeels hetzelfde als thumbs.addEventListener("click", verwerkLinks); iets hierboven.
De twee verschillen: deze eventlistener luistert niet naar 'click', maar naar 'keydown': het indrukken van een toets. En als dat gebeurt wordt niet function verwerkLinks()
aangeroepen, maar function focusBijTabEnter(e) {.
De functie focusBijTabEnter() handelt de Tab-toets af en zorgt dat bij Enter een grote afbeelding wordt getoond.
function ververs() {
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 de pagina wordt geopend of wordt herladen. Het luisteren naar daarnaar wordt geregeld bij window.addEventListener("load", ververs);.
De code in deze functie regelt, wat er gebeurt, als pagina wordt geopend of herladen. Eigenlijk zijn die dingen alleen nodig, als de pagina wordt herladen. Maar het maakt niets uit, als ze ook gebeuren, als de pagina voor de eerste keer wordt geladen. Je kúnt wel kijken of wordt herladen of nieuw geopend, maar dat kost alleen maar extra code.
Deze functie regelt twee dingen:
- Safari op OS X en Firefox zetten bij herladen de eerste thumbnail niet helemaal links. Dat wordt hier geregeld.
- Als er een '#' in het adres zit, wordt die – met alles erachter – verwijderd. Anders komt bij herladen niet altijd in elke browser de eerste thumbnail helemaal links te staan. (Het adres bevat een '#', als een van de knoppen rechtsboven is gebruikt: een link binnen de pagina.)
function
: het sleutelwoord waarmee het begin van een functie wordt aangegeven.
ververs
: 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.)
()
: 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. Dat gebeurt hier niet.
{
: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }
.
let url = location.toString();
Deze regel is onderdeel van function ververs() {
let
: met het sleutelwoord let
wordt aangegeven dat het erop volgende woord de naam van een 'variabele' is. Een variabele is een soort portemonnee: er kan van alles in zitten, en de inhoud kan veranderen.
Gelijk na let
volgt de naam van één of meer variabelen. Hier staat maar één variabele achter let
: 'url'.
Een uitgebreidere beschrijving van wat een variabele is, staat bij let heeftFocus;.
Er is één verschil met de daar beschreven variabele: de bij let heeftFocus;
: aangemaakte variabele werkt in het hele script, omdat die binnen de buitenste functie wordt aangemaakt. En het hele script staat nou eenmaal binnen die buitenste functie.
De hier aangemaakte variabele werkt alleen binnen function ververs()
. Een met let
aangemaakte variabele werkt altijd alleen maar binnen de functie, waarbinnen die is aangemaakt (en binnen eventuele functies die weer binnen function ververs()
staan).
url
: de naam van de variabele. Hierin wordt het volledige adres van de pagina opgeslagen.
=
: 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.
location.toString()
: dit is wat in de variabele url
wordt opgeslagen.
location
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door 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 en op het browservenster. 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.toString()
moeten gebruiken, maar omdat 'window' het allerbovenste object is, mag je dat weglaten: de browser vult dat automatisch aan.)
Een object is een bij elkaar horende verzameling van functies en andere code, waarin informatie is. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
toString()
: dit is zo'n hierboven genoemde methode. In location
zit het volledige adres van de pagina. Maar dat is in de vorm van een object. En verderop in de functie worden bewerkingen uitgevoerd, die alleen op een gewoon stuk tekst, op een 'string', kunnen worden uitgevoerd.
Deze methode zet het in location
zittende adres van de pagina om naar een gewoon stuk tekst, zodat daar van alles mee gedaan kan worden.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
document.querySelector("#picture-1").focus();
Als je in de html een andere id dan 'picture-1'hebt gebruikt bij picture#picture-1
, moet je die id ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function ververs() {
Vaak is het beter om iets in een variabele op te slaan, zoals beschreven bij let heeftFocus;, omdat anders iets steeds opnieuw moet worden opgezocht. Of omdat er iets ingewikkelds met iets moet gebeuren. Hier gaat het om een simpele handeling, die mogelijk zelfs nooit wordt uitgevoerd.
De functie, waarin deze regel zit, wordt alleen uitgevoerd, als de pagina wordt geopend of ververst. Dit zal geen tientallen keren achter elkaar gebeuren. Daarom wordt hier de hele handel gelijk in één keer uitgevoerd: het opzoeken van een element en het uitvoeren van de bewerking daarop, zonder de tussenliggende stap van het opslaan van het element in een variabele.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
querySelector("#picture-1")
: dit is een van die hierboven genoemde methodes. Deze methode is te vinden in het object document
, vandaar dat document
ervoor staat.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelector("#picture-1")
Omdat het te zoeken gegeven tussen aanhalingstekens staat, weet JavaScript dat het om een letterlijke tekst gaat, een 'string'. Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar het element met id="picture-1" gezocht, net zoals je in css in een selector #picture-1 {...}
zou gebruiken.
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
In document.querySelector("#picture-1")
wordt dus het element met id="picture-1" opgeslagen, ook weer in de vorm van een object. In het voorbeeld gaat het om picture#picture-1
: de <picture> waarin onder andere de eerste thumbnail zit. De stap voor het opslaan in een variabele wordt overgeslagen, maar verder werkt het min of meer hetzelfde als bij een variabele: ook in dit tijdelijke object (het wordt niet echt opgeslagen) zit heel veel informatie over picture#picture-1
, en ook kunnen weer allerlei methoden worden gebruikt. Zo zit in het object bijvoorbeeld alle css, die aan picture#picture-1
is gegeven. Maar niet alleen de in de stylesheet opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd. Alle nakomelingen van picture#picture-1 en hun css, attributen, enzovoort, zitten ook in het object. En ook kunnen bij dit object weer allerlei ingebakken methodes worden gebruikt.
Van al deze informatie in het object kan het script gebruik maken. En veel dingen binnen het object kunnen worden veranderd door het script, zoals een kleur veranderen, of een element verwijderen, of juist toevoegen.
.focus()
: dit is zo'n methode. picture1#picture-1
, het element dat bij het gevonden object hoort, krijgt de focus. Safari op OS X en Firefox zetten bij herladen van de pagina de eerste thumbnail niet weer helemaal links. Door de <picture> waar die thumbnail in zit de focus te geven, gebeurt dat wel.
Andere browsers hebben dit niet nodig, maar ze hebben er ook geen last van. Dus om de code simpel te houden, wordt niet uitgefilterd voor alleen Safari op OS X en Firefox (wat trouwens niet altijd honderd procent betrouwbaar is).
;
: De puntkomma geeft het eind van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel in gewone taal: geef de focus aan picture#picture-1
.
Een <picture> kan normaal genomen niet de focus krijgen. Maar door het toevoegen van tabindex="-1" aan <picture> kan het script de focus wel aan de <picture> geven.
document.querySelector("main").focus();
Deze regel is onderdeel van function ververs() {
Deze regel is vrijwel hetzelfde als document.querySelector("#picture-1").focus(); gelijk hierboven. Alleen wordt hier met behulp van querySelector("main")
gezocht naar het element <main>. Tussen de aanhalingstekens wordt weer dezelfde syntax gebruikt als bij css, waar je main { ... }
zou gebruiken.
Eén regel voor deze regel is de focus op de eerste thumbnail gezet. Als een gebruiker van de Tab-toets nu op Tab drukt, gaat die gelijk naar de tweede thumbnail en wordt met behulp van css de bij die tweede thumbnail horende grote afbeelding getoond. De eerste grote afbeelding wordt niet getoond, en ook de voor de eerste thumbnail zittende skip-link en de hulp worden gemist.
De regel voor deze regel heeft de focus op de eerste thumbnail gezet, waardoor deze weer helemaal links komt te staan. Deze regel zet de focus vervolgens weer op <main>, waardoor bij het indrukken van de Tab-toets als eerste de skip-link wordt getoond, gevolgd door de hulp en de eerste grote afbeelding.
Een <main> kan normaal genomen niet de focus krijgen. Maar door het toevoegen van tabindex="-1" aan <picture> kan het script de focus wel aan <main> geven.
if (url.indexOf("#") > -1) {
Deze regel is onderdeel van function ververs() {
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
.
url
: in variabele url
is eerder bij let url = location.toString(); het adres van de pagina opgeslagen in de vorm van een gewoon stuk tekst, een 'string'. Op de site is het adres van de pagina met het voorbeeld 'https://www.css-voorbeelden.nl/url
gebruikt, staat daar dus eigenlijk dat hele adres, alsof je dat zelf elke keer volledig in zou typen.
indexOf()
: dit is een in JavaScript ingebouwde functie. Een functie is een stukje bij elkaar horende code, dat je makkelijk kunt uitvoeren. Je roept de naam van de functie (hier 'indexOf()'), en het beestje begint te werken.
Je kunt zelf functies maken, maar JavaScript bevat ook al een groot aantal ingebouwde functies voor handelingen die vaak voorkomen. indexOf()
is zo'n ingebouwde functie.
De haakjes achter de naam geven aan dat het om een functie gaat. Tussen die haakjes staat vaak informatie die de code in de functie kan gebruiken. Dat is hier ook het geval: aan de functie wordt het teken '#' doorgegeven.
indexOf("#")
: kijk of het teken '#' in het adres, in variabele url
, zit. Als in dat adres het teken '#' zit, kan het niet anders, dan dat het om een link naar een anker binnen de pagina gaat.
indexOf()
laat met behulp van een getal weten, of het teken '#' wel of niet in het adres in url
zit. Als het teken '#' in url
zit, wordt de positie van de plaats van het teken '#' in het adres in url
geretourneerd door indexOf()
. Als het teken '#' niet in het adres in url
zit, wordt -1 geretourneerd door indexOf()
.
>
: met het hierboven door indexOf()
geretourneerde getal kan iets worden gedaan. In dit geval wordt met behulp van het groterdanteken >
gekeken, of dat getal groter is dan een ander getal, dat gelijk op dit teken volgt.
-1
: als het teken '#' niet in het adres van href
zit, retourneert indexOf()
het getal -1.
Als het geretourneerde getal 0 of groter is, dan móét het teken '#' dus ergens in het adres van href
zitten. Soms is het handig om te weten, op welke plaats iets precies zit, maar dat is hier niet van belang. Het gaat er hier alleen maar om, óf het teken '#' in het adres van href
zit.
{
: de code die eventueel moet worden uitgevoerd, staat tussen twee accolades {}
. Het gaat hier maar om één regel code, maar dat maakt niets uit. Aan het eind van de bij de if
horende code staat de bijbehorende }
.
De hele regel samengevat: voer de code tussen de {}
uit, maar alleen als het teken '#'in het adres van de pagina zit. Want in dat geval móét het om een link binnen de pagina gaan.
url = url.substring(0, url.indexOf("#"));
Deze regel is onderdeel van function ververs() {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:
– Het adres van de pagina bevat een '#'.
Deze regel verwijdert de '#' en alles wat erop volgt uit het adres van de pagina. Als dit niet gebeurt, wordt in sommige browsers de eerste thumbnail niet altijd helemaal links gezet bij herladen van de pagina.
url
: dit is de eerder bij let url = location.toString(); aangemaakte variabele, waarin het adres van de pagina in de vorm van gewone tekst is opgeborgen.
=
: 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.
url
: dit is weer de variabele, waarin het adres van de pagina zit.
substr()
: dit is een in JavaScript ingebouwde functie. Een functie is een stukje bij elkaar horende code, dat je makkelijk kunt uitvoeren. Je roept de naam van de functie (hier 'substr()'), en het beestje begint te werken.
Je kunt zelf functies maken, maar JavaScript bevat ook al een groot aantal ingebouwde functies voor handelingen die vaak voorkomen. substr()
is zo'n ingebouwde functie.
De haakjes achter de naam geven aan dat het om een functie gaat. Tussen die haakjes staat vaak informatie die de code in de functie kan gebruiken. Dat is hier ook het geval: aan de functie worden twee argumenten doorgegeven, gescheiden door een komma: (0, url.indexOf("#")
.
substr()
haalt een deel (een 'substring') uit een stukje tekst. Omdat substr()
hier achter url
staat, waarin het adres van de pagina zit, wordt hier een stukje uit het adres van de pagina gehaald. Het argument voor de komma geeft het begin van de substring aan, het argument na de komma de lengte.
0
: het begin van de op te halen substring. Dat is makkelijk: vanaf het begin. (Een computer begint vaak met '0' te tellen. Dus '0' is het eerste teken van de inhoud van url
, hier is dat het adres van de pagina.)
url.indexOf("#")
: dit is het tweede argument: de lengte van de op te halen substring. De substring moet het adres van de pagina bevatten, zonder een eventuele '#' en wat daar eventueel nog op volgt. Die lengte is echter onbekend, want die is onder andere afhankelijk van de lengte van de naam van de site. Daarom moet eerst de positie van de '#' worden opgezocht, en als die positie is gevonden, kan ook de lengte van de op te halen substring worden bepaald.
url
: weer de variabele, waarin het volledige adres van de pagina is opgeslagen.
indexOf("#")
: deze functie zoekt de positie op van het deel tussen de aanhalingstekens, in dit geval alleen het teken '#'. Als dat teken op de tiende plaats staat, is die positie 9 (en geen 10, omdat de computer met 0 begint te tellen).
In het adres:
'https://example.com/afbeelding-055.html#div-1'
staat het teken '#' op de 40e plaats. indexOf("#")
levert dan de waarde 39 op.
Het hele stukje url.indexOf("#")
levert in bovenstaand adres dus uiteindelijk 39 op. Oftewel: dat hele stukje kan worden vervangen door het getal 39.
substr(0, url.indexOf("#")
wordt dan, met bovenstaand adres, substr(0, 39)
: een substring beginnend op positie 0 en 39 tekens lang. Dat is precies het deel zonder de '#' en wat daarop volgt:
'https://example.com/afbeelding-055.html'
Dit wordt de nieuwe inhoud van variabele url
.
url.substr(0, url.indexOf("#")) samen
: dit hele deel na de komma levert uiteindelijk, als het adres 'https://example.com/afbeelding-055.html#div-1' is, als resultaat 'https://example.com/afbeelding-055.html' op. Het adres van de pagina, maar dan zonder de '#' en alles wat daar eventueel op volgt.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel samengevat: verwijder '#' en alles wat daar eventueel op volgt uit het adres van de pagina, en stop het resterende deel vervolgens in variabele url
. Waarmee de oorspronkelijke url
voor het isgelijkteken nu is veranderd in wat achter het isgelijkteken is gevonden: het adres van de pagina, maar zonder de '#' en alles wat daarop volgt.
location.assign(url);
Deze regel is onderdeel van function ververs() {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:
– Het adres van de pagina bevat een '#'.
location
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door 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 en op het browservenster. De informatie in 'window' is weer opgesplitst in allerlei kleinere objecten.
Een van die objecten is het 'location' object, waarin het adres van de pagina zit. Een object is een bij elkaar horende verzameling van functies en andere code. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
(Eigenlijk zou je dus window.location
moeten gebruiken, maar omdat 'window' het allerbovenste object is, mag je dat weglaten: de browser vult dat automatisch aan.)
assign(url)
: dit is zo'n methode. Het adres in location
wordt vervangen door wat tussen de haakjes van replace()
zit. Hier is dat variabele url
.
Gelijk hierboven is in url
het adres van de pagina opgeslagen, maar zonder het teken '#' en wat daar eventueel op volgt. Het adres in location
wordt daardoor vervangen door hetzelfde adres, maar zonder de '#' en wat daar eventueel op volgt.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Omdat ditzelfde adres zichtbaar is in de adresbalk van de browser, kun je dit ook zien gebeuren. Als je 'n link naar 'n vorige of volgende thumbnail of grote afbeelding volgt, verandert het adres in de adresbalk in iets met een '#' en iets daarachter. Als je de pagina herlaadt, wordt de '#' en alles daarachter weer weggehaald.
function sluitGrote(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 kan op twee manieren. De eerste manier is door het scherm ergens aan te raken of er ergens op te klikken. Dit wordt geregeld bij document.querySelector("html").addEventListener("click", sluitGrote);.
De tweede manier is door het indrukken van een toets. Dit wordt geregeld bij document.querySelector("html").addEventListener("keydown", sluitGrote);.
De code in deze functie regelt een aantal dingen.
- Als de ingedrukte toets een andere toets is dan de Tab-toets, wordt class 'gebruikt-tab' verwijderd bij div#thumbs, waardoor speciale css voor gebruikers van de Tab-toets niet meer werkt.
- Als de hulp wordt geopend, nadat al een <picture> met een thumbnail de focus heeft gehad, en de hulp wordt vervolgens met Escape weer gesloten, wordt de focus teruggezet op die thumbnail.
- Als een grote afbeelding wordt gesloten door het indrukken van Escape, of door het aanraken van of klikken op het scherm ergens buiten de grote afbeelding, wordt de focus teruggezet op de bijbehorende <picture> met thumbnail.
- Als de hulp wordt geopend, nadat al een <picture> met een thumbnail de focus heeft gehad, en de hulp wordt vervolgens weer gesloten door het scherm ergens aan te raken of aan te klikken, wordt de focus teruggezet op die <picture>.
function
: het sleutelwoord waarmee het begin van een functie wordt aangegeven.
sluitGrote
: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)
Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke niet gebruikt mogen worden in een naam. In css zou je hier bijvoorbeeld sluit-grote in plaats van sluitGrote
kunnen gebruiken.
(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. Hier wordt e
doorgegeven.
In e
zit een zogenaamd object, waarin onder andere informatie zit over welke toets is ingedrukt. Omdat het tussen de haakjes achter function()
staat, is de informatie uit e
door de code binnen de functie te gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
{
: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }
.
if (e.key !== "Tab") { thumbs.classList.remove("gebruikt-tab"); }
Als je in de css een andere class dan 'gebruikt-tab' hebt gebruikt, moet je die class ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function sluitGrote(e) {
Als een andere toets dan de Tab-toets is ingedrukt, verwijder dan class 'gebruikt-tab' bij div#thumbs
. Hierdoor werkt speciale css voor gebruikers van de Tab-toets niet meer.
Als iemand met behulp van de Tab-toets door alle thumbnails loopt, wordt steeds de bijbehorende grote afbeelding getoond. Om vertraging bij het weergeven te voorkomen, worden ook alvast de twee volgende grote afbeeldingen gedownload.
Als een andere toets dan de Tab-toets is ingedrukt, gebruikt de bezoeker kennelijk niet de Tab-toets, of is daarmee gestopt. Het van tevoren downloaden is dan niet meer nodig. Door het verwijderen van de class gebeurt dat niet meer.
Zodra eventueel de Tab-toets weer gebruikt gaat worden, zet een andere functie de class weer terug.
(Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
if (e.key !== "Tab")
, het eerste deel van de regel:
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
.
e
: in e
zit een zogenaamd object, waarin allerlei informatie zit. Bij function sluitGrote(e) {, de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
key
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.
!==
: als wat hiervoor staat niet helemaal precies, echt helemaal precies, hetzelfde is, als wat hierachter staat.
Het uitroepteken betekent 'niet'. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden twee isgelijktekens gebruikt.
"Tab"
: dit is in JavaScript de naam van de Tab-toets. De inhoud van key
, oftewel de naam van de ingedrukte toets moet 'Tab' zijn: de Tab-toets moet zijn ingedrukt.
Dit deel van de regel samengevat: als de Tab-toets is ingedrukt. Alleen dan wordt de code die hierachter tussen {}
staat uitgevoerd.
{ thumbs.classList.remove("gebruikt-tab"); }
, het tweede deel van de regel:
{
: de code die wordt uitgevoerd als aan de hierboven beschreven if
wordt voldaan, staat tussen {
en }
. Daardoor weet het script, wat het begin en het eind van de bij de if
horende code is.
Vaak wordt dit deel voor de leesbaarheid op een nieuwe regel gezet, maar omdat het zo weinig is, gebeurt dat hier niet. Het is hier juist leesbaarder, als het op één regel staat. Dit is trouwens grotendeels een kwestie van voorkeur, de browser maakt het niets uit.
thumbs
: in de variabele thumbs
is bij const thumbs = document.querySelector("#thumbs"); div#thumbs
opgeslagen in de vorm van een object. Een object is een bij elkaar horende verzameling van functies en andere code, waarin veel informatie over het opgeslagen element, de kinderen daarvan, enzovoort zit. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
.classList
: dit is zo'n stukje in thumbs
opgeslagen informatie. Alle eventueel aanwezige classes bij div#thumbs
zitten hierin.
remove("gebruikt-tab")
: dit is zo'n iets hierboven genoemde methode. Met remove()
kun je een class verwijderen bij het object, bij div#thumbs
. Als de class niet aanwezig is, gebeurt er gewoon niets.
De class die wordt verwijderd, zet je tussen de haakjes. En tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst (een 'string') gaat.
Deze class wordt door het script bij function focusBijTabEnter(e) { toegevoegd, zodra binnen de slideshow de Tab-toets wordt ingedrukt.
(Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
;
: de puntkomma geeft het eind van dit deel van de regel aan, van het deel tussen {
en }
.
}
: dit is de afsluitende }
die bij de {
aan het begin van dit deel van de regel hoort.
Dit deel van de regel samengevat: verwijder bij div#thumbs
class 'gebruikt-tab'.
De hele regel in gewone taal: als een andere toets dan de Tab-toets is ingedrukt, verwijder dan bij div#thumbs class 'gebruikt-tab'.
if ( heeftFocus && e.key === "Escape" && document.querySelector("#uitleg").contains(document.activeElement) ) {
Als je in de html een andere id dan 'uitleg' hebt gebruikt bij div#uitleg
, moet je die id ook in bovenstaande regel aanpassen. Dit is de enige plaats in het script, waar je dit moet aanpassen.
Deze regel is onderdeel van function sluitGrote(e) {
Dit lijkt mogelijk 'n ingewikkelde regel, maar als je de regel in stukjes hakt, valt het hopelijk mee.
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
. Het gaat hier om drie voorwaarden, waaraan moet worden voldaan. De drie voorwaarden worden gescheiden door &&
, wat in JavaScript betekent én: er moet aan alle drie de voorwaarden worden voldaan. Pas dan wordt de bij deze if
horende code uitgevoerd.
Het begin van de eventueel bij deze if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze if
uit te voeren code wordt aangegeven met een }
. In dit geval is dat de }
bij } else if ( ( (! thumbs.contains (e.target) ... iets verderop. Dat er nog meer dan alleen die }
op die regel staat, maakt niet uit.
Er zijn dus drie voorwaarden, waaraan voldaan moet worden:
heeftFocus
: dit is de eerste voorwaarde, waaraan voldaan moet worden. Dit is een bij let heeftFocus; aangemaakte variabele, waarin wordt bijgehouden, welk element eventueel de focus heeft.
Als er alleen maar een variabele staat en verder niets, is de voorwaarde om aan de if
te voldoen dat de variabele niet leeg is.
Bij openen van de pagina is deze variabele leeg, want geen enkel element heeft al de focus gehad. Als de variabele leeg is, is deze 'false', of eigenlijk 'falsy'. (Echt 'false' is net iets anders.) Als een variabele falsy is, wordt niet aan de voorwaarde van de if
voldaan en wordt de code tussen de {}
dus niet uitgevoerd.
Zodra een <picture> of <div> binnen de slideshow de focus heeft gekregen, wordt die <picture> of <div> door het script opgeborgen in variabele heeftFocus
. De variabele is dan niet meer leeg en 'true', of eigenlijk 'truthy'. (Echt 'true' is net iets anders). Nu wordt aan de eerste voorwaarde van de if
voldaan en wordt, wat deze voorwaarde betreft, de code tussen de {}
uitgevoerd.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
e.key === "Escape"
: de tweede voorwaarde, waaraan voldaan moet worden.
e
: in e
zit een zogenaamd object, waarin allerlei informatie zit. Bij function sluitGrote(e) {, de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
key
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.
===
: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.
"Escape"
: dit is in JavaScript de naam van de Escape-toets. De inhoud van key
, oftewel de naam van de ingedrukte toets moet 'Escape' zijn: de Escape-toets moet zijn ingedrukt.
De tweede voorwaarde, waaraan moet worden voldaan, is dus dat Escape is ingedrukt.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
document.querySelector("#uitleg").contains(document.activeElement)
: de derde voorwaarde, waaraan voldaan moet worden.
document.querySelector("#uitleg")
: het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen aanhalingstekens tussen de haakjes:
querySelector("#uitleg")
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar het element met id="uitleg" gezocht, net zoals je in css in een selector #uitleg {...}
zou gebruiken.
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
Het hele deel document.querySelector("#uitleg")
: zoek het element met id="uitleg" op in de huidige pagina. Je zou kunnen zeggen dat dit dus eigenlijk gewoon div#uitleg
betekent: er moet iets gebeuren met div#uitleg
.
De gevonden div#uitleg
wordt weer behandeld als een object: er is allerlei informatie in opgeslagen, en ook kunnen weer methoden worden gebruikt.
.contains.(document.activeElement)
: dit is zo'n hierboven genoemde methode: contains()
. Omdat gelijk voor deze methode, gescheiden door een punt, document.querySelector("#uitleg")
staat, werkt deze methode op de daarmee gevonden div#uitleg
.
contains()
kijkt, of iets aanwezig is in het voor de punt staande gegeven. Waarnaar gekeken moet worden, staat tussen de haakjes achter contains
: document.activeElement
.
document
is weer hetzelfde object als iets hierboven bij document.querySelector("#uitleg")
. In document
is in activeElement
opgeslagen, welk element eventueel de focus heeft.
document.activeElement
betekent dus gewoon: het element dat de focus heeft. En contains.document.activeElemen
betekent dan gewoon: kijk of het element met de focus binnen het voor de punt staande gegeven, in div#uitleg
, zit.
De derde voorwaarde waaraan voldaan moet worden, is dus: het element met de focus moet binnen div#uitleg
zetten.
De code tussen de {}
achter de if
wordt alleen uitgevoerd, als aan alle drie de voorwaarden is voldaan: er moet al een <picture> of <div> de focus hebben gehad én de Escape-toets moet zijn ingedrukt én het element met de focus moet binnen div#uitleg
zitten. Als aan één van deze voorwaarden niet wordt voldaan, wordt de code tussen de {}
niet uitgevoerd.
heeftFocus.focus();
Deze regel is onderdeel van function sluitGrote(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– heeftFocus
is niet leeg (er heeft al 'n <div> of <picture> focus gehad);
– Escape is ingedrukt;
– een element binnen div#uitleg
heeft de focus.
heeftFocus
: als een <div> of <picture> binnen div#thumbs
, waarin de hele slideshow zit, de focus heeft gekregen, wordt die <div> of <picture> in de bij let heeftFocus; aangemaakte variabele heeftFocus
opgeslagen. Hierdoor kan de focus eventueel weer worden teruggezet.
Die focus kan de <div> of <picture> op verschillende manieren krijgen, maar dat maakt verder niet uit. Het gaat erom, dat het element met de focus wordt opgeslagen, niet hoe die focus is gekregen.
focus()
: geef de focus aan het gegeven dat voor de punt staat. Dat is hier het element dat in variabele heeftFocus
is opgeslagen.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Deze regel wordt alleen uitgevoerd, als aan de voorwaarden van de if is voldaan. De hulp moet zijn geopend (alleen dan wordt voldaan aan de derde voorwaarde: het element met de focus moet binnen div#uitleg
zitten). Verder moet de Escape-toets zijn ingedrukt (daarmee wordt de hulp gesloten). En heeftFocus
moet al een element bevatten (er heeft al een <div> of <picture> de focus gehad).
Deze situatie kan zich alleen voordoen, als de hulp wordt geopend, nadat al een <picture> met een thumbnail is geselecteerd of een grote afbeelding is geopend. Bij het sluiten van de hulp met Escape zorgt deze regel ervoor, dat de focus wordt teruggezet op de <div> of de <picture> met de thumbnail, die bij het openen van de hulp de focus had. Als je de hulp opent, nadat je al door de slideshow hebt gebladerd, hoef je nu niet helemaal opnieuw vanaf de eerste thumbnail te beginnen. Je kunt weer doorgaan, waar je was gebleven.
return;
Deze regel is onderdeel van function sluitGrote(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– heeftFocus
is niet leeg (er heeft al 'n <div> of <picture> focus gehad);
– Escape is ingedrukt;
– een element binnen div#uitleg
heeft de focus.
Verder hoeft er niets te gebeuren. Daar zorgt deze regel voor: stop en ga terug naar waar je vandaan kwam.
Dat 'terug naar waar je vandaan kwam' betekent: terug naar de plek, waarvandaan is aangeroepen. In dit geval is er geen terug, want function sluitGrote(e) {, waar deze return
een onderdeel van is, is niet aangeroepen door een ander deel van het script, maar door een klik, een aanraking of het indrukken van een toets. Deze functie is vanuit de html 'aangeroepen'. 'Terug naar waar je vandaan kwam' betekent hier dus: weg uit het script en 'terug' naar de html. Oftewel: de rest van het script wordt niet uitgevoerd.
De puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
} else if ( ( (! thumbs.contains(e.target) && (e.target.id !== "vraagteken") ) || e.key === "Escape") && heeftFocus && heeftFocus.id.startsWith("div-") ) {
Als je in de html een andere id dan id="div-...(nummer)..." hebt gebruikt bij div#div-...
, of als je een andere id dan 'vraagteken' hebt gebruikt bij span#vraagteken
, moet je die id ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function sluitGrote(e) {
Deze else if
hoort bij een eerdere if.
Als aan de voorwaarden bij de if, waar deze } else if {
bij hoort niet is voldaan, volgt hier een herkansing. Ook dit ziet er ingewikkeld uit, maar in stukjes gehakt wordt het weer veel overzichtelijker.
} else if
: de }
aan het begin is de afsluitende }
van de code, die wordt uitgevoerd als aan de voorwaarden bij de if
is voldaan. Die heeft dus eigenlijk niets met de else
te maken.
else if
letterlijk vertaald betekent 'anders als'. Oftewel: als aan de bij if
gestelde voorwaarden niet is voldaan, kijk dan of aan deze andere voorwaarden misschien wel wordt voldaan.
Het begin van de eventueel bij deze else if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze else if
uit te voeren code wordt aangegeven met een }
. In dit geval is dat de } else if (e.target.id !== "vraagteken" ... iets verderop. Dat er nog meer dan alleen die }
op die regel staat, maakt niet uit.
(
: de voorwaarden waaraan moet worden voldaan om de code bij deze else if
uit te voeren, staan tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de else if
. Er staan vijf voorwaarden, maar eigenlijk gaat het hier maar om drie voorwaarden, waaraan moet worden voldaan: de eerste drie voorwaarden vormen samen eigenlijk maar één voorwaarde.
De voorwaarden worden gescheiden door &&
en ||
, wat in JavaScript én en óf betekent: er moet aan alle twee de voorwaarden voor en na de &&
, óf aan één van de voorwaarden voor en na de ||
worden voldaan. Pas dan wordt de bij deze else if
horende code uitgevoerd.
Als je de voorwaarden even door letters vervangt, staat hier:
if ((a && b) || c) && d && e)
In gewoon Nederlands staat hier: als (a én b) óf alleen c waar is, én als d én als e waar is.
De buitenste haakjes zijn de verplichte haakjes die bij de voorwaarden van de if
horen. De vier binnenste haakjes staan er alleen maar voor de leesbaarheid. Ze maken duidelijk dat bepaalde dingen bij elkaar horen. (Hier met alleen maar letters letters is dat niet nodig, bij de echte voorwaarden scheelt het wel.)
Bij &&
en ||
worden eerst de bewerkingen met &&
uitgevoerd. Die hebben voorrang boven bewerkingen met ||
, net zoals bij gewoon rekenen bijvoorbeeld vermenigvuldigen voor optellen gaat. Daarna wordt als volgorde van links naar rechts aangehouden, ook hetzelfde als bij gewoon rekenen.
De haakjes rondom a && b
maken het voor mensen duidelijker dat deze allebei waar moeten zijn. En voor de de haakjes rondom (a && b) || c
geldt hetzelfde.: ze maken het voor mensen duidelijker dat het voldoende is als a én b, óf c waar is. Daarna komen nog twee overzichtelijke voorwaarden met alleen &&
, dus daar zijn geen extra haakjes voor de leesbaarheid gebruikt.
Daarnaast hebben haakjes nog een belangrijke extra functie. Net als bij gewoon rekenen, worden bewerkingen tussen haakjes eerder uitgevoerd. Toevallig komt het hier zo uit, dat haakjes geen verschil maken. Maar bij een combinatie van &&
en ||
(en vaak nog veel meer soortgelijke operators), waarbij &&
dus voorrang heeft boven ||
, maken haakjes wel heel vaak een enorm verschil, of je eerst &&
of eerst ||
(of eerst nog eventuel eandere operators) uitvoert.
Zeker bij iets ingewikkelder bewerkingen is het echt heel erg makkelijk om hier fouten in te maken. Die vaak ook nog eens bijzonder lastig te vinden zijn. Of die bij testen niet opvallen, omdat testdata worden gebruikt die toevallig een goede uitkomst geven. Daarom is het bij een combinatie van verschillende operators verstandig om haakjes te gebruiken, zodra het ook maar 'n tikkeltje ingewikkeld begint te lijken.
! thumbs.contains(e.target)
: de eerste voorwaarde:
!
: een uitroepteken betekent in JavaScript 'niet'. Wat achter het uitroepteken staat, mag niet waar zijn.
thumbs
: in variabele thumbs
is bij const thumbs = document.querySelector("#thumbs"); div#thumbs
, de <div> met de slideshow, opgeslagen in de vorm van een object. In dat object is een een enorme hoeveelheid informatie over div#thumbs
opgeslagen (en over de nakomelingen van div#thumbs
). Naast die in thumbs
opgeslagen informatie kun je ook gebruik maken van allerlei functies, die JavaScript gratis en voor niets toevoegt aan het object thumbs
.
Een functie bij een object werkt iets anders dan een gewone functie, daarmee heet een functie bij een object een 'methode'.
contains(e.target)
: dit is zo'n hierboven genoemde methode: contains()
. Omdat gelijk voor deze methode, gescheiden door een punt, thumbs
staat, werkt deze methode op het in thumbs
opgeslagen object, op div#thumbs
.
Waarnaar gekeken moet worden, staat tussen de haakjes achter contains
: e.target
.
In e
zit een zogenaamd object, waarin allerlei informatie zit over het element, waarop functie sluitGrote()
is aangeroepen. Omdat e
bij function sluitGrote(e) { tussen de haakjes achter de functie stond, kan die informatie uit e
overal binnen functie sluitGrote()
worden gebruikt.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
In e
is onder andere opgeslagen, op welk element functie sluitGrote()
precies is aangeroepen. Dat element is opgeslagen onder de naam target
. e.target
bevat dus, ook weer in de vorm van een object, het element waarop functie sluitGrote()
precies is aangeroepen.
! thumbs.contains(e.target)
: bij elkaar betekent dit deel van de regel: als het element waarop functie sluitGrote()
werd aangeroepen niet binnen div#thumbs
zit.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
(e.target.id !== "vraagteken")
: de tweede voorwaarde:
(
: het eerste haakje staat er alleen voor de leesbaarheid. Nu is voor mensen duidelijker, dat dit deel bij elkaar hoort. Achter "vraagteken"
staat het bijbehorende afsluitende haakje.
e
: in e
zit een zogenaamd object, waarin allerlei informatie zit over het element, waarop functie sluitGrote()
is aangeroepen. Omdat e
bij function sluitGrote(e) { tussen de haakjes achter de functie stond, kan die informatie uit e
overal binnen functie sluitGrote()
worden gebruikt.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target.id
: in e
is onder andere opgeslagen, op welk element functie sluitGrote()
precies is aangeroepen. Dat element is opgeslagen onder de naam target
. e.target
bevat dus, ook weer in de vorm van een object, het element waarop functie sluitGrote()
precies is aangeroepen.
In target
zit onder andere de id van het element dat in de vorm van een object in target
zit. De naam van dat stukje informatie is, o grote verrassing, id
. De id wordt, anders dan bij css, opgeslagen zonder de '#' aan het begin. Als er geen id is, is id
gewoon leeg.
e.target.id
bij elkaar is dus de id van het element, waarop functie sluitGrote()
is aangeroepen. De id wordt, anders dan bij css, opgeslagen zonder de '#' aan het begin.
!==
: als wat hiervoor staat niet helemaal precies, echt helemaal precies, hetzelfde is, als wat hierachter staat.
"vraagteken"
: hiermee wordt e.target.id
vergeleken, dit moet precies hetzelfde zijn. Omdat JavaScript de id opslaat zonder de '#' aan het begin, wordt die hier ook weggelaten.
(e.target.id!== "vraagteken")
: bij elkaar betekent dit stukje van de regel: als de id van het element waarop functie sluitGrote()
werd aangeroepen niet 'vraagteken' is. 'vraagteken' is de id van span#vraagteken
, de <span> met het vraagteken.
( (! thumbs.contains(e.target) && (e.target.id !== "vraagteken") )
: bij elkaar betekent het eerste deel van de regel voor de ||
, de eerste twee voorwaarden: het element waarop functie sluitGrote()
is aangeroepen mag niet binnen div#thumbs
zitten én het mag niet span#vraagteken
zijn.
||
: de twee staande streepjes is JavaScript voor 'of'. Voor de streepjes staat een voorwaarde, en achter de streepjes staat nog een andere voorwaarde.
Als aan de voorwaarde voor ||
is voldaan, wordt niet eens meer gekeken naar de voorwaarde achter de ||
. Als aan één van de voorwaarden is voldaan, is dat bij ||
(of) voldoende.
In dit geval moet aan twee voorwaarden voor de ||
zijn voldaan, omdat die twee voorwaarden met &&
(én) als het ware aan elkaar gekoppeld zijn: (a én b) óf c moeten waar zijn.
e.key === "Escape"
: de derde voorwaarde:
e
: in e
zit een zogenaamd object, waarin allerlei informatie zit over het element, waarop functie sluitGrote()
is aangeroepen. Omdat e
bij function sluitGrote(e) { tussen de haakjes achter de functie stond, kan die informatie uit e
overal binnen functie sluitGrote()
worden gebruikt.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
key
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.
===
: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.
"Escape"
: dit is in JavaScript de naam van de Escape-toets. De inhoud van key
, oftewel de naam van de ingedrukte toets moet 'Escape' zijn: de Escape-toets moet zijn ingedrukt.
e.key === "Escape"
: bij elkaar betekent dit deel van de regel dat de functie moet zijn aangeroepen door het indrukken van de Escape-toets om aan de derde voorwaarde te voldoen.
( (! thumbs.contains(e.target) && (e.target.id !== "vraagteken") ) || e.key === "Escape")
: dit deel van de regel bij elkaar:
– als aan de eerste voorwaarde is voldaan (het element waarop functie sluitGrote()
is aangeroepen mag niet binnen div#thumbs
zitten);
én
– als aan de tweede voorwaarde is voldaan (de id van het element waarop functie sluitGrote()
is aangeroepen mag niet 'vraagteken' zijn);
óf (maar als dat niet zo is, is het volgende ook goed)
– functie sluitGrote()
is aangeroepen door het indrukken van de Escape-toets.
Van deze eerste drie voorwaarden is het dus voldoende, als aan de eerste twee óf aan de derde wordt voldaan. Daarom zijn ze samen uiteindelijk eigenlijk maar één voorwaarde.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
heeftFocus
: de vierde voorwaarde. Dit is een bij let heeftFocus; aangemaakte variabele, waarin wordt bijgehouden, welk element eventueel de focus heeft.
Als er alleen maar een variabele staat en verder niets, is de voorwaarde om aan de if
te voldoen dat de variabele niet leeg is.
Bij openen van de pagina is deze variabele leeg, want geen enkel element heeft al de focus gehad. Als de variabele leeg is, is deze 'false', of eigenlijk 'falsy'. (Echt 'false' is net iets anders.)
Zodra een <picture> of <div> binnen de slideshow de focus heeft gekregen, wordt die <picture> of <div> door het script opgeborgen in variabele heeftFocus
. De variabele is dan niet meer leeg en 'true', of eigenlijk 'truthy'. (Echt 'true' is net iets anders). Nu wordt aan de vierde voorwaarde van de if
voldaan.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
heeftFocus.id.startsWith("div-")
: de vijfde voorwaarde:
heeftFocus
: dit is een bij let heeftFocus; aangemaakte variabele, waarin wordt bijgehouden, welk element eventueel de focus heeft. Iets Zodra een <div> of <picture> binnen de slideshow de focus heeft gekregen, wordt die <div> of <picture> in heeftFocus
opgeslagen. Die <div> of <picture> is weer in de vorm van een object opgeslagen, waarin ook allerlei informatie over de <div> of <picture> zit. Ook kunnen er weer allerlei functies van dat object worden gebruikt. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
id
: dit is zo'n stukje informatie: het is de id van de <div> of <picture>. De id wordt, anders dan bij css, opgeslagen zonder de '#' aan het begin. Als er geen id is, is id
gewoon leeg.
startsWith("div-")
: dit is zo'n methode. Er wordt gekeken of wat voor de punt staat (heeftFocus.id
, de id van het in heeftFocus
opgeslagen object) begint met wat tussen de haakjes staat: "div-"
. Tussen aanhalingstekens, zodat het script weet dat het hier om een stukje letterlijke tekst gaat, een 'string'.
Als het object in heeftFocus
bij een <picture> hoort, begint de id met 'picture-'. Alleen bij een <div> begint de id met 'div-'.
heeftFocus.id.startsWith("div-")
: bij elkaar betekent dit deel van de regel: de id van het in heeftFocus
opgeslagen object (de <div> of de <picture>) moet met 'div-' beginnen. Alleen een <div> voldoet aan deze eis, dus eigenlijk staat er: het moet een <div> zijn.
) {
: aan het eind van de regel staat dan nog het afsluitende haakje, dat bij de (
gelijk achter de else if
hoort. Het geeft het eind van de voorwaarden aan. Daarachter staat nog de {
die het begin aangeeft van de code, die uitgevoerd wordt als aan de voorwaarden wordt voldaan.
In gewone taal staat hier: het element waarop functie sluitGrote()
werd aangeroepen mag niet binnen div#thumbs
liggen én het mag niet span#vraagteken
zijn. Als één van die twee niet zo is, is het ook goed als sluitGrote()
is aangeroepen door het indrukken van de Escape-toets.
Als bovenstaande zo is, moet bovendien heeftFocus
al zijn gevuld (er moet al een <picture> of <div> de focus hebben gehad.
Ten slotte moet de id van het element waarop functie sluitGrote()
is aangeroepen beginnen met '#div-' (dat is alleen zo, als er een <div> is opgeslagen in heeftFocus
).
Aan deze voorwaarden wordt maar in twee situaties voldaan:
* De eerste mogelijkheid: een <div> heeft de focus, waardoor een grote afbeelding wordt getoond. Bovendien moet die grote afbeelding kennelijk worden gesloten, want het scherm is buiten de grote afbeelding aangeraakt of -geklikt, of de Escape-toets is ingedrukt. In dat geval moet de focus worden teruggezet op de bij de grote afbeelding horende <picture>, waarin de thumbnail zit. Nu kun je, na het sluiten van een grote afbeelding, met de knoppen rechtsboven gewoon verder gaan, waar je was gebleven.
Als span#vraagteken
wordt aangeraakt of -geklikt, wordt dat specifiek uitgesloten. span#vraagteken
ligt ook buiten div#thumbs
. Bij openen van de pagina zou de hulp nog openen als je het vraagteken aanraakt of -klikt, maar zodra een <div> de focus heeft gekregen niet meer, want de focus zou dan onmiddellijk aan de bij de <div> horende <picture> worden gegeven. En de hulp opent nou juist, als span#vraagteken
de focus heeft.
* De tweede mogelijkheid: de hulp is geopend, nadat al een <div> de focus heeft gehad. De hulp moet kennelijk weer worden gesloten, want het scherm is ergens aangeraakt, of de Escape-toets is ingedrukt.
document.querySelector("#" + heeftFocus.id + "> picture").focus();
Deze regel is onderdeel van function sluitGrote(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarden is voldaan:
– het aangeraakte of -geklikte element zit niet binnen div#thumbs
(de <div> met de slideshow) én het aangeraakte of -geklikte element is niet span#vraagteken
, óf de Escape-toets is ingedrukt;
– heeftFocus
is niet leeg (er heeft al 'n <div> of <picture> de focus gehad);
– het element dat al de focus heeft gehad, is een <div> (de id begint met '#div-')
document.querySelector("#" + heeftFocus.id + "> picture")
: het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen de haakjes:
querySelector("#" + heeftFocus.id + "> picture")
Hierbij is de syntax van het deel tussen de haakjes precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar een <picture> gezocht, die een direct kind is van de in heeftFocus
zittende <div> (gelijk hieronder wordt dit uitgebreider beschreven).
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
In dit geval is het deel tussen de aanhalingstekens wat ingewikkelder, omdat het bestaat uit een aantal onderdelen, die aan elkaar gekoppeld worden:
"#" + heeftFocus.id + "> picture"
"#"
: dit staat tussen aanhalingstekens, wat betekent dat het een stukje letterlijke tekst is, een 'string'. Dit wordt gewoon letterlijk zo gebruikt.
+
: het stukje voor de +
wordt samengevoegd met het stukje achter de +
.Omdat het hier om tekst gaat die bij elkaar wordt 'opgeteld' en je tekst uiteraard niet kunt 'optellen', wordt tekst gewoon achter elkaar gezet: het deel achter de +
wordt achter het deel voor de +
gezet.
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
zit, in de vorm van een object, de <picture> of de <div>, die de focus heeft op het moment dat functie sluitGrote()
wordt aangeroepen. Bij de } else if, waar deze code bij hoort, werd onder andere als voorwaarde voor het uitvoeren van deze code opgegeven, dat de id van het in heeftFocus
opgeslagen object moet beginnen met 'div-'. Het kan dus niet anders, of het object in heeftFocus
hoort bij een <div>.
Omdat de <div> is opgeslagen in de vorm van een object, zit er veel informatie over de <div> in heeftFocus
.
id
: een van die stukjes informatie is de id van de <div>. Stel dat het om de derde <div> gaat, dan is de opgeslagen id 'div-3' (JavaScript laat de '#' aan het begin weg).
"#" + heeftFocus.id
bij elkaar wordt dan, als het om de derde <div> gaat: '#div-3', de derde <div> waarbinnen een thumbnail met bijbehorende knoppen en dergelijke zit.
+
: het deel voor de +
wordt weer 'opgeteld' bij het deel achter de +
.
"> picture"
: het laatste stukje van het gegeven, waar querySelector()
naar moet zoeken.
Alles tussen de haakjes bij querySelector()
bij elkaar staat hier, als het bijvoorbeeld om de derde <div> zou gaan: querySelector("#div-3 > picture")
. Een gewone css-selector: de <picture>'s die een direct kind van het element met id='div-3' zijn. Dat is er maar één: de <picture> waar de thumbnail in zit. (Wat een direct kind is, is te vinden bij Elke thumbnail...)
querySelector("#" + heeftFocus.id + "> picture")
: als de derde <div> de focus had op het moment dat functie sluitGrote()
werd aangeroepen, staat er dus eigenlijk:
querySelector("#div-3 > picture")
Zoek de <picture> op die in de <div> zit die de focus had, op het moment dat functie sluitGrote()
werd aangeroepen.
focus()
: zet de focus terug op de gevonden <picture>.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Deze regel wordt alleen uitgevoerd (vanwege de voorwaarden bij de } else if), als een <div> de focus had terwijl functie sluitGrote()
werd aangeroepen. Oftewel: er was een grote afbeelding geopend. Bovendien werd het scherm buiten de grote afbeelding aangeraakt of ‑geklikt, of de Escape-toets werd ingedrukt: de grote afbeelding werd gesloten.
In dat geval zet deze regel de focus terug op de <picture>, de thumbnail, die bij de <div> hoort, zodat je met de knoppen rechtsboven gewoon verder kunt gaan bij de vorige of volgende thumbnail of grote afbeelding. In plaats van weer helemaal terug te gaan naar het begin van de pagina.
heeftFocus = document.activeElement;
Deze regel is onderdeel van function sluitGrote(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarden is voldaan:
– het aangeraakte of -geklikte element zit niet binnen div#thumbs
(de <div> met de slideshow) én het aangeraakte of -geklikte element is niet span#vraagteken
, óf de Escape-toets is ingedrukt;
– heeftFocus
is niet leeg (er heeft al 'n <div> of <picture> de focus gehad);
– het element dat al de focus heeft gehad, is een <div> (de id begint met '#div-')
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
In de regel hierboven is de focus van een <div> naar de daarin zittende <picture> verplaatst. Daarom moet de inhoud van heeftFocus
ook worden aangepast, want die klopt niet meer.
=
: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina.
activeElement
: dit is zo'n stukje informatie. Hierin zit, weer in de vorm van een object, het element dat op dit moment de focus heeft.
document.activeElement
bij elkaar betekent: het element dat de focus heeft.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Nu is in heeftFocus
de <picture> opgeslagen, die nu de focus heeft. En kan deze, als dat nodig is, weer worden teruggezet.
} else if (e.target.id !== "vraagteken" && heeftFocus && heeftFocus.id.startsWith("picture-") ) {
Als je in de html een andere id dan "picture-...(nummer)..." hebt gebruikt bij picture-picture-...
, of als je een andere id dan 'vraagteken' hebt gebruikt bij span#vraagteken
moet je die id ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function sluitGrote(e) {
Deze else if
hoort bij een eerdere if.
Voor deze else if
staat nog een eerdere else if.
Als aan de voorwaarden bij de if, waar deze } else if {
bij hoort, en aan de voorwaarden van de eerdere else if, niet is voldaan, volgt hier een herkansing. Ook dit ziet er ingewikkeld uit, maar in stukjes gehakt wordt het weer veel overzichtelijker.
} else if
: de }
aan het begin is de afsluitende }
van de code, die wordt uitgevoerd als aan de voorwaarden bij de vorige else if
is voldaan. Die heeft dus eigenlijk niets met deze else if
te maken.
else if
letterlijk vertaald betekent 'anders als'. Oftewel: als aan de eerdere bij if
en else if
gestelde voorwaarden niet is voldaan, kijk dan of aan deze andere voorwaarden misschien wel wordt voldaan.
Het begin van de eventueel bij deze else if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze else if
uit te voeren code wordt aangegeven met een }
.
(
: de voorwaarden waaraan moet worden voldaan om de code bij deze else if
uit te voeren, staan tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de else if
. Er staan drie voorwaarden, die worden gescheiden door &&
, wat in JavaScript én betekent: er moet aan alle drie de voorwaarden worden voldaan. Pas dan wordt de bij deze else if
horende code uitgevoerd.
e.target.id !== "vraagteken"
: dit is de eerste voorwaarde.
e
: dit is een object met onder andere informatie, dat bij het aanroepen van function sluitGrote(e) { is meegegeven.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target.id
: dit is zo'n stukje informatie. Hierin zit de id (zonder de '#' aan het begin) van het element, waarop de functie werd aangeroepen.
!==
: de id hiervoor mag niet hetzelfde zijn als wat hierachter staat.
"vraagteken"
: de id mag dus niet 'vraagteken' zijn.
De eerste voorwaarde samengevat: de id van het element, waarop de functie werd aangeroepen, mag niet 'vraagteken' zijn. Het mag niet span#vraagteken zijn, de <span> met het vraagteken.
Een uitgebreidere beschrijving van deze eerste voorwaarde staat bij (e.target.id !== "vraagteken"). Alleen missen hier de twee buitenste haakjes, die daar voor de duidelijkheid worden gebruikt.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
heeftFocus
: de tweede voorwaarde. Dit is een bij let heeftFocus; aangemaakte variabele, waarin wordt bijgehouden, welk element eventueel de focus heeft.
Als er alleen maar een variabele staat en verder niets, is de voorwaarde om aan de else if
te voldoen dat de variabele niet leeg is.
Bij openen van de pagina is deze variabele leeg, want geen enkel element heeft al de focus gehad. Als de variabele leeg is, is deze 'false', of eigenlijk 'falsy'. (Echt 'false' is net iets anders.)
Zodra een <picture> of <div> binnen de slideshow de focus heeft gekregen, wordt die <picture> of <div> door het script opgeborgen in variabele heeftFocus
. De variabele is dan niet meer leeg en 'true', of eigenlijk 'truthy'. (Echt 'true' is net iets anders). Nu wordt aan de tweede voorwaarde van de else if
voldaan.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
heeftFocus.id.startsWith("picture-")
: de derde voorwaarde:
heeftFocus
: iets hierboven bij heeftFocus
staat, dat in heeftFocus
een <div> of <picture> is opgeborgen, zodra die de focus heeft gekregen. Die <div> of <picture> is weer in de vorm van een object opgeslagen, waarin ook allerlei informatie over de <div> of <picture> zit. Ook kunnen er weer allerlei functies van dat object worden gebruikt. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
id
: dit is zo'n stukje informatie: het is de id van de <div> of <picture>. De id wordt, anders dan bij css, opgeslagen zonder de '#' aan het begin. Als er geen id is, is id
gewoon leeg.
startsWith("picture-")
: dit is zo'n methode. Er wordt gekeken of wat voor de punt staat (heeftFocus.id
, de id van het in heeftFocus
opgeslagen object) begint met wat tussen de haakjes staat: "picture-"
. Tussen aanhalingstekens, zodat het script weet dat het hier om een stukje letterlijke tekst gaat, een 'string'.
Alleen als het object in heeftFocus
bij een <picture> hoort, begint de id met 'picture-'.
heeftFocus.id.startsWith("picture-")
: bij elkaar betekent dit deel van de regel: de id van het in heeftFocus
opgeslagen object (de <div> of de <picture>) moet met 'picture-' beginnen. Alleen een <picture> voldoet aan deze eis, dus eigenlijk staat er: het moet een <picture> zijn.
) {
: aan het eind van de regel staat dan nog het afsluitende haakje, dat bij de (
gelijk achter de else if
hoort. Het geeft het eind van de voorwaarden aan. Daarachter staat nog de {
die het begin aangeeft van de code, die uitgevoerd wordt als aan de voorwaarden wordt voldaan.
Deze situatie doet zich alleen voor, als al een <picture> met een thumbnail de focus heeft gehad en het scherm ergens wordt aangeraakt of -geklikt. Als de hulp wordt geopend nadat al een <picture> de focus heeft gehad en de hulp wordt weer gesloten door het scherm ergens aan te raken, wordt de focus teruggezet op de <picture> die de focus had. Zodat je met de knoppen rechtsboven verder kunt gaan, waar je was gebleven.
span#vraagteken
wordt expliciet uitgesloten van die aanraking of klik, want anders zou de hulp niet meer openen. Bij openen van de pagina zou de hulp nog openen als je het vraagteken aanraakt of -klikt, maar zodra een <picture> de focus heeft gekregen niet meer, want de focus zou dan onmiddellijk weer aan de <picture> worden gegeven. En de hulp opent nou juist, als span#vraagteken
de focus heeft.
document.querySelector("#" + heeftFocus.id).focus();
Deze regel is onderdeel van function sluitGrote(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarden is voldaan:
– Het aangeraakte of -geklikte element is niet span#vraagteken
;
– heeftFocus
is niet leeg (er heeft al 'n <div> of <picture> de focus gehad;
– De id van het in heeftFocus
opgeslagen element begint met 'picture-'.
document.querySelector("#" + heeftFocus.id)
: het middelste stukje querySelector
is een zogenaamde 'functie'. Deze functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document
, vandaar dat document
ervoor staat.
Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
Met de methode querySelector()
uit document
kan JavaScript het eerste element van 'n bepaalde soort opzoeken, zoals de eerste <div>, het eerste element met een class="hoera", en dergelijke. Het gegeven waarnaar wordt gezocht, staat tussen de haakjes:
querySelector("#" + heeftFocus.id)
Hierbij is de syntax van het deel tussen de aanhalingstekens precies hetzelfde als bij een selector in css. In bovenstaande regel wordt naar een <picture> gezocht, die de id heeft van de in heeftFocus
zittende <picture> (gelijk hieronder wordt dit uitgebreider beschreven).
Zou je naar de eerste <span> zoeken die een eerste kind is, dan zou je de volgende regel gebruiken:
querySelector("span:first-child")
Precies zoals selectors in css werken.
In dit geval is het deel tussen de aanhalingstekens wat ingewikkelder, omdat het bestaat uit een twee onderdelen, die aan elkaar gekoppeld worden, "#" + heeftFocus.id
:
"#"
: dit staat tussen aanhalingstekens, wat betekent dat het een stukje letterlijke tekst is, een 'string'. Dit wordt letterlijk zo gebruikt.
+
: het stukje voor de +
wordt samengevoegd met het stukje achter de +
. Omdat het hier om tekst gaat die bij elkaar wordt 'opgeteld' en je tekst uiteraard niet kunt 'optellen', wordt tekst gewoon achter elkaar gezet: het deel achter de +
wordt achter het deel voor de +
gezet.
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus zit
, in de vorm van een object, de <picture> of de <div>, die de focus heeft op het moment dat functie sluitGrote()
wordt aangeroepen. Bij de } else if, waar deze code bij hoort, werd onder andere als voorwaarde voor het uitvoeren van deze code opgegeven, dat de id van het in heeftFocus
opgeslagen object moet beginnen met 'picture-'. Het kan dus niet anders, of het object in heeftFocus
hoort bij een <picture>.
Omdat de <picture> is opgeslagen in de vorm van een object, zit er veel informatie over de <picture> in heeftFocus
.
id
: een van die stukjes informatie is de id van de <picture>. Stel dat het om de derde <picture> gaat, dan is de opgeslagen id 'picture-3' (JavaScript laat de '#' aan het begin weg).
"#" + heeftFocus.id
bij elkaar wordt dan, als het om de derde <picture> gaat: '#picture-3', de derde <picture> waarbinnen een thumbnail zit.
Alles tussen de haakjes bij querySelector()
bij elkaar staat hier, als het bijvoorbeeld om de derde <picture> zou gaan: #picture-3
, een gewone css-selector.
querySelector("#" + heeftFocus.id)
: als de derde <picture> de focus had op het moment dat functie sluitGrote()
werd aangeroepen, staat er dus eigenlijk:
querySelector("#picture-3")
Zoek de <picture> op die de focus had, op het moment dat functie sluitGrote()
werd aangeroepen.
focus()
: zet de focus terug op de gevonden <picture>.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
function verwerkLinks(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 door ergens binnen div#thumbs
het scherm aan te raken of aan te klikken. Dit wordt geregeld bij thumbs.addEventListener("click", verwerkLinks);.
Als je een link volgt, wordt die link normaal genomen opgeslagen in de geschiedenis van de browser. Je kunt dan met de Terug- en Vooruit-knop snel naar eerder bezochte links gaan. De zes knoppen rechtsboven zijn in werkelijkheid ook gewone links en worden dus ook opgeslagen in de geschiedenis. Als je van pagina 'De vrolijke vakantiehater' naar de slideshow gaat en vervolgens dertig keer een knop indrukt om alles te bekijken, moet je daarna 31 keer op de Terug-knop van de browser drukken, om weer vrolijk de vakantie te kunnen haten. Daardoor veranderd De Vrolijke Vakantiehater in De Chagrijnige Vakantiehater, en dat moeten we niet hebben.
Deze functie voorkomt, dat de knoppen rechtsboven (eigenlijk dus links) in de geschiedenis van de browser worden opgeslagen. Nu kan de vrolijke vakantiehater met één druk op de Terug-knop weer terug naar zijn vrolijke medehaters.
function
: het sleutelwoord waarmee het begin van een functie wordt aangegeven.
verwerkLinks
: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)
Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke niet gebruikt mogen worden in een naam. In css zou je hier bijvoorbeeld verwerk-links in plaats van verwerkLinks
kunnen gebruiken.
(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. Hier wordt e
doorgegeven.
In e
zit een zogenaamd object, waarin onder andere informatie zit over het element, waarop de functie is aangeroepen. Omdat het tussen de haakjes achter function()
staat, is de informatie uit e
door de code binnen de functie te gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
{
: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }
.
if (e.target.href && (e.target.href.indexOf(path) > -1) {
Deze regel is onderdeel van function verwerkLinks(e) {
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
.
e
: dit is een object met onder andere informatie, dat bij het aanroepen van function verwerkLinks(e) { is meegegeven.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit het element, wat de functie heeft aangeroepen. Hier is dat de knop rechtsboven (eigenlijk een link) die is aangeraakt of -geklikt.
Feitelijk is target
ook weer een object. Hierdoor zit er allerlei informatie in over de <a> die is gevolgd (en zou je ook van alles met die <a> kunnen doen, maar dat gebeurt hier niet.)
href
: dit is zo'n stukje informatie dat in het hierboven genoemde object in target
zit. In href
zit de volledige bestemming van de link, inclusief protocol, domein, enzovoort. Ook al heb je zelf in de html een kortere bestemming ingetypt, in href
zit toch het volledige adres.
In de html van het voorbeeld is de link om naar de derde <div> te gaan <a href="#div-3">. In de href
uit het object dat bij deze <a> hoort, staat echter:
'https://www.css-voorbeelden.nl/href
op de site. Op een andere site zal de inhoud van href
er uiteraard anders uitzien.)
e.target.href
: de hele eerste voorwaarde bij elkaar levert dus het volledige adres, waar de <a> naartoe linkt op.
Functie verwerkLinks()
is bij thumbs.addEventListener("click", verwerkLinks); aan div#thumbs
gekoppeld. Overal waar binnen div#thumbs
het scherm wordt aangeraakt of -geklikt, wordt functie verwerkLinks()
aangeroepen. Dat hoeft dus helemaal geen link te zijn.
Als er iets anders dan een link is aangeraakt of -geklikt, is er geen href
aanwezig. In dat geval is e.target.href
niet aanwezig. Omdat verderop iets met de inhoud van e.target.href
wordt gedaan, gaat het mis als dat helemaal niet aanwezig is. Daarom wordt hier gekeken, of e.target.href
wel bestaat, of het element waarop functie verwerkLinks()
is aangeroepen wel een href
heeft. Als dat niet zo is, wordt niet aan de eerste voorwaarde van de if
voldaan en wordt de code tussen de {}
niet uitgevoerd.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
(
: dit haakje staat er alleen maar voor de leesbaarheid. De tweede voorwaarde bestaat uit een aantal losse delen. Door dat tweede deel tussen haakjes te zetten, is duidelijker dat die delen bij elkaar horen.
e.target.href
: dit is weer de inhoud van de href
, eventuele aangevuld tot het volledige adres, uit de link. Hier iets boven wordt dit uitgebreider besproken.
indexOf()
: dit is een in JavaScript ingebouwde functie. Een functie is een stukje bij elkaar horende code, dat je makkelijk kunt uitvoeren. Je roept de naam van de functie (hier 'indexOf()'), en het beestje begint te werken.
Je kunt zelf functies maken, maar JavaScript bevat ook al een groot aantal ingebouwde functies voor handelingen die vaak voorkomen. indexOf()
is zo'n ingebouwde functie.
De haakjes achter de naam geven aan dat het om een functie gaat. Tussen die haakjes staat vaak informatie die de code in de functie kan gebruiken. Dat is hier ook het geval: aan de functie wordt path
doorgegeven.
In variabele path
is bij const path = location.pathname + "#"; het adres van de pagina (zonder protocol en domeinnaam) opgeslagen met daarachter '#'. Op de site is dat '/afbeelding/slideshow/afbeelding-055.html#'. Eigenlijk staat hier dus:
indexOf("/afbeelding/slideshow/afbeelding-055.html#")
indexOf()
kijkt of het deel tussen de haakjes bij indexOf()
ook in het deel voor indexOf()
zit (dat is hier de inhoud van e.target.href)
.
Als een van de links bij de knoppen rechtsboven is gevolgd, móét de inhoud van path
aanwezig zijn in de href
. En wordt aan de voorwaarde voldaan.
> -1
: indexOf()
laat met behulp van een getal weten, of de inhoud van path
wel of niet in e.target.href
zit. Als dat zo is, wordt de positie van de plaats van het begin van e.target.href
in path
door indexOf()
geretourneerd. Als href
niet in path
zit, wordt -1 geretourneerd door indexOf()
.
)
: het afsluitende haakje, dat hoort bij de (
aan het begin van de tweede voorwaarde.
{
: Het begin van de eventueel bij deze if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze if
uit te voeren code wordt aangegeven met een }
.
(e.target.href.indexOf(path) > -1)
: de hele tweede voorwaarde bij elkaar kijkt, of de inhoud van path
(het adres van de pagina, zonder protocol en domeinnaam en aangevuld met '#') aanwezig is in de inhoud van e.target.href
(de inhoud van het attribuut href
van de link, eventueel aangevuld tot het volledige adres).
Als op de site de link naar de derde <div> is gevolgd, is de inhoud van e.target.href
:
'https://www.css-voorbeelden.nl/
Op de site is de inhoud van path
:
'afbeelding/slideshow/afbeelding-055.html#'.
In dat geval is de inhoud van path
inderdaad aanwezig binnen de href
van de link en wordt aan de tweede voorwaarde voldaan. Dat betekent dat de link een link binnen de pagina is (er zit een '#' in de link) en niet moet worden opgeslagen in de geschiedenis van de browser.
Er wordt dus bij de eerste voorwaarde gekeken of er op een link is geklikt (het element heeft het attribuut href
), en als dat zo is wordt in de tweede voorwaarde gekeken, of het een link naar een anker binnen de pagina is. Het zou ook om een link naar een andere pagina kunnen gaan, en die moet wel gewoon worden opgeslagen in de geschiedenis van de browser.
location.replace(e.target.href);
Deze regel is onderdeel van function verwerkLinks(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– Het aangeraakte of -geklikte element heeft het attribuut href
(het is een link);
– In die href
zit het pad van de pagina + '#' (het is een link binnen de pagina).
location
: als een pagina wordt geopend, wordt allerlei informatie in de browser opgeslagen. Die informatie kan door 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 en op het browservenster. 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' het allerbovenste object is, 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.
In location
zit ook een aantal functies. Deze functies zijn een stukje in de browser ingebakken code, waarmee je iets kunt doen. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
replace()
: dit is zo'n methode: 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. Het is de href van de link waarop functie verwerkLinks()
is aangeroepen. De browser zal nu naar dit adres gaan, zonder dat het adres wordt opgeslagen in de geschiedenis van de browser.
heeftFocus = document.activeElement;
Deze regel is onderdeel van function verwerkLinks(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– Het aangeraakte of -geklikte element heeft het attribuut href
(het is een link);
– In die href
zit het pad van de pagina + '#' (het is een link binnen de pagina).
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
De code binnen functie sluitGrote()
wordt uitgevoerd, als een van de knoppen rechtsboven is gevolgd. Die knoppen zijn links naar een <picture> of een <div> binnen de slideshow. Omdat een link is gevolgd, heeft het doel van doel link nu de focus. Daarom moet dat ook worden aangepast in heeftFocus
.
=
: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
activeElement
: dit is zo'n stukje informatie. Hierin zit, weer in de vorm van een object, het element dat op dit moment de focus heeft.
document.activeElement
bij elkaar betekent: het element dat de focus heeft.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Nu is in heeftFocus
de <picture> of <div> opgeslagen, die de focus heeft. En kan deze, als dat nodig is, weer worden teruggezet.
function focusBijTabEnter(e) {
Ook deze functie is, zoals elke functie, weer een stukje bij elkaar horende code. Maar anders dan de buitenste functie wordt deze niet automatisch uitgevoerd. (Waarom de buitenste functie wel automatisch wordt uitgevoerd, is te vinden bij (function () {.)
De code in deze functie wordt alleen uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen gebeurt, als binnen div#thumbs
een toets wordt ingedrukt. Dit wordt geregeld bij thumbs.addEventListener("keydown", focusBijTabEnter);.
Deze functie regelt drie dingen:
- Als een <picture> met een thumbnail de focus heeft en er wordt op Enter gedrukt, opent de bijbehorende grote afbeelding.
- Als de Tab-toets wordt ingedrukt wordt aan
div#thumbs
de class 'gebruikt-tab' toegevoegd. Hierdoor worden bij gebruik van de Tab-toets alvast de volgende twee grote afbeeldingen gedownload, zodat geen vertraging bij het weergeven optreedt. - Bij gebruik van de Tab-toets krijgt mogelijk een volgende <div> de focus. Als dat zo is, wordt die opgeslagen in variabele
heeftFocus
, die bijhoudt welk element de focus heeft.
function
: het sleutelwoord waarmee het begin van een functie wordt aangegeven.
focusBijTabEnter
: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)
Het is bij namen in JavaScript gebruikelijk om nieuwe woorden met een hoofdletter te beginnen, omdat spaties, koppeltekens, en dergelijke niet gebruikt mogen worden in een naam. In css zou je hier bijvoorbeeld focus-bij-tab-enter in plaats van focusBijTabEnter
kunnen gebruiken.
(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. Hier wordt e
doorgegeven.
In e
zit een zogenaamd object, waarin onder andere informatie zit over welke toets is ingedrukt. Omdat het tussen de haakjes achter function()
staat, is de informatie uit e
door de code binnen de functie te gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
{
: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }
.
if (e.key === "Enter" && e.target.id.startsWith("picture-")) {
Als je in de html een andere id dan id="picture-...(nummer)..." hebt gebruikt bij picture#picture-...
, moet je die id ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function focusBijTabEnter(e) {
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
. Het gaat het hier om twee voorwaarden, waaraan moet worden voldaan. De twee voorwaarden worden gescheiden door &&
, wat in JavaScript betekent én: er moet aan beide voorwaarden worden voldaan. Pas dan wordt de bij deze if
horende code uitgevoerd.
Het begin van de eventueel bij deze if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze if
uit te voeren code wordt aangegeven met een }
. In dit geval is dat de }
bij } else if (e.key === "Tab") { iets verderop. Dat er nog meer dan alleen die }
op die regel staat, maakt niet uit.
Er zijn dus twee voorwaarden, waaraan voldaan moet worden.
e.key === "Enter"
, de eerste voorwaarde:
e
: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function focusBijTabEnter(e) {, de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
key
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.
===
: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.
"Enter"
: dit is in JavaScript de naam van de Enter-toets. De inhoud van key
, oftewel de naam van de ingedrukte toets moet 'Enter' zijn: de Enter-toets moet zijn ingedrukt.
&&
: dit betekent in JavaScript én. Zowel aan de voorwaarde voor als aan de voorwaarde na de &&
moet worden voldaan. Als niet wordt voldaan aan de voorwaarde voor de &&
, wordt niet eens meer gekeken naar de voorwaarde achter de &&
.
e.target.id.startsWith("picture-")
, de tweede voorwaarde:
e
: dit is een object met onder andere informatie, dat bij het aanroepen van function focusBijTabEnter(e) { is meegegeven.
Los van die informatie zitten er ook allerlei functies in het object. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target.id
: dit is zo'n stukje informatie. Hierin zit de id (zonder de '#' aan het begin) van het element, waarop de functie werd aangeroepen.
startsWith("picture-")
: dit is zo'n methode. Er wordt gekeken of wat voor de punt staat (e.target.id
, de id van het element waarop functie focusBijTabEnter()
is aangeroepen, begint met wat tussen de haakjes staat: "picture-"
. Tussen aanhalingstekens, zodat het script weet dat het hier om een stukje letterlijke tekst gaat, een 'string'.
Alleen als het element, waarop functie focusBijTabEnter()
is aangeroepen een <picture> is, begint de id met 'picture-'.
heeftFocus.id.startsWith("picture-")
: bij elkaar betekent de tweede voorwaarde: functie focusBijTabEnter()
moet zijn aangeroepen op een <picture>.
) {
: aan het eind van de regel staat dan nog het afsluitende haakje, dat bij de (
gelijk achter de else if
hoort. Het geeft het eind van de voorwaarden aan. Daarachter staat nog de {
die het begin aangeeft van de code, die uitgevoerd wordt als aan de voorwaarden wordt voldaan.
Om aan de voorwaarden van de if
te voldoen, moet dus de Enter-toets zijn ingedrukt én dat moet op een <picture> zijn gebeurd.
Functie focusBijTabEnter()
is gekoppeld aan div#thumbs
. Aan de eerste voorwaarde wordt dus voldaan, als je waar dan ook binnen div#thumbs
op Enter drukt. De tweede voorwaarde beperkt dat echter, want in target
moet een <picture> zitten. Dat is alleen zo, als een <picture> de focus heeft. Dat is het geval als de thumbnail in de <picture> een wit kadertje heeft, zonder dat de bijbehorende grote afbeelding wordt getoond.
Een <picture> kan alleen in drie situaties de focus hebben:
- Een grote afbeelding is gesloten door het indrukken van Escape of het aanraken of ‑klikken van het scherm ergens buiten de grote afbeelding;
- Een knop (eigenlijk een link) naar een vorige of volgende <picture> met thumbnail is ingedrukt;
- De hulp is geopend en weer gesloten, nadat al door de slideshow is gelopen.
In alle drie de gevallen moet de bij de thumbnail horende grote afbeelding worden getoond, als Enter wordt ingedrukt. Dat is waar deze if
voor zorgt.
e.target.parentElement.focus();
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– Enter is ingedrukt;
– De id van het element waarop de Enter is ingedrukt, begint met 'picture-'.
e
: dit is een object met onder andere informatie, dat bij het aanroepen van function focusBijTabEnter(e) { is meegegeven. Los van die informatie zitten er ook allerlei functies in het object. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).
target
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit het element, wat de functie heeft aangeroepen. Hier is dat de <picture>, waarbij de Enter is ingedrukt.
Feitelijk is target
ook weer een object. Hierdoor zit er allerlei informatie in over de <picture> (en zou je ook van alles met die <picture> kunnen doen, maar dat gebeurt hier niet.)
parentElement
: dit is de directe ouder van het object in target
, van de <picture>. Dat is altijd de <div>, waarbinnen de <picture> zit.
focus()
: zet de focus op dit element, op die <div>. Dit is een van de dertig <div>'s, waarbinnen een thumbnail met bijbehorende knoppen, grote afbeelding, en dergelijke zit. Als een van deze <div>'s de focus heeft, wordt met behulp van css de bijbehorende grote afbeelding getoond.
Deze regel verplaatst de focus van de <picture> naar de <div>, waar de <picture> in zit. Waardoor de grote afbeelding zichtbaar wordt.
heeftFocus = document.activeElement;
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarden is voldaan:
– Enter is ingedrukt;
– De id van het element waarop de Enter is ingedrukt, begint met 'picture-'.
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
In de regel hierboven is de focus van een <picture> naar de <div>, waarin de <picture> zit, verplaatst. Daarom moet de inhoud van heeftFocus
ook worden aangepast, want die klopt niet meer.
=
: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
activeElement
: dit is zo'n stukje informatie. Hierin zit, weer in de vorm van een object, het element dat op dit moment de focus heeft. Dat is nu de <div>, waarbinnen de <picture> zit, waarop functie focusBijTabEnter()
is aangeroepen.
document.activeElement
bij elkaar betekent: het element dat de focus heeft.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Nu is in heeftFocus
de <div> opgeslagen, die de focus heeft. En kan deze, als dat nodig is, weer worden teruggezet.
} else if (e.key === "Tab") {
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze else if
hoort bij een eerdere if.
Als aan de voorwaarden bij de if, waar deze } else if
{ bij hoort, niet is voldaan, volgt hier een herkansing.
} else if
: de }
aan het begin is de afsluitende }
van de code, die wordt uitgevoerd als aan de voorwaarden bij de if
is voldaan. Die heeft dus eigenlijk niets met deze else if
te maken.
else if
letterlijk vertaald betekent 'anders als'. Oftewel: als aan de eerdere bij if
gestelde voorwaarden niet is voldaan, kijk dan of aan deze andere voorwaarden misschien wel wordt voldaan.
Het begin van de eventueel bij deze else if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze else if
uit te voeren code wordt aangegeven met een }
.
(e.key === "Tab")
: de voorwaarden waaraan moet worden voldaan om de code bij deze else if
uit te voeren, staan tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de else if
.
e
: in e
zit een zogenaamd object, waarin allerlei informatie zit. Bij function focusBijTabEnter(e) {, de functie waar deze regel een onderdeel van is, is dit object aan de functie doorgegeven. Hierdoor kan de code in de functie de informatie uit dit object gebruiken.
key
: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit de naam van de ingedrukte toets.
===
: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.
"Tab"
: dit is in JavaScript de naam van de Tab-toets. De inhoud van key
, oftewel de naam van de ingedrukte toets moet 'Tab' zijn: de Tab-toets moet zijn ingedrukt.
De hele regel samengevat: als de Tab-toets is ingedrukt. Alleen dan wordt de code die hierachter tussen {}
staat uitgevoerd.
thumbs.classList.add("gebruikt-tab");
Als je in de css een andere class dan 'gebruikt-tab' hebt gebruikt, moet je die class ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarde is voldaan:
– De Tab-toets is ingedrukt.
thumbs
: in de variabele thumbs
is bij const thumbs = document.querySelector("#thumbs"); div#thumbs
opgeslagen in de vorm van een object. Een object is een bij elkaar horende verzameling van functies en andere code, waarin veel informatie over het opgeslagen element, de kinderen daarvan, enzovoort zit. Elk object heeft ook een aantal ingebakken functies, die gratis en voor niets gebruikt kunnen worden. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
classList
: dit is zo'n stukje in thumbs
opgeslagen informatie. Alle eventueel aanwezige classes bij div#thumbs
zitten hierin.
add("gebruikt-tab")
: dit is zo'n iets hierboven genoemde methode. Met add()
kun je een class toevoegen bij het object, bij div#thumbs
.
De class die wordt toegevoegd zet je tussen de haakjes. En tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst (een 'string') gaat.
Deze class wordt door het script bij function sluitGrote(e) { weer verwijderd, zodra binnen de slideshow een andere toets dan de Tab-toets wordt ingedrukt.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
De hele regel in gewone taal: als binnen div#thumbs
de Tab-toets is ingedrukt, voeg dan class 'gebruikt-tab' toe aan div#thumbs
.
(Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.)
Door het toevoegen van deze class gaat bepaalde css werken, die alleen voor gebruikers van de Tab-toets is bedoeld.
setTimeout(function() {
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij else if gestelde voorwaarde is voldaan:
De Tab-toets is ingedrukt.
Als de Tab-toets wordt ingedrukt binnen div#thumbs
(binnen de slideshow), krijgt de volgende <div> de focus, waardoor de volgende grote afbeelding wordt getoond.
In de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
Als de Tab-toets wordt ingedrukt, krijgt een volgende <div> de focus. Daarom moet de inhoud van heeftFocus
worden bijgewerkt naar die volgende <div>. Dat bijwerken gebeurt hier in het script.
Nu doet zich echter een fors probleem voor. Op het moment dat de Tab-toets wordt ingedrukt, heeft een <div> al de focus. In heeftFocus
is dus op het moment van indrukken die <div> opgeslagen. Na het indrukken van de Tab-toets heeft de volgende <div> de focus, en moet die worden opgeslagen.
Het probleem is dat het script eerst wordt uitgevoerd, en pas daarna wordt de link gevolgd. Dat gaat maar om milliseconden verschil, maar dat maakt niets uit.
Als je dus op het moment van indrukken opslaat, welke <div> de focus heeft, is dat de <div>, waarop de Tab-toets werd ingedrukt. Terwijl je de daarop volgende <div> moet opslaan in heeftFocus
, want die heeft 'n fractie van 'n seconde later de focus. En die <div> moet eventueel de focus weer krijgen, als die teruggezet moet worden. Niet de <div> die zojuist met de Tab-toets is verlaten.
Daarom wordt een vertraging van 50 milliseconden ingebouwd, voordat de <div> met de focus in heeftFocus
wordt opgeslagen. Nu wordt eerst de link gevolgd, en 50 milliseconden later, als de nieuwe <div> de focus heeft, wordt pas het script uitgevoerd. Die voor 'n mens nauwelijks waarneembare vertraging is voor de computer voldoende om verschil te maken.
Het hele stuk dat zorgt voor de vertraging en het opslaan van de nieuwe <div> met focus:
setTimeout(function() {
if (document.activeElement.id.startsWith("div-")) {
heeftFocus = document.activeElement;
} else {
heeftFocus = "";
}
}, 50 );
setTimeout()
: dit is een in JavaScript ingebouwde functie met een speciaal doel: iets pas na een bepaalde vertraging uitvoeren. Deze functie heeft geen eigen geschreven code, daarom ontbreken de {}
, waarbinnen normaal genomen de code voor een functie staat.
Tussen de eerste (
op de eerste regel van bovenstaande code en de )
op de onderste regel staat, wat aan setTimeout()
wordt meegegeven: de 'argumenten'. Er zijn hier twee argumenten, gescheiden door de komma op de onderste regel.
Het eerste argument is verreweg het grootste, het is het hele deel van function()
tot en met de eerste }
op de onderste regel. Dit is de code die, met een kleine vertraging, uitgevoerd gaat worden.
Het tweede argument is een stuk korter: de 50
die achter de komma op de onderste regel staat. Dit is de vertraging in milliseconden: 50 milliseconden. Als je de code even inkort, staat er achter setTimeout
:
setTimeout( ... uit te voeren code ...) , 50)
function()
: anders dan vaak het geval is heeft deze functie geen naam: de functie wordt gelijk uitgevoerd (na die kleine vertraging), omdat de code binnen deze functie vrij eenvoudig is. Het is een zogenaamde 'anonieme' functie.
De haakjes achter function
horen daar nou eenmaal te 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. Dat gebeurt hier niet.
{
: de door function()
uit te voeren code staat achter de {
. De bijbehorende }
staat gelijk voor de komma op de onderste regel.
if (document.activeElement.id.startsWith("div-")) {
Als je in de html een andere id dan id="div-...(nummer)..." hebt gebruikt bij div#div-...
, moet je die id ook in bovenstaande regel aanpassen.
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel is onderdeel van de anonieme functie die begint bij setTimeout(function() {
if
: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if
.
Het begin van de eventueel bij deze if
uit te voeren code wordt aangegeven met de {
aan het eind van de regel. Het eind van de eventueel bij deze if
uit te voeren code wordt aangegeven met een }
. In dit geval is dat de }
bij de iets verderop staande } else {.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
activeElement
: dit is zo'n stukje informatie. Hierin zit, weer in de vorm van een object, het element dat op dit moment de focus heeft. Ook dit object bevat weer allerlei informatie.
id
: hierin zit de id van het object in activeElement
.
document.activeElement.id
bij elkaar: de id van het element dat de focus heeft.
startsWith("div-")
: dit is zo'n methode. Er wordt gekeken of wat voor de punt staat (de id van het in element dat de focus heeft) begint met wat tussen de haakjes staat: "div-"
. Tussen aanhalingstekens, zodat het script weet dat het hier om een stukje letterlijke tekst gaat, een 'string'.
{
: de code die wordt uitgevoerd als aan de hierboven beschreven voorwaarde wordt voldaan, staat tussen {
en }
. Daardoor weet het script, wat het begin en het eind van de bij de if horende code is.
De hele regel if (document.activeElement.id.startsWith("div-")) {
: als de id van het element met de focus begint met 'div-'. Als dat zo is, moet het een <div> met daarin een thumbnail, knoppen, en dergelijke zijn.
Deze controle is nodig, omdat deze functie focusBijTabEnter()
overal binnen div#thumbs
wordt aangeroepen, als een toets wordt ingedrukt. Binnen div#thumbs
zit ook div#uitleg
met de hulp, en ook die kan door gebruik van de Tab-toets de focus krijgen. Nu worden alleen <div>'s met een thumbnail en dergelijke in heeftFocus
opgeslagen.
heeftFocus = document.activeElement;
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel is onderdeel van de anonieme functie die begint bij setTimeout(function() {
Deze regel wordt alleen uitgevoerd, als aan deze eerder bij if gestelde voorwaarde is voldaan:
– De id van het element dat de focus heeft begint met 'div-'.
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
In de regel hierboven is de focus van de <div> met focus naar de daarop volgende <div> verplaatst. Daarom moet de inhoud van heeftFocus
ook worden aangepast, want die klopt niet meer.
=
: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.
document
: dit is een zogenaamd 'object'. Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.
JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document
bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.
activeElement
: dit is zo'n stukje informatie. Hierin zit, weer in de vorm van een object, het element dat op dit moment de focus heeft.
document.activeElement
bij elkaar betekent: het element dat de focus heeft.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
Nu is in heeftFocus
de <div> opgeslagen, die nu de focus heeft. En kan deze, als dat nodig is, weer worden teruggezet.
} else {
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel is onderdeel van de anonieme functie die begint bij setTimeout(function() {
Deze else
hoort bij een eerdere if. Hij geldt alleen, als aan onderstaande voorwaarde is voldaan:
= – De id van het element dat de focus heeft begint niet met 'div-'.
}
: dit is de afsluitende accolade van de code die bij de hierboven genoemde if
hoort. Die heeft dus eigenlijk niets met deze else te maken.
else
: als de voorwaarde bij die if
niet waar is, voer dan de code tussen de {}
bij deze else
uit. Er staat hier verder geen voorwaarde of zo: deze code wordt gewoon altijd uitgevoerd, als de voorwaarde bij de if
niet waar is.
{
: de code die wordt uitgevoerd, als aan deze else
wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze else
hoort. Aan het eind van de code bij deze else
staat de afsluitende }
.
De hele regel in gewone taal: voer de code tussen de {}
bij de else
uit, als de id van het element dat de focus heeft niet begint met 'div-'.
heeftFocus = "";
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel is onderdeel van de anonieme functie die begint bij setTimeout(function() {
Deze regel hoort bij de iets hierboven staande else. Hij wordt alleen uitgevoerd, als aan onderstaande voorwaarde is voldaan:
– De id van het element dat de focus heeft begint niet met 'div-'.
heeftFocus
: in de bij let heeftFocus; aangemaakte variabele heeftFocus
wordt bijgehouden, welk element de focus heeft. Die focus kan dan worden teruggezet, als dat zo uitkomt.
=
: 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.
""
: er wordt helemaal niets opgeslagen in heeftFocus
. Deze regel wordt alleen uitgevoerd, als de id van het element met de focus niet begint met 'div-'. In dat geval is het kennelijk geen <div> met daarin een thumbnail, knoppen, enzovoort en moet de focus niet worden opgeslagen.
Als er alleen ""
in heeftFocus
zit, is de variabele leeg. En als de variabele leeg is, is deze 'false', of eigenlijk 'falsy'. (Echt 'false' is net iets anders.) Bij elke if
waar als voorwaarde wordt gesteld dat heeftFocus
niet leeg is, wordt nu niet meer aan die voorwaarde voldaan, en wordt de code tussen de {}
achter die if
nu niet uitgevoerd.
;
: de puntkomma geeft het einde van de regel aan. In gewone tekst zou je hier een punt gebruiken.
}, 50 );
Deze regel is onderdeel van function focusBijTabEnter(e) {
Deze regel is onderdeel van de anonieme functie die begint bij setTimeout(function() {
Dit is de vertraging in milliseconden bij setTimeout()
. Deze regel wordt besproken bij setTimeout(function() {.