Skip links en inhoudsopgave

Uitleg slideshow met thumbnails en afbeel­dingen van verschil­lende grootte. Gebruikt zo weinig mogelijk bandbreedte.

Laatst aangepast: .

Afbeelding 1: slideshow in vensters van verschillende grootte

Korte omschrijving

Afbeeldingen worden pas gedownload, als een thumbnail wordt aangeraakt of -geklikt. Je kunt door de thumbnails navigeren door vegen, klikken, aanraken, slepen of het toetsenbord, of rechtstreeks door de afbeeldingen navigeren.

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 regelmatig bijgewerkt. Het is daarom niet toegestaan deze uitleg op welke manier dan ook te verspreiden, zonder daarbij duidelijk te vermelden dat de uitleg afkomstig is van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie is te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.

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

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

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

Opmerkingen

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

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

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

Achterliggend idee

Dit voorbeeld is ietwat ingewikkelder geworden, dan waar het in het begin naar uitzag. Om het wat overzichtelijk te houden is dit hoofdstukje daarom opgesplitst in een aantal onderdelen.

Waarom het volledig herschreven moest worden

In eerdere versies werd voor de grote afbeeldingen geen gebruik gemaakt van gewone afbeeldingen (<img>), maar van in de css opgegeven background-images. Een <img> wordt altijd gelijk bij het openen van de pagina gedownload, zelfs als deze verborgen is met iets als display: none;. Een background-image daarentegen wordt pas gedownload, als deze daadwerkelijk wordt weergegeven.

Voor dit voorbeeld maakt dit verschil niet zoveel uit, want het gaat hier om slechts veertien afbeeldingen van een niet al te hoge kwaliteit. Maar als je veel afbeeldingen hebt en/of afbeeldingen van hoge kwaliteit, kan dat tientallen megabytes schelen.

Helaas werkt dit niet meer zo.

Google Chrome, Opera, binnenkort Edge, en tal van andere browsers maken gebruik van de weergave-machine Blink. Oftewel: zo'n beetje alle grote browsers, behalve Firefox en Safari. In 2018 is het downloaden van background-images ingrijpend gewijzigd door Blink (en ook door het huidige Edge, maar die gebruikt binnenkort dus ook Blink).

Sinds 2018 worden ook background-images bij het openen van de pagina allemaal in één keer gedownload. De reden daarvoor is snelheid: de afbeelding hoeft dan niet meer te worden gedownload, gedecodeerd, en dergelijke. Maar dat downloaden kan er dus voor de bandbreedte nogal inhakken.

Ongeveer gelijktijdig met de verandering bij Blink kwam de Intersection Observer API beschikbaar. API staat voor Application Programming Interface. In dit geval een manier om met behulp van JavaScript afbeeldingen pas te tonen, als deze zichtbaar zijn op het scherm. Helaas is deze API niet bruikbaar voor dit voorbeeld, omdat de API werkt door naar scrollen te kijken: een afbeelding of iets anders wordt pas gedownload, als deze (bijna) binnen het browservenster wordt gescrold. In dit voorbeeld worden de grote afbeeldingen echter niet gescrold.

Hetzelfde geldt voor het splinternieuwe attribuut bij <img> loading="lazy": ook dit kijkt naar scrollen en downloadt een afbeelding pas, als deze (bijna) binnen het browservenster komt te staan. (Dit attribuut werkt vooralsnog alleen in een deel van de op Blink gebaseerde browsers, maar dat zijn precies de browsers, waarin dit probleem speelt.) Maar ook dit valt dus af, want de grote afbeeldingen worden niet gescrold.

Daarom moest dit voorbeeld ingrijpend worden herschreven. Ook het uiterlijk is veranderd en de toegankelijkheid is verbeterd, maar dat zijn relatief kleine wijzigingen.

Hoewel Blink een wel erg drastische manier heeft gekozen om afbeeldingen sneller weer te geven, is dat natuurlijk niet helemaal onbelangrijk. Afhankelijk van de manier waarop de slideshow door de bezoeker wordt aangestuurd, wordt daarom ook in dit voorbeeld in sommige situaties de gelijk voor en/of na de getoonde grote afbeelding zittende afbeelding alvast gedownload. Maar dat beperkt zich dus tot één of twee afbeeldingen, en niet alles in één keer.

Kleine browservensters

Bij openen van de pagina wordt een serie thumbnails op het scherm gezet. In kleinere browservensters is dit feitelijk alles wat er gebeurt. Afhankelijk van de oriëntatie van het venster staan de thumbnails naast of boven elkaar. Ze vullen het volledige venster en kunnen door middel van vegen worden verplaatst. In principe zou scrollen en dergelijke ook werken, maar dit soort kleine venstertjes vind je alleen op mobieltjes.

Eigenlijk is de naam 'thumbnail' hier wat misleidend, want afhankelijk van de grootte van het browservenster en/of de pixeldichtheid van het scherm wordt een grotere of kleine afbeelding gedownload, die – afhankelijk van de grootte van het venster – nog tamelijk groot kan worden.

Grotere browservensters

Ook in grotere browservensters worden thumbnails getoond bij het openen van de pagina, maar deze vullen niet het volledige venster. Afhankelijk van de oriëntatie van het venster staan de thumbnails aan de boven- of linkerkant van het venster.

Daaronder of daarnaast blijft ruimte over, waarbinnen een grotere versie van de thumbnail kan worden getoond.

De grote afbeeldingen worden zo groot mogelijk weergegeven, waarbij rekening wordt gehouden met de grootte van het browservenster en de pixeldichtheid van het scherm.

Als een thumbnail wordt aangeraakt of -geklikt, wordt de bijbehorende grote afbeelding getoond. Dat wil zeggen dat je door de thumbnails kunt bewegen, zonder dat een grote afbeelding wordt getoond, zolang je de thumbnail maar niet aanraakt of -klikt. De grote afbeelding wordt dan niet (onnodig) gedownload.

Je kunt op verschillende manieren door de thumbnails bewegen:

* Op een touchscreen kun je de thumbnails slepen. Weliswaar raak je dan 'n thumbnail aan, maar dat geldt niet als een 'echte' aanraking. Pas als je een thumbnail aanraakt zonder deze te slepen, wordt de bijbehorende afbeelding getoond.

* Met het toetsenbord: al bij openen van de pagina kunnen de thumbnails met de pijltjestoetsen worden gescrold. Afhankelijk van de richting van de rij thumbnails kan dat met de links met de liggende of de staande pijltjes.

* Met behulp van de scrollbalk, als die er is.

* Met het muiswieltje. Afhankelijk van de richting van de thumbnails moet daarbij eventueel ook Control worden ingedrukt.

* Door aanraken of -klikken van de links met de pijltjes op de thumbnail.

In al deze gevallen wordt geen grote afbeelding getoond, en wordt deze dus ook niet gedownload.

Als een grote afbeelding eenmaal is gedownload, omdat een thumb is aangeraakt of -geklikt, wordt deze grote afbeelding wel gewoon vertoond, als de links met de pijltjes boven de thumbnails worden aangeraakt of -geklikt. Er is dan immers geen reden meer om de grote afbeelding niet te tonen. (Er is ook niets op tegen om de grote afbeelding te tonen als de thumbnails worden gesleept of gescrold of zo, alleen is dan niet duidelijk welke grote afbeelding moet worden getoond.)

De pijltjes boven de thumbnails zijn gewone links naar de vorige of volgende div#wrap, waarin de vorige of volgende thumbnail, grote afbeelding, en dergelijke zitten. Normaal genomen staan deze boven het browservenster. Als met behulp van aanraken of -klikken van een thumbnail is welke thumbnail actief is (die thumbnail heeft een outline), worden de bijbehorende links naar de vorige en volgende div#wrap binnen het venster gezet.

Als de Tab-toets (of Shift+Tab) wordt gebruikt, wordt elke keer dat een toets wordt ingedrukt gelijk de volgende (of vorige) grote afbeelding getoond.

Als een grote afbeelding wordt getoond, staan aan weerszijden daarvan ook pijltjes. Ook dit zijn weer gewone links naar de vorige of volgende div#wrap, die normaal genomen boven het browservenster staan. Alleen wordt nu gelijk de vorige of volgende grote afbeelding getoond. Op deze manier kun je dus ook rechtstreeks door de grote afbeeldingen wandelen, zonder gebruik te moeten maken van de thumbnails.

Blink heeft het downloaden van background-images veranderd om de weergave te versnellen: alles staat al klaar om getoond te worden. Alleen is dat nogal met de botte bijl gedaan: alles of niets. (In dit geval in ieder geval, want als er gescrold wordt, gebeurt het downloaden wel genuanceerd.)

Maar snelheid is natuurlijk niet helemaal onbelangrijk. Daarom wordt bij deze manier van bladeren door de grote afbeeldingen ook alvast de vorige en volgende grote afbeelding gedownload, zodat die al klaar staan om getoond te worden.

Verder zijn er nog wat relatief simpele dingen, zoals pijltjes die meedraaien met de richting, waarin wordt bewogen, de afbeeldingen worden gecentreerd, en dergelijke.

Zo weinig mogelijk downloaden

Om onnodig downloaden van grote afbeeldingen te voorkomen was background-image dus niet meer bruikbaar, omdat de meeste browsers dan toch alle afbeeldingen downloaden, of ze die nu gebruiken of niet. Ook methoden als display: none;, afbeeldingen een grootte van 0 x 0 px geven, de gekste probeersels, werkten niet. Alleen als de grote afbeeldingen binnen <noscript> werden gezet, werden ze niet gedownload.

Dat bleek de oplossing:

<noscript> <img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> </noscript>

In de <img> zit de grote afbeelding (of eigenlijk een aantal afbeeldingen, waaruit de browser kan kiezen. Meer hierover is te vinden bij <img src="039-pics/alpaca-300w.jpg" ...)

Als JavaScript uitstaat, wordt gewoon de inhoud van <noscript> gebruikt. In dat geval worden wel alle grote afbeeldingen in één keer gedownload bij het openen van de pagina, want wat de browser betreft staat er dan een gewone <img> in de html.

Als JavaScript aanstaat, wordt de inhoud van <noscript> genegeerd. En ook niet gedownload, en daar gaat het hier om. Als de afbeelding moet worden weergegeven, wordt met behulp van JavaScript de tag <noscript> en de bijbehorende </noscript> verwijderd en staat er dus plotsklaps een gewone <img>.

Althans: in een ideale wereld zou het zo moeten gaan. En inderdaad verwijdert een aantal browsers netjes met behulp van JavaScript <noscript> en </noscript>, zodat een gewone <img> overblijft. Maar Edge (behalve op Android), Internet Explorer en Safari veranderen de inhoud van de <noscript>-tag niet in html, maar in gewone tekst. En omdat alle browsers op iOS en iPadOS verplicht de weergave-machine van Safari moeten gebruiken, geldt dit ook gelijk voor alle browsers op iOS en iPadOS.

Wat uiteindelijk in álle browsers blijkt te werken: maak commentaar van de <img>:

<!-- <img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> -->

Commentaar wordt door alle browsers volledig genegeerd en dus ook niet gedownload. Als de grote afbeelding getoond moet worden, verwijderd het JavaScript de <!-- en --> en heb je 'n gewone <img>. Ook op iOS en iPadOS werkt dit. Op het moment dat het commentaar in gewone html wordt veranderd, wordt de grote afbeelding direct alsnog gedownload.

Nadeel is dat je in de html elke grote afbeelding twee keer neer moet zetten: één keer binnen een <noscript>-tag, en één keer als commentaar. Maar veel extra werk is dit niet, omdat je het simpel kunt kopiëren. De inhoud van de <img>-tag is in beide gevallen exact hetzelfde.

Het script had ook de inhoud van de <noscript> kunnen inlezen en hier een <img> met de juiste attributen van kunnen maken. Vervolgens had die <img> weer ingevoegd kunnen worden in de DOM (simpel gezegd: de lijst met elementen die de browser gebruikt om de pagina weer te kunnen geven). Maar dat is veel omslachtiger dan de methode die nu is gebruikt.

Schermlezers en dergelijke

Afbeeldingen tonen in een schermlezer lijkt misschien wat vreemd, maar niet iedereen die een schermlezer gebruikt, is volledig blind.

Het is extreem ingewikkeld om verschijnende en verdwijnende afbeeldingen overzichtelijk te tonen in een schermlezer. Dat komt mede doordat de hiervoor benodigde WAI-ARIA-codes extreem ingewikkeld zijn. Bovendien werkt dan ook nog eens niet alles helemaal hetzelfde in verschillende schermlezers. Daarom is voor een andere oplossing gekozen.

Helemaal aan het begin van de pagina staat een korte tekst. Omdat die buiten het browservenster staat, verstoort die de lay-out niet. Maar een schermlezer leest de tekst gewoon voor. Als daarvoor wordt gekozen, worden de grote afbeeldingen allemaal onder elkaar getoond. Omdat het gewone <img>'s zijn, kunnen ze op de voor die schermlezer gebruikelijke manier worden bezocht, vergroot, enzovoort. Ook de sneltoetsen voor die schermlezer werken dan op de gebruikelijke manier.

Tab-toets

Door het gebruik van het attribuut tabindex kunnen de grote afbeeldingen ook worden geopend door de Tab-toets of Shift+Tab in te drukken.

JavaScript

In principe werkt de slideshow zonder JavaScript, maar een aantal zaken werkt dan wat ongemakkelijk. Mensen die JavaScript uit hebben staan, zullen trouwens aan dit soort dingen gewend zijn. Het merendeel van de slideshows werkt zelfs helemaal niet zonder JavaScript.

Enkele voorbeelden van onhandigheden die worden verholpen met behulp van JavaScript:

* Elke gevolgde link naar een vorig of volgend anker wordt in de geschiedenis van de browser vastgelegd;

* Bij openen van de pagina staan er geen links naar de vorige of volgende thumbnail (eigenlijk links naar de vorige of volgende div.wrap);

* Op iOS en iPadOS kan de grote afbeelding niet worden gesloten door een aanraking naast de afbeelding;

* Als de uitleg wordt geopend en weer gesloten, is niet te zien, welke thumbnail gemarkeerd. Daarmee is ook onduidelijk, bij welke thumbnail de links naar vorige en volgende thumbnail horen (eigenlijk links naar de vorige of volgende div.wrap).

Allemaal geen onoverkomelijke dingen, maar soms wat onhandig.

Een volledig overzicht van wat het script allemaal doet, is te vinden bij JavaScript.

Ruziënde browsers

Los van al het bovenstaande bleken browsers op een aantal punten volstrekt verschillend te werken en soms elkaar zelfs actief tegen te werken. Er waren nogal wat aanpassingen nodig om alles overal werkend te krijgen.

iOS ouder dan versie 13 heeft grote problemen, als niet de hele pagina, maar een alleen <div> of zoiets moet worden gescrold. En dat is precies wat er gebeurt, als je de over de thumbnails veegt. Feitelijk zijn die problemen zo groot, dat scrollen onbruikbaar wordt op iOS ouder dan versie 13. Een dansende jichtige olifant met spit en ingegroeide teennagels is een toonbeeld van soepelheid vergeleken met het scrollen van een element op iOS ouder dan versie 13.

Dat scrollen is op te lossen met behulp van de eigenschap -webkit-overflow-scrolling. Alleen heeft die eigenschap de wildste bijwerkingen. Op internet zijn honderden oplossingen hiervoor te vinden. Als er zoveel oplossingen zijn, dan werkt er meestal geen enkele echt altijd goed. Dat was ook hier het geval.

Op tablets was het in portretstand werkend te krijgen, maar in landschapsstand bleven de grote afbeeldingen onzichtbaar. Daarom was voor iOS ouder dan versie 13 aparte css nodig. Dat kon met behulp van @supports (-webkit-overflow-scrolling: touch). De css die binnen deze @supports-regel staat, werkt alleen op iOS, omdat alleen dat systeem -webkit-overflow-scrolling ondersteunt.

(Een uitgebreider verhaal over -webkit-overflow-scrolling en het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

Lastiger was het om Firefox in het gareel te krijgen. De pijltjes naar vorige en volgende thumbnail zijn gewone links naar een anker. Alle browsers volgen netjes de link naar zo'n anker, maar Firefox geeft – als enige browser – geen focus aan zo'n anker. Waardoor de bij de pseudo-class :focus opgegeven css in Firefox niet werkt.

Bij Firefox werkt echter wel de pseudo-class :target. Opgelost? Nou, nee.

Bij gebruik van de Tab-toets of als een thumbnail wordt aangeraakt of -geklikt, krijgt ook in Firefox de betreffende div.wrap focus. Maar het kan prima dat een andere div.wrap nog onder :target valt. Bovendien werkt in andere browsers :target ook, dus ook daar kan gelijktijdig css voor :target én voor :focus werken.

Dit valt op te lossen door gebruik van de pseudo-class :focus-within: als een element of een nakomeling van dat element de focus heeft. In dat geval zou je de css voor :target weer kunnen uitschakelen. Dat werkt perfect, op één klein ietsigheidje na: Internet Explorer, Edge op Windows en browsers op iOS voor versie 10.3 ondersteunen deze tamelijk nieuwe pseudo-class niet.

Je kunt :focus-within simuleren met behulp van een zogenaamde polyfill: een stukje JavaScript dat iets laat werken in oudere browsers. Maar dat zou hier extreem ingewikkeld worden, omdat :focus-within op nogal wat plaatsen gebruikt zou moeten worden. En overal zou het JavaScript dan css moeten invoegen of verwijderen.

Uiteindelijk is Firefox getemd met behulp van @supports (-moz-appearance: none). appearance is weliswaar een eigenschap die door alle vrijwel alle browsers wordt ondersteund, maar het voorvoegsel -moz- wordt alleen door Firefox herkend. De css die binnen deze @supports-regel staat, werkt daardoor alleen in Firefox.

Hiernaast waren er her en der nog wat aanpassingen nodig, maar die waren minder ingrijpend. In de uitleg van de code worden deze allemaal beschreven.

Voorwaarden van het JavaScript waaraan html en css moeten voldoen

Voor dit voorbeeld wordt gebruik gemaakt van JavaScript. In principe werkt de slideshow ook zonder JavaScript, maar met JavaScript is het iets gebruiksvriendelijker te maken. Een volledig overzicht van wat JavaScript doet, is te vinden bij JavaScript.

Om dit script goed te laten werken, moet de html aan een aantal voorwaarden voldoen. Dit lijkt mogelijk een wat lange lijst, maar het gaat om tamelijk simpele voorwaarden, die niet echt problemen zullen opleveren. Als je de html erbij pakt, zul je zien dat het niet al te ingewikkeld is.

Structuur, id's en classes

  • De hele pagina moet in een <main> zitten. Deze <main> moet het volledige browservenster vullen, zodat bij aanraken of -klikken van een lege plek op het venster de grote afbeelding kan sluiten. Het script gebruikt die element om ernaar te zoeken.

    Als je een ander element dan <main> wilt gebruiken, moet je dit ook in het script aanpassen.

  • De hele slideshow moet binnen één overkoepelend element zitten. In het voorbeeld is dit <div id="wrapper">.

    Als je een andere id dan 'wrapper' wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

  • Elke thumbnail met bijbehorende links naar vorige en volgende thumbnail en grote afbeelding moet binnen een element class="wrap firefox" (een element met twee classes) zitten. In het voorbeeld is dit veertien keer <div class="wrap firefox">. Het script gebruikt de class 'wrap' om deze elementen op te zoeken. De class 'firefox' wordt gebruikt om in Firefox het tonen en verbergen van grote afbeeldingen en links naar vorige en vorige thumbnail en afbeelding te regelen. Naast deze twee classes kun je bij deze elementen gewoon andere classes gebruiken.

    Als je een andere class dan 'wrap' en/of 'firefox' wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

  • De hierboven genoemde elementen moeten allemaal een direct kind van het overkoepelende element zijn (in het voorbeeld is dat <div id="wrapper">). Tussen de div.wrap's mogen geen andere elementen zitten.

    Als je dit wilt wijzigen, moet het script volledig worden herschreven.

    (Het script maakt op nogal wat plaatsen gebruik van eigenschappen als parentElement, previousElementSibling en nextElementSibling. Vandaar dat je het script volledig moet herschrijven, als je aan dit deel van de html-structuur gaat sleutelen.)

    <div id="wrapper"> (...) <div class="wrap firefox"></div> <div class="wrap firefox"></div> (...) </div>

    In de code hierboven zijn beide div.wrap.firefox's een direct kind van div#wrapper: er zit geen enkel ander element tussen de div.wrap.firefox's en div#wrapper.

    <div id="wrapper"> (...) <div class="wrap firefox"></div> <div> <div class="wrap firefox"></div> </div> <div class="wrap firefox"></div> (...) </div>

    In de code hierboven is de middelste div.wrap.firefox geen direct kind van div#wrapper, omdat er een andere <div> tussen zit. Deze code levert problemen met het script op.

    <div id="wrapper"> (...) <div class="wrap firefox"><div> <span></span> <div class="wrap firefox"><div> (...) </div>

    In de code hierboven zit tussen de div.wrap.firefox's een <span>. Dit levert problemen op met het script, omdat het script soms naar het vorige of volgende element met class="wrap" kijkt.

  • Het eerste element met class="wrap" – het eerste element, waarin een thumbnail met bijbehorende links, grote afbeelding, en dergelijke zit – moet een id="wrap-1" hebben. In het voorbeeld is dit <div id="wrap-1" class="wrap firefox">.

    Als je een andere id wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

  • Het laatste element in het overkoepelende element (dat is in het voorbeeld div#wrapper) moet een element met class="wrap" zijn: een element met daarin een thumbnail met bijbehorende links grote afbeelding, en dergelijke.

    Als je dit wilt wijzigen, moet het script volledig worden herschreven.

    (Het script maakt op nogal wat plaatsen gebruik van eigenschappen als parentElement, previousElementSibling en nextElementSibling. Vandaar dat je het script volledig moet herschrijven, als je aan dit deel van de html-structuur gaat sleutelen.)

  • Elke grote afbeelding staat twee keer in de html. De eerste keer staat deze binnen een <noscript>-tag, de tweede keer is deze met behulp van <!-- en --> veranderd in commentaar:

    <!-- <img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> -->

    Deze tweede afbeelding moet binnen een <span> met class="foto" staan. Het script gaat op zoek naar de inhoud van die <span class="foto">. Als de <img> niet binnen die <span> staat, wordt de <img> niet gevonden en werkt het script dus niet.

    Binnen deze <span class="foto"> mag verder geen enkel commentaar staan. Het script verwijdert elke <!-- en --> binnen de <span class="foto">. Als je als commentaar bijvoorbeeld ook zou hebben opgenomen 'Mijn baas is een rotzak', wordt dat plotsklaps ook zichtbaar.

    Als je dit wilt wijzigen, moet het script volledig worden herschreven.

    (Het script maakt op nogal wat plaatsen gebruik van querySelector("span.foto"). Vandaar dat je het script volledig moet herschrijven, als je aan dit deel van de html-structuur gaat sleutelen.)

  • Alle thumbnails moeten een direct kind van het element met de thumbnail en de bijbehorende afbeelding, links, en dergelijke zijn (in het voorbeeld is dat veertien keer <div class="wrap firefox">. In het voorbeeld is de eerste <img> in elke div.wrap.firefox de thumbnail.

    <div class="wrap firefox"> <img src="039-pics/alpaca-300w.jpg"> </div>

    In de code hierboven is de <img> een direct kind van div.wrap.firefox, want er zit geen enkel ander element tussen div.wrap.firefox en de <img>.

    <div class="wrap firefox"> <span> <img src="039-pics/alpaca-300w.jpg"> <span> </div>

    In de code hierboven is de <img> geen direct kind van div.wrap.firefox, omdat er een <span> tussen zit. Deze code levert problemen met het script op.

    Als je dit wilt wijzigen, moet het script fors worden aangepast.

    (Het script maakt op een aantal plaatsen gebruik de eigenschap parentElement en querySelectorAll(".wrap > img");. Vandaar dat je het script behoorlijk moet aanpassen, als je aan dit deel van de html-structuur gaat sleutelen.)

  • Bij elke thumbnail met bijbehorende afbeelding zitten vier links. De eerste twee links zijn om naar een vorige of volgende thumbnail te gaan. De laatste twee links zijn om rechtstreeks een vorige of volgende grote afbeelding te tonen, buiten de thumbnails om.

    De eerste twee links moeten een class="vorige-t" en een class="volgende-t" hebben. ('vorige-t' bij de link naar de vorige thumbnail, 'volgende-t' bij de link naar de volgende thumbnail.)

    De laatste twee links moeten een class="vorige-a" en een class="volgende-a" hebben. ('vorige-a' bij de link naar de vorige afbeelding, 'volgende-a' bij de link naar de volgende grote afbeelding.)

    Als je andere classes wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen. De links mogen naast deze verplichte class gewoon andere classes of id's hebben.

    (Eigenlijk zijn dit geen links naar een volgende of vorige thumbnail of grote afbeelding, maar links naar de vorige of volgende div.wrap met daarin een thumbnail en grote afbeelding.)

    Alle links moeten een direct kind van het element met de thumbnail en de bijbehorende afbeelding, links, en dergelijke zijn (in het voorbeeld is dat veertien keer <div class="wrap firefox">).

    <div class="wrap firefox"> <a class="vorige-a"></a> <a class="volgende-a"></a> </div>

    In de code hierboven zijn beide <a>'s een direct kind van div.wrap.firefox: er zit geen enkel ander element tussen div.wrap.firefox en de <a>'s.

    <div class="wrap firefox"> <span> <a class="vorige-a"></a> </span> <span> <a class="volgende-a"></a> </span> </div>

    In de code hierboven zijn de beide <a>'s geen direct kind van div.wrap.firefox, omdat er een <span> tussen zit. Deze code levert problemen met het script op.

    Als je dit wilt wijzigen, moet het script fors worden aangepast.

    (Het script maakt op een aantal plaatsen gebruik de eigenschap lastElementChild. Vandaar dat je het script behoorlijk moet aanpassen, als je aan dit deel van de html-structuur gaat sleutelen.)

  • Het script regelt allerlei zaken door het toevoegen en verwijderen van de classes 'xverbergx' en 'xactiefx'. Ietwat eigenaardige namen, maar dat maakt de kans dat de namen al in gebruik zijn uitermate klein.

    Als je om een of andere reden een andere naam voor één of beide classes wilt gebruiken, moet je die ook in het script en in de css wijzigen. Wijzigen in het script gaat heel simpel: zoek naar xverbergx en/of xactiefx en vervang deze door de nieuwe naam.)

    Als je wilt kijken, of deze classes inderdaad door het script worden toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

  • Om te zorgen dat gelijk bij openen van de pagina de thumbnails gescrold kunnen worden met de pijltjestoetsen, is een <input id="checkbox-voor-focus"> aanwezig. Het script gebruikt deze id om de <input> op te zoeken.

    Als je een andere id wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

    Als je om een of andere reden deze <input> helemaal niet wilt gebruiken, moet die ook uit het script worden verwijderd. Als het script gaat zoeken naar een ontbrekend element, levert dat een fout op. Hierdoor werkt niet alleen het stukje voor deze <input> niet meer, maar mogelijk weigert het hele script dienst.

    Verwijderen uit het script is heel simpel:

    Haal elke regel die begint met checkboxVoorFocus weg.

    Verander de regel

    if (e.target.id === "checkbox-voor-focus" || e.target.id === "wrap-1") {

    in

    if (e.target.id === "wrap-1") {
  • Om de uitleg te kunnen tonen, is boven de pagina een <input id="uitleg"> aanwezig. Het script gebruikt deze id om de <input> op te zoeken.

    Als je een andere id wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

    Als je om een of andere reden deze <input> helemaal niet wilt gebruiken, moet die ook uit het script worden verwijderd. Als het script gaat zoeken naar een ontbrekend element, levert dat een fout op. Hierdoor werkt niet alleen het stukje voor deze <input> niet meer, maar mogelijk weiger het hele script dienst.

    Verwijderen uit het script is heel simpel. Verwijder de regel

    document.getElementById("uitleg").addEventListener("change", sluitHulp);

    Omdat function sluitHulp(e) nu niet meer wordt gebruikt, kan ook deze worden verwijderd. Dat is het hele stuk code beginnend met de regel function sluitHulp(e){ tot aan function bewaarId(e). 'Tot aan', dus de regel met function bewaarId(e) { blijft staan.

  • Om de slideshow aan te kunnen passen voor schermlezers is iets bovenin de pagina een <input id="schermlezer"> aanwezig. Het script gebruikt deze id om de <input> op te zoeken.

    Als je een andere id wilt gebruiken in de html, moet je die ook in het script en in de css wijzigen.

    Als je om een of andere reden deze <input> helemaal niet wilt gebruiken, bijvoorbeeld omdat je toch volledig met WAI-ARIA-codes wilt werken, moet die ook uit het script worden verwijderd. Als het script gaat zoeken naar een ontbrekend element, levert dat een fout op. Hierdoor werkt niet alleen het stukje voor deze <input> niet meer, maar mogelijk weiger het hele script dienst.

    Verwijderen uit het script is heel simpel. Verwijder de regel

    document.getElementById("schermlezer").addEventListener("change", voorSchermlezers);

    Omdat function voorSchermlezers(e) nu niet meer wordt gebruikt, kan ook deze worden verwijderd. Dat is het hele stuk code beginnend met de regel function voorSchermlezers(e){ tot aan function sluitHulp(e). 'Tot aan', dus de regel met function sluitHulp(e) { blijft staan.

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

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

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

De belangrijkste browsers hebben elk een eigen voorvoegsel:

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

Op webkit gebaseerde browsers, zoals 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.)

Internet Explorer: -ms-, naar de maker: Microsoft.

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 Firefox herkent -moz-appearance. Zodra Firefox appearance gaat herkennen, zal dit moz-appearance overrulen, omdat het er later in staat. Dat ze er beide in staan, is dus geen enkel probleem.

In dit voorbeeld worden appearance, user-select en -webkit-overflow-scrolling gebruikt.

appearance

Op dit moment moet je nog het volgende schrijven:

{-moz-appearance: ...; -webkit-appearance: ...; appearance: ...;}

In de toekomst kun je volstaan met:

{appearance: ...;}

user-select

Op dit moment moet je nog het volgende schrijven:

{-moz-user-select: ...; -ms-user-select: ...; -webkit-user-select: ...;, user-select: ...;}

In de toekomst kun je volstaan met:

{user-select: ...;}

-webkit-overflow-scrolling

Deze eigenschap wordt alleen maar gebruikt om een probleem op iOS ouder dan versie 13 op te lossen. Alleen browsers op iOS ondersteunen deze eigenschap. Alle andere browsers en systemen negeren hem. Dat geldt ook voor browsers op iPadOS. Daarom hoeft alleen maar op browsers gelet te worden die op iOS draaien. In dit geval is daarom het volgende voldoende:

{-webkit-overflow-scrolling: ...;}

(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 een probleem op iOS gaat. Andere browsers hebben deze css helemaal niet nodig.)

(Een uitgebreider verhaal over -webkit-overflow-scrolling en het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

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

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

Vanwege alle problemen met 'vendor prefixes' worden deze 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-overflow-scrolling, want dat wordt alleen gebruikt om een probleem op iOS op te lossen.)

iOS, iPadOS, scrollen en -webkit-overflow-scrolling

Omdat browsers op iOS en iPadOS verplicht de weergave-machine van Safari gebruiken, geldt dit verhaal voor álle browsers op iOS en iPadOS.

Met ingang van iOS 13 is dit besturingssysteem gesplitst in twee verschillende systemen: iOS 13 voor iPhones, en iPadOS voor iPads. Er zijn op dit moment nog niet zoveel verschillen tussen beide systemen, maar het zit er dik in dat het aantal verschillen gaat toenemen.

Het scrollen van de hele pagina op iOS was nooit een probleem. Dat verloopt gewoon mooi soepel. Maar als een deel van een pagina moest worden gescrold, bijvoorbeeld alleen een <div> met een rij thumbnails, was het een ander verhaal. Zo'n onderdeel was nauwelijks te scrollen. Het ging ongelooflijk schokkerig en houterig. Feitelijk was scrollen van een deel van een pagina daardoor nauwelijks bruikbaar op iOS.

Door gebruik van -webkit-overflow-scrolling: touch; konden ook delen van een pagina soepel worden gescrold. Maar het gebruik van deze eigenschap had nogal wat nadelen: de vreemdste bijwerkingen traden op. Soms verdwenen zelfs hele delen van de pagina. Op internet zijn honderden oplossingen voor dit soort problemen te vinden. Als er zoveel oplossingen zijn, is er meestal geen enkele die altijd werkt. Dat is helaas ook hier het geval.

In het voorbeeld zijn er in browservensters smaller of lager dan 600 px geen bijwerkingen. Omdat dit echter vaak wel het geval is en die bijwerkingen soms bijzonder moeilijk te vinden zijn, wordt voor de zekerheid ‑webkit-overflow-scrolling alleen gebruikt in landschapsstand. In portretstand blijkt het niet nodig te zijn om soepel te kunnen scrollen. (Wanneer het wel en niet nodig is, en bij welke elementen het dan moet worden neergezet, is een kwestie van uitproberen, want over deze eigenschap is nauwelijks documentatie te vinden.)

Maar in browservensters minimaal 600 px breed en hoog is er wel een probleem. In landschapsstand staan in deze grotere vensters de thumbnails in een rij bovenaan het venster. De grote afbeeldingen worden dan daaronder getoond. Zonder de eigenschap -webkit-overflow-scrolling werkt dit, maar is scrollen van de thumbnails nauwelijks werkbaar. Maar mét die eigenschap worden de grote afbeeldingen niet getoond. In landschapsstand was dat op te lossen met speciale css voor iOS, die te vinden is bij @supports (-webkit-overflow-scrolling: touch).

In portretstand horen de thumbnails in een kolom aan de linkerkant van het browservenster te staan. De grote afbeeldingen worden daar rechts van getoond. Hier bleek het onmogelijk de grote afbeeldingen zichtbaar te krijgen. Tenzij -webkit-overflow-scrolling niet wordt gebruikt, maar dan was scrollen van de thumbnails vrijwel onmogelijk. Daarom worden op iOS ook in bredere vensters de thumbnails altijd bovenaan het venster gezet.

Met ingang van iOS 13 en iPadOS 13 is dit probleem eindelijk opgelost door Apple. De eigenschap -webkit-overflow-scrolling is niet langer nodig.

Er is nog wel verschil tussen de afhandeling van -webkit-overflow-scrolling op iOS 13 en iPadOS 13. Op iPadOS 13 wordt de eigenschap helemaal niet meer ondersteund en gewoon volledig genegeerd. Op iOS 13 wordt de eigenschap (nog?) wel ondersteund, maar is deze niet meer nodig.

Je kunt dat makkelijk testen met iets als:

@supports (-webkit-overflow-scrolling: auto) { #titel, h1 {background: red;} }

De achtergrondkleur wordt alleen veranderd, als de browser -webkit-overflow-scrolling: auto ondersteund. Op iOS 13 blijkt de achtergrondkleur dan wel te veranderen, op iPadOS 13 niet.)

Waarom deze overbodige eigenschap nog wordt ondersteund in iOS 13, is volstrekt onduidelijk. Zoals gebruikelijk bij Apple is er vrijwel geen documentatie over deze eigenschap te vinden.

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 wordt hiervan alleen <main> gebruikt. <main> gedraagt 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 slideshow met alles erop en eraan).

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

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.

Bovenaan de pagina staat een <input>:

<input id="checkbox-voor-focus" type="checkbox" aria-hidden="true" autofocus tabindex=‑1">

Een schermlezer zou hier meedelen, dat hier een keuze gemaakt kan worden. Maar deze <input> zorgt er alleen voor dat de pijltjestoetsen bij openen van de pagina voor scrollen gebruikt kunnen worden. Daarom wordt deze <input> met aria-hidden="true" verborgen voor schermlezers.

Iets lager staat nog een <input>:

<input id="uitleg" type="checkbox" aria-hidden="true" aria-haspopup="true" tabindex="-1">

Deze <input> is bedoeld om een korte uitleg te openen. Gebruikers van een schermlezer hebben eerder de mogelijkheid gekregen om de slideshow aan te passen voor schermlezers. Bovendien staat in deze uitleg niets over schermlezers. Daarom is deze uitleg niet echt zinvol voor schermlezers en wordt deze verborgen.

De bij de <input> horende <label> en de <p> met de uitleg zelf worden ook verborgen:

<label id="label-voor-uitleg" for="uitleg" aria-hidden="true">?</label>

<p id="uitleg-tekst" aria-hidden="true" aria-haspopup="true">Grote afbeeldingen worden pas (...)

tot en met

(...) toe verdwijnen.

</p>

In elke div.wrap zitten vier links naar de vorige en volgende thumbnail en afbeelding. Deze zijn tamelijk nutteloos voor schermlezers, want die kunnen met sneltoetsen door de afbeeldingen navigeren. Deze worden dus ook verborgen:

<a class="vorige-t" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-t" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

<a class="vorige-a" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-a" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

aria-haspopup

Bovenaan de pagina staat een <input>:

<input id="uitleg" type="checkbox" aria-hidden="true" aria-haspopup="true" tabindex="-1">

Bij aanraken of -klikken van deze <input> wordt een korte uitleg over de werking van de slideshow getoond. In Internet Explorer 11 en Edge op touchscreens wordt deze uitleg echter niet getoond bij aanraking van de <input>. Of hij opent pas na een hele tijd. Of het contextuele menu wordt geopend. 'n Soort loterij. Kortom: enorme kommer en kwel. Door het toevoegen van aria-haspopup="true" wordt de uitleg gewoon zichtbaar.

Volgens de WAI-ARIA-specificatie 1.0 kan met aria-haspopup="true" worden aangegeven dat het element een contextueel menu of een submenu heeft. Microsoft gebruikt dit attribuut dus voor iets, waar het niet voor is bedoeld.

In versie 1.1 van de specificatie is de waarde 'true' bij aria-haspopup vervallen, maar wordt vanwege terugwaartse compatibiliteit met versie 1.0 nog wel gedefinieerd op eenzelfde wijze als in versie 1.0. Hiermee lijken mogelijke toekomstige problemen voor dit oneigenlijke gebruik te worden voorkomen. Versie 1.2 is nog een ontwerp-specificatie, maar ook hierin is dit hetzelfde.

Elke thumbnail met bijbehorende links, grote afbeelding, en dergelijke zit in een eigen div.wrap, waarin dit attribuut wordt gebruikt:

<div id="wrap-1" class="wrap firefox" aria-haspopup="true" tabindex="0">

Hetzelfde verhaal als gelijk hierboven bij de <input id="uitleg">: in Internet Explorer 11 en Edge op touchscreens wordt de uitleg niet goed getoond bij aanraken of -klikken van de <input>. Een iets langer verhaal staat gelijk hierboven bij <input id="uitleg">. Door het toevoegen van aria-haspopup="true" wordt de afbeelding gewoon zichtbaar.

Iets hierboven bij In elke div.wrap zitten vier links ... staan vier links naar de vorige en volgende thumbnail en afbeelding, waarin ook aria-haspopup wordt gebruikt, maar nu aria-haspopup="false". Door het gebruik van aria-haspopup="true" bij div.wrap denken Internet Explorer en Edge op touchscreens nu kennelijk dat hier ook iets te tonen valt: je moet de links (de pijltjes) twee keer aanraken, voordat de link wordt gevolgd.

De eerste aanraking is voor de (hier niet bestaande) pop-up die getoond moet worden, de tweede volgt pas de link. Door het attribuut aria-haspopup="false" worden ook Internet Explorer en Edge in het gareel gemept.

Overigens is het elke keer weer een verrassing, bij welke elementen je aria-haspopup moet gebruiken. Daar is eigenlijk alleen maar door uitproberen achter te komen. Maar goed, dat houdt het leven spannend en opwindend en zo.

Tabindex en Tab-toets

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

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

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

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

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

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

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

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

In principe is de volgorde bij gebruik van de Tab-toets als volgt: eerst worden alle positieve getallen in volgorde afgewerkt. Als twee tabindexen dezelfde waarde hebben, wordt de volgorde in de html aangehouden. Een waarde van '0' wordt, afhankelijk van browser en besturingssysteem, verschillend behandeld, waarover iets hieronder bij Tabindex="0" meer.

tabindex="-1"

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

In dit voorbeeld wordt tabindex="-1" voor iets anders gebruikt. Als de Tab-toets wordt gebruikt, krijgt ook input#checkbox-voor-focus op een gegeven moment de focus. Deze <input> is echter alleen bedoeld om de pijltjestoetsen gelijk bij openen van de pagina te kunnen gebruiken om te scrollen. Het heeft geen enkel nut als deze <input> focus krijgt en is alleen maar verwarrend. Daarom krijgt de <input> een negatieve tabindex, waardoor deze wordt genegeerd bij gebruik van de Tab-toets:

<input id="checkbox-voor-focus" type="checkbox" aria-hidden="true" autofocus tabindex="-1">

Voor schermlezers is een input#schermlezer aangebracht, waarmee de slideshow kan worden aangepast voor schermlezers. Ook deze <input> zou door de Tab-toets worden bezocht, wat ook bij deze <input> nutteloos en verwarrend is. Daarom wordt ook deze voorzien van een negatieve tabindex:

<input id="schermlezer" type="checkbox" tabindex="-1">

De derde <input> die bij gebruik van de Tab-toets zou worden bezocht is input#uitleg, waarin een korte uitleg over de werking van de slideshow staat. Maar die uitleg is volstrekt overbodig bij gebruik van de Tab-toets, omdat door het simpele indrukken van de Tab-toets de grote afbeeldingen zichtbaar worden. Verbergen voor de Tab-toets dus:

<input id="uitleg" type="checkbox" aria-hidden="true" aria-haspopup="true" tabindex="-1">

Ten slotte zitten er in elke div.wrap nog vier links naar de vorige en volgende thumbnail en afbeelding. Deze zijn overbodig, omdat dat al met de Tab-toets gebeurt. Als die links op de normale wijze door de Tab-toets zouden worden bezocht, zou je voor het tonen van een grote afbeelding vier keer overbodig de Tab-toets moeten indrukken. Verbergen dus:

<a class="vorige-t" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-t" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

<a class="vorige-a" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-a" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

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 hiervan bij de veertien div.wrap's gebruik gemaakt. Normaal genomen worden die genegeerd door de Tab-toets, maar door het gebruik van tabindex="0" kunnen ze toch focus krijgen. Dit maakt het mogelijk de grote afbeelding te tonen, als een div.wrap focus heeft:

<div id="wrap-1" class="wrap firefox" aria-haspopup="true" tabindex="0">

tabindex="..."

Op de plaats van de puntjes moet een positief getal worden ingevuld: het volgnummer. Positieve tabindexen worden in dit voorbeeld 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 dit 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.

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.

De vorige versie van dit voorbeeld was bijna tien jaar oud. Toen gebruikte vrijwel iedereen nog een muis. Inmiddels is dat niet meer het geval. In deze versie wordt hoveren helemaal niet meer gebruikt: de grote afbeelding kan alleen met het toetsenbord (de Tab-toets), door aanraken en door klikken worden getoond. Hierdoor worden bovenstaande problemen voorkomen.

Dat is niet de enige reden. Terugkijkend naar die vorige versie ontstaat toch ook wel de verdenking van enig pervers sadisme. Als je ook maar één millimeter boven die thumbnails kwam met de muis, floepte er gelijk een grote afbeelding tevoorschijn...)

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

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 echter volkomen onbelangrijk. Er wordt niets geopend met de Enter-toets of iets soortgelijks. En bij gebruik van de Tab-toets opent de grote afbeelding die bij de thumbnail met focus hoort. Als dat niet duidelijk genoeg is, valt te vrezen dat een kadertje ook geen redding meer biedt...

(Er verschijnt wel iets wat op een kadertje lijkt rondom de thumbnails. Maar dat is er eigenlijk alleen maar om duidelijk te maken, bij welke thumbnail de links naar de vorige of volgende thumbnail horen.)

Door de links met de pijltjes of de thumbnails aan te raken of te klikken, kan een bepaald element focus krijgen, waardoor eventueel de bijbehorende grote afbeelding kan worden getoond. Klikken en aanraken werkt in dit geval op precies dezelfde manier. Omdat elke div.wrap een tabindex van '0' heeft gekregen, kan de Tab-toets focus geven aan de div.wrap's.

Er zijn slechts twee lastige dingen.

Als je het browservenster op een lege plek aanraakt, moet een geopende grote afbeelding verdwijnen. Op iOS en iPadOS gebeurt dit niet. Daar sluit een grote afbeelding pas, als een ander element focus krijgt. Als je alleen maar het venster op een lege plaats aanraakt, krijgt geen enkel element focus, waardoor de grote afbeelding geopend blijft.

Dit is simpel op te lossen met een regeltje JavaScript als

body.addEventListener("touchstart", function () {return null;});

Deze regel doet verder helemaal niets, maar zorgt ervoor dat op iOS en iPadOS bij aanraking van het scherm de afbeelding toch gesloten kan worden. Kennelijk krijgt – bij gebruik van de regel hierboven – <body> focus, of zoiets.

In dit voorbeeld is zo'n speciale regel niet nodig. Zonder JavaScript sluit de grote afbeelding niet. En met JavaScript zitten er in het script al andere regels die een soortgelijk effect op iOS en iPadOS hebben.

Firefox heeft, als enige geteste browser, een afwijkende werking bij de selector :focus. Bij gebruik van de Tab-toets of bij aanraken of -klikken van een thumbnail werkt het net als in alle andere browsers. Maar bij het volgen van een link werkt het anders.

Als echter naar de div.wrap met de vorige of volgende thumbnail of grote afbeelding wordt gegaan door op een link daarnaar te klikken, werkt Firefox anders dan alle andere geteste browsers. In alle browsers krijgt de bij de thumbnail of grote afbeelding horende div.wrap de focus, maar niet in Firefox. In Firefox wordt de link wel gevolgd, maar de div.wrap krijgt niet echt de focus. Daardoor werkt de css die bij :focus hoort niet in Firefox. De thumbnail krijgt geen outline, de links naar vorige en volgende thumbnail worden niet binnen het browservenster gezet, en – als dat eventueel zou moeten – worden ook de links naar vorige en volgende grote afbeelding en de afbeelding zelf niet getoond.

In Firefox werkt :target echter wel, als een link wordt aangeraakt of -geklikt. :target werkt echter ook in andere browsers, waardoor soms twee thumbnails een outline zouden hebben: één in een div.wrap met :focus, en één in een div.wrap met :target. Daarom is speciaal voor Firefox bij @supports (-moz-appearance: none) aparte css neergezet. Firefox kan nu :target gebruiken, en andere browsers hebben daar geen last van.

: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 (naast nieuwere formaten als WebP, dat nog onvoldoende door browsers wordt ondersteund). 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. Ook in dit voorbeeld worden jpg-bestanden gebruikt. (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.)

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.

Afbeelding 2: foto van wesp met veel kleine details

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.

Afbeelding 3: foto van wesp sterk vergroot

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 pixels. 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 (’pixels per inch’). (Vaak wordt foutief de eenheid dpi (’dots per inch’) 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 pixels van het beeldscherm worden 'schermpixels' genoemd (in het Engels 'device pixel') en 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. 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.

Afbeelding 4: foto van wesp in lage en hoge resolutie

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

In dit geval wordt niet sizes gebruikt, maar het veel minder vaak gebruikte device-pixel ratio: de verhouding tussen css-pixels en schermpixels. Als een scherm fysiek 400 px hoog is, maar slechts 200 css-pixels, is de device-pixel-ratio 400 / 200 = 2. Hier is voor gekozen, omdat sommige browsers bij gebruik van sizes in dit voorbeeld hardnekkig de verkeerde afbeelding kiezen. Normaal genomen zal dat niet spelen, omdat je normaal genomen niet een totaal verschillende opbouw van de pagina hebt in grotere en kleinere browservensters.

Nu heeft de browser voldoende informatie om een afbeelding te kiezen, die het best bij de grootte en resolutiedichtheid van scherm en venster past. 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.

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 → Webontwikkelaar → 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 bloedig 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 → Webontwikkelaar → Hulpmiddelen in-/uitschakelen. 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. In het verleden veranderde Internet Explorer bijvoorbeeld een <p> juist in een <P>, nu is dat niet meer zo.

De code aanpassen aan je eigen ontwerp

Toegankelijkheid en zoekmachines

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

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

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

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

Enkele tips die helpen bij toegankelijkheid:

Getest in

Laatst gecontroleerd op 9 maart 2020.

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 Internet Explorer 11, in grotere en kleinere browservensters.

OS X 10.14.6 ('Mojave') (1680 x 1050 px, resolution: 96: ppi, device-pixel-ratio: 1):
Firefox, Safari en Google Chrome, in grotere en kleinere browservensters.

Linux (Kubuntu 18.04 LTS, 'Bionic Beaver') (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: 96 ppi):
Bureaublad-versie: Firefox, Google Chrome en Internet Explorer 11, in grotere en kleinere browservensters.
Startscherm-versie: Internet Explorer 11.

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

Tablets

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

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

Android 4.4.2 ('Kitkat') (1280 x 800 px, resolution: 96 ppi):
Firefox en Chrome (alle portret en landschap).

Android 4.4.2 ('Kitkat') (2560 x 1600 px, resolution: 192 ppi):
Firefox en Chrome (alle portret en landschap).

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

Android 9.0 ('Pie') (1920 x 1200 px, resolution: 144 ppi):
Dolphin, Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Smartphones

Windows 10 Mobile (1280 x 720 px, resolution: 192 ppi):
Edge.

iPhone met iOS 10.3.1 (1334 x 750 px, 326 ppi) (gesimuleerd in Xcode):
Safari (portret en landschap).

iPhone met iOS 12.4 (1334 x 750 px, 326 ppi) (gesimuleerd in Xcode):
Safari (portret en landschap).

iPhone met iOS 13.4 (1136 x 640 px, 326 ppi):
Safari, Chrome, Firefox en Microsoft Edge (alle portret en landschap).

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

Android 7.0 ('Nougat') (1920 x 1080 px, resolution: 424 ppi):
Dolphin, Samsung Internet, Firefox, Microsoft Edge en Chrome (alle portret en landschap).

Android 9.0 ('Pie') (1280 x 720 px, resolution: 192 ppi):
Dolphin, 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, Android en Windows 10 Mobile, 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.

Als JavaScript is gebruikt, is op de desktop ook getest zonder JavaScript. Op iOS, Android en Windows 10 Mobile is niet getest zonder JavaScript, omdat je JavaScript in een toenemend aantal mobiele browsers niet uit kunt zetten. Bovendien is een mobiel apparaat zonder JavaScript niet veel meer dan een duur en groot uitgevallen horloge. Ook in Edge is niet getest zonder JavaScript, omdat Microsoft het onmogelijk heeft gemaakt dit uit te zetten. Ten slotte is getest zonder css en – als afbeeldingen worden gebruikt – zonder afbeeldingen.

Schermlezers en dergelijke

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

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

WebbIE. is een browser die gericht is op mensen met een handicap. Er is getest op Windows7. (LET OP: kies voor 'Install WebbIE 4 Web Browser Now'. Dat is – op het moment van schrijven – de een na bovenste knop. Als je voor de bovenste download kiest, krijg je 'n hele berg hulpprogramma's erbij, waar je voor het testen niets aan hebt.)

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

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

VoiceOver is een in iOS en OS X ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS 12.4.6, 13.4, iPadOS 13.4 en OS X 10.14.6.

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

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 validator van w3c, de css ook. Als om een of andere reden niet volledig gevalideerd kon worden, wordt dat bij Bekende problemen (en oplossingen) vermeld.

Nieuwe browsers worden pas getest, als ze uit het bèta-stadium zijn. 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 algemene problemen in alle of in 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.

Als bij het probleem geen oplossing is gevonden, staat de samenvatting in een rode ononderbroken lijn.

De lijst hieronder is gruwelijk lang, maar dat is voor een groot deel, omdat in principe álle problemen erin worden opgenomen. Ook de onbenulligste. Bovendien wordt het overgrote deel van de problemen opgelost met behulp van JavaScript. In principe werkt de slideshow ook prima zonder JavaScript, maar sommige dingen zijn dan wat onhandig.

Zonder JavaScript

Geen problemen in browservensters smaller of lager dan 600 px.

Omdat het script alleen werkt in browservensters met een minimale breedte van 600 px én een minimale hoogte van 600 px, maakt het in deze kleinere vensters niets uit of JavaScript aan- of uitstaat.

Probleem: in browservensters minimaal 600 px breed en hoog, behalve op iOS en iPadOS, verdwijnen links en outline bij de thumbnail, als het venster op een lege plaats wordt aangeraakt.

Als het browservenster op een lege plaats wordt aangeraakt, bijvoorbeeld om een grote afbeelding te sluiten, verdwijnen de links en de outline bij de thumbnail.

Als je weer een thumbnail aanraakt, verschijnen outline en links weer. Als de pagina wordt herladen, komt de eerste thumbnail links bovenaan te staan. Deze eerste thumbnail heeft ook weer gewoon een outline en de bij de thumbnail horende links zijn weer gewoon zichtbaar.

Als JavaScript aanstaat, wordt gekeken, welke thumbnail een outline heeft, en van welke thumbnail de bijbehorende links worden getoond. Vervolgens wordt gezorgd dat die thumbnail een outline houdt en dat de bij die thumbnail horende links worden getoond.

Niet het einde van de wereld dus, als JavaScript uitstaat, maar wel onhandig.

Probleem: in browservensters minimaal 600 px breed en hoog op iOS en iPadOS kan de grote afbeelding niet worden gesloten.

Als je op iOS en iPadOS het browservenster op een lege plaats aanraakt, gebeurt er helemaal niets. De aanraking wordt volledig genegeerd. De div.wrap die focus heeft, houdt gewoon die focus. Voordeel is dat het gelijk hierboven beschreven probleem niet speelt. Nadeel is dat de laatst getoonde grote afbeelding altijd zichtbaar blijft.

Met behulp van JavaScript wordt het hele browservenster gevoelig gemaakt voor aanraking, waardoor ook op iOS en iPadOS de grote afbeelding gesloten kan worden door een lege plek op het venster aan te raken.

Probleem: als in browservensters minimaal 600 px breed en hoog de hulp wordt geopend en weer wordt gesloten, verdwijnen links en markering bij de thumbnail.

Als door aanraken of -klikken van het vraagteken de uitleg bij de slideshow wordt geopend en weer wordt gesloten, verdwijnen de links en de outline bij de thumbnail.

Als je weer een thumbnail aanraakt, verschijnen outline en links weer. Als de pagina wordt herladen, komt de eerste thumbnail links bovenaan te staan. Deze eerste thumbnail heeft ook weer gewoon een outline en de bij de thumbnail horende links zijn weer gewoon zichtbaar.

Als JavaScript aanstaat, wordt gekeken, welke thumbnail een outline heeft, en van welke thumbnail de bijbehorende links worden getoond. Vervolgens wordt gezorgd dat die thumbnail een outline houdt en dat de bij die thumbnail horende links worden getoond, als de hulp weer wordt gesloten.

Als de thumbnail met de outline eventueel buiten het browservenster staat, omdat je terug naar boven of naar links bent gescrold om de hulp te openen, wordt die thumbnail bij sluiten van de hulp links bovenin het venster gezet.

Probleem: in browservensters minimaal 600 px breed en hoog worden alle grote afbeeldingen gelijk bij openen van de pagina gedownload, of ze nu worden getoond of niet.

Als JavaScript uitstaat, wordt gebruik gemaakt van de <img>'s die binnen de <noscript>-tag staan. Omdat dit gewone <img>'s zijn, worden die allemaal gelijk bij openen van de pagina gedownload.

Met JavaScript aan worden alleen de grote afbeeldingen gedownload die worden getoond. (Met soms 'n extra, zodat ze sneller getoond kunnen worden.)

Probleem: in browservensters minimaal 600 px breed en hoog wordt, als je met de links een vorige of volgende thumbnail of afbeelding bezoekt, dat vastgelegd in de geschiedenis van de browser.

De pijltjes zijn gewone links naar een anker in de pagina. Zulke links worden, als ze worden gevolgd, opgeslagen in de geschiedenis van de browser. Als je de Terug- of Vooruit-knop van de browser gebruikt, volgt die de geschiedenis: er wordt een eerdere of latere pagina of anker op het scherm gezet.

Als je van de pagina warchild.nl komt en vervolgens alle veertien thumbnails en/of grote afbeeldingen langsloopt met de links, worden ze alle veertien opgeslagen in de geschiedenis. Als je terug wilt naar de pagina warchild.nl, moet je vijftien keer de Terug-toets van de browser indrukken. Bij een slideshow met vijfhonderd afbeeldingen... Enfin, je begrijpt het.

Met behulp van JavaScript worden links binnen de pagina niet in de geschiedenis opgeslagen. Hoeveel thumbnails en/of grote afbeeldingen je ook bezoekt met de links met de pijltjes, één keer de Terug-toets van de browser indrukken en je bent weer op de vorige pagina.

Probleem: in Firefox krijgen soms twee thumbnails gelijktijdig een outline.

Als in Firefox een link binnen de pagina wordt gevolgd, krijgt het doel van die link niet de focus. In alle andere geteste browsers gebeurt dat wel. (Meer hierover bij Ruziënde browsers.)

Daarom staat voor Firefox bij @supports (-moz-appearance: none) aparte css, die met :target werkt.

Zonder JavaScript kunnen in Firefox twee thumbnails gelijktijdig een outline hebben: één in de <div> met :target, en één in de <div> met :focus.

Met behulp van JavaScript wordt dit opgelost. Alle css-regels voor Firefox hebben in de selector 'firefox' staan. Het script verwijdert deze class uit de html, waardoor deze regels niet meer werken. Alleen als een thumbnail een outline heeft en de erbij horende links worden getoond, wordt de class 'firefox' teruggezet.

Probleem: als in Firefox de links (de pijltjes) worden gebruikt, moet de Tab-toets soms twee keer worden ingedrukt om een grote afbeelding te tonen.

Als in Firefox een volgende div.wrap met thumbnail of grote afbeelding wordt bezocht met behulp van de links, krijgt die div.wrap geen focus. Voor Firefox wordt daarom in de css soms :target gebruikt in plaats van :focus. (Meer hierover bij Ruziënde browsers.)

Als een toets wordt ingedrukt, wordt met behulp van JavaScript gekeken, of dat de Tab-toets is. Als dat zo is, wordt de grote afbeelding getoond die hoort bij de thumbnail met de outline. Dat luisteren naar welke toets is ingedrukt, gebeurt bij de div#wrap die focus heeft. In Firefox krijgt een div.wrap echter geen focus, als een link naar een div.wrap wordt gevolgd. Daarom wordt in Firefox niet geluisterd, welke toets is ingedrukt.

Zodra de Tab-toets is ingedrukt, wordt naar de volgende div.wrap gegaan. De Tab-toets werkt in Firefox hetzelfde als in andere browsers: de div.wrap waar met de Tab-toets naartoe wordt gegaan, krijgt focus. En dus wordt nu wel geluisterd, welke toets is ingedrukt.

Hierdoor kan het soms voorkomen dat in Firefox een Tab-toets twee keer moet worden ingedrukt. Bovendien wordt dan niet de grote afbeelding bij de thumbnail met outline getoond, maar de grote afbeelding bij de volgende thumbnail. Want die heeft de focus gekregen bij het de eerste keer indrukken van de Tab-toets.

Dit speelt alleen, zolang een grote afbeelding nog niet is getoond. Zodra een grote afbeelding eenmaal is getoond, wordt deze altijd gelijk bij het indrukken van de Tab-toets getoond.

Dit is waarschijnlijk wel op te lossen met JavaScript, maar het gaat om een heel erg klein ongemak. Het script is al ingewikkeld genoeg zonder dit soort kleinigheden op te lossen. Bovendien zullen vrijwel geen bezoekers omschakelen tussen aanraken of ‑klikken van een link en het gebruiken van de Tab-toets.

Probleem: als JavaScript is uitgeschakeld met behulp van een extensie, wordt de grote afbeelding soms helemaal niet getoond.

Dit probleem speelt in ieder geval in Firefox, maar mogelijk in alle browsers, waaraan je extensies kunt toevoegen. En als het nu niet speelt, zou het in de toekomst kunnen gaan spelen. Hoewel hieronder voornamelijk Firefox wordt genoemd, geldt dit verhaal dus eigenlijk voor alle browsers.

Als een extensie JavaScript uitschakelt, kan dat op verschillende manieren. Als een browser het toestaat aan een extensie om gewoon JavaScript uit te schakelen, zoals in Google Chrome bijvoorbeeld kan, weet de browser dat JavaScript uitstaat en zal de inhoud van de <noscript>-tags worden gebruikt. Alles werkt dan gewoon.

In Firefox echter kan een extensie JavaScript niet uitschakelen. Dit is één van de ahum verbeteringen die in het nieuwe model van Firefox is uitgevoerd. Noodgedwongen zal een extensie in Firefox daarom een omweg moeten vinden.

Sommige extensies doen dat door elk domein te bekijken aan de hand van enorme lijsten. Afhankelijk van het domein dat het script verzendt, wordt dit dan geblokkeerd of niet. Deze extensies leveren geen enkel probleem op: ze laten het in dit voorbeeld gebruikte script gewoon door.

Simpeler extensies doen het vaak door de 'Content Security Policy' aan te passen. Dat is een veiligheidssysteem, waarmee bijvoorbeeld je kunt voorkomen dat een script van buitenaf 'inbreekt' op een site en enge dingen doet. Of leuke dingen, zoals creatief boekhouden op de site van je bank. In principe kunnen ook extensies in andere browsers zo werken, waardoor ook in andere browsers onderstaand probleem zich kan voordoen.

Het probleem is dat bij deze methode JavaScript niet wordt uitgezet. Er wordt een script geblokkeerd, maar JavaScript staat – wat Firefox betreft – gewoon nog steeds aan. De inhoud van de <noscript>-tags wordt dan ook niet gebruikt. Maar het script om commentaar in een afbeelding te veranderen wordt geblokkeerd. Met als resultaat dat er geen grote afbeelding wordt getoond.

De extensie JavaScript Toggle On and Off bijvoorbeeld werkt op bovenstaande manier. Als iemand JavaScript op deze manier blokkeert, is dat niet op te lossen. Vermoedelijk zal zo iemand wel snel aan het inschakelen van JavaScript denken, want dit probleem zal op (heel veel) meer sites spelen.

De extensie JavaScript Switcher bijvoorbeeld doet het beter. Ook die verandert de Content Security Policy, maar kijkt ook of er <noscript>-tags aanwezig zijn. Als dat zo is, worden de door de extensie aan de pagina toegevoegd. Bij deze extensie zie je dus wel grote afbeeldingen.

De verreweg meest gebruikte extensie om JavaScript (gedeeltelijk) te blokkeren is NoScript. Ook die extensie kijkt naar de inhoud van de <noscript>-tags en levert dus geen problemen op.

Probleem: op iOS en iPadOS in browservensters minimaal 600 px breed en hoog zijn bij openen van de pagina geen links naar vorige en volgende thumbnail aanwezig, en de eerste thumbnail is niet gemarkeerd.

Bij openen van de pagina staat de eerste thumbnail links bovenaan. De links naar volgende en vorige thumbnail linken dan naar de div.wrap's met de tweede en de veertiende (dat is in het voorbeeld de laatste) thumbnail. Daarnaast heeft de eerste thumbnail een outline, zodat duidelijk is, wat de vorige en de volgende thumbnail is.

Hiervoor staat in de html de regel:

<input id="checkbox-voor-focus" type="checkbox" aria-hidden="true" autofocus tabindex="-1">

Safari op iOS en iPadOS, en daarmee alle andere browsers op deze systemen, ondersteunen het attribuut autofocus niet. Hierdoor krijgt input#checkbox-voor-focus bij openen van de pagina niet de focus, en daardoor werken selectors met :focus niet.

Met behulp van het script krijgt input#checkbox-voor-focus ook op iOS en iPadOS bij openen van de pagina de focus.

(Nadat het scherm één keer ergens is aangeraakt, zijn links en markering overigens wel te zien, ook zonder JavaScript.)

Zonder css

Geen problemen.

Zonder css werkt alles gewoon, maar het ziet er natuurlijk niet uit.

Gebruikers Tab-toets

Geen problemen.

Het vraagteken bovenaan de pagina hoort bij een <input> en zou normaal genomen ook bij gebruik van de Tab-toets de hulp openen. Omdat de Tab-toets echter gewoon vanaf het begin werkt, is het openen van die hulp overbodig.

Met de Tab-toets kan alleen door alle grote afbeeldingen worden gelopen. Je kunt geen afbeeldingen overslaan. Om afbeeldingen over te kunnen slaan, zouden de links met de pijltjes ook door de Tab-toets moeten worden bezocht. Dat zou betekenen dat je vijf keer Tab of Shift+Tab moet indrukken om één grote afbeelding vooruit of terug te gaan. Nu worden alleen de div.wrap's bezocht, waarbij steeds de bijbehorende grote afbeelding wordt geopend.

Omdat er toch al JavaScript wordt gebruikt, zijn ook hier wat kleine verbeteringen aangebracht. Bij gebruik van de Tab-toets wordt niet alleen de grote afbeelding getoond, die bij de thumbnail in de div.wrap met focus zit, maar worden gelijk daarmee de vorige en volgende afbeelding alvast gedownload (als dat al niet is gebeurd). Hierdoor kunnen ze vlugger worden getoond.

Tekstbrowsers

Geen problemen.

Omdat het in een slideshow om afbeeldingen gaat, zijn tekstbrowsers natuurlijk feitelijk redelijk zinloos. Maar goed, voor de volledigheid toch maar...

Lynx toont alles, inclusief de pijltjes, de tekst van de hulp, enzovoort.

WebbIE toont alleen de tekst voor schermlezers (de tekst binnen div#voor-schermlezers), de titel en, afhankelijk van de instellingen, de alt-tekst bij de afbeeldingen.

Schermlezers

In browservensters smaller of lager dan 600 px staat alleen een rij of kolom met afbeeldingen. Deze zijn op de voor de betreffende schermlezer gebruikelijke manier te benaderen.

In browservensters minstens 600 px breed én minstens 600 px hoog staat bovenaan de pagina een tekst, die alleen voor schermlezers is bedoeld. Gelijk na die tekst kan worden gekozen voor een aangepaste weergave voor schermlezers. Alle grote afbeeldingen komen dan onder elkaar te staan. Deze vervolgens zijn weer op de voor de betreffende schermlezer gebruikelijke manier te benaderen.

Probleem: in TalkBack op Android moet een link en dergelijke binnen het browservenster staan.

In oudere versies van TalkBack op Android moet een link, aankruisvakje, enzovoort een stukje van minstens 1 x 1 px binnen het venster van de browser hebben staan, anders werken link, aankruisvakje, en dergelijke niet. Bovendien mag die pixel niet zijn verborgen onder een ander element.

De link, aankruisvakje, en dergelijke worden wel voorgelezen door TalkBack, en er wordt ook gezegd dat deze aangeklikt, aangevinkt, en dergelijke kan worden, maar in werkelijkheid kan dat dus niet. Daarom is ook gekozen voor de ietwat onhandige oplossing om een aankruisvakje binnen het browservenster te zetten, waarmee de gebruiker van een schermlezer alle grote afbeeldingen in één keer kan openen. Het is een heel vervelend probleem, want gebruikers van een schermlezer bedienen een link nou juist met het toetsenbord of door het scherm op een willekeurige plek aan te raken.

Wat er kennelijk gebeurt: de zogenaamde toegankelijkheids-focus wordt wel op link, aankruisvakje, en dergelijke gezet. Hierdoor worden link, aankruisvakje, en dergelijke wel correct aangekondigd. Maar de gewone focus wordt er niet op gezet. En die heb je wel nodig om link, aankruisvakje, en dergelijke te laten reageren op een aanraking.

In Android 8.1.0 is dit probleem eindelijk opgelost. Maar tot op de dag van vandaag worden schandalig genoeg zelfs nog smartphones met Android 4.4 verkocht. Het zal hoe dan ook dus nog vele jaren duren voor oudere versies van Android zijn verdwenen.

Daarom zullen voorlopig oplossingen zoals in dit voorbeeld nog nodig zijn.

Zoomen en lettergroottes

In de meeste browsers is er geen enkel probleem bij vergroten of verkleinen en in- en uitzoomen. De uitzonderingen zijn iOS en iPadOS.

Probleem: op iOS ouder dan versie 13 in browservensters smaller of lager dan 600 px in landschapsstand kun je bij inzoomen (vergroten) niet meer door de thumbs scrollen.

Althans: er zijn steeds maar zo'n vier thumbnails zichtbaar, de rest verdwijnt gewoon. Met heel veel moeite en geduld lukt het soms toch om naar 'n vorige of volgende thumbnail te gaan, maar dat is meer een gezelschapsspel voor gevorderde masochisten.

Op iOS versie 13 is dit kennelijk (eindelijk) opgelost.

Probleem: op iOS en iPadOS in browservensters minimaal 600 px breed en hoog kun je bij inzoomen (vergroten) niet meer door de thumbs scrollen.

Althans: er zijn steeds maar zo'n vier thumbnails zichtbaar, de rest verdwijnt.

In browservensters minimaal 600 px breed en hoog werken de links naar de volgende en vorige thumbnail en afbeelding nog gewoon, dus je kunt nog steeds door de thumbnails en afbeeldingen navigeren.

Op iOS 13 is dit kennelijk opgelost, maar dat geldt niet voor iPadOS 13.

Probleem: bij vergroten van de tekst verschijnt in Internet Explorer een scrollbalk onder de titel.

Afbeelding 5: overbodige scrollbalk in Internet Explorer

Als de tekst wordt vergroot, verschijnt bij de titel 'Mijn huisdieren' een verticale scrollbalk. Als de titel niet meer volledig is te zien, kan dat door te scrollen alsnog. (Dat is alleen nodig als de thumbnails langs de bovenkant van het browservenster staan.)

In Internet Explorer verschijnt om duistere redenen aan de onderkant een volkomen overbodige horizontale scrollbalk, zoals op de afbeelding is te zien. Wat je ook aan de css verandert: het kreng is niet weg te meppen.

Ook weer geen vreselijk groot probleem, maar het oogt niet zo mooi.

iOS (alle browsers) en internet explorer in vensters minimaal 900 px breed en 600 px hoog

Probleem: op iOS ouder dan versie 13 staan in browservensters minstens 600 px breed en hoog de thumbnails altijd in een horizontale rij bovenaan het venster.

Omdat alle browsers op iOS verplicht de weergave-machine van Safari moeten gebruiken, speelt dit probleem in alle browsers. In iPadOS is dit probleem eindelijk opgelost, dus daarin speelt dit probleem niet.

Als je op iOS ouder dan versie 13 niet de hele pagina, maar bijvoorbeeld alleen <div> wilt scrollen, is dat vrijwel onmogelijk. Het scrollen gaat ongelooflijk stroef. Zo stroef dat het eigenlijk vrijwel onmogelijk is om enigszins normaal te scrollen.

Helaas is scrollen binnen een <div> precies wat er in dit voorbeeld bij het bewegen van de thumbnails gebeurt. (In kleinere browservensters speelt dit probleem niet, omdat daarin de hele pagina wordt gescrold.)

Als je zo'n <div> en dergelijke toch soepel wilt laten scrollen, moet je gebruik maken van de eigenschap -webkit-overflow-scrolling. Deze eigenschap wordt alleen op iOS eerder ondersteund, browsers op andere systemen negeren de eigenschap volledig. Met -webkit-overflow-scrolling: touch; scrolt de <div> opeens als een zonnetje. Soepel en elegant, Epke Zonderland kan er nog wat van leren.

Helaas heeft deze eigenschap bijzonder vervelende bijwerkingen. Zo verdwijnt regelmatig spontaan gewoon een deel van de pagina. Dat is toch wat anders dan bij Epke Zonderland. Die maakt misschien wel 'ns 'n doodsmak, maar hij heeft nog nooit spontaan 'n been of arm verloren.

In dit voorbeeld had -webkit-overflow-scrolling als bijwerking dat de grote afbeeldingen stomweg niet zichtbaar werden. Dit is opgelost met css speciaal voor iOS, maar alleen in portretstand: met de thumbnails aan de bovenkant van het browservenster.

In landschapsstand bleek het onmogelijk de grote afbeeldingen zichtbaar te maken. Daarom is op iOS in browservensters minimaal 600 px breed en hoog de weergave altijd met thumbnails aan de bovenkant van het venster. Een echt groot probleem is dit dus eigenlijk niet, want alles werkt gewoon.

In iPadOS en iOS 13 is dit eindelijk opgelost. Scrollen werkt nu zoals het hoort te werken, ook als alleen een <div> of zo wordt gescrold. Browsers op iPadOS negeren nu ook, net als alle andere browsers, de eigenschap -webkit-overflow-scrolling volledig. Daarom is op iPadOS ook weergave in landschapsstand mogelijk.

Door gebruik te maken van de regel

@supports not (-webkit-overflow-scrolling: touch)

kan css worden opgegeven voor alle browsers die -webkit-overflow-scrolling niet ondersteunen. Dat zijn álle browsers, behalve die op iOS. Alleen in die browsers worden in browservensters minimaal 900 px breed en minimaal 600 px hoog de thumbnails in een verticale rij weergegeven.

(Een uitgebreider verhaal over -webkit-overflow-scrolling en het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

Omdat Internet Explorer @supports niet ondersteunt, negeert ook Internet Explorer de css binnen deze @supports-regel. Ook in Internet Explorer worden de thumbnails dan ook altijd als een horizontale rij bovenaan het browservenster weergegeven. Een groot probleem is dit niet, want alles werkt gewoon.

(Het bijzonder grote en lastige probleem van het scrollen van een onderdeel van de pagina speelde al vanaf iOS 5 in 2011. Al die jaren was de communicatie van Apple hierover vrijwel afwezig. Maar gelukkig is het dit steenrijke bedrijf dan toch gelukt om deze ellende in de recordtijd van acht jaar op te lossen. Hulde!)

Internet Explorer

Probleem: in Internet Explorer wordt altijd de standaard-thumbnail en -afbeelding getoond.

Om onnodig downloaden van te grote bestanden te voorkomen, wordt gebruik gemaakt van srcset. Met behulp hiervan kan de browser bepalen, welke thumbnail of afbeelding het meest geschikt is voor de resolutiedichtheid en de grootte van het browservenster.

Voor browsers die dit niet ondersteunen, wordt een standaard-afbeelding opgegeven:

<img src="039-pics/alpaca-300w.jpg" srcset="039-pics/alpaca-300w.jpg 1x, 039-pics/alpaca-600w.jpg 2x, 039-pics/alpaca-900w.jpg 3x" alt="Alpaca">

De afbeelding die achter src staat, wordt gebruikt in browsers die het iets verderop staande srcset niet ondersteunen. Omdat Internet Explorer alleen nog op de desktop voorkomt, is dat geen echt groot probleem. De grootte van thumbnail en afbeelding klopt. En een hogere resolutiedichtheid zal op apparaten die zo'n oude browser gebruiken niet zo vaak voorkomen. Bovendien is, ook op schermen met een grotere resolutiedichtheid, de kwaliteit van thumbnail en afbeelding nog steeds acceptabel.

Als je dit toch een probleem vindt, kun je een polyfill gebruiken: een stukje JavaScript om niet-ondersteunde eigenschappen en elementen te kunnen gebruiken in oudere browsers. Een polyfill voor srcset is te vinden op scottjehl.github.io/picturefill. Daar staat ook beschreven, hoe je die polyfill kunt gebruiken.

Alle browsers (alleen op de site)

Probleem: op de site mist de <h1> bij een bepaalde breedte van het browservenster.

Dit 'probleem' speelt alleen op de site. In het voorbeeld in de download is de titel:

<h1>Mijn huisdieren</h1>

Op de site is dat echter:

<h1>Slideshow met thumbnails en afbeeldingen van verschillende grootte. Gebruikt zo weinig mogelijk bandbreedte</h1>

Het wat nogal 'n gedoe om die titel in alle verschillende uitvoeringen van de slideshow zichtbaar te maken. Normaal genomen zou dat niet zo'n probleem zijn, maar het voorbeeld op de site moest wel enigszins op het voorbeeld in de download blijven lijken. Waardoor het soms nogal proppen werd om de <h1> op de site ergens tussen te kunnen pielen.

Als het browservenster minimaal 760 px breed én minder dan 600 px hoog, is de hele <h1> gewoon weggelaten. Het bleek niet goed mogelijk die ergens neer te poten. Deze maat komt eigenlijk niet voor, maar hier worden nou eenmaal voor de volledigheid álle bekende problemen neergezet.

(Ook dit is op te lossen met extra css en dergelijke, maar dat is de moeite van het extra werk niet waard.)

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. De code hoefde niet veranderd te worden.

11 april 2009:

Er was toch 'n aanpassing nodig voor Internet Explorer 8. Door een bug daarin bleven - alleen in de download, niet op de site - lle afbeeldingen openstaan als je de Tab-toets gebruikte.

25 november 2009:

Doctype veranderd in dat voor html5, zodat 'n negatieve tabindex gebruikt kan worden. Zie verder bij <! doctype html>.

15 november 2010:

9 maart 2020

Inhoud van de download en licenties

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

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

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

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

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

afbeelding-039-dl.html: de pagina met het voorbeeld.

afbeelding-039.pdf: deze uitleg (aangepast aan de inhoud van de download).

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

039-css-dl:

afbeelding-039-dl.css: stylesheet voor afbeelding-039-dl.html.

039-pics:

Veertien afbeeldingen, elk in vier verschillende groottes. Het getal voor de 'w' in de naam geeft de breedte in pixels aan.

De afbeeldingen zijn afkomstig van pixabay.com/nl. De licentie is te vinden op pixabay.com/nl/service/license en komt er ongeveer op neer dat je alles met de afbeeldingen mag doen, behalve verkopen en gebruiken voor illegale doeleinden.

039-pics-met-breedte

Precies dezelfde afbeeldingen als in map 039-pics, maar op deze afbeeldingen is de breedte van de afbeelding aangegeven (handig als je wilt kijken, welke afbeelding nou eigenlijk bij een bepaalde pixeldichtheid of zo wordt gebruikt).

039-js-dl:

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

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

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

<!DOCTYPE html>

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

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

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

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

<html lang="nl">

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

<meta charset="utf-8">

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

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

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

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

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

Mobiele apparaten variëren enorm in 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 hij of zij wil zien.

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

Nieuwe sites of pagina's kunnen echter wel rekening houden met de veel kleinere vensters van mobiele apparaten. In dit voorbeeld bijvoorbeeld passen richting en grootte van de afbeeldingen zich aan de grootte van het venster aan.

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

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

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

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

<input id="checkbox-voor-focus" type="checkbox" aria-hidden="true" autofocus tabindex="-1">

In browservensters smaller of lager dan 600 px wordt deze <input> volledig verborgen. Alleen in grotere vensters heeft hij enig effect.

Deze <input> heeft geen enkele zichtbare werking. Maar door het gebruik van het attribuut autofocus wordt de focus binnen div#android-temmer gezet. Hierdoor kan gelijk bij openen van de pagina door de thumbnails worden gescrold met behulp van de pijltjestoetsen (en, afhankelijk van de browser, met PgUp en PgDn).

Zonder deze <input> met autofocus zouden die toetsen werken voor de hele pagina. Pas als een plek binnen div#android-temmer (of eventueel de scrollbalk) wordt aangeraakt of -geklikt, werken de pijltjestoetsen (en eventueel PgUp en PgDn) voor scrollen binnen de thumbnails.

(Omdat autofocus niet werkt op iOS en iPadOS, wordt dat voor die systemen geregeld met een stukje JavaScript.)

Hoewel de <input> links buiten het scherm wordt neergezet, melden schermlezers toch dat hier een keuze kan worden gemaakt, wat nogal verwarrend is. Om dat te voorkomen wordt de WAI-ARIA-code aria-hidden="true" gebruikt. Nu negeren schermlezers deze <input> volledig.

Als mensen de Tab-toets gebruiken om links, <input>'s, en dergelijke af te lopen, wordt ook deze <input> bezocht. Ook hier heeft dat geen enkel nut. Om de Tab-toets deze <input> te laten negeren, is het attribuut tabindex=-"1" toegevoegd.

<input id="schermlezer" type="checkbox" tabindex="-1">

In browservensters smaller of lager dan 600 px wordt deze <input> volledig verborgen. Alleen in grotere vensters kun je hem aanvinken.

Deze <input> is alleen van belang voor schermlezers. Bij #schermlezer wordt hij vrijwel onzichtbaar gemaakt. Helemaal onzichtbaar maken kan niet, want dan werkt de <input> niet in oudere versies van Android. Om dezelfde reden kan deze <input> ook niet buiten het browservenster worden geplaatst. Maar omdat de <input> vrijwel onzichtbaar is, is het eigenlijk onmogelijk deze per ongeluk aan te raken of te -klikken. (Tijdens het testen lukte dat niet.) Ook wordt de lay-out niet verstoord door deze <input>

Als deze <input> wordt aangevinkt, wordt de slideshow aangepast voor schermlezers: alle grote afbeeldingen worden onder elkaar op het scherm gezet. Dit wordt geregeld met behulp van de css bij @media screen and (min-width: 600px) and (min-height: 600px).

Als mensen de Tab-toets gebruiken om links, <input>'s, en dergelijke af te lopen, wordt ook deze <input> bezocht. Dat heeft echter voor deze gebruikers geen enkel nut. Om de Tab-toets deze <input> te laten negeren, is het attribuut tabindex=-"1" toegevoegd.

<label id="label-voor-schermlezer" for="schermlezer">Maak toegankelijk voor schermlezer</label>

De <label> die bij de <input id="schermlezer"> gelijk hierboven hoort. In browservensters smaller of lager dan 600 px wordt deze <label> volledig verborgen. Alleen in grotere vensters heeft hij enig effect.

Deze <label> is niet zichtbaar, omdat hij onder de thumbnails staat. Dat maakt voor schermlezers echter niets uit: de tekst erin wordt gewoon voorgelezen. Maar omdat de <label> niet zichtbaar is, verstoort deze de lay-out niet.

Afbeelding 6
Afbeelding 6: <label> en <input> in Google Chrome, gelijk na het aanvinken..

Als de bijbehorende <input> is aangevinkt, wordt de <label> bij #schermlezer:checked + #label-voor-schermlezer zichtbaar gemaakt, samen met de bijbehorende <input id="schermlezer">. Op die manier is het duidelijk, hoe je de weergave weer naar standaard kunt terugzetten. Zonder dat zichtbaar maken is dat alleen duidelijk voor gebruikers van een schermlezer.

<input id="uitleg" type="checkbox" aria-hidden="true" aria-haspopup="true" tabindex="-1">

Als deze <input> wordt aangeraakt of -geklikt, opent een korte uitleg over de werking van de slideshow.

Voor schermlezers heeft deze uitleg geen nut, daarom wordt deze verborgen met de WAI-ARIA-code aria-hidden="true".

Als mensen de Tab-toets gebruiken om links, <input>'s, en dergelijke af te lopen, wordt ook deze <input> bezocht. De uitleg gaat echter voornamelijk over muis en scrollbalk, daarom heeft een gebruiker van de Tab-toets er weinig aan. Om de Tab-toets deze <input> te laten negeren, is het attribuut tabindex=-"1" toegevoegd.

<label id="label-voor-uitleg" for="uitleg" aria-hidden="true">?</label>

De <label> die bij de <input id="uitleg"> gelijk hierboven hoort. De tekst is wat kort: alleen een vraagteken. Een schermlezer leest dit vraagteken echter trouwhartig voor, daarom wordt dit verborgen met behulp van de WAI-ARIA-code aria-hidden="true".

<p id="uitleg-tekst" aria-hidden="true" aria-haspopup="true">

In deze <p> staat de een korte uitleg over de werking van de slideshow. Deze uitleg wordt getoond, als <input id="uitleg"> wordt aangevinkt.

Voor schermlezers heeft deze uitleg geen nut, daarom wordt deze verborgen met de WAI-ARIA-code aria-hidden="true".

In Internet Explorer 11 en Edge op touchscreens opent de uitleg niet goed bij het aanraken van <input id="uitleg">. Door toevoeging van aria-haspopup="true" wordt dit verholpen.

<b>Door thumbnails navigeren:</b><br>

De 'kopjes' in de uitleg worden wat opvallender gemaakt door ze met behulp van <b> vet weer te geven. De <b>-tag is eigenlijk een overblijfsel uit de tijd dat opmaak van een pagina gewoon in de html stond. Gebruik ervan wordt afgeraden: je kunt beter css gebruiken om iets vet te maken. Of, als het om een kopregel gaat, een <h>.

Dit is echter de afdeling liever lui dan moe. Bij gebruik van een <span> met css of zo, is extra css nodig. Bij gebruik van een <h> geldt dat nog sterker. En eigenlijk is dit alleen van belang voor schermlezers en zoekmachines. Schermlezers lezen die hele uitleg echter niet voor, en de kop is nou ook niet echt interessant voor zoekmachines. Daarom wordt in dit geval toch voor de simpele <b> gekozen. Maar normaal genomen is dat niet de beste oplossing.

Hetzelfde geldt voor de <br> aan het eind van de regel. Elke regel in de uitleg eindigt daarop. Normaal genomen zorgt een <p> of zoiets voor een nieuwe regel. In dit geval zijn het echter allemaal heel korte regels en zou je extra css nodig hebben, als je die allemaal in een <p> of zoiets zet.

&#x25cf; Veeg door de thumbnails (alleen op een touchscreen);<br>

Elke regel binnen de uitleg (met uitzondering van de inleiding en de 'kopjes') begint met &#x25cf;. Dit is een zogenaamde entiteit: een code die door de browser in een teken wordt omgezet, in dit geval het teken ●.

Het teken & aan het begin en de ; aan het eind geven het begin en het eind van de entiteit aan. Veel voorkomende symbolen en dergelijke hebben een naam, want dat is voor mensen veel makkelijker te onthouden dan een getal. Zo is bijvoorbeeld de entiteit voor het ©-teken &copy;. (In html5 is de lijst met dit soort namen sterk uitgebreid, maar niet alle namen worden door alle browsers ondersteund.)

Voor de ● bestaat geen naam. Daarom wordt voor de weergave hiervan teruggevallen op een nummer. Er bestaat een gestandaardiseerd systeem voor álle lettertekens ter wereld, voor alle talen. Daarnaast zitten daar nog tal van andere tekens in, zoals runentekens, dominostenen, emoticons, en noem maar op. Binnen dat systeem heeft elk teken een eigen volgnummer.

Dat volgnummer kan op verschillende manieren worden weergegeven. Het teken # betekent dat er een nummer volgt. De x geeft aan dat dat nummer in het hexadecimale (zestientallig) talstelsel is. Daarbij worden behalve de cijfers ook de letters 'a' tot en met 'f' gebruikt.

&#x25cf; bij elkaar betekent dus: zoek in de unicode-tabel het teken met het hexadecimale volgnummer 25cf op en geef dat weer.

(Bovenstaande is een wel heel sterk versimpelde weergave. Als je hierover meer wilt weten, kun je op internet zoeken naar 'unicode'. Ook op de pagina met links staan onder het kopje HTML → Charsets, unicode en entiteiten links naar meer informatie.)

<div id="wrap-1" class="wrap firefox" aria-haspopup="true" tabindex="0">

Elke thumbnail met bijbehorende links, grote afbeelding, en dergelijke staat binnen een <div class="wrap firefox">. Daarnaast heeft elke <div> een eigen id, zodat ernaartoe gelinkt kan worden.

Firefox handelt :focus anders af dan alle andere geteste browsers. Bij aanraken of -klikken van een thumbnail en bij gebruik van de Tab-toets is er geen verschil, maar als op 'n link naar vorige of volgende thumbnail of afbeelding wordt geklikt wel. Firefox negeert dan de bij :focus opgegeven css. Daarom wordt voor Firefox :target gebruikt, want dat werkt wel. Voor de afhandeling hiervan wordt speciaal voor Firefox class 'firefox' gebruikt. Meer hierover is te vinden bij @supports (-moz-appearance: none).

In Internet Explorer 11 en Edge op touchscreens opent de grote afbeelding niet goed bij aanraken van een <div class="wrap">. Door toevoeging van aria-haspopup="true" wordt dit verholpen.

Als mensen de Tab-toets gebruiken om links, <input>'s, en dergelijke af te lopen, wordt een <div> normaal genomen genegeerd door de Tab-toets. Een <div> kan normaal genomen geen focus krijgen. Hier wordt echter in de css :focus gebruikt om de grote afbeelding te tonen. Door het toevoegen van tabindex="0" kan de <div> toch focus krijgen, waardoor ook gebruikers van de Tab-toets de grote afbeeldingen kunnen zien.

<img src="039-pics/alpaca-300w.jpg" srcset="039-pics/alpaca-300w.jpg 1x, 039-pics/alpaca-600w.jpg 2x, 039-pics/alpaca-900w.jpg 3x" alt="Alpaca">

Deze <img> hoort bij de thumbnails. In browservensters smaller of lager dan 600 px wordt alleen een rij thumbnails getoond. Alleen in grotere vensters, waar meer ruimte is, wordt desgewenst naast of onder de thumbnail een grotere afbeelding getoond. Met behulp van deze regel kan de browser kiezen uit meerdere groottes van deze thumbnail.

<img: dit is gewoon de <img>-tag.

src="039-pics/alpaca-300w.jpg": gewoon de naam van de afbeelding. Dit is bedoeld voor oudere browsers die de hierop volgende srcset niet ondersteunen. Deze gebruiken altijd deze thumbnail. De '300w' in de naam is een simpele manier om aan te geven dat deze afbeelding 300 px breed is.

Als het browservenster groter is dan de thumbnail, wordt de thumbnail vergroot weergegeven. Dat kan, afhankelijk van de foto, een onduidelijke afbeelding opleveren. Het venster is echter nooit breder en hoger dan 600 px, dus het gaat hier om mobieltjes. Waarschijnlijk zijn er inmiddels geen mobieltjes meer in omloop die (veel) breder zijn dan 300 px én een browser hebben die srcset niet ondersteunt.

(Eventueel kun je dit oplossen door een zogenaamde polyfill te gebruiken: een stukje JavaScript dat zorgt voor ondersteuning voor oudere browsers. Voor srcset is zo'n polyfill te vinden op scottjehl.github.io/picturefill. Daar staat ook beschreven, hoe je die polyfill kunt gebruiken.)

srcset="039-pics/alpaca-300w.jpg 1x, 039-pics/alpaca-600w.jpg 2x, 039-pics/alpaca-900w.jpg 3x": hier staan drie afbeeldingen, gescheiden door een komma, waaruit de browser kan kiezen.

Er staan drie gewone bestandsnamen in, eindigend op '300w', '600w' en '900w'. De afbeeldingen zijn allemaal hetzelfde, maar de breedte is respectievelijk 300 px, 600 px en 900 px. (En uiteraard is ook de hoogte verschillend, anders krijg je 'n lachspiegel-foto.)

Achter de naam van de afbeelding staat '1x', '2x'of '3x'. Dat is de zogenaamde 'device-pixel ratio' Een hogeresolutiescherm bevat meer schermpixels op dezelfde afstand als een 'gewoon' scherm. Op alle schermen wordt de afbeelding even groot weergegeven, bijvoorbeeld 300 px breed. Als het scherm 600 pixels gebruikt om die afbeelding weer te geven op een breedte van 300 px, is de device-pixel ratio 600 / 300 = 2.

(Een veel langere uitleg over hogeresolutieschermen is te vinden bij Resolutie en afbeeldingen. Simpel ezelsbruggetje: een scherm met een device-pixel ratio van 2 heeft twee keer zoveel pixels op dezelfde afstand als een 'ouderwets' beeldscherm. Bij een device-pixel ratio van 3 drie keer zoveel, bij een device-pixel ratio van 1 evenveel, enzovoort.)

Op een hogeresolutiescherm wordt een grotere afbeelding gebruikt, maar die wordt op dezelfde grootte weergegeven als op een lageresolutiescherm. Hierdoor is de kwaliteit van de afbeelding (veel) beter.

De kans dat één van de drie afbeeldingen exact past binnen het browservenster is heel klein. Dat is geen probleem, omdat de browser dan in principe een grotere afbeelding zal nemen en deze zal verkleinen.

De opgegeven device-pixel ratio is niet meer dan een hint. De browser beslist uiteindelijk, welke afbeelding wordt gebruikt. Hierbij kunnen ook dingen als de snelheid van de verbinding meespelen.

(Meestal wordt bij srcset niet de device-pixel ratio opgegeven, maar de breedte waarop de afbeelding moet worden weergegeven. Dat werkt hier niet goed, omdat verschillende browsers de verkeerde afbeelding downloaden (een veel te grote). De weergave klopt wel, maar de bestanden zijn veel te groot. Meestal zal de opbouw van een pagina niet zoveel verschillen in grotere en kleinere browservensters en zal dit probleem niet spelen.)

<a class="vorige-t" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-t" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

Dit zijn de links naar de div.wrap met de vorige en volgende thumbnail. Omdat deze links bij de eerste thumbnail horen, linkt de vorige naar de laatste (veertiende) thumbnail, en de volgende naar de tweede thumbnail. Afgezien van waar ze naartoe linken en de class, zijn deze twee links volledig hetzelfde.

Met behulp van :target en :focus wordt de bij de div.wrap horende thumbnail gemarkeerd en worden de bij die thumbnail horende links op het scherm gezet.

In Internet Explorer 11 en Edge op touchscreens opent de grote afbeelding niet goed bij het aanraken van een thumbnail. Door toevoeging van aria-haspopup="true" bij elke div.wrap wordt dit verholpen. Door het gebruik van aria-haspopup="true" bij div.wrap denken Internet Explorer en Edge op touchscreens nu kennelijk dat hier ook iets te tonen valt: je moet de links (de pijltjes) twee keer aanraken, voordat de link wordt gevolgd. Door het attribuut aria-haspopup="false" is één aanraking weer genoeg.

<noscript>

<img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> </noscript>
<!-- <img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> -->

Afgezien van het verschil tussen <noscript> en <!--, en </noscript> en -->, staat hier twee keer hetzelfde.

De <img> tussen de <noscript>-tags wordt gebruikt, als JavaScript uitstaat. Alle afbeeldingen worden dan bij openen van de pagina gelijk gedownload.

Als JavaScript aanstaat, wordt geen enkele grote afbeelding gedownload bij openen van de pagina. De inhoud van <noscript> wordt genegeerd, en de tweede <img is veranderd in commentaar. Pas als een afbeelding daadwerkelijk wordt getoond, wordt het commentaar tussen <!-- en --> met behulp van JavaScript verandert in een normale <img>, die weergegeven kan worden.

De reden van deze ietwat vreemde constructie is te vinden bij Waarom het volledig herschreven moest worden.

De <img zelf en alles wat daarin staat, werkt precies hetzelfde als bij <img src="039-pics/alpaca-300w.jpg" ... iets hierboven, waar de thumbnails zijn beschreven. Het enige verschil is dat het hier niet om drie, maar om twee afbeeldingen gaat met een breedte van respectievelijk 900 en 1800 px.

<a class="vorige-a" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

<a class="volgende-a" href="#wrap-2" aria-haspopup="false" tabindex="-1" aria-hidden="true">→</a>

Dit zijn de links naar de div.wrap met de vorige en volgende grote afbeelding. Omdat deze links bij de eerste thumbnail horen, linkt de vorige naar de laatste (veertiende) afbeelding, en de volgende naar de tweede afbeelding. Afgezien van waar ze naartoe linken en de class, zijn deze twee links volledig hetzelfde.

Met behulp van :target en :focus wordt de bij de div.wrap horende thumbnail gemarkeerd en wordt de grote afbeelding met de bij die afbeelding horende links op het scherm gezet.

In Internet Explorer 11 en Edge op touchscreens opent de grote afbeelding niet goed bij het aanraken van een thumbnail. Door toevoeging van aria-haspopup="true" bij elke div.wrap wordt dit verholpen. Door het gebruik van aria-haspopup="true" bij div.wrap denken Internet Explorer en Edge op touchscreens nu kennelijk dat hier ook iets te tonen valt: je moet de links (de pijltjes) twee keer aanraken, voordat de link wordt gevolgd. Door het attribuut aria-haspopup="false" is één aanraking weer genoeg.

<script src="039-js-dl/afbeelding-039-dl.js"></script>

Met deze tag wordt het JavaScript aan de pagina gekoppeld, zodat het gebruikt kan worden. Deze tag wordt helemaal onderaan de pagina gezet, gelijk boven </body>. Het script zoekt namelijk elementen in de pagina op, en als die nog niet zijn gemaakt door de browser, gaat het mis. 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.

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

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

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

CSS

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

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

html

Het buitenste element, waarbinnen de hele pagina staat.

Normaal genomen zul je dit met een id gebruiken, omdat de css anders voor álle pagina's geldt, want elke pagina heeft nou eenmaal <html>. Dat werkt precies hetzelfde als een id bij andere tags:

<html id="ik-ben-een-id">

In de css komt dan te staan:

#ik-ben-een-id {...} of html#ik-ben-een-id {...}

Op de site bijvoorbeeld is dit opgelost door het gebruik van:

<html lang="nl" id="h-039> height: 100%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Omdat <html> het buitenste element is, is dat in dit geval het venster van de browser.

(Deze hoogte is nodig om div#android-temmer in browservensters smaller of lager dan 600 px in landschapsstand de volle hoogte van het venster te kunnen laten vullen. Een uitgebreider verhaal staat bij #android-temmer.)

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.

Sommige eigenschappen, zoals font-family en padding, die hieronder bij <body> worden gebruikt, zullen geen probleem zijn, als ze op alle pagina's van een site worden gebruikt. Maar voor een eigenschap als height: 100%; kan het wel een probleem zijn, als die op elke pagina wordt gebruikt. Als alleen body als selector wordt gebruikt, geldt die selector voor élke pagina.

Normaal genomen zul je daarom, als je een ietwat bijzondere eigenschap bij <body> gebruikt, <body> met een id gebruiken, omdat de css anders voor álle pagina's geldt, want elke pagina heeft nou eenmaal <body>. Dat werkt precies hetzelfde als een id bij andere tags:

<body id="ik-ben-een-id">

In de css komt dan te staan:

#ik-ben-een-id {...} of body#ik-ben-een-id {...}

Op de site bijvoorbeeld is dit opgelost door het gebruik van:

<body id="b-039"> 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.

height: 100%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. De ouder van <body> is <html>, die bij html even hoog als het venster van de browser is gemaakt.

(Deze hoogte is nodig om div#android-temmer in browservensters smaller of lager dan 600 px in landschapsstand de volle hoogte van het venster te kunnen laten vullen. Een uitgebreider verhaal staat bij #android-temmer.)

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

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

margin: 0; padding: 0;

Slim om te doen vanwege verschillen tussen browsers.

(Mogelijk is padding niet meer nodig, maar in het verleden verschilde de standaard-padding tussen browsers. Het is simpeler om dit gewoon te blijven gebruiken dan om een uitgebreide test uit te gaan voeren om te kijken, of dit nog wel nodig is.)

main

Alle <main>'s. Dat is er maar één. De belangrijkste inhoud van de pagina staat erin. In dit voorbeeld is dat de hele pagina.

display: block;

Oudere browsers kennen <main> niet. Een onbekend element is standaard een inline-element, waardoor eigenschappen als breedte niet kunnen worden gebruikt. Nu weten alle browsers dat dit een blok-element is.

height: 100%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. De ouder van <main> is <body>, die bij body even hoog als het venster van de browser is gemaakt.

(Deze hoogte is nodig om div#android-temmer in browservensters smaller of lager dan 600 px in landschapsstand de volle hoogte van het venster te kunnen laten vullen. Een uitgebreider verhaal staat bij #android-temmer.)

#android-temmer

Het element met id="android-temmer".

Dit element is nodig voor Android in browservensters minimaal 600 px breed en hoog. Over het waarom hiervan is meer te vinden bij #android-temmer. En omdat het element nou eenmaal toch aanwezig is, kan het ook worden gebruikt in kleinere vensters.

height: 100%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Dat is hier <main>, die bij main ook een hoogte van 100% heeft gekregen. <main> is hierdoor ook weer even hoog als z'n ouder <body>, die bij body ook een hoogte van 100% heeft gekregen: even hoog als z'n ouder <html>. Het wordt eentonig: ook <html> heeft bij html een hoogte van 100% gekregen. En omdat <html> het buitenste element is, geldt die hoogte ten opzichte van het browservenster: even hoog als het venster. Via <html>, <body> en <main> is #android-temmer hierdoor even hoog als het venster gemaakt.

In browservensters smaller of lager dan 600 px in landschapsstand, moeten de thumbnails de volledige hoogte van het venster vullen. Daarom wordt div#android-temmer even hoog als het venster gemaakt. (In grotere vensters en in portretstand wordt dit later aangepast.)

Normaal genomen wordt een blok-element niet hoger dan nodig is om de inhoud ervan weer te geven. Als je bij <html>, <body>, <main> of hier bij #android-temmer ergens height: 100%; weghaalt, wordt dat element niet hoger meer dan nodig is om de inhoud ervan weer te geven. De height: 100%; van kinderen van dat element geldt dan ten opzichte van die hoogte, en niet meer ten opzichte van het venster van de browser.

Afbeelding 7: zonder hoogte is het halve scherm leeg in Android

Als je bij <html>, <body>, <main> of div#android-temmer ergens height: 100%; weghaalt, is het resultaat hiervan op de afbeelding links te zien: niet hoger dan nodig is om de inhoud ervan weer te geven.

Je zou denken dat je ook gewoon bij div#wrapper een hoogte van 100 vh kan gebruiken. (1 vh is 1% van de hoogte van het venster van de browser, 100 vh is even hoog als het venster.) Dan is een hoogte bij <html>, <body> en <main> helemaal niet nodig, want vh geldt altijd ten opzichte van het venster, ongeacht tussenliggende hoogtes van voorouders.

Maar bij een hoogte van 100 vh wordt geen rekening gehouden met een eventuele horizontale scrollbalk. Nou zullen browservensters smaller of lager dan 600 px meestal geen scrollbalk hebben, maar in sommige browsers wordt ook bijvoorbeeld een adresbalk niet altijd meegeteld. Hierdoor kan een (fors) deel van de pagina soms onder onderdelen van de browser verdwijnen. Bij gebruik van % wordt wel rekening gehouden met dingen als een scrollbalk en adresbalk.

Het is onduidelijk, wat de diepere gedachtegang achter deze verschillen tussen vh (en vw) en procenten was bij het schrijven van de specificatie. Deze gedachtegang moet echter héél diep zijn geweest, want nogal wat mensen begrijpen geen fluit van het waarom van deze verschillen. Behalve dat het gigantisch onhandig is.

-webkit-overflow-scrolling: touch;

Op iOS ouder dan versie 13 gaat het scrollen van een deel van de pagina soms ongelooflijk houterig. Zo houterig, dat je feitelijk niet echt meer kunt scrollen. Dat speelt in dit voorbeeld in browservensters smaller of lager dan 600 px in landschapsstand, en in grotere vensters. Om toch soepel te kunnen scrollen, wordt deze eigenschap gebruikt. Alleen browsers op iOS ondersteunen dit, andere browsers negeren het.

(Een uitgebreider verhaal over -webkit-overflow-scrolling en het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a

De elementen met id="checkbox-voor-focus", id="voor-schermlezers", id="schermlezer", id="label-voor-schermlezer", id="uitleg", id="label-voor-uitleg", id="uitleg-tekst", class="foto", class="vorige-t", class="volgende-t", class="vorige-a" en class="volgende"a".

Deze elementen regelen scrollen met de pijltjestoetsen, aanpassingen voor een schermlezer, tonen van de uitleg, tonen van de grote afbeelding, en naar de vorige of volgende thumbnail of afbeelding gaan. Allemaal dingen die alleen in browservensters minimaal 600 px breed en hoog nodig zijn, want in kleinere vensters staat alleen maar een rij of kolom met (grotere of kleinere) thumbnails.

display: none;

Verbergen. Later worden ze weer tevoorschijn gehaald voor browservensters minimaal 600 px breed en hoog.

#wrapper

Het element met id="wrapper". De <div> waar de hele slideshow in zit.

Buiten deze <div> zitten alleen wat hulpmiddelen voor scrollen met de pijltjestoetsen en een keuze om de slideshow aan te passen voor een schermlezer. (Ook div#android-wrapper zit hier buiten, maar die is alleen nodig voor een probleem in Android. Daar is meer over te vinden bij #android-temmer.)

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

Hiermee wordt div#wrapper 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 div#wrapper zijn hier div#titel (met de titel), input#uitleg, label#label-voor-uitleg en p#uitleg-tekst (met alles voor de uitleg) en de veertien div#wrap's met elk een thumbnail met bijbehorende elementen. Deze hebben allemaal <main> als ouder en veranderen daardoor in flex items.

(input#uitleg, label#label-voor-uitleg en p#uitleg-tekst worden later apart afgehandeld, want die zijn alleen bedoeld om de uitleg te tonen. Bovendien zijn ze gelijk hierboven bij #checkbox-voor-focus, ..." verborgen en worden pas later weer zichtbaar gemaakt.)

Flex items worden standaard naast elkaar weergegeven, ook als het <div>'s of andere blok-elementen zijn. Als je verder helemaal geen flex-eigenschappen meer zou opgeven, heb je met alleen display: flex; nu al div#titel en de veertien div.wrap's naast elkaar staan.

(In dit geval komen ze naast elkaar te staan, omdat dat de standaardinstelling van flex-direction is. Voor een aantal flex-eigenschappen zoals flex-basis is die richting van belang. flex-basis bijvoorbeeld heeft nu invloed op de breedte van de flex items. Zou je hier flex-direction: column; gebruiken, dan komen de flex items onder elkaar te staan en zou flex-basis invloed hebben op de hoogte van de flex items. Dat soort eigenschappen wordt hier verder niet gebruikt.)

Dat de div.wrap's naast elkaar staan en niet onder elkaar, is leuk in landschapsstand, maar niet in portretstand. Dat wordt later bij #wrapper aangepast met een andere flex-eigenschap: flex-wrap.

height: 98%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Dat is hier #android-temmer, die bij #android-temmer met height: 100%; even hoog als het browservenster is gemaakt.

Door de hoogte hier iets minder te nemen, komt er wat ruimte aan boven- en onderkant vrij, waardoor het er wat minder opgepropt uitziet.

Omdat bij img de thumbnails 100% hoog worden gemaakt, krijgen ook de thumbnails deze hoogte.

overflow-y: hidden;

Op Firefox, Safari, Internet Explorer, Edge en nog wat kleinere browsers na, is elke browser op weergave-machine Blink gebaseerd. Dat is dus het overgrote deel van de browsers. (Binnenkort stapt ook Edge daar op over.) Alleen op iOS en iPadOS is dat niet zo, omdat browsers daarop verplicht de weergave-machine van Safari moeten gebruiken.

Hierboven is bij flex beschreven, dat div#titel en de veertien div.wrap's, de directe kinderen van div#wrapper, flex items worden. De hoogte van die flex items wordt bij #titel en .wrap opgegeven in procenten: 98%. Blink kan met een hoogte in procenten bij flex items niet goed uit de voeten, om het netjes te zeggen. In browservensters smaller of lager dan 600 px in landschapsstand worden de flex items verkleind tot ongeveer een derde van de hoogte van het venster.

Deze regel lost dat om een of andere reden op.

(Op internet zijn meer oplossingen te vinden. Zo werkt bijvoorbeeld ook het toevoegen van flex: 0 0 98%; height: 1px; aan #titel en .wrap. Het gaat om die hoogte van 1 px: zodra er iets van 'n hoogte met pixel staat, werkt de 98%. Maar dat heeft mogelijk weer andere bijwerkingen en dit is de simpelste oplossing.)

Deze regel heeft nog een andere gewenste bijwerking. #wrapper is een blok-element en wordt daardoor normaal genomen automatisch even breed als z'n ouder. Die ouder is div#android-temmer, ook een blok-element en dus normaal genomen ook even breed als z'n ouder <body>. Ook weer een blok-element, dus <body> wordt normaal genomen even breed als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk wordt #wrapper via deze hele keten dus even breed als het venster, ongeacht de breedte van het venster.

Maar de rij met thumbnails is veel breder dan het browservenster. Omdat overflow standaard op visible staat, zie je die thumbnails toch allemaal. Dat is normaal genomen prima: als iets niet binnen het element past, wordt het toch getoond. Mogelijk wordt de lay-out wat verstoord, maar er verdwijnt in ieder geval geen tekst of zo.

Hierboven is aan div#wrapper een witte achtergrondkleur gegeven. Omdat div#wrapper eigenlijk niet breder is dan het venster van de browser, zie je die witte achtergrondkleur alleen binnen die breedte. Zodra je de thumbnails gaat scrollen, verdwijnt de witte achtergrond.

Afbeelding 8: achtergrondkleur is niet over de volle breedte aanwezig

Op de afbeelding is de tweede thumbnail in beeld gescrold. De achtergrondkleur is even veranderd in rood, omdat dat duidelijker is dan wit.

De achtergrondkleur eindigt halverwege de tweede thumbnail. Precies op de breedte van het browservenster.

Als bij overflow (of overflow-x of overflow-y) een andere waarde dan visible wordt opgegeven, omspant div#wrapper de volledige inhoud ervan. Hier is dat de hele rij met thumbnails. Nu staat tussen alle thumbnails een witte achtergrond.

padding-top: 1%;

⁠Bij marge en padding geldt een grootte in procenten ten opzichte van de breedte van de ouder, niet ten opzichte van de hoogte. Dat geldt ook voor horizontale marges en paddings. (Dat lijkt vreemd, maar meestal is dat, wat je wilt.)

Die ouder is div#android-wrapper, die even breed is als het venster van de browser. Die 1% geldt dus ten opzichte van het venster. Dat is niet heel erg precies, maar in de praktijk komen de thumbnails hierdoor ongeveer verticaal in het midden van het venster te staan.

#titel

Het element met id="titel". De <div> waar de titel 'Mijn huisdieren' in staat.

box-sizing: border-box;

Standaard worden marge, padding en border bij de breedte van het element opgeteld. Marge en padding worden niet gebruikt bij div#titel, maar border wel. Door deze regel worden border en padding niet bij de breedte opgeteld, maar geldt de breedte inclusief border en padding (de marge wordt nog steeds opgeteld bij de breedte).

In dit geval komt dat handiger uit, omdat het nu makkelijker is de border van de <div> aan boven- en onderkant gelijk te laten vallen met de boven- en onderkant van de thumbnails.

min-width: 3em; max-width: 3em;

Door de minimum- en maximumbreedte even groot te maken, is div#titel altijd 3 em breed.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen div#titel en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

height: 98%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper. Omdat later bij .wrap de div.wrap's met de thumbnails even hoog worden gemaakt, is de <div> even hoog als de thumbnails.

overflow: auto;

Als in browservensters minimaal 600 px breed en hoog in portretstand de tekst wordt vergroot, kan die te groot worden om nog binnen div#titel te passen. Als dat het geval is, kan de tekst nu worden gescrold. Afhankelijk van browser en besturingssysteem kan rechts van de titel een verticale scrollbalk verschijnen.

text-align: center;

Tekst horizontaal centreren.

margin: 0 0 5px 5px;

Boven en rechts geen marge, onder en links een marge van 5 px.

border: black solid 1px;

Zwart randje.

h1

Alle <h1>'s. Dat is er maar één: de belangrijkste kopregel, waarin de titel staat.

font-size: 1.2rem;

Lettergrootte.

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

De rem is ongeveer hetzelfde als de bekendere eenheid em, maar is gebaseerd op de lettergrootte van <html>. Hierdoor is de rem, anders dan de em, overal op de pagina even groot. Ook als de bezoeker de lettergrootte heeft veranderd.

font-weight: normal;

Een <h> is van zichzelf vet. Hier wordt dat veranderd naar normaal.

margin-top: 2px;

Een <h1> heeft van zichzelf een marge aan boven- en onderkant. De marge aan de onderkant mag blijven, maar de marge aan de bovenkant wordt wat kleiner gemaakt.

-moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;

Hier staat in feite vier keer hetzelfde: user-select: none;. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Als op een touchscreen het vraagteken iets langer wordt aangeraakt, opent de uitleg niet, maar wordt het vraagteken geselecteerd. Vooral in Internet Explorer en Edge kun je lange winteravonden opvrolijken met het familiespel 'Wie lukt het los te laten zonder iets te selecteren?'.

Dit maakt het onmogelijk het vraagteken te selecteren.

h1 span

Alle <span>'s binnen een <h1>. Dat is er hier maar één. Het tweede woord van de titel 'Mijn huisdieren' staat erin. In een aantal groottes van het browservenster neemt gewone horizontale tekst te veel ruimte in. Door 'huisdieren' in een <span> te zetten, kun je dit verticaal onder 'Mijn' neerzetten.

display: block;

Een <span> is een inline-element. Bij de meeste inline-elementen, waaronder de <span>, kan transform niet worden gebruikt. Door de <span> in een blok-element te veranderen, kan die eigenschap wel worden gebruikt.

font-size: 1.3rem;

Lettergrootte. Door de lettergrootte van 'huisdieren' iets groter te maken dan die van 'Mijn', lijken ze voor het oog juist even groot.

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

De rem is ongeveer hetzelfde als de bekendere eenheid em, maar is gebaseerd op de lettergrootte van <html>. Hierdoor is de rem, anders dan de em, overal op de pagina even groot. Ook als de bezoeker de lettergrootte heeft veranderd.

margin-top: 14px; margin-left: 6px;

Boven een marge van 14 px, links een marge van 6 px. Hier gelijk onder wordt de <span> met het woord 'huisdieren' 90° gedraaid. Deze marges zetten de tekst op de juiste plaats neer. Ze zijn gevonden door gewoon even uitproberen.

transform: rotate(90deg); Afbeelding 9: titel horizontaal en titel gedeeltelijk verticaal

<span> 90° draaien, zodat de tekst verticaal komt te staan.

Op de afbeelding links staat de tekst gewoon horizontaal. Dat is tamelijk breed in verhouding tot de thumbnails (die even doorzichtig zijn gemaakt voor de duidelijkheid). Je krijgt dan 'n brede ruimte met slechts enkele woorden. Je zou die ruimte ook kunnen vullen met meer tekst.

In dit geval is gekozen voor het verticaal neerzetten van het woord 'huisdieren', zoals op de rechterafbeelding is te zien.

.wrap

Alle elementen met class="wrap".

In elke div.wrap zit een thumbnail met bijbehorende grote afbeelding en de bijbehorende links naar de div.wrap met de vorige en volgende thumbnail en grote afbeelding.

height: 98%;

Een hoogte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper. Omdat bij #titel de <div> met de titel even hoog is gemaakt, zijn deze <div>'s even hoog als de <div> met de titel. En omdat verderop bij img de thumbnails met height: 100%; even hoog worden gemaakt als deze <div>'s, zijn ook de thumbnails even hoog als de <div> met de titel.

margin: 0 5px;

Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch de zelfde 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 een marge van 5 px.

.wrap:last-child

De elementen met class="wrap" die een laatste kind zijn.

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 elementen selecteren op basis van het volgnummer binnen zo'n reeks kinderen. In dit geval wordt geen echt volgnummer gebruikt, maar :last-child: alleen het laatste kind.

.wrap: de elementen met class="wrap". De veertien div.wrap's met thumbnail en bijbehoren.

:last-child: alleen de elementen met class="wrap" die een laatste kind zijn.

In dit voorbeeld is maar één serie elementen met class="wrap" aanwezig. Dat zijn allemaal kinderen van div#wrapper. Het gaat hier dus om het laatste element met class="wrap", de laatste div.wrap in div#wrapper. In deze div.wrap zit de laatste thumbnail met toebehoren.

De selector :last-child kan onverwachte bijwerkingen hebben. De div.wrap met de laatste thumbnail en toebehoren is het laatste kind van div#wrapper. Deze <div> moet anders worden behandeld dan de eerdere <div>'s.

Je moet zeker weten dat het element met de laatste thumbnail en toebehoren inderdaad het laatste kind is. Dat is hier echt het geval: de <div> met de laatste thumbnail is altijd het laatste kind van div#wrapper. Maar als later aan de html een element toegevoegd zou worden dat na de <div> met de laatste thumbnail komt te staan, is die <div> ineens geen laatste kind meer.

Verder mogen andere elementen die geen kind van div#wrapper zijn geen class="wrap" hebben. Als buiten div#wrapper ergens nog andere elementen met class="wrap" staan, zit ook daar mogelijk een laatste kind bij. Deze selector zou dan ook voor dat element gelden.

Maar hier is deze selector zonder problemen te gebruiken, want de <div> met de laatste thumbnail moet altijd het laatste kind van div#wrapper zijn. Dat is een voorwaarde voor het JavaScript, zoals omschreven bij Voorwaarden van het JavaScript waaraan html en css moeten voldoen.

padding-right: 5px;

Kleine afstand tussen de rechterkant van de laatste div.wrap (en daarmee de laatste thumbnail) en de rechterkant van het browservenster.

img

Alle afbeeldingen.

box-sizing: border-box;

Standaard worden marge, padding en border bij de breedte van het element opgeteld. Marge en padding worden niet gebruikt bij de <img>'s, maar border wel. Door deze regel worden border en padding niet bij de breedte opgeteld, maar geldt de breedte inclusief border en padding (de marge wordt nog steeds opgeteld bij de breedte).

In dit geval komt dat handiger uit.

Gelijk hieronder worden de afbeeldingen even hoog worden gemaakt als het element, waar ze in zitten. Dat is voor de thumbnails div.wrap, die bij .wrap even hoog is gemaakt als div#titel met de titel. Hierdoor worden ook de thumbnails even hoog als de titel. (Voor de grote afbeeldingen wordt de hoogte later aangepast, dus dit geldt alleen voor de thumbnails.)

Hier iets onder wordt een border van 1 px aan alle kanten gegeven. Als die border bij de thumbnails zou worden opgeteld, worden de thumbnails 2 px hoger dan de <div> met de titel.

height: 100%;

Een hoogte in procenten is normaal genomen ten opzichte van de ouder van het element. Voor de thumbnails is dat de div.wrap, waar de thumbnail in zit (voor de grote afbeeldingen wordt de hoogte later aangepast).

border: black solid 1px;

Zwart randje.

css voor vensters in portretstand en maximaal 500 px breed

@media screen and (orientation: portrait) and (max-width: 500px)

De css die hier tot nu toe staat, geldt voor alle browservensters. De css die hieronder staat, geldt alleen voor browservensters in portretstand maximaal 500 px breed. In deze vensters worden de thumbnails niet naast, maar onder elkaar gezet.

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

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: 500px): het browservenster mag niet breder zijn dan 500px. 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: 500px) { 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 }.

Afbeelding 10: weergave in smartphones in portretstand

(Deze media query is bedoeld voor tablets, smartphones, en dergelijke, waar het browservenster meestal even groot als het scherm zal zijn. Voor desktop monitors zal de stand meestal niet echt van belang zijn. In combinatie met de lage maximumbreedte van 500 px wordt helemaal duidelijk, dat dit niet voor de desktop is bedoeld. Maar in vrijwel alle browsers werkt dit ook op de desktop: maak het venster smaller dan 500 px en hoger dan 500 px en je hebt een venster in portretstand.)

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 500 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 ('pixels per inch'). Als dat mobieltje een resolutie van 192 ppi heeft, 192 pixels per inch, zijn de pixels ervan twee keer zo klein als op een origineel beeldscherm. Er zijn per inch twee keer zoveel schermpixels aanwezig.

Om nu te voorkomen dat alles op dat mobieltje twee keer zo klein wordt, geeft het mobieltje niet het echte aantal schermpixels (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 (Althans: in portretstand. In landschapsstand is het wel breder.)

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.

#android-temmer

Deze selector werkt alleen in browservensters maximaal 500 px breed in portretstand. 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.)

#android-temmer {height: 100%; -webkit-overflow-scrolling: touch;}

-webkit-overflow-scrolling: auto;

Eerder is bij #android-temmer de waarde touch opgegeven voor deze eigenschap, omdat enigszins normaal scrollen van de thumbnails in horizontale richting anders onmogelijk is op iOS ouder dan versie 13.

In portretstand staan de thumbnails niet naast, maar onder elkaar. Scrollen blijkt nu ook zonder deze eigenschap goed te werken. Waarom dat zo is, is onduidelijk, want de precieze werking van deze eigenschap behoort tot de Grote Raadselen Der Kosmos. Documentatie erover is vrijwel afwezig.

De waarde touch bij deze eigenschap kan uiterst ongewenste bijwerkingen hebben, tot en met het verdwijnen van delen van de pagina. Als die bijwerkingen kleiner zijn, zijn ze uiterst moeilijk te vinden. Daarom wordt, zodra het kan, bij deze ellendepukkel pardon eigenschap de standaardwaarde auto weer teruggezet.

(Een uitgebreider verhaal over -webkit-overflow-scrolling en het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

#wrapper

Deze selector werkt alleen in browservensters maximaal 500 px breed in portretstand. 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.)

#wrapper {background: white; display: flex; height: 98%; overflow-y: hidden; padding-top: 1%;}

Het element met id="wrapper". De <div> waar de hele slideshow in zit.

Buiten deze <div> zitten alleen wat hulpmiddelen voor scrollen met de pijltjestoetsen en een keuze om de slideshow aan te passen voor een schermlezer. (Ook div#android-wrapper zit hier buiten, maar die is alleen nodig voor een probleem in Android. Daar is meer over te vinden bij #android-temmer.)

flex-wrap: wrap;

Bij #wrapper is div#wrapper met display: flex; veranderd in een zogenaamde 'flex container'. Daarmee zijn de directe kinderen van div#wrapper veranderd in 'flex items'. Die directe kinderen zijn div#titel met de titel en de veertien div.wrap's met in elk een thumbnail met toebehoren.

Afbeelding 11: zonder flex-wrap worden de afbeeldingen sterk verkleind

Flex items worden standaard naast elkaar weergegeven, ook als het blok-elementen zoals een <div> zijn. Bovendien worden ze, tenzij dat wordt aangepast, verkleind tot ze allemaal in de flex container passen. Op de afbeelding is dat te zien: alle veertien thumbnails zijn zichtbaar. Hoewel 'zichtbaar' een wat grootse omschrijving is, gezien hoe klein ze zijn geworden.

Met deze regel worden de flex items niet naast, maar onder elkaar gezet.

width: 96%;

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#android-temmer. De ouder daarvan is <body>, en de ouder daarvan is <html>. Omdat bij deze drie blok-elementen nergens een breedte is opgegeven, worden deze automatisch steeds even groot als hun ouder. <html> is het buitenste element en wordt daarom net zo breed als het venster van de browser.

Uiteindelijk krijgt div#wrapper hierdoor 96% van de breedte van het venster van de browser, ongeacht de breedte van het venster. Hier iets onder wordt een padding van 2% opgegeven. De totale breedte wordt hierdoor 100%, even breed als het venster.

height: auto;

Eerder is bij #wrapper div#wrapper met height: 98%; bijna even hoog gemaakt als het venster van de browser. In portretstand staan de thumbnails onder elkaar. Normaal genomen wordt een blok-element precies hoog genoeg om de inhoud ervan weer te kunnen geven. Door de standaardwaarde auto in te vullen, gebeurt dat nu weer ook bij div#wrapper.

padding: 2%;

Aan alle kanten 2% ruimte tussen inhoud en buitenkant van div#wrapper.

#titel

Deze selector werkt alleen in browservensters maximaal 500 px breed in portretstand. 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

Het element met id="titel". De <div> waar de titel 'Mijn huisdieren' in staat.

width: 100%;

De ouder van div#titel is div#wrapper, die bij #wrapper met display: flex; in een 'flex container' is veranderd. div#titel is daardoor in een 'flex item' veranderd. Standaard wordt een flex item niet breder, dan nodig is om de inhoud ervan weer te geven. Daarom moet een breedte worden opgegeven.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper.

div#wrapper is altijd even breed als het venster van de browser, en daarmee div#titel ook. Omdat de div.wrap's verderop bij .wrap dezelfde breedte krijgen, zijn de daarin zittende thumbnails even breed als de titel.

max-width: none;

De eerder opgegeven maximumbreedte verwijderen.

In landschapsstand stond 'huisdieren' verticaal onder 'Mijn'. In portretstand komt de titel gewoon op normale wijze horizontaal boven de thumbnails te staan. Een maximumbreedte is niet meer nodig.

margin-left: 0;

De marge aan de linkerkant verwijderen.

h1

Deze selector werkt alleen in browservensters maximaal 500 px breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.

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

h1 {font-size: 1.2rem; font-weight: normal; margin-top: 2px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

Alle <h1>'s. Dat is er maar één: de belangrijkste kopregel, waarin de titel staat.

margin: 5px;

Aan alle kanten een marge van 5 px voor wat afstand tussen de <h1> en z'n ouder div#titel.

h1 span

Deze selector werkt alleen in browservensters maximaal 500 px breed in portretstand. Voor andere vensters is de uitleg hieronder niet van belang.

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

h1 span {display: block; font-size: 1.3rem; margin-top: 14px; margin-top: 6px; transform: rotate(90deg);}

Alle <span>'s binnen een <h1>. Dat is er hier maar één. Het tweede woord van de titel 'Mijn huisdieren' staat erin.

display: inline; Afbeelding 12: titel weer volledig horizontaal

Eerder is deze <span> in een blok-element veranderd. Daardoor komt de <span>, en daarmee ook het erin zittende 'huisdieren', op een nieuwe regel te staan. Door de <span> weer in een inline-element te veranderen, komen 'Mijn' en 'huisdieren' op dezelfde regel te staan.

Eerder is met behulp van transform de <span> met de tekst 90° gedraaid. Omdat transform niet werkt op een inline-element, komt de tekst weer gewoon horizontaal te staan.

font-size: inherit;

Eerder is bij h1 span de lettergrootte van de <span> met 'huisdieren' iets groter gemaakt dan de <h1>, waar de <span> in zit. Omdat het hier weer een gewone regel met tekst is, moet de lettergrootte van beide woorden in de titel even groot zijn.

Met het sleutelwoord inherit erft de <span> de lettergrootte van z'n ouder <h1>.

margin: 0;

De eerder opgegeven marges zijn ook niet meer nodig.

.wrap

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

.wrap {height: 98%; margin: 0 5px;}

Alle elementen met class="wrap".

In elke div.wrap zit een thumbnail met bijbehorende grote afbeelding en de bijbehorende links naar de div.wrap met de vorige en volgende thumbnail en grote afbeelding.

width: 100%;

De ouder van de div.wrap's is div#wrapper, die bij #wrapper met display: flex; in een 'flex container' is veranderd. De div#wrap's zijn daardoor in een 'flex item' veranderd. Standaard wordt een flex item niet breder, dan nodig is om de inhoud ervan weer te geven. Daarom moet een breedte worden opgegeven.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper.

div#wrapper is altijd even breed als het venster van de browser, en daarmee de div.wrap's ook. Omdat ook de in de div.wrap's zittende thumbnails iets hieronder bij img een breedte van 100% krijgen, worden ook deze even breed als het venster.

Eerder heeft bij #titel ook div#titel een breedte van 100% gekregen, dus ook de titel heeft deze breedte.

margin: 2px 0;

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

Boven en onder een marge van 2 px voor iets meer afstand tussen de thumbnails, rechts en links geen marge.

img

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

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

Alle afbeeldingen.

width: 100%;

Een breedte in procenten geldt normaal genomen ten opzichte van de ouder van het element. Die ouder is hier de div.wrap, waar de <img> in zit. Bij .wrap zijn die even breed gemaakt als het browservensters. Hierdoor zijn ook de thumbnails even breed als het venster.

(Voor de grote afbeeldingen wordt dit later aangepast.)

css voor vensters minimaal 600 px breed en hoog

@media screen and (min-width: 600px) and (min-height: 600px)

Afbeelding 13: slideshow in vensters minimaal 600 px breed en hoog

De opbouw van deze regel staat beschreven bij @media screen and (orientation: portrait) and (max-width: 500px). Er zijn drie verschillen: portret- of landschapsstand maakt niet uit, de breedte moet minimal 600 px zijn, en ook de hoogte moet minimaal 600 px zijn.

In browservensters van deze grootte staan de thumbnails in een rij bovenaan het venster en wordt de grote afbeelding eventueel daaronder getoond. (In vensters minimaal 900 px breed wordt dit later veranderd in een verticale rij thumbnails links, en de grote afbeelding wordt eventueel daarnaast getoond.)

#android-temmer

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#android-temmer {height: 100%; -webkit-overflow-scrolling: touch;}

Het element met id="android-temmer". Dit element is alleen nodig om een probleem in Android op te lossen.

In browservensters van deze grootte komt bovenin het venster een rij thumbnails te staan. Elk van deze thumbnails zit (met alle toebehoren) in een eigen div.wrap, die ongeveer even breed is als de thumbnail. Er zijn veertien div.wrap's, die naast elkaar komen te staan in hun ouder div#wrapper. Hierdoor wordt div#wrapper veel breder dan het venster.

Als een grote afbeelding moet worden getoond, wordt deze bij .foto img met position: fixed; in het midden van het browservenster neergezet. Althans: dat zou moeten gebeuren. In Android blijken de grote afbeeldingen echter in het midden van div#wrapper te worden neergezet. En omdat die <div> veel breder is dan het venster, komen de meeste afbeeldingen ergens buiten het venster te staan. Om een of andere reden lijken browsers op Android het venster even breed te maken als div#wrapper.

Daarom wordt om div#wrapper een extra <div> neergezet: div#android-temmer. Als deze <div> absoluut wordt gepositioneerd en even breed als het venster van de browser wordt gemaakt, blijken de grote afbeeldingen ook op Android op de juiste plaats neergezet te worden. div#wrapper met de thumbnails kan scrollen binnen div#android-temmer, dus alle thumbnails zijn gewoon bereikbaar.

Browsers op alle andere geteste systemen gebruiken gewoon de position: fixed; bij .foto img, maar hebben geen last van deze extra dingen voor Android. Op de desktop is er zelfs een bijkomend voordeel: de scrollbalk staat nu gelijk onder of rechts van de thumbnails, en niet helemaal onderaan of helemaal rechts in het browservenster.

height: auto;

Normaal genomen wordt een blok-element zoals een <div> automatisch precies hoog genoeg om de inhoud ervan weer te kunnen geven. Eerder is bij #android-temmer is div#android-temmer even hoog gemaakt als het venster van de browser, zodat ook de thumbnails zo hoog kunnen zijn.

Hier is dat niet meer nodig en wordt de hoogte naar de standaardinstelling auto teruggezet. Nu wordt de hoogte van #android-temmer bepaald door de inhoud ervan: de veertien thumbnails met toebehoren.

overflow: auto;

Als de inhoud van div#wrapper breder of hoger is dan de <div>, kan de inhoud nu worden gescrold. Die inhoud is hier breder, want die is veertien thumbnails breed, en gelijk hieronder wordt div#wrapper even breed als het venster van de browser gemaakt. Afhankelijk van systeem en browser kan onder de thumbnails een horizontale scrollbalk verschijnen.

border-bottom: black solid 1px;

Randje aan de onderkant.

position: absolute;

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.

Door div#wrapper een andere positie dan statisch te geven, kunnen nakomelingen van de <div> worden gepositioneerd ten opzichte van de <div>.

right: 0; left: 0;

De rechterkant van div#wrapper rechts in het browservenster zetten, de linkerkant links in het venster. Omdat div#wrapper nu horizontaal 'klem staat' in het venster, wordt gelijk een probleem in Android opgelost. (Meer over dat probleem staat iets hierboven gelijk onder het kopje #android-temmer.)

#checkbox-voor-focus, #voor-schermlezers

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

De elementen met id="checkbox-voor-focus" en id="voor-schermlezers".

input#checkbox-voor-focus wordt alleen gebruikt om gelijk bij openen van de pagina door de thumbnails scrollen met de pijltjestoetsen mogelijk te maken. In div#voor-schermlezers zit een tekst die is bedoeld voor gebruikers van een schermlezer.

display: block;

De eerder verborgen elementen zichtbaar maken.

position: absolute;

Om de <input> en de <div> 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. In dit geval is dat div#android-wrapper, de ouder van de elementen, die bij #android-temmer een absolute positie heeft gekregen.

left: -20000px;

Ver links buiten het scherm parkeren, zodat de lay-out niet wordt verstoord.

Voor input#checkbox-voor-focus maakt dit niets uit voor de werking. Omdat de <input> ook niet aangevinkt hoeft te worden of zo, levert het buiten het scherm plaatsen geen enkel probleem op.

De tekst in div#voor-schermlezers wordt gewoon voorgelezen door schermlezers, ook al staat deze buiten het scherm.

#schermlezer

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

Het element met id="schermlezer". Met behulp van dit aankruisvakje (<input type="checkbox">) kan de slideshow worden aangepast voor schermlezers. Als dit aankruisvakje is aangevinkt, worden alle grote afbeeldingen in één keer getoond.

Het zou het makkelijkste zijn, als dit aankruisvakje gewoon buiten het scherm kon worden geparkeerd. Helaas werkt het dan niet in TalkBack op oudere versies van Android. Voor TalkBack op oudere versies van Android moet minimaal een plekje van 1 x 1 px van een aankruisvakje binnen het browservenster staan, niet verborgen onder een ander element. (Meer hierover is te vinden bij Probleem: in TalkBack op Android moet een link en dergelijke binnen het venster staan.)

Daarom worden ze noodgedwongen op het scherm gezet, maar zo klein mogelijk gemaakt. Dit maakt de kans dat ze per ongeluk worden aangeraakt of -geklikt door niet-gebruikers van een schermlezer uiterst klein. Bij het testen is het in ieder geval niet gelukt om dit aankruisvakje met de hand of de muis aan te vinken.

-moz-appearance: none; -webkit-appearance: none; appearance: none;

Hier staat in feite drie keer hetzelfde: appearance: none;. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Hiermee wordt het standaard-uiterlijk van een aankruisvakje uitgeschakeld. Of juister: er blijft helemaal niets van over. Het aankruisvakje is er nog wel, maar je ziet er niets meer van.

Helaas doet zich nu in TalkBack op oudere versies van Android weer het hierboven omschreven probleem voor: je kunt het aankruisvakje niet meer aan- of uitvinken, daarvoor is nog steeds een oppervlakte van minimaal 1 x 1 px nodig, wat gelijk hieronder wordt geregeld.

display: block;

Het eerder met display: none; verborgen aankruisvakje weer tonen.

width: 1px; height: 1px;

De kleinst mogelijke oppervlakte aan het aankruisvakje geven. Zonder oppervlakte kun je het niet uit- en aanvinken in TalkBack op oudere versies van Android, zoals hierboven beschreven.

position: absolute;

Om de <input> 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. In dit geval is dat div#android-wrapper, de ouder van de <input>, die bij #android-temmer een absolute positie heeft gekregen.

top: 10px; right: 10px;

In de rechterbovenhoek neerzetten.

z-index: 10;

De <input> is absoluut gepositioneerd. Daardoor negeren andere elementen hem, waardoor lager in de html zittende elementen bovenop de <input> komen te staan. Zoals iets hoger beschreven, werkt deze dan niet meer in oudere versies van Android.

Door de <input> een hogere z-index te geven, staat deze altijd bovenaan.

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

#label-voor-schermlezer

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

Het element met id="label-voor-schermlezer". De <label> die bij input#schermlezer hoort. In deze <label> zit de tekst 'Maak toegankelijk voor schermlezer'.

display: block;

De eerder met display: none; verborgen <label> weer tonen. Althans: dat echte tonen gebeurt later, want iets hieronder wordt de <label> iets boven het browservenster neergezet. Voor een schermlezer maakt dat echter niets uit, die leest de tekst in de <label> gewoon voor.

position: absolute;

Om de <label> 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. In dit geval is dat div#android-wrapper, de ouder van de <label>, die bij #android-temmer een absolute positie heeft gekregen.

top: -40px;

Iets boven het venster van de browser neerzetten, zodat de lay-out niet wordt verstoord.

right: 0;

Helemaal rechts neerzetten.

#wrapper

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#wrapper {background: white; display: flex; height: 98%; overflow-y: hidden; padding-top: 1%;}

Het element met id="wrapper". De <div> waar de hele slideshow in zit.

Buiten deze <div> zitten alleen wat hulpmiddelen voor scrollen met de pijltjestoetsen en een keuze om de slideshow aan te passen voor een schermlezer. (Ook div#android-wrapper zit hier buiten, maar die is alleen nodig voor een probleem in Android. Daar is meer over te vinden bij #android-temmer.)

height: 150px;

Eerder is div#wrapper bij #wrapper ongeveer even hoog als het venster van de browser gemaakt. Hier wordt die hoogte teruggebracht tot 150 px, zodat eronder ruimte komt om eventueel een grote foto te tonen.

Bij .wrap zijn de div.wrap's vrijwel even hoog gemaakt, en bij img zijn de in de div.wrap's zittende thumbnails even hoog als de div.wrap's gemaakt. Hierdoor krijgen de thumbnails vrijwel dezelfde hoogte als div#wrapper.

overflow-y: visible;

Eerder is bij #wrapper overflow-y: hidden; opgegeven: verberg wat in de hoogte niet binnen div#wrapper past. Dat was nodig om een probleem in op Blink gebaseerde browsers op te lossen. (Meer daarover is te vinden bij overflow-y: hidden;.)

In deze grotere browservensters is geen overflow aanwezig: de inhoud past gewoon binnen de hoogte van div#wrapper. Je zou overflow: hidden; dus gewoon kunnen laten staan. (De grote foto's komen weliswaar buiten div#wrapper te staan, maar dat gebeurt met position: fixed; en dat trekt zich niets van overflow en dergelijke aan.)

Afbeelding 14: scrollbalk staat in Internet Explover over thumbnails

In Internet Explorer wordt dan echter de scrollbalk bínnen div#wrapper gezet, zoals rechts op de afbeelding is te zien. Hierdoor valt aan de onderkant een deel van de thumbnail weg.

Met deze regel gedraagt ook Internet Explorer zich fatsoenlijk, zoals links op de afbeelding is te zien: de scrollbalk staat nu netjes onder de thumbnails.

float: left;

Hier gelijk boven is de waarde bij overflow-y veranderd. Eerder is bij #wrapper overflow-y: hidden; opgegeven om een probleem in op Blink gebaseerde browsers op te lossen. Een prettige bijwerking daarvan was dat de witte achtergrond van div#wrapper over de volle breedte van de thumbnails doorliep. (Meer daarover is te vinden bij overflow-y: hidden;.)

Nu overflow-y gelijk hierboven is teruggezet naar de standaardwaarde visible, werkt deze truc niet meer. float: left; heeft echter dezelfde werking: div#wrapper, en daarmee de witte achtergrond, omsluit alle thumbnails.

Hetzelfde geldt voor de gelijk hieronder opgegeven border aan de onderkant: deze is nu ook even lang als de rij met thumbnails.

border-bottom: black solid 1px;

Randje aan de onderkant.

padding-top: 5px;

Kleine afstand tussen de thumbnails in div#wrapper en de bovenkant van het browservenster.

position: relative;

Nakomelingen van een element worden gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Door aan div#wrapper een relatieve positie te geven, kunnen nakomelingen nu ten opzichte van deze <div> worden gepositioneerd.

Omdat voor top en dergelijke verder niets wordt opgegeven, heeft dit geen invloed op div#wrapper zelf.

#titel

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

Het element met id="titel". De <div> waar de titel 'Mijn huisdieren' in staat.

min-width: 4em; max-width: 4em;

Eerder is een minimum- en maximumbreedte van 3 em opgegeven. In deze grotere browservensters is meer ruimte, daarom wordt dit hier iets verhoogd.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen div#titel en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

h1

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.

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

h1 {font-size: 1.2rem; font-weight: normal; margin-top: 2px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

Alle <h1>'s. Dat is er maar één: de belangrijkste kopregel, waarin de titel staat.

text-align: left;

Links uitlijnen.

padding-left: 4px;

Kleine ruimte tussen tekst in en buitenkant van de <h1>.

h1 span

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.

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

h1 span {display: block; font-size: 1.3rem; margin-top: 14px; margin-top: 6px; transform: rotate(90deg);}

Alle <span>'s binnen een <h1>. Dat is er hier maar één. Het tweede woord van de titel 'Mijn huisdieren' staat erin.

margin-top: 40px; margin-left: -38px;

De eerder opgegeven waarden worden aangepast aan het grotere browservenster. Omdat de <span> 90° is gedraaid, zijn die waarden moeilijk te berekenen. Ze zijn gevonden door gewoon uitproberen.

#uitleg

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

Het element met id="uitleg". Bij aanraken of -klikken van deze <input> wordt een korte uitleg getoond. De <input type="checkbox"> zelf is verborgen onder de bijbehorende label#label-voor-uitleg.

display: block;

De eerder met display: none; verborgen input#uitleg weer tonen.

position: absolute;

Om de <input> 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. In dit geval is dat div#wrapper, de ouder van de <input>, die bij #wrapper een relatieve positie heeft gekregen.

top: 37px; left: 34px; Afbeelding 15: input voor uitleg staat op verkeerde plaats

De <input> op de juiste plaats neerzetten. Als je geen positie opgeeft, komt de <input> met het aankruisvakje gewoon linksboven te staan, over de titel heen, zoals op de afbeelding is te zien. Met deze top en left komt de <input> onder de bijbehorende <label> met het vraagteken te staan. Je mag elementen natuurlijk niet op hun uiterlijk beoordelen, en ik heb <input>'s onder m'n beste vrienden en zo, maar ik ben toch stiekem blij dat deze aartslelijke <input> onder het vraagteken wordt verstopt.

#label-voor-uitleg

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

Het element met id="label-voor-uitleg". De <label> die bij input#uitleg hoort. In deze <label> staat alleen een vraagteken.

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;

De eerder met display: none; verborgen <label> wordt weer zichtbaar gemaakt.

width: 1em;

Breedte.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen label#label-voor-uitleg en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

font-size: 1.8em;

Grotere lettergrootte.

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

line-height: 1em;

Regelhoogte.

Omdat geen gewone hoogte is opgegeven, is dit tevens de hoogte van de <label>. Omdat de breedte ook 1 em is, levert dit een vierkante <label> op. Omdat tekst automatisch halverwege de regelhoogte wordt neergezet, staat de tekst in de <label> ook gelijk verticaal gecentreerd.

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: black solid 1px;

Zwart randje.

border-radius: 0.5em;

Ronde hoeken. Omdat maar één maat is opgegeven, zijn alle hoeken precies een kwart cirkel. 0,5 em is precies de helft van de breedte en hoogte van de <label>, waardoor het uiteindelijke resultaat een cirkel is.

position: absolute;

Om de <label> 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. In dit geval is dat div#wrapper, de ouder van de <label>, die bij #wrapper een relatieve positie heeft gekregen.

top: 34px; left: 31px;

Op de juiste plaats neerzetten. Op deze plaats staat ook de bij de <label> horende input#uitleg, een aankruisvakje. Deze <input> is hierdoor niet meer zichtbaar.

-moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;

Hier staat in feite vier keer hetzelfde: user-select: none;. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Als op een touchscreen het vraagteken iets langer wordt aangeraakt, opent de uitleg niet, maar wordt het vraagteken geselecteerd. Dit maakt het onmogelijk het vraagteken te selecteren.

#label-voor-uitleg::after

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.
Afbeelding 16: sluitkruisje

Met behulp van ::after wordt bij label#label-voor-uitleg een pseudo-element gemaakt. Hierin komt het rode sluitkruisje te staan, waarmee de uitleg weer gesloten kan worden.

background: white;

Witte achtergrond.

color: red;

Rode voorgrondkleur. Dit is onder andere de kleur van de tekst.

content: "X";

De inhoud van het pseudo-element: alleen de letter 'X'. Door deze enigszins te mishandelen, kan hier een sluitkruisje van worden gemaakt.

display: none;

Verbergen. Normaal genomen is dit sluitkruisje verborgen. Het wordt verderop bij #uitleg:checked + #label-voor-uitleg::after, #uitleg:checked ~ #uitleg-tekst zichtbaar gemaakt, als de bij de <label> horende input#uitleg is aangevinkt.

width: 0.65em; height: 0.65em;

Breedte en hoogte.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen het pseudo-element en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

overflow: hidden;

Om de 'X' te veranderen in een fatsoenlijk sluitkruisje, moet deze iets groter zijn dan het pseudo-element. Het deel van de 'X' dat buiten het pseudo-element komt, wordt verborgen.

font-size: 1.7em; line-height: 0.7em;

Deze lettergrootte in combinatie met de regelhoogte levert het beste sluitkruisje op in alle browsers op alle systemen.

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

border: black solid 1px;

Zwart randje.

position: absolute;

Om het pseudo-element 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. In dit geval is dat label#label-voor-uitleg, de <label> waar het pseudo-element bij hoort, die bij #label-voor-uitleg een absolute positie heeft gekregen.

top: -2px; left: -4px;

Verderop bij #uitleg:checked + #label-voor-uitleg::after, #uitleg:checked ~ #uitleg-tekst wordt het sluitkruisje zichtbaar gemaakt, als de bij de <label> horende input#uitleg is aangevinkt. Op deze positie komt het over het vraagteken in de <label> heen te staan.

#uitleg-tekst

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

Het element met id="uitleg-tekst". De <p> met de tekst van 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: 30em;

Breedte.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen p#uitleg-tekst en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

max-width: 80vw;

Maximumbreedte.

Hier gelijk boven is een breedte van 30 em opgegeven. Bij een sterk vergrote lettergrootte zou de <div> met de uitleg daardoor breder dan het venster van de browser kunnen worden. Daarom wordt 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 80 vw is 80% van de breedte. De <div> met de uitleg wordt hierdoor nooit breder dan 80% van de breedte van het venster, ongeacht de breedte van het venster.

overflow: auto;

De breedte van de <div> met de uitleg is hierboven geregeld, maar de hoogte nog niet.

Iets hieronder wordt de <div> absoluut gepositioneerd en met top en bottom ongeveer even hoog gemaakt als z'n ouder div#wrapper. Er zit meer tekst in de <div> dan binnen deze hoogte past. Door deze regel kan de tekst verticaal gescrold worden. Afhankelijk van browser en besturingssysteem kan hierbij rechts een verticale scrollbalk verschijnen.

border: black solid 1px;

Zwart randje.

padding: 5px;

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

position: absolute;

Om p#uitleg-tekst 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. In dit geval is dat div#wrapper, de ouder van p#uitleg-tekst, die bij #wrapper een relatieve positie heeft gekregen.

top: -10px; bottom: -10px;

Omdat wordt gepositioneerd ten opzichte van div#wrapper, de <div> waarin ook de rij met thumbnails zit, komt de uitleg iets binnen div#wrapper te staan.

left: 60px;

Op deze afstand vanaf links sluit de <div> met de uitleg goed aan op het sluitkruisje, waarmee de uitleg weer kan worden verborgen.

z-index: 10;

Om te zorgen dat de uitleg altijd boven andere elementen staat, ook als die later in de <html> komen, wordt de z-index verhoogd.

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

-moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;

Hier staat in feite vier keer hetzelfde: user-select: none;. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-.

Als op een touchscreen bij het scrollen de uitleg iets te lang wordt aangeraakt, wordt tekst geselecteerd. Dit maakt het onmogelijk tekst in div#uitleg te selecteren.

Deze eigenschap heeft één nadeel: je kunt de tekst ook niet meer kopiëren. In dit geval lijkt dat geen probleem, omdat het ietwat onwaarschijnlijk lijkt dat iemand in amechtige bewondering de uitleg zal willen voordragen op een literair festival.

#uitleg:checked + #label-voor-uitleg::after, #uitleg:checked ~ #uitleg-tekst

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#label-voor-uitleg::after {background: white; color: red; content: "X"; display: none; width: 0.65em; height: 0.65em; overflow: hidden; font-size: 1.7em; line-height: 0.7em; text-decoration: none; border: black solid 1px; position: absolute; top: -2px; left: -4px;}

#uitleg-tekst {background: white; color: black; width: 30em; max-width: 80vw; overflow: auto; border: black solid 1px; padding: 5px; position: absolute; top: -10px; bottom: -10px; left: 60px; z-index: 10; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

Dit zijn twee selectors, gescheiden door een komma.

Eerst de eerste selector #uitleg:checked + #label-voor-uitleg::after:

#uitleg: het element met id="uitleg". De <input type="checkbox"> waarmee de uitleg kan worden geopend.

:checked: maar alleen als dit aankruisvakje is aangevinkt.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om div#label-voor-uitleg die gelijk op input#uitleg volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beide als ouder div#wrapper.

#label-voor-uitleg::after: het met behulp van ::after bij #label-voor-uitleg gemaakte pseudo-element.

De hele selector in normale mensentaal: doe iets met het pseudo-element bij label#label-voor-uitleg, die gelijk op input#uitleg volgt, maar alleen als input#uitlegx is aangevinkt.

De tweede selector #uitleg:checked ~ #uitleg-tekst:

#uitleg: het element met id="uitleg". De <input type="checkbox"> waarmee de uitleg kan worden geopend.

:checked: maar alleen dit aankruisvakje is aangevinkt.

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

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#uitleg voor de ~ en p#uitleg-tekst na de ~ hebben beide als ouder div#wrapper.

De hele selector in normale mensentaal: doe iets met p#uitleg-tekst die in de html ergens op input#uitleg volgt, maar alleen als input#uitleg is aangevinkt.

display: block;

Na deze indrukwekkende inleiding vanwege de selectors zou je heel toeters en bellen verwachten. Helaas, het is ongeveer zoals een persconferentie van Rutte: veel drukte, weinig inhoud. Alle benodigde css voor de opmaak is al eerder opgegeven. Hier worden het eerder met display: none; verborgen pseudo-element met het sluitkruisje en de <div> met de tekst alleen zichtbaar gemaakt.

.vorige-t, .volgende-t, .vorige-a, .volgende-a

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

De elementen met class="vorige-t", class="volgende-t", class="vorige-a" en class="volgende-a". De vier keer veertien links (<a>) naar de div.wrap's met de volgende en vorige thumbnail en grote afbeelding.

Omdat deze vier links er grotendeels hetzelfde uitzien – pijltjes naar boven, rechts, onder en links –, kan heel veel css hier voor alle vier in één keer worden opgegeven.

background: rgba(255, 255, 255, 0.7);

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

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

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

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

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

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

255, 255, 255 levert wit op, 0, 0, 0 zwart.

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

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

Het vierde getal staat voor het alfa-kanaal. Hiermee wordt de doorzichtigheid aangegeven. Dit getal loopt van 0 naar 1. Volledig doorzichtig is 0, volledig ondoorzichtig is 1.

Het getal voor het alfa-kanaal wordt als decimale breuk aangegeven, dus bijvoorbeeld 0.5 wil zeggen halfdoorzichtig. Let erop dat je 'n punt gebruikt, de Amerikaanse manier om breuken aan te geven. Als je 'n komma gebruikt, denkt de browser dat er twee verschillende getallen staan.

In dit voorbeeld is deze achtergrondkleur enigszins doorzichtig: 0.7. Hierdoor zie je de thumbnails nog enigszins door de achtergrond heen, maar zijn de pijltjes in de links toch goed zichtbaar, ook op donkere thumbs.

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;

Eerder zijn deze elementen verborgen met display: none;. Hier worden ze weer zichtbaar gemaakt.

Van zichzelf is een <a> een inline-element. Daardoor zijn eigenschappen als breedte en hoogte niet te gebruiken. Door het weer te geven als blok-element, zijn deze eigenschappen wel te gebruiken. (Overigens verandert ook de iets verderop gegeven fixed positie de <a> in een soort blok-element.)

width: 3rem; height: 3rem;

Breedte en hoogte.

Als eenheid wordt de relatieve eenheid rem gebruikt, omdat bij een absolute eenheid zoals px de breedte niet mee verandert met de lettergrootte.

De rem werkt ongeveer hetzelfde als de bekendere eenheid em. Ook de rem is op de lettergrootte gebaseerd, maar altijd op de lettergrootte van <html>. Daardoor is de rem altijd overal even groot, ongeacht de lettergrootte van het element zelf. En ongeacht eventuele afwijkende lettergroottes in voorouders van het element. Als de bezoeker de lettergrootte verandert, wordt in feite de lettergrootte van <html> veranderd. Hierdoor verandert ook de grootte van de rem, maar die verandering is overal op de pagina precies hetzelfde.

In dit geval maakt het weinig uit of je de em of de rem gebruikt, maar als je bijvoorbeeld twee elementen op een bepaalde afstand van elkaar wilt zetten, is de rem vaak beter, omdat die overal op de pagina even groot is.

(In dit voorbeeld was bij het maken van dit voorbeeld eerst de rem nodig. Later is dat veranderd en kon ook de em worden gebruikt. Maar omdat de rem hier ook prima werkte, is die eenheid gewoon blijven staan.)

font-size: 1.9rem; line-height: 2.7rem;

Tamelijk grote lettergrootte en regelhoogte. Als eenheid wordt de rem gebruikt, waarover gelijk hierboven iets meer staat.

text-align: center;

Tekst horizontaal centreren.

text-decoration: none;

De standaard-onderstreping van een <a> verwijderen.

border: black solid 1px;

Zwart randje.

border-radius: 1.5rem;

Ronde hoeken. Omdat maar één maat is opgegeven, zijn alle hoeken precies een kwart cirkel. 1,5 em is precies de helft van de breedte en hoogte van de <a>, waardoor het uiteindelijke resultaat een cirkel is.

position: fixed;

Positioneren ten opzichte van het venster van de browser.

top: -500px;

Er zijn vier keer veertien <a>'s, één naar elke div.wrap met vorige en volgende thumbnail en grote afbeelding. Alleen de vier <a>'s bij de de thumbnail met een outline of grote afbeelding moeten kunnen worden aangeraakt of -geklikt. Daarom worden alle links boven het venster van de browser gezet, zodat ze niet aangeraakt of -geklikt kunnen worden. Pas als het nodig is, worden de juiste links binnen het venster gezet.

Normaal genomen zou je de <a>'s verbergen met display: none;. Dat kan hier echter niet, want bij het weer buiten het browservenster zetten van de links is iets hieronder met behulp van transition-delay een kleine vertraging ingebouwd. En transition-delay werkt niet in combinatie met display, maar wel in combinatie met top.

left: 58px;

58 px vanaf links neerzetten. Voor de links naar de volgende thumbnail en grote afbeelding wordt dit iets verderop aangepast, want die moeten meer naar rechts komen te staan.

transition-delay: 0.3s;

Iets hierboven is met top: -500px; een verplaatsing naar boven opgegeven, waardoor de <a>'s met de links naar de div.wrap met de vorige en volgende thumbnail en grote afbeelding boven het browservenster komen te staan. Ze kunnen daardoor niet aangeraakt of -geklikt worden.

Als je nu bij :hover, :focus, :target, en dergelijke een andere waarde bij top opgeeft, zorgt transition-delay ervoor dat top niet onmiddellijk naar die nieuwe waarde wordt veranderd bij :hover en dergelijke, maar pas na een vertraging. Als de verplaatsing van -500px wordt veranderd naar 0 (geen verplaatsing), vindt die verandering pas na een vertraging plaats. De hier opgegeven vertraging is 0,3 seconde.

transition-delay wordt op meerdere plaatsen gebruikt: om de <a>'s boven het browservenster neer te zetten (en dus onbereikbaar te maken), en om de links die aangeraakt of -geklikt moeten kunnen worden juist weer binnen het venster te zetten.

Bij het boven het browservenster zetten is een vertraging van 0,3 seconde opgegeven met behulp van transition-delay. Deze vertraging is zo kort, dat je het nauwelijks merkt.

Als een link wordt aangeraakt of -geklikt, krijgt een volgende thumbnail een outline: de links op het scherm moeten dan worden vervangen door de links bij die nieuwe thumbnail (en eventueel die bij de daarbij horende grote afbeelding). De aangeraakte link hoort bij de div.wrap met de vorige thumbnail of grote afbeelding en moet dus boven het browservenster worden gezet, zodat deze niet meer aangeraakt of -geklikt kan worden.

Maar als je die link zonder vertraging boven het browservenster zet, verdwijnt de link, voordat deze gevolgd kan worden. De vertraging van 0,3 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 <a>'s getoond moeten worden, worden ze met behulp van een andere waarde bij top op de juiste hoogte binnen het browservenster gezet. Bij die verandering van top is transition-delay: 0s; opgegeven. Daardoor worden de <a>'s zonder enige vertraging getoond. Zou dat niet zo zijn, dan zou je regelmatig even geen <a>'s, geen links met pijltjes, zien, waarna ze na 0,3 seconde alsnog tevoorschijn floepen. En hoe kort 0,3 seconde ook is, het levert toch een een nogal onrustige indruk op.

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 top wordt veranderd bij :hover en dergelijke.

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.

.volgende-t

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

De elementen met class="volgende-t". De links naar de div.wrap met de volgende thumbnail.

right: 58px;

Eerder zijn alle links op 58 px vanaf de linkerkant van het browservenster neergezet. De links naar de volgende thumbnail moeten vanaf rechts worden neergezet.

left: auto;

Eerder is bij deze links left: 58px; en width: 3rem; opgegeven. Hier gelijk boven wordt right: 58px; opgegeven. Omdat eerder ook position: fixed; is opgegeven, is dit ten opzichte van het browservenster.

Deze combinatie van left, right en width is onmogelijk. In zo'n geval wordt right genegeerd. Om dat te voorkomen, wordt bij left de standaardwaarde auto opgegeven. Nu werkt right: 58px; gewoon en komt de link 58 px vanaf de rechterkant van het browservenster te staan.

.vorige-a

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

De elementen met class="vorige-a". De links naar de div.wrap met de vorige grote afbeelding.

left: 10px;

Eerder is bij deze links left: 58px; opgegeven. Deze links komen iets verder naar links te staan. Omdat eerder ook position: fixed; is opgegeven, is dit ten opzichte van het browservenster.

.volgende-a

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

De elementen met class="volgende-a". De links naar de div.wrap met de volgende grote afbeelding.

right: 10px;

Eerder zijn alle links op 58 px vanaf de linkerkant van het browservenster neergezet. De links naar de volgende thumbnail moeten vanaf rechts worden neergezet.

left: auto;

Eerder is bij deze links left: 58px; en width: 3rem; opgegeven. Hier gelijk boven wordt right: 10px; opgegeven. Omdat eerder ook position: fixed; is opgegeven, is dit ten opzichte van het browservenster.

Deze combinatie van left, right en width is onmogelijk. In zo'n geval wordt right genegeerd. Om dat te voorkomen, wordt bij left de standaardwaarde auto opgegeven. Nu werkt right: 10px; gewoon en komt de link 10 px vanaf de rechterkant van het browservenster te staan.

.xverbergx .vorige-a, .xverbergx .volgende-a

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.vorige-a {left: 10px;}

.volgende-a {right: 10px; left: auto;}

Dit zijn twee selectors, gescheiden door een komma. De eerste selector .xverbergx .vorige-a:

.xverbergx: de elementen met class="xverbergx". Deze class wordt door JavaScript toegevoegd en verwijderd, waarover iets hieronder meer.

.vorige-a: de elementen met class="vorige-a". De links naar de div.wrap met de vorige grote afbeelding.

De hele selector in normale mensentaal: de elementen met class="vorige-a" (de links naar een vorige div.wrap met grote afbeelding) die nakomelingen zijn van een element met class="xverbergx".

De tweede selector is precies hetzelfde als de eerste, maar nu voor de elementen met class="volgende-a", de links naar de div.wrap met de volgende grote afbeelding.

Als een grote afbeelding is geopend, staan bij die grote afbeelding links naar de div.wrap met de vorige en de volgende grote afbeelding. Die links moeten alleen zichtbaar zijn, als de grote afbeelding ook zichtbaar is. Met deze links kun je rechtstreeks door de grote afbeeldingen lopen, zonder dat je gebruik moet maken van de thumbnails.

Deze grote links zijn heel moeilijk met alleen css op de juiste momenten te verbergen en te tonen. Van de twee keer veertien links naar een vorige en volgende div.wrap met grote afbeelding mogen alleen de twee links, die bij de getoonde grote afbeelding horen, worden getoond. De twee keer dertien andere links niet. Dit is met alleen css werkend te krijgen, maar dan zijn vrijwel altijd twee links zichtbaar. Ook als geen grote afbeelding wordt getoond.

Met JavaScript kan dit preciezer worden geregeld. Bij openen van de pagina voegt het script aan elke div.wrap een class 'xverbergx' toe. Elke a.vorige-a en a.volgende-a zit in een div.wrap, dus deze selector werkt op alle links naar de div.wrap met de vorige en volgende grote afbeelding. Zolang class 'xverbergx' aanwezig is, werkt deze selector en verbergt de css hier gelijk onder de links.

Als de links getoond moeten worden, haalt het script bij de betreffende div.wrap class 'xverbergx' weg en werkt de css hieronder niet meer: de links worden zichtbaar. Omdat dat alleen gebeurt, als de bijbehorende grote afbeelding wordt getoond, zijn dat altijd de juiste links.

Althans: de links kúnnen zichtbaar worden. Ze staan, zoals eerder opgegeven, nog steeds 500 px boven het venster van de browser. Met :focus en dergelijke wordt geregeld, welke links echt binnen het venster worden neergezet, want alleen de links bij de getoonde grote afbeelding mogen getoond worden.

(Om het verwijderen en toevoegen van 'xverbergx' te zien moet je niet de gewone broncode, maar de Gegenereerde code bekijken.)

display: none;

Verbergen.

.foto img

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

De afbeeldingen binnen een element met class="foto". Dit zijn de grote afbeeldingen.

Binnen elke span.foto staan twee grote afbeeldingen: eentje binnen een <noscript>-tag, en eentje is met <!-- en --> in commentaar veranderd. Van deze afbeelding wordt er maar één gebruikt. Zonder JavaScript die binnen de <noscript>-tag, met JavaScript die binnen het commentaar.

Voor de werking van de css maakt het niets uit, welk van de twee afbeeldingen wordt gebruikt. (Over het waarom van twee afbeeldingen, waarvan er maar één wordt gebruikt, is meer te lezen bij <noscript>.)

max-width: 96vw;

Maximumbreedte. De grote afbeeldingen verschillen in breedte. Een afbeelding kan wel smaller zijn dan 96 vw, maar nooit breder.

De eenheid vw is gebaseerd op de breedte van het browservenster. 1 vw is 1% van de breedte van het venster. 96 vw is 96% van de breedte van het venster. Hierdoor is er links en rechts van de afbeelding een kleine ruimte, wat er beter uitziet. (Links en rechts, want iets hieronder wordt de afbeelding horizontaal gecentreerd.)

Iets hieronder wordt ook een maximumhoogte opgegeven. Omdat geen vaste breedte en hoogte zijn opgegeven, vervormt de afbeelding niet. Breedte of hoogte worden beide automatisch relatief evenveel vergroot of verkleind, zodat je geen lachspiegel-effect krijgt.

height: auto;

Eerder is een hoogte van 100% opgegeven voor alle <img>'s. Die hoogte is prima voor de thumbnails, maar niet voor de grote afbeeldingen. Die mogen gewoon op hun werkelijke grootte worden weergegeven, met als enige beperking de gelijk hieronder opgegeven maximumhoogte.

max-height: calc(100% - 190px);

Als een afbeelding wat aan de hoge kant is, zou die te hoog voor het browservenster kunnen worden. Een deel van de afbeelding zou dan onder het venster verdwijnen. Daarom wordt hier een maximumhoogte opgegeven.

(Omdat de afbeelding hieronder fixed wordt gepositioneerd, kan niet naar beneden worden gescrold. Bovendien is het uiterst onhandig als je 'n afbeelding moet scrollen om deze in z'n geheel te zien.)

Aan de bovenkant van het browservenster staat een rij thumbnails. Omdat de hoogte van het venster niet bekend is, is de vrije ruimte – de beschikbare ruimte voor de grote afbeelding – onbekend. Die moet daarom worden berekend. Dat kan met behulp van calc().

De regel hier is een simpele formule, waarmee de maximumhoogte wordt aangepast aan de hoogte van het browservenster. Omdat calc() bij berekeningen met lengtematen alles eerst omzet naar de eenheid px, kun je berekeningen met verschillende eenheden maken, zoals hier met % en px. (% is eigenlijk geen echte eenheid, omdat het altijd een percentage van iets anders is.)

In de berekening komt 100% voor. Bij max-height is een hoogte in procenten normaal genomen ten opzichte van de ouder van het element. Maar iets hieronder wordt de <img> fixed gepositioneerd: vast neergezet ten opzichte van het browservenster. Daarom geldt die 100% hier ten opzichte van het venster. Oftewel: 100% is hier evenveel als de hoogte van het venster.

Bij het maken van de pagina weet je de hoogte van het browservenster niet. Maar de browser weet, op het moment dat de pagina wordt weergegeven, die hoogte wel. De 100% wordt vervangen door de hoogte van het venster in px.

div#wrapper heeft bij #wrapper een hoogte van 150 px gekregen. Als je van de 100%, die door de browser is omgezet naar de hoogte van het browservenster in px, 150 px aftrekt, houd je de voor de grote afbeelding beschikbare vrije ruimte over.

Er wordt geen 150 px van de hoogte van het browservenster afgetrokken, maar 190 px. Daardoor blijft er 40 px ruimte over voor wat lege ruimte onder en boven de grote afbeelding.

Iets hierboven is ook een maximumbreedte opgegeven. Omdat geen vaste breedte en hoogte zijn opgegeven, vervormt de foto niet. Breedte of hoogte worden beide automatisch relatief evenveel vergroot of verkleind, zodat je geen lachspiegel-effect krijgt.

border: black solid 2px;

Zwart randje rondom de afbeelding.

position: fixed;

Vast neerzetten ten opzichte van het venster van de browser.

top: 180px;

180 px vanaf de bovenkant van het venster van de browser neerzetten. Dat is 30 px onder de aan de bovenkant staande thumbnails.

left: 50%; transform: translateX(-50%);

De afbeelding halverwege het venster van de browser neerzetten.

Met transform() wordt de afbeelding vervolgens weer de helft van z'n eigen breedte terug naar links gezet: translateX(-50%). Hierdoor staat de helft van de afbeelding links van het midden van het browservenster, en de andere helft staat rechts van het midden. Oftewel: de afbeelding staat altijd horizontaal gecentreerd, ongeacht de breedte van het venster.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img, .wrap:focus > img, .xactiefx > img

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

.foto img {max-width: 96vw; height: auto; max-height: calc(100% - 190px); border: black solid 2px; position: fixed; top: 180px; left: 50%; transform: translateX(-50%);}

Hier staan drie selectors, gescheiden door een komma.

De eerste selector #checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img:

#checkbox-voor-focus:focus: het element met id="checkbox-voor-focus", maar alleen als dit element de focus heeft. Als de pagina wordt geopend, heeft deze <input type="checkbox"> de focus. Ook als wordt teruggegaan van de weergave voor schermlezers naar de normale weergave en als de pagina wordt herladen, krijgt deze <input> de focus. Dit element maakt het mogelijk bij laden van de pagina gelijk door de thumbnails te navigeren met behulp van de pijltjestoetsen. Er is meer over te lezen bij <input id="checkbox-voor-focus" ...

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

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: #checkbox-voor-focus voor de ~ en #wrapper na de ~ hebben beide als ouder div#android-temmer.

#wrapper: het element met id="wrapper". De <div> waar vrijwel de hele slideshow in zit. Eigenlijk gaat het om elementen binnen deze <div>, maar omdat ~ alleen werkt op elementen die dezelfde ouder hebben, is dit 'tussen'-element nodig om de selector te laten werken.

#wrap-1: het element met id="wrap-1". De <div> waarbinnen de eerste thumbnail en bijbehorende links, afbeelding, en dergelijke zitten.

>: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <img> moet een direct kind van div#wrap-1 zijn. Onderstaande <img> is een direct kind van div#wrap-1:

<div id="wrap-1"> <img> </div>

De <img> hieronder is geen direct kind van div#wrap-1, omdat er een <span> tussen div#wrap-1 en de <img> zit:

<div id="wrap-1"> <span> <img> </span> </div>

In div#wrap-1 zitten de thumbnail en de bijbehorende grote afbeelding. Maar alleen de <img> met de thumbnail is een direct kind van #wrap-1.

img: alle afbeeldingen.

De hele selector in normale mensentaal: doe iets met de afbeeldingen die een direct kind van div#wrap-1 zijn, maar alleen als de ervoor zittende #checkbox-voor-focus de focus heeft. Alleen de thumbnail in div#wrap-1 valt onder deze selector.

De tweede selector .wrap:focus > img,:

.wrap:focus: de elementen met class="wrap", maar alleen als het element de focus heeft. Er zijn veertien <div class="wrap">'s. In elk daarvan zit een thumbnail met de bijbehorende links, grote afbeelding, en dergelijke.

Normaal genomen kan een <div> geen focus krijgen, maar door het toevoegen van het attribuut tabindex="0" kunnen deze <div>'s wel focus krijgen.

> img: dit is hetzelfde als het laatste deel van de eerste selector gelijk hierboven: de afbeeldingen die een direct kind van div.wrap zijn. Dit zijn alleen de thumbnails.

De hele selector in normale mensentaal: doe iets met de afbeeldingen die een direct kind van een div.wrap zijn, maar alleen als die <div> de focus heeft.

De derde selector .xactiefx > img:

.xactiefx: de elementen met class="xactiefx". Deze class wordt door JavaScript toegevoegd en verwijderd, waarover iets hieronder meer.

> img: dit is hetzelfde als het laatste deel van de eerste selector iets hierboven: de afbeeldingen die een direct kind van div.xactiefx zijn. Dit zijn alleen de thumbnails.

De hele selector in normale mensentaal: doe iets met de afbeeldingen die een direct kind van een element met class="xactiefx" zijn.

Door middel van een outline wordt aangegeven, bij welke thumbnail de op het scherm staande links naar de div.wrap met de vorige en volgende thumbnail (en eventueel de volgende en vorige grote afbeelding) horen. Dit wordt grotendeels met behulp van :focus geregeld. Er is echter een aantal momenten, waarop dat niet gaat. Bijvoorbeeld als een grote afbeelding wordt gesloten door het scherm ergens aan te raken of er ergens op te klikken. De links horen dan nog steeds bij de afbeelding die is gesloten, maar er is geen enkele thumbnail met een outline. Wat uiterst verwarrend is.

Alleen met css is dit niet goed te regelen, mede omdat verschillende besturingssystemen zo'n aanraking of klik niet allemaal hetzelfde afhandelen. Als een thumbnail een outline hoort te krijgen, wordt daarom door het JavaScript een class 'xactiefx' toegevoegd aan de <div>, waarbinnen de thumbnail zit.

(Om het verwijderen en toevoegen van 'xactiefx' te zien moet je niet de gewone broncode, maar de Gegenereerde code bekijken.)

Omdat in Firefox :focus niet in alle gevallen werkt, is bij @supports (-moz-appearance: none) aparte css voor Firefox neergezet.

outline: grey groove 7px;

Tamelijk brede grijze gegroefde outline rondom de thumbnail zetten, zodat duidelijk is bij welke thumbnail de links naar de div.wrap met de vorige en volgende thumbnail (en eventueel naar de div.wrap met de vorige en volgende grote afbeelding) horen.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.volgende-t {right: 58px; left: auto;}

Deze zes selectors zijn vrijwel hetzelfde als de drie iets hierboven bij #checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img, .wrap:focus > img, .xactiefx > img. Alleen is de > img vervangen door .vorige-t en .volgende-t: de elementen met class="vorige-t" en class="volgende-t", de links naar de div.wrap met de vorige en volgende thumbnail. Omdat er twee links bij elke thumbnail horen, zijn er twee keer drie selectors: drie met .vorige-t en drie met .volgende-t.

In dit geval moet er iets worden gedaan met de links naar de div.wrap met de vorige en volgende thumbnail, als input#checkbox-voor-focus de focus heeft, als een div.wrap de focus heeft, of als class 'xactiefx' is toegevoegd aan de div.wrap, waar de links in zitten.

Omdat in Firefox :focus niet in alle gevallen werkt, is bij @supports (-moz-appearance: none) aparte css voor Firefox neergezet.

top: 98px;

Eerder zijn deze links met top: -500px; boven het venster van de browser gezet, zodat ze niet aangeraakt of -geklikt kunnen worden. De links die bij de thumbnail met de outline horen, worden hier binnen het venster gezet. Omdat dit de enige links zijn binnen het venster, werken altijd alleen de links die bij die thumbnail horen.

transition-delay: 0s;

De links zonder vertraging binnen het browservenster zetten.

De links moeten met een vertraging van 0,3 seconde verdwijnen, want anders werken ze niet. Maar ze moeten wel zonder vertraging zichtbaar worden, omdat dat anders erg storend is. Een uitgebreider verhaal hierover is te vinden bij transition-delay: 0.3s;.

.wrap:focus .vorige-a, .wrap:focus .volgende-a

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.vorige-a {left: 10px;}

.volgende-a {right: 10px; left: auto;}

.xverbergx .vorige-a, .xverbergx .volgende-a {display: none;}

Dit zijn twee selectors, gescheiden door een komma. De eerste selector .wrap:focus .vorige-a:

.wrap:focus: de elementen met class="wrap", maar alleen als deze focus hebben.

Binnen elke div.wrap zit een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.

.vorige-a: de elementen met class="vorige-a". De links naar de div.wrap met de vorige grote afbeelding.

De hele selector in normale mensentaal: doe iets met de elementen met class="vorige-a" die in een div.wrap zitten, als die <div> de focus heeft.

De tweede selector is precies hetzelfde, maar nu voor de elementen met class="volgende-a", de links naar div.wrap met de volgende grote afbeelding.

Omdat in Firefox :focus niet in alle gevallen werkt, is bij @supports (-moz-appearance: none) aparte css voor Firefox neergezet.

top: 190px;

Eerder zijn de twee links naar de div.wrap met de vorige en volgende thumbnail boven buiten het venster van de browser gezet. Nu worden ze binnen het venster gezet, zodat ze aangeraakt en -geklikt kunnen worden.

transition-delay: 0s;

De links moeten met een vertraging van 0,3 seconde verdwijnen, want anders werken ze niet, omdat dat anders erg storend is. Maar ze moeten wel zonder vertraging zichtbaar worden. Een uitgebreider verhaal hierover is te vinden bij transition-delay: 0.3s;.

.wrap:focus .foto

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.wrap:focus: de elementen met class="wrap", maar alleen als deze focus hebben.

Binnen elke div.wrap zit een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.

.foto: de elementen met class="foto". De <span>'s waarbinnen de grote afbeelding zit.

De hele selector in normale mensentaal: doe iets met de elementen met class="foto" die in een div.wrap zitten, als die <div> de focus heeft.

Omdat in Firefox :focus niet in alle gevallen werkt, is bij @supports (-moz-appearance: none) aparte css voor Firefox neergezet.

display: block;

De eerder met display: none; verborgen span.foto wordt zichtbaar gemaakt. En daarmee ook de daarin zittende grote afbeelding.

Speciaal voor Firefox in vensters minimaal 600 px breed en hoog

@supports (-moz-appearance: none)

De css die hier tot nu toe staat, geldt voor alle browservensters minimaal 600 px breed en hoog.

De css die binnen deze 'feature query' staat, geldt alleen voor browsers die -moz-appearance: none; ondersteunen. appearance wordt door alle browsers ondersteund, maar met het voorvoegsel -moz- ervoor wordt het alleen door Firefox ondersteund. Andere browsers negeren hierdoor de css binnen deze feature query.

(Er bestaat geen goede Nederlandse vertaling voor 'feature query'. Het is een vraag of een bepaalde eigenschap en waarde worden ondersteund.)

Omdat deze feature query binnen de media query @media screen and (min-width: 600px) and (min-height: 600px) staat, geldt ook nog steeds de voorwaarde dat het browservenster minimaal 600 px breed en hoog moet zijn.

Om een thumbnail een outline te geven, links naar de div.wrap met de vorige en volgende thumbnail en grote afbeelding binnen het browservenster te zetten, en om de grote afbeelding te tonen, wordt gekeken welke div.wrap focus heeft. Dat werkt prima als een thumbnail wordt aangeraakt of -geklikt, of als met de Tab-toets een div.wrap wordt bezocht.

Maar als met behulp van een link naar de div.wrap met de vorige of volgende thumbnail of grote afbeelding wordt gegaan, krijgt die <div> in Firefox niet de normale focus. Daardoor werkt in Firefox de css bij :focus niet. Om dat op te vangen wordt voor Firefox soortgelijke css als die met :focus gebruikt, maar dan met :target. Want dat werkt wel in Firefox.

In dit voorbeeld zijn de links naar de div.wrap met de vorige en volgende thumbnails en grote afbeeldingen links binnen de pagina, links naar zogenaamde 'ankers':

<a class="vorige-t" href="#wrap-14" aria-haspopup="false" tabindex="-1" aria-hidden="true">←</a>

Na de # in de href staat het anker. Dat is in de regel hierboven div#wrap-14. Als de link wordt gevolgd, werkt de pseudo-class :target op div#wrap-14. Daardoor kan :target worden gebruikt om css op te geven aan een element, op dezelfde manier als dat met bijvoorbeeld :focus of :hover kan.

Door de regels die in Firefox niet werken met :focus min of meer te herhalen met :target, werken ze ook in Firefox.

Omdat in andere browsers :focus én :target werken, zouden in andere browsers nu twee thumbnails een outline kunnen hebben. Om dat te voorkomen wordt de css voor Firefox in een aparte feature query gezet, die alleen door Firefox wordt begrepen.

(Je zou dit ook kunnen oplossen met behulp van de pseudo-class :focus-within, maar die werkt weer niet in Internet Explorer en Edge.)

Alle css-regels hieronder hebben in de selector .firefox staan. Bij openen van de pagina wordt de class 'firefox' door het JavaScript uit de html verwijderd. Hierdoor werkt binnen deze feature query staande css niet meer. Alleen als een thumbnail een outline moet krijgen en bepaalde links en/of de grote afbeelding getoond moet worden, wordt de class 'firefox' door het script weer teruggezet in de html. (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.)

Zonder JavaScript werkt alles ook, maar dan kunnen in Firefox twee thumbnails gelijktijdig een outline hebben: eentje met behulp van :focus, en eentje met behulp van :target. Met alleen css is dit niet goed te voorkomen, met behulp van wat JavaScript wel.

Voor browservensters minimaal 900 px breed en minimaal 600 px hoog staat bij @supports (-moz-appearance: none) nog een klein beetje css speciaal voor Firefox in vensters van die grootte.

Mogelijk hoeft niet álle hieronder staande css beslist binnen deze feature query te staan. Maar voor de overzichtelijkheid zijn alle regels, die met de pseudo-class :target te maken hebben en voor Firefox zijn bedoeld, hier bij elkaar gezet.

Op iOS en iPadOS is :target niet nodig, want daar wordt verplicht de weergave-machine van Safari gebruikt.

.wrap.firefox:target > img

{outline: grey groove 7px;}

Deze selector werkt alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img, .wrap:focus > img, .xactiefx > img {outline: grey groove 7px;}

Deze regel vervangt de middelste selector .wrap:focus > img in de eerder opgegeven regel.

De uitleg is precies hetzelfde als bij die selector, alleen is :focus door :target vervangen, omdat :focus in Firefox niet altijd het gewenste effect heeft. Een uitgebreider verhaal over het waarom van die vervanging is hierboven bij @supports (-moz-appearance: none) te vinden.

#wrapper:focus-within .wrap:target:not(:focus) > img

{outline: none;}

Deze selector werkt alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img, .wrap:focus > img, .xactiefx > img {outline: grey groove 7px;}

.wrap.firefox:target > img {outline: grey groove 7px;}

In het blokje hierboven staan twee regels, die beide een grijze outline om de thumbnail opleveren. In de eerste regel is de middelste selector .wrap:focus > img van belang. Deze is ongeveer hetzelfde als de selector .wrap.firefox:target > img in de tweede regel.

In Firefox levert :focus een grijze outline op, als een thumbnail wordt aangeraakt of -geklikt, of als de Tab-toets wordt gebruikt om de thumbnails af te lopen, maar niet als een link wordt gevolgd. Een uitgebreider verhaal daarover staat iets hierboven bij @supports (-moz-appearance: none).

Als je in Firefox een thumbnail aanraakt, krijgt die een grijze outline, omdat de selector .wrap:focus > img daarvoor zorgt. Maar als in Firefox een link wordt gevolgd, werkt dat niet. In dat geval zorgt de selector .wrap.firefox:target > img voor een outline bij de vorige of volgende thumbnail.

Maar ook de thumbnail in de div.wrap met de focus heeft nog steeds de outline, vanwege de :focus. Je hebt nu dus twee thumbnails met een outline. Uiterst verwarrend, want je weet nu ook niet, bij welke thumbnail de getoonde links horen.

Deze selector voorkomt dat.

#wrapper: het element met id="wrapper". De <div> waar de hele slideshow in zit.

:focus-within: dit is een tamelijk nieuwe pseudo-class. Het wil zeggen: als het ervoor staande element de focus heeft, óf als een van de nakomelingen van dat element de focus heeft.

#wrapper:focus-within bij elkaar: als div#wrapper of een van z'n nakomelingen focus heeft.

.wrap: de elementen met class="wrap". Binnen elke div.wrap zit een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.

:target: als een link is gevolgd naar dit element, is het element de 'target'.

:not(): hierachter staat wat juist níét het geval mag zijn. De voorwaarde staat tussen de haakjes.

(:focus): het element mag geen focus hebben.

.wrap:target:not(:focus) bij elkaar: de div.wrap moet wel de target zijn, maar mag niet de focus hebben. Dat hier drie pseudo-classes achter elkaar worden gebruikt, is geen enkel probleem.

>: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <img> moet een direct kind van div.wrap zijn. Onderstaande <img> is een direct kind van div.wrap:

<div class="wrap"> <img> </div>

De <img> hieronder is geen direct kind van div.wrap, omdat er een <span> tussen div.wrap en de <img> zit:

<div class="wrap"> <span> <img> </span> </div>

In elke div.wrap zitten een thumbnail en de bijbehorende grote afbeelding. Maar alleen de <img> met de thumbnail is een direct kind van de div.wrap.

img: alle afbeeldingen.

De hele selector in normale mensentaal: als div#wrapper of een div.wrap focus heeft, haal dan bij de thumbnail in de div.wrap met :target de outline weg. Behalve als dat toevallig de div.wrap is, die ook de focus heeft. Zo kan ook in Firefox nooit meer dan één thumbnail een outline hebben.

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t

{top: 98px; transition-delay: 0s;}

Deze selectors werken alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t {top: 98px; transition-delay: 0s;}

Deze regel vervangt de middelste twee selectors .wrap:focus .vorige-t en .wrap:focus .volgende-t in de eerder opgegeven regel.

De uitleg is precies hetzelfde als bij die selector, alleen is :focus door :target vervangen, omdat :focus in Firefox niet altijd het gewenste effect heeft. Een uitgebreider verhaal over het waarom van die vervanging staat iets hierboven bij @supports (-moz-appearance: none).

#wrapper:focus-within .wrap:target:not(:focus) .vorige-t, #wrapper:focus-within .wrap:target:not(:focus) .volgende-t

Deze selectors werken alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t {top: 98px; transition-delay: 0s;}

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t {top: 98px; transition-delay: 0s;}

Alle links naar de div.wrap met de vorige en volgende thumbnail zijn eerder boven het browservenster gezet. Alleen de twee links die bij de thumbnail met de outline horen, moeten binnen het venster staan. De twee regels in het blokje hierboven zorgen daarvoor.

In de bovenste regel zijn de twee selectors .wrap:focus .vorige-t en .wrap:focus .volgende-t van belang, de links naar de div.wrap met de vorige en volgende thumbnail die horen bij de div.wrap die de focus heeft.

Deze twee selectors zijn ongeveer hetzelfde als die in de onderste regel: .wrap.firefox.target .vorige-t en .wrap.firefox:target .volgende-t.

In Firefox zet :focus de twee links naar de div.wrap met de vorige en volgende thumbnail binnen het browservenster, als een thumbnail wordt aangeraakt of -geklikt, of als de Tab-toets wordt gebruikt om de thumbnails af te lopen, maar niet als een link naar de vorige of volgende thumbnail wordt gevolgd. Een uitgebreider verhaal daarover staat iets hierboven bij @supports (-moz-appearance: none).

Als je in Firefox een thumbnail aanraakt, krijgt die een grijze outline, omdat de selector .wrap:focus > img daarvoor zorgt. Maar als in Firefox een link wordt gevolgd, werkt dat niet. In dat geval zorgt de selector .wrap.firefox:target > img voor een outline bij de vorige of volgende thumbnail.

Als je in Firefox een thumbnail aanraakt, worden de twee links naar de div.wrap met de vorige en volgende thumbnail binnen het browservenster gezet, omdat de selectors .wrap:focus .vorige-t en .wrap:focus .volgende-t daarvoor zorgen. Maar als in Firefox een link naar de div.wrap met de vorige of volgende thumbnail wordt gevolgd, werkt dat niet. In dat geval zorgen de selectors .wrap.firefox:target .vorige-t en .wrap.firefox:target .volgende-t voor hetzelfde.

Als in Firefox de twee links door middel van de selector met :focus binnen het browservenster zijn gezet, en je volgt daarna een link naar de div.wrap met de vorige of volgende thumbnail, worden ook de twee links bij die vorige of volgende thumbnail binnen het venster gezet. Daar zorgt de selector met :target voor. Maar ook de twee links die bij de eerste thumbnail horen staan nog binnen het venster vanwege de :focus bij de div.wrap met de eerste thumbnail. Je hebt nu dus vier links binnen het venster staan.

Dat is uiterst verwarrend, want de volgorde van de html bepaalt welke links bovenaan staan: de links die het laatst in de html staan. Hierdoor kunnen de links bij een andere thumbnail dan de thumbnail met de outline horen.

Deze selectors voorkomen dat. Omdat ze vrijwel hetzelfde zijn, worden ze hieronder samen beschreven.

#wrapper: het element met id="wrapper". Het element met id="wrapper". De <div> waar de hele slideshow in zit.

:focus-within: dit is een tamelijk nieuwe pseudo-class. Het wil zeggen: als het ervoor staande element de focus heeft, óf als een van de nakomelingen van dat element de focus heeft.

#wrapper:focus-within bij elkaar: als div#wrapper of een van z'n nakomelingen focus heeft.

.wrap: de elementen met class="wrap". Binnen elke div.wrap zit een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.

:target: als een link is gevolgd naar dit element, is het element de 'target'.

:not(): hierachter staat wat juist níét het geval mag zijn. De voorwaarde staat tussen de haakjes.

(:focus): het element mag geen focus hebben.

.wrap:target:not(:focus) bij elkaar: de div.wrap moet wel de target zijn, maar mag niet de focus hebben. Dat hier drie pseudo-classes achter elkaar worden gebruikt, is geen enkel probleem.

.vorige-t en .volgende-t: de twee links naar de div.wrap met de vorige en de volgende thumbnail.

Beide selectors in normale mensentaal: als div#wrapper of een div.wrap focus heeft, zet dan bij de div.wrap met :target de twee links naar de vorige en volgende div.wrap een thumbnail weer buiten het browservenster. Behalve als dat toevallig de div.wrap is, die ook de focus heeft. Zo kunnen ook in Firefox nooit meer dan twee links binnen het venster staan.

top: -500px;

Onbereikbaar boven het venster van de browser zetten. Nu staan alleen nog de twee links die bij de thumbnail met outline horen binnen het venster.

transition-delay: 0.3s;

De links met een kleine vertraging boven het browservenster zetten.

De links moeten met een vertraging van 0,3 seconde verdwijnen, want anders werken ze niet. Een uitgebreider verhaal hierover is te vinden bij transition-delay: 0.3s;.

.wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a

{top: 190px; transition-delay: 0s;}

Deze selectors werken alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

.wrap:focus .vorige-a, .wrap:focus .volgende-a {top: 190px; transition-delay: 0s;}

Deze regel vervangt de twee selectors .wrap:focus .vorige-a en .wrap:focus .volgende-a in de eerder opgegeven regel.

De uitleg is precies hetzelfde als bij die selector, alleen is :focus door :target vervangen, omdat :focus in Firefox niet altijd het gewenste effect heeft. Een uitgebreider verhaal over het waarom van die vervanging staat iets hierboven bij @supports (-moz-appearance: none).

#wrapper:focus-within .wrap:target:not(:focus) .vorige-a, #wrapper:focus-within .wrap:target:not(:focus) .volgende-a

{top: -500px; transition-delay: 0.3s;}

Deze selectors werken alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

.wrap:focus .vorige-a, .wrap:focus .volgende-a {top: 190px; transition-delay: 0s;}

.wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a {top: 190px; transition-delay: 0s;}

Deze regel voorkomt in Firefox dat de verkeerde links naar een div.wrap met vorige of volgende grote afbeelding worden getoond. Hij werkt op precies dezelfde manier als de eerdere regel die voorkomt dat verkeerde links naar een div.wrap met vorige of volgende thumbnail worden getoond, alleen nu voor de links naar de div.wrap met de vorige of volgende grote afbeelding. De uitleg staat bij die eerdere regel #wrapper:focus-within .wrap:target:not(:focus) .vorige-t, #wrapper:focus-within .wrap:target:not(:focus) .volgende-t.

#wrapper:not(:focus-within) .wrap.firefox:target .foto

{display: block;}

Deze selector werkt alleen in Firefox in browservensters minimaal 600 px breed en hoog. Voor andere browsers is de uitleg hieronder niet van belang. Voor deze elementen is eerder css opgegeven. Hier staan alleen de regels, die hier voor Firefox worden aangepast.

.wrap:focus .foto {display: block;}

Deze regel vervangt de eerder opgegeven regel .wrap:focus .foto. Met een kleine uitbreiding, om te voorkomen dat in Firefox meer dan één grote afbeelding geopend kan zijn.

Het tweede deel van de selector .wrap.firefox:target .foto is precies hetzelfde als dat in de eerder opgegeven regel, behalve dat :focus door :target is vervangen. In alle andere browsers wordt een grote afbeelding getoond, als een div.wrap met de grote afbeelding de focus heeft.

In Firefox gebeurt dat alleen als een thumbnail wordt aangeraakt of -geklikt, of als met de Tab-toets door de thumbnails wordt gelopen. Als een link wordt gevolgd naar de div.wrap met de vorige of volgende thumbnail of grote afbeelding, wordt die link wel gevolgd, maar div.wrap krijgt niet de focus. Hierdoor wordt de grote afbeelding niet getoond. Door :focus te vervangen door :target, werkt het ook in Firefox. Een uitgebreider verhaal over het waarom van die vervanging staat iets hierboven bij @supports (-moz-appearance: none).

Nu doet zich echter een nieuw probleem voor. Als een div.wrap in Firefox de focus heeft gekregen, en vervolgens wordt een link gevolgd naar een andere div.wrap, heb je een div.wrap met focus én een div.wrap die target is. Oftewel: er zijn twee grote afbeeldingen geopend. Omdat de grote afbeeldingen allemaal op dezelfde plaats worden neergezet, zie je er altijd maar één. Maar als er twee zijn geopend, 'wint' de afbeelding die het laatst in de html staat. Dat hoeft niet de afbeelding bij de thumbnail met de outline te zijn, dus dat is tamelijk verwarrend.

Het eerste deel van de selector voorkomt dat.

#wrapper: het element met id="wrapper". Het element met id="wrapper". De <div> waar de hele slideshow in zit.

:not(): hierachter staat wat juist níét het geval mag zijn. De voorwaarde staat tussen de haakjes.

:focus-within: dit is een tamelijk nieuwe pseudo-class. Het wil zeggen: als het ervoor staande element de focus heeft, óf als een van de nakomelingen van dat element de focus heeft.

#wrapper:not(:focus-within) bij elkaar: als div#wrapper en geen van z'n nakomelingen de focus heeft. Dat hier twee pseudo-classes achter elkaar worden gebruikt, is geen enkel probleem.

Als je het tweede deel van de selector .wrap.firefox:target .foto achter dit eerste deel zet, is het geheel in normale mensentaal: doe iets met het element met class="foto" in de div.wrap die target is, maar alleen als div#wrapper en geen van zijn nakomelingen de focus heeft. Oftewel: :target werkt alleen, als geen enkele div.wrap de focus heeft. Er kan nu ook in Firefox slechts één grote afbeelding tegelijk worden getoond.

Speciaal voor iOS in vensters minimaal 600 px breed en hoog

@supports (-webkit-overflow-scrolling: touch)

De css die hier tot nu toe staat, geldt voor alle browservensters minimaal 600 px breed en hoog (met uitzondering van een klein deel dat alleen voor Firefox geldt).

De css die binnen deze 'feature query' staat, geldt alleen voor browsers die -webkit-overflow-scrolling: touch; ondersteunen. Dit zijn alleen browsers op iOS, andere browsers ondersteunen deze eigenschap niet. Andere browsers negeren hierdoor de css binnen deze feature query. Ook iPadOS 13 ondersteunt deze eigenschap niet meer.

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

De css binnen deze feature query zorgt ervoor dat in iOS ouder dan versie 13 de thumbnails altijd aan de bovenkant van het browservenster staan, omdat anders de grote afbeelding niet kan worden getoond.

(Een uitgebreider verhaal over -webkit-overflow-scrolling, het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, en de reden waarom deze hele poespas nodig is, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

Omdat deze feature query binnen de media query @media screen and (min-width: 600px) and (min-height: 600px) staat, geldt ook nog steeds de voorwaarde dat het browservenster minimaal 600 px breed en hoog moet zijn.

#android-temmer

Deze selector werkt alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere 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.)

#android-temmer {height: 100%; -webkit-overflow-scrolling: touch;}

#android-temmer {height: auto; overflow: auto; border-bottom: black solid 1px; position: absolute; right: 0; left: 0;}

Het element met id="android-temmer". Het element waar de hele slideshow, inclusief hulp en dergelijke, in zit.

height: 100vh;

1 vh is 1% van de hoogte van het browservenster. 100 vh is dus even hoog als het venster van de browser.

Omdat de grote afbeeldingen zo groot mogelijk worden weergegeven, moet ook div#android-wrapper zo groot mogelijk worden. Groter dan het venster van de browser kan niet, dus hier moet je het mee doen.

#label-voor-schermlezer

Deze selector werkt alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#label-voor-schermlezer {display: block; position: absolute; top: -40px; right: 0;}

Het element met id="label-voor-schermlezer". De <label> die bij input#schermlezer hoort. In deze <label> zit de tekst 'Maak toegankelijk voor schermlezer'.

top: 0;

Eerder is top: -40px; opgegeven. Op iOS staat de <label> dan echter over de knoppen van de browser heen. Als wordt gekozen voor aanpassen voor een schermlezer, worden dan die knoppen bediend, in plaats van dat de aanpassingen voor een schermlezer worden aan- of uitgezet.

Nu staat ook op iOS de <label> onder de knoppen van de browser.

#wrapper

Deze selector werkt alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere 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.)

#wrapper {background: white; display: flex; height: 98%; overflow-y: hidden; padding-top: 1%;}

#wrapper {height: 150px; overflow-y: visible; float: left; border-bottom: black solid 1px; padding-top: 5px; position: relative;}

Het element met id="wrapper". De <div> waar de hele slideshow in zit.

Buiten deze <div> zitten alleen wat hulpmiddelen voor scrollen met de pijltjestoetsen en een keuze om de slideshow aan te passen voor een schermlezer. (Ook div#android-wrapper zit hier buiten, maar die is alleen nodig voor een probleem in Android. Daar is meer over te vinden bij #android-temmer.)

background: #ff9;

Achtergrondkleurtje.

Dit is eigenlijk de achtergrondkleur van <body>, maar op iOS zie je niets van <body>, omdat div#wrapper gelijk hieronder even hoog als het browservenster wordt gemaakt. Daarom wordt de achtergrondkleur aan deze <div> gegeven. Nu ziet het er hetzelfde uit als in andere browsers.

height: 100%;

Een hoogte in procenten is normaal genomen ten opzichte van de ouder van het element. Die ouder is hier div#android-temmer, die bij #android-temmer even hoog als het browservenster is gemaakt. div#wrapper wordt hierdoor ook even hoog als het venster, ongeacht de hoogte van het venster.

padding: 0;

Eerder opgegeven padding verwijderen.

#titel, .wrap

Deze selectors werken alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere browsers 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

.wrap {height: 98%; margin: 0 5px;}

#titel {min-width: 4em; max-width: 4em;}

Het element met id="titel en de elementen met class="wrap". De <div> met de titel en de veertien div.wrap's met in elk een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.

background: white;

Witte achtergrond.

box-sizing: border-box;

Normaal genomen worden border en padding bij de breedte en hoogte opgeteld. Hierdoor komen ze binnen de breedte te staan, waardoor het makkelijker is div#titel en de veertien div.wrap's even hoog te maken.

height: 170px;

Hoogte.

outline: black solid 1px;

Zwarte outline. Meestal wordt een border gebruikt, maar iets hieronder wordt die border al gebruikt.

margin: 0;

Eerder opgegeven marges weghalen.

border: white solid 10px;

Brede witte rand zorgt voor een omlijsting van de thumbnails in de div.wrap's.

#titel

Deze selector werkt alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

#titel {min-width: 4em; max-width: 4em;}

#titel, .wrap {background: white; box-sizing: border-box; height: 170px; outline: black solid 1px; margin: 0; border: white solid 10px;}

Het element met id="titel". De <div> met de titel.

min-width: 3.4em; max-width: 3.4em;

Door de minimum- en maximumbreedte even groot te maken, is div#titel altijd 3,4 em breed. Dat is de breedte die hier het best uitkomt.

Als eenheid wordt de relatieve eenheid em gebruikt. Deze is gebaseerd op de lettergrootte binnen div#titel en verandert, anders dan een absolute eenheid als bijvoorbeeld px, mee met de lettergrootte.

border: none;

De eerder opgegeven brede witte border is hier niet nodig, dus weghalen.

border-left: black solid 1px;

Zwart randje links.

#uitleg, #label-voor-uitleg

Deze selectors werken alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere browsers 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#uitleg {display: block; position: absolute; top: 37px; left: 34px;}

#label-voor-uitleg {background: white; color: black; display: block; width: 1em; font-size: 1.8em; line-height: 1em; text-align: center; border: black solid 1px; border-radius: 0.5em; position: absolute; top: 34px; left: 31px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

De elementen met id="uitleg" en id="label-voor-uitleg".

Bij aanraken of -klikken van input#uitleg wordt een korte uitleg getoond. De <input type="checkbox"> zelf is verborgen onder de bijbehorende label#label-voor-uitleg. In deze <label> staat alleen een vraagteken.

left: 20px;

20 px vanaf de linkerkant van div#wrapper neerzetten.

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#wrapper, die bij #wrapper een relatieve positie heeft gekregen.

#uitleg-tekst

Deze selector werkt alleen in browsers op iOS in browservensters minimaal 600 px breed en hoog. Voor andere 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#uitleg-tekst {background: white; color: black; width: 30em; max-width: 80vw; overflow: auto; border: black solid 1px; padding: 5px; position: absolute; top: -10px; bottom: -10px; left: 60px; z-index: 10; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

#uitleg:checked + #label-voor-uitleg::after, #uitleg:checked ~ #uitleg-tekst {display: block;}

Het element met id="uitleg-tekst". De <p> waarin de tekst van de uitleg staat.

bottom: auto;

Eerder is p#uitleg-tekst absoluut gepositioneerd en is bottom: -10px; opgegeven. 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#wrapper, die bij #wrapper een relatieve positie heeft gekregen.

Iets hierboven bij #wrapper is div#wrapper even hoog gemaakt als het venster van de browser. Daardoor zou p#uitleg-tekst doorlopen tot 10 px vanaf de onderkant van het venster, of dat nou nodig is of niet. Door bottom op de standaardwaarde auto te zetten, wordt p#uitleg-tekst niet hoger dan nodig is om de tekst erin weer te kunnen geven.

left: 50px;

50 px vanaf links neerzetten.

css voor vensters minimaal 900 px breed en minimaal 600 px hoog

@media screen and (min-width: 900px) and (min-height: 600px)

Afbeelding 17: slideshow in vensters minimaal 900 px breed

De opbouw van deze regel staat beschreven bij @media screen and (orientation: portrait) and (max-width: 500px). Er zijn drie verschillen: portret- of landschapsstand maakt niet uit, de breedte moet minimaal 900 px zijn, en de hoogte moet minimaal 600 px zijn.

In browservensters van deze grootte staan de thumbnails in een kolom aan de linkerkant van het venster en wordt de grote afbeelding eventueel links daarvan getoond.

@supports not (-webkit-overflow-scrolling: touch)

De css die binnen deze 'feature query' staat, geldt alleen voor browsers die -webkit-overflow-scrolling: touch; niet ondersteunen. Dit zijn alle browsers, behalve die op iOS. Browsers op iOS negeren hierdoor de css binnen deze feature query. Ook iPadOS 13 ondersteunt deze eigenschap niet meer en gebruikt de css binnen deze feature query gewoon.

In browservensters met een minimale breedte van 900 px (en een minimale hoogte van 600 px) kan de grote afbeelding het best naast de thumbnails worden gezet. Maar op iOS levert dat grote problemen op. Door de css binnen deze feature query te plaatsen, worden die problemen voorkomen. Op iOS blijven de thumbnails bovenaan het venster staan en de grote afbeeldingen eronder.

(Een uitgebreider verhaal over -webkit-overflow-scrolling, het verschil tussen aan de ene kant iOS ouder dan versie 13, en aan de andere kant iOS 13 en iPadOS, en de reden waarom deze hele poespas nodig is, is te vinden bij iOS, iPadOS, scrollen en -webkit-overflow-scrolling.)

Omdat deze feature query binnen de media query @media screen and (min-width: 900px) and (min-height: 600px) staat, geldt ook nog steeds de voorwaarde dat het browservenster minimaal 900 px breed en 600 px hoog moet zijn.

#android-temmer

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#android-temmer {height: 100%; -webkit-overflow-scrolling: touch;}

#android-temmer {height: auto; overflow: auto; border-bottom: black solid 1px; position: absolute; right: 0; left: 0;}

Het element met id="android-temmer". Dit element is alleen nodig om een probleem in Android op te lossen in browservensters minimaal 600 px breed en hoog. In deze bredere vensters speelt het probleem niet, omdat de thumbnails nu in een verticale kolom komen te staan. Maar omdat de <div> toch aanwezig is, kan die gewoon gebruikt worden om css aan te koppelen. (Over het probleem is meer te vinden bij #android-temmer.)

background: white;

Witte achtergrond.

width: 332px;

Breedte.

border-right: black solid 1px;

Rechts zwart randje.

top: 0; right: auto; bottom: 0;

Eerder is div#android-temmer bij #android-temmer absoluut gepositioneerd. Er wordt gepositioneerd ten opzichte van het 'containing block'. Dat is de eerste voorouder die zelf een bepaalde eigenschap heeft, zoals een andere positie dan statisch. Als zo'n voorouder niet aanwezig is, wordt gepositioneerd ten opzichte van het venster van de browser. Omdat ook bottom: 0; wordt opgegeven, beslaat div#android-temmer de volle hoogte van het venster.

Het eerder opgegeven right: 0; wordt veranderd naar de standaardwaarde auto, want anders zou de <div> tot de rechterkant van het browservenster doorlopen.

(Eigenlijk is right: auto; niet nodig. Eerder is ook left: 0; opgegeven. En iets hierboven width: 332px;. Deze combinatie van right: 0;, width 332px; en left: 0; is onmogelijk. In dat geval wordt right genegeerd. Maar als er ooit iets verandert aan de css, waardoor right: 0; weer wel gaat werken, zoek je je ongans waar die vreemde right vandaan komt. Het is beter om dit soort dingen op te ruimen. Disclaimer: deze laatste mededeling is gesponsord door het Departement Schade, Schande en Onwijs, afdeling Schuimbekken en Razernij.)

Omdat eerder overflow: auto; is opgegeven, kan de inhoud van div#android-temmer worden gescrold, als die inhoud te hoog is om in de <div> te passen. Dat is hier zeker het geval, want die inhoud bestaat uit de veertien thumbnails onder elkaar. Afhankelijk van browser en besturingssysteem kan aan de rechterkant van de thumbnails een verticale scrollbalk verschijnen.

#wrapper

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#wrapper {background: white; display: flex; height: 98%; overflow-y: hidden; padding-top: 1%;}

#wrapper {height: 150px; overflow-y: visible; float: left; border-bottom: black solid 1px; padding-top: 5px; position: relative;}

Het element met id="wrapper". De <div> waar de hele slideshow in zit.

Buiten deze <div> zitten alleen wat hulpmiddelen voor scrollen met de pijltjestoetsen en een keuze om de slideshow aan te passen voor een schermlezer. (Ook div#android-wrapper zit hier buiten, maar die is alleen nodig voor een probleem in Android. Daar is meer over te vinden bij #android-temmer.)

flex-wrap: wrap;

Eerder is div#wrapper bij #wrapper met display: flex; veranderd in een zogenaamde 'flex container'. Kinderen van een flex container worden standaard naast elkaar gezet, ook als het blok-elementen zoals een <div> zijn.

Hier wordt opgegeven dat ze, als ze niet naast elkaar passen, onder elkaar moeten worden gezet.

width: 310px;

Breedte.

height: auto;

De eerder opgegeven hoogte van 150 px vervangen door de standaardwaarde auto. Nu bepaalt de inhoud van div#wrapper de hoogte ervan.

float: none;

Het eerder opgegeven float: left; is hier niet meer nodig. Het heeft ook geen enkel effect, omdat alle <div>'s onder elkaar staan. Het wordt hier veranderd naar de standaardwaarde none, om mogelijke problemen in de toekomst te voorkomen. Als het bij een of andere verandering van de code toch zou gaan werken, zoek je je anders mogelijk 'n ongeluk, waar die vreemde verschijnselen opeens vandaan komen.

margin: 0 auto;

Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk Omdat voor onder en links geen waarden zijn opgegeven, krijgen die automatisch dezelfde waarde als boven 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 staat div#wrapper altijd horizontaal gecentreerd binnen z'n ouder div#android-temmer. Omdat div#android-temmer bij #android-temmer een breedte van 332 px heeft gekregen en div#wrapper 310 px breed is, ontstaat hierdoor links en rechts een opening van 11 px.

#titel

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

#titel {min-width: 4em; max-width: 4em;}

Het element met id="titel". De <div> waar de titel in zit.

width: 300px;

Breedte.

Omdat iets hieronder rechts en links een marge van 5 px wordt opgegeven, is de totale breedte 310 px. Dat is even breed als de rest van de inhoud van div#wrapper.

max-width: none;

Eerder opgegeven maximumbreedte verwijderen, anders heeft de hierboven opgegeven breedte geen effect.

De eerder opgegeven minimumbreedte levert geen problemen op, dus die mag blijven leven.

h1

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS is de uitleg hieronder niet van belang.

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

h1 {font-size: 1.2rem; font-weight: normal; margin-top: 2px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

h1 {text-align: left; padding-left: 4px;}

Alle <h1>'s. Dat is er maar één: de belangrijkste kopregel, waarin de titel staat.

font-size: 1.7em;

Grotere lettergrootte dan eerder is opgegeven, omdat hier meer ruimte is.

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.

text-align: center;

Tekst horizontaal centreren.

margin: 5px 0;

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

Boven en onder een kleine marge, rechts en links geen marge.

h1 span

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS is de uitleg hieronder niet van belang.

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

h1 span {display: block; font-size: 1.3rem; margin-top: 14px; margin-top: 6px; transform: rotate(90deg);}

h1 span {margin-top: 40px; margin-left: -38px;}

Alle <span>'s binnen een <h1>. Dat is er hier maar één. Het tweede woord van de titel 'Mijn huisdieren' staat erin. In een aantal groottes van het browservenster neemt gewone horizontale tekst te veel ruimte in. Door 'huisdieren' in een <span> te zetten, kun je dit verticaal onder 'Mijn' neerzetten.

display: inline;

Weergeven als inline-element, zodat alle tekst uit de titel op één regel komt te staan.

Eerder is met behulp van transform de <span> met de tekst 90° gedraaid. Omdat transform niet werkt op een inline-element, is de draaiing hiermee ook gelijk opgeheven.

font-size: inherit;

Zelfde lettergrootte als z'n ouder <h1>.

margin: 0;

Eerder opgegeven marges weghalen.

#uitleg

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#uitleg {display: block; position: absolute; top: 37px; left: 34px;}

Het element met id="uitleg". Bij aanraken of -klikken van deze <input> wordt een korte uitleg getoond. De <input type="checkbox"> zelf is verborgen onder de bijbehorende label#label-voor-uitleg.

top: 14px; left: 23px;

Omdat de thumbnails nu in een kolom onder elkaar staan, moet de positie van input#uitleg worden aangepast.

#label-voor-uitleg

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#label-voor-uitleg {background: white; color: black; display: block; width: 1em; font-size: 1.8em; line-height: 1em; text-align: center; border: black solid 1px; border-radius: 0.5em; position: absolute; top: 34px; left: 31px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

Het element met id="label-voor-uitleg". De <label> die bij input#uitleg hoort. In deze <label> staat alleen een vraagteken.

top: 11px; left: 20px;

Hier iets boven is bij #uitleg de positie van input#uitleg aangepast. Deze <label> moet die <input> verbergen, dus ook de positie van de <label> wordt aangepast.

#uitleg-tekst

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#uitleg-tekst {background: white; color: black; width: 30em; max-width: 80vw; overflow: auto; border: black solid 1px; padding: 5px; position: absolute; top: -10px; bottom: -10px; left: 60px; z-index: 10; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

#uitleg:checked + #label-voor-uitleg::after, #uitleg:checked ~ #uitleg-tekst {display: block;}

Het element met id="uitleg-tekst". De <p> met de tekst van de uitleg.

width: auto;

Eerder is een breedte opgegeven. Hier wordt de tekst met de uitleg hier gelijk onder met behulp van top, right, bottom en left op de juiste plaats gezet.

top: 26px; right: 4px; bottom: auto; left: 4px;

top, right en left zetten de <p> met de uitleg op de juiste plaats.

Eerder is p#uitleg-tekst absoluut gepositioneerd en is bottom: -10px; opgegeven. 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#wrapper, die bij #wrapper een relatieve positie heeft gekregen.

Iets hierboven bij #wrapper is div#wrapper met height: auto; precies hoog genoeg om de inhoud ervan weer te geven. Dat zijn de veertien div.wrap's met de thumbnails, die boven elkaar staan. Dat komt zelfs ver onder het venster van de browser uit.

p#uitleg-tekst heeft eerder bottom: -10px; gekregen. Daardoor zou p#uitleg-tekst ook tot ver beneden het venster van de browser doorlopen, of dat nou nodig is of niet. Door bottom op de standaardwaarde auto te zetten, wordt p#uitleg-tekst niet hoger dan nodig is om de tekst erin weer te kunnen geven.

.wrap

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

.wrap {height: 98%; margin: 0 5px;}

Alle elementen met class="wrap".

In elke div.wrap zit een thumbnail met bijbehorende grote afbeelding en de bijbehorende links naar de div.wrap met de vorige en volgende thumbnail en grote afbeelding.

margin: 2px 5px;

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

De marge links en rechts was al eerder opgegeven, maar hier wordt ook aan boven- en onderkant een marge opgegeven voor wat afstand tussen de div.wrap's, en daarmee ook voor de erin zittende thumbnails.

.wrap > img​

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 > img, .wrap:focus > img, .xactiefx > img {outline: grey groove 7px;}


Alleen voor Firefox:

.wrap.firefox:target > img {outline: grey groove 7px;}

#wrapper:focus-within .wrap:target:not(:focus) > img {outline: none;}

.wrap: de elementen met class="wrap". De veertien div.wrap's met in elk een thumbnail, bijbehorende links, grote afbeelding, en dergelijke.

>: de elementen achter dit teken moeten een direct kind van het element voor dit teken zijn. De hierachter staande <img> moet een direct kind van div.wrap zijn. Onderstaande <img> is een direct kind van div.wrap:

<div class="wrap"> <img> </div>

De <img> hieronder is geen direct kind van div.wrap, omdat er een <span> tussen div.wrap en de <img> zit:

<div class="wrap"> <span> <img> </span> </div>

In elke div.wrap zitten een thumbnail en de bijbehorende grote afbeelding. Maar alleen de <img> met de thumbnail is een direct kind van de div.wrap.

img: afbeelding.

De hele selector in normale mensentaal: de afbeeldingen die een direct kind van een div.wrap zijn, oftewel: de thumbnails.

max-width: 300px;

Als een thumbnail breder is dan 300 px, zou een deel wegvallen. Daarom wordt hier een maximumbreedte opgegeven. De hoogte wordt automatisch aangepast in dezelfde verhouding als de breedte, zodat geen lachspiegel-effect ontstaat.

.vorige-t, .volgende-t

Deze selectors werken alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.volgende-t {right: 58px; left: auto;}

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t {top: 98px; transition-delay: 0s;}


Alleen voor Firefox:

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t {top: 98px; transition-delay: 0s;}

#wrapper:focus-within .wrap:target:not(:focus) .vorige-t, #wrapper:focus-within .wrap:target:not(:focus) .volgende-t {top: -500px; transition-delay: 0.3s;}

De elementen met class="vorige-t" en class="volgende-t". De links naar de div.wrap met de vorige en volgende thumbnail.

line-height: 2.6rem;

Regelhoogte iets aanpassen, omdat de pijltjes dan iets beter in het midden komen te staan. Omdat de pijltjes hier iets onder 90° worden gedraaid, past de regelhoogte in dit geval de horizontale positie aan.

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

De rem werkt ongeveer hetzelfde als de bekendere eenheid em. Ook de rem is op de lettergrootte gebaseerd, maar altijd op de lettergrootte van <html>. Daardoor is de rem altijd overal even groot, ongeacht de lettergrootte van het element zelf. En ongeacht eventuele afwijkende lettergroottes in voorouders van het element. Als de bezoeker de lettergrootte verandert, wordt in feite de lettergrootte van <html> veranderd. Hierdoor verandert ook de grootte van de rem, maar die verandering is overal op de pagina precies hetzelfde.

left: 19px;

19 px vanaf links neerzetten. De links naar de volgende thumbnail worden iets hieronder meer rechts neergezet.

transform: rotate(90deg);

90° draaien, zodat de pijltjes nu naar boven en beneden wijzen.

.volgende-t

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.volgende-t {right: 58px; left: auto;}

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t {top: 98px; transition-delay: 0s;}

.vorige-t, .volgende-t {line-height: 2.6rem; left: 19px; transform: rotate(90deg);}


Alleen voor Firefox:

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t {top: 98px; transition-delay: 0s;}

#wrapper:focus-within .wrap:target:not(:focus) .vorige-t, #wrapper:focus-within .wrap:target:not(:focus) .volgende-t {top: -500px; transition-delay: 0.3s;}

De elementen met class="volgende-t". De links naar de div.wrap met de volgende thumbnail.

left: 255px;

Iets hierboven zijn de links met left: 19px; aan de linkerkant neergezet. Daar staan ook de links naar de div.wrap met de vorige thumbnail. Deze links naar de div.wrap met de volgende thumbnail worden hier aan de rechterkant neergezet.

.vorige-a

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.vorige-a {left: 10px;}

.xverbergx .vorige-a, .xverbergx .volgende-a {display: none;}

.wrap:focus .vorige-a, .wrap:focus .volgende-a {top: 190px; transition-delay: 0s;}


Alleen voor Firefox:

.wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a {top: 190px; transition-delay: 0s;}

#wrapper:focus-within .wrap:target:not(:focus) .vorige-a, #wrapper:focus-within .wrap:target:not(:focus) .volgende-a {top: -500px; transition-delay: 0.3s;}

De elementen met class="vorige-a". De links naar de div.wrap met de vorige grote afbeelding.

left: 345px;

Eind naar rechts zetten, zodat ze niet op de kolom met thumbnails komen te staan, maar ernaast.

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t

{top: 60px; transition-delay: 0s;}

Deze selectors werken alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.volgende-t {right: 58px; left: auto;}

#checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t {top: 98px; transition-delay: 0s;}

.vorige-t, .volgende-t {line-height: 2.6rem; left: 19px; transform: rotate(90deg);}

.volgende-t {left: 255px;}


Alleen voor Firefox:

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t {top: 98px; transition-delay: 0s;}

#wrapper:focus-within .wrap:target:not(:focus) .vorige-t, #wrapper:focus-within .wrap:target:not(:focus) .volgende-t {top: -500px; transition-delay: 0.3s;}

Deze zes selectors zijn precies hetzelfde als die bij #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .vorige-t, #checkbox-voor-focus:focus ~ #wrapper #wrap-1 .volgende-t, .wrap:focus .vorige-t, .wrap:focus .volgende-t, .xactiefx .vorige-t, .xactiefx .volgende-t. Ze zorgen dat op de juiste momenten de juiste links naar de juiste div.wrap met de vorige en volgende thumbnail worden getoond.

Het enige verschil: hier worden de links niet op 98 px van de bovenkant neergezet, maar op 60 px.

.wrap:focus .vorige-a, .wrap:focus .volgende-a

{top: 10px; transition-delay: 0s;

Deze selectors werken alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.vorige-t, .volgende-t, .vorige-a, .volgende-a {background: rgba(255, 255, 255, 0.7); color: black; display: block; width: 3rem; height: 3rem; font-size: 1.9rem; line-height: 2.7rem; text-align: center; text-decoration: none; border: black solid 1px; border-radius: 1.5rem; position: fixed; top: -500px; left: 58px; transition-delay: 0.3s;}

.vorige-a {left: 10px;}

.volgende-a {right: 10px; left: auto;}

.xverbergx .vorige-a, .xverbergx .volgende-a {display: none;}

.wrap:focus .vorige-a, .wrap:focus .volgende-a {top: 190px; transition-delay: 0s;}

.vorige-a {left: 345px;}


Alleen voor Firefox:

.wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a {top: 190px; transition-delay: 0s;}

#wrapper:focus-within .wrap:target:not(:focus) .vorige-a, #wrapper:focus-within .wrap:target:not(:focus) .volgende-a {top: -500px; transition-delay: 0.3s;}

Deze twee selectors zijn precies hetzelfde als die bij .wrap:focus .vorige-a, .wrap:focus .volgende-a. Ze zorgen dat op de juiste momenten de juiste links naar de juiste div.wrap met de vorige en volgende grote afbeelding worden getoond.

Het enige verschil: hier worden de links niet op 190 px van de bovenkant neergezet, maar op 10 px.

.foto img

Deze selector werkt alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS. Voor andere venstergroottes en iOS 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.)

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

.foto img {max-width: 96vw; height: auto; max-height: calc(100% - 190px); border: black solid 2px; position: fixed; top: 180px; left: 50%; transform: translateX(-50%);}

De afbeeldingen binnen een element met class="foto". Dit zijn de grote afbeeldingen.

Binnen elke span.foto staan twee grote afbeeldingen: eentje binnen een <noscript>-tag, en eentje is met <!-- en --> in commentaar veranderd. Van deze afbeelding wordt er maar één gebruikt. Zonder JavaScript die binnen de <noscript>-tag, met JavaScript die binnen het commentaar.

Voor de werking van de css maakt het niets uit, welk van de twee afbeeldingen wordt gebruikt. (Over het waarom van twee afbeeldingen, waarvan er maar één wordt gebruikt, is meer te lezen bij <noscript>.)

max-width: calc(100% - 345px); Afbeelding 18: afbeelding is breder dan het venster

Als een afbeelding wat aan de brede kant is, zou die te breed voor het browservenster kunnen worden. Een deel van de afbeelding zou dan rechts van het venster verdwijnen. Omdat de afbeeldingen bij .foto img fixed zijn gepositioneerd, kunnen ze ook niet worden gescrold. (Los van dat dit bijzonder onhandig zou zijn.)

Links komt een te brede afbeelding gedeeltelijk over de thumbnails heen te staan, wat ook niet echt ideaal is, zoals op de afbeelding is te zien.

Daarom wordt hier een maximumbreedte opgegeven.

Hier gelijk onder wordt ook een maximumhoogte opgegeven. Omdat bij breedte en hoogte alleen een maximum is opgegeven, worden breedte en hoogte in dezelfde verhouding aangepast, waardoor geen lachspiegel-effect ontstaat.

Aan de linkerkant van het browservenster staat een kolom thumbnails. Omdat de breedte van het venster niet bekend is, is de vrije ruimte – de beschikbare ruimte voor de grote afbeelding – onbekend. Die moet daarom worden berekend. Dat kan met behulp van calc().

De regel hier is een simpele formule, waarmee de maximumbreedte wordt aangepast aan de breedte van het browservenster. Omdat calc() bij berekeningen met lengtematen alles eerst omzet naar de eenheid px, kun je berekeningen met verschillende eenheden maken, zoals hier met % en px. (% is eigenlijk geen echte eenheid, omdat het altijd een percentage van iets anders is.)

In de berekening komt 100% voor. Bij max-width is een breedte in procenten normaal genomen ten opzichte van de ouder van het element. Maar de <img>'s zijn eerder fixed gepositioneerd: neergezet ten opzichte van het browservenster. Daarom geldt die 100% hier ten opzichte van het venster. Oftewel: 100% is hier evenveel als de breedte van het venster.

Bij het maken van de pagina weet je de breedte van het browservenster niet. Maar de browser weet, op het moment dat de pagina wordt weergegeven, die breedte wel. De 100% wordt vervangen door de breedte van het venster in px.

div#android-temmer heeft bij #android-temmer een breedte van 332 px gekregen. Als je van de 100%, die door de browser is omgezet naar de breedte van het browservenster in px, 332 px aftrekt, houd je de voor de grote afbeelding beschikbare vrije ruimte over.

Er wordt geen 332 px van de breedte van het browservenster afgetrokken, maar 345 px. Daardoor blijft er een kleine ruimte over voor wat lege ruimte links en rechts van de grote afbeelding.

Hiermee is de beschikbare vrije ruimte berekend, de precieze plaatsing vindt gebeurt hieronder bij top, left en transform().

max-height: 90%;

Maximumhoogte.

Het verhaal is vrijwel hetzelfde als voor de gelijk hierboven opgegeven maximumbreedte. Alleen is calc() niet nodig, omdat aan de bovenkant geen thumbnails of zo staan. Je zou de afbeeldingen zelfs een hoogte van 100% kunnen geven, maar omdat aan de bovenkant de links naar de div.wrap met de vorige en grote afbeelding staan, is de hoogte iets minder genomen.

top: 50%;

Omdat de <img>'s eerder bij .foto img fixed zijn gepositioneerd, geldt de afstand van de bovenkant ten opzichte van het venster van de browser. De afbeelding wordt dus halverwege de hoogte van het venster neergezet. Iets hieronder bij transform() wordt dat weer gecorrigeerd.

left: calc(50% + 166px);

Met behulp van calc() wordt de grote afbeelding halverwege de vrije ruimte rechts van de thumbnails neergezet. Omdat calc() bij berekeningen met lengtematen alles eerst omzet naar de eenheid px, kun je berekeningen met verschillende eenheden maken, zoals hier met % en px. (% is eigenlijk geen echte eenheid, omdat het altijd een percentage van iets anders is.)

In de berekening komt 50% voor. Bij left is een afstand in procenten normaal genomen ten opzichte van de ouder van het element. Maar de <img>'s zijn eerder fixed gepositioneerd: neergezet ten opzichte van het browservenster. Daarom geldt die 50% hier ten opzichte van het venster. Oftewel: 50% wil hier zeggen: halverwege het venster.

Omdat links div#android-wrapper (met de kolom met thumbnails) staat met een breedte van 332 px, wordt bij dat midden nog de helft van 332 px opgeteld: 166 px. Nu staat de afbeelding niet halverwege het browservenster, maar halverwege de lege ruimte links van de kolom met thumbnails.

transform: translate(-50%, -50%); Afbeelding 19: linkerbovenhoek van afbeelding staat precies in het midden van het venster

Hierboven is met top: 50%; en left: calc(50% + 166px); de grote afbeelding horizontaal en verticaal (ongeveer) halverwege het browservenster neergezet. Het resultaat daarvan is op de afbeelding te zien: de linkerbovenhoek van de afbeelding staat inderdaad precies in het midden. Maar dit is toch niet helemaal, wat de bedoeling is.

Met behulp van transform: translate() wordt de afbeelding horizontaal en verticaal gecentreerd in de lege ruimte. De eerste waarde van -50% is de verplaatsing in horizontale richting, de tweede in verticale richting. Omdat beide waarden negatief zijn, is de verplaatsing naar links en naar boven.

Bij translate() geldt een waarde in procenten ten opzichte van het element zelf. Met -50% wordt de afbeelding dus de helft van zijn eigen breedte en hoogte terug naar links en naar boven verplaatst. Oftewel: de afbeelding staat nu precies gecentreerd binnen de vrije ruimte links van de thumbnails, ongeacht hoe groot die ruimte is, en ongeacht de grootte van het browservenster.

Speciaal voor Firefox in vensters minimaal 900 px breed en minimaal 600 px hoog

@supports (-moz-appearance: none) {

.wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t {top: 60px; transition-delay: 0s;} .wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a {top: 10px; transition-delay: 0s;} }

Deze css werkt alleen in Firefox. Het is bedoeld om een probleem met :focus op te lossen. Bij aanraken of -klikken van een thumbnail en bij gebruik van de Tab-toets openen de grote afbeeldingen net als in de andere browsers. Als echter een link naar een vorige of volgende div.wrap met een thumbnail of grote afbeelding wordt gevolgd, volgt Firefox die link wel, maar de div.wrap krijgt geen focus. Waardoor de grote afbeelding niet wordt getoond en de bij de selector met :focus horende css ook niet werkt.

In Firefox werkt :target echter wel. Daarom staat hier wat css speciaal voor Firefox.

Dit werkt precies hetzelfde als de css speciaal voor Firefox in browservensters minimaal 600 px breed en hoog bij @supports (-moz-appearance: none). Dit is echter voor vensters minimaal 900 px breed en minimaal 600 px hoog, omdat het binnen de media query @media screen and (min-width: 900px) and (min-height: 600px) staat.

Alleen de hoogte van de links naar de div.wrap met de vorige en volgende thumbnail en afbeelding wordt hier aangepast. Omdat alle css voor browservenster van minimaal 600 px breed en hoog ook binnen deze media query nog steeds geldt, is het meeste al eerder aangepast voor Firefox. Het uitgebreide verhaal staat bij die eerdere aanpassingen.

De twee regels css hier vervangen alleen de eerder voor Firefox opgegeven regels .wrap.firefox:target .vorige-t, .wrap.firefox:target .volgende-t en .wrap.firefox:target .vorige-a, .wrap.firefox:target .volgende-a.

Speciaal voor schermlezers in vensters minimaal 600 px breed en hoog

@media screen and (min-width: 600px) and (min-height: 600px)

Deze media query is precies hetzelfde als die bij de eerdere media query @media screen and (min-width: 600px) and (min-height: 600px): de css binnen deze media query werkt alleen binnen browservensters die minimaal 600 px breed en hoog zijn. Meer uitleg over hoe deze regel werkt, is te vinden bij die eerdere media query.

Omdat deze media query precies dezelfde is als die eerdere, werkt ook de css binnen die eerdere media query binnen deze media query.

Ook de css binnen de media query @media screen and (min-width: 900px) and (min-height: 600px) werkt nog steeds, maar uiteraard alleen als het browservenster minimaal 900 px breed is.

De css die binnen deze media query staat past de slideshow aan voor schermlezers. Die regels hadden voor een deel ook elders verspreid kunnen staan. Maar het is veel overzichtelijker hier alles op één plaats te hebben.

Als voor deze aanpassingen wordt gekozen, worden alle grote afbeeldingen in één keer op het scherm gezet. Omdat de grote afbeeldingen doodgewone <img>'s zijn, kunnen deze met de gebruikelijke sneltoetsen van de betreffende schermlezer snel achter elkaar worden bezocht.

(Voor browservensters smaller of lager dan 600 px zijn deze aanpassingen niet nodig, omdat ook de thumbnails gewone <img>'s zijn. En grote afbeeldingen worden in deze kleinere vensters helemaal niet getoond.)

#schermlezer:checked

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#schermlezer {-moz-appearance: none; -webkit-appearance: none; appearance: none; display: block; width: 1px; height: 1px; position: absolute; top: 10px; right: 10px; z-index: 10;}

Het element met id="schermlezer", maar alleen als dit is aangevinkt. Met behulp van dit aankruisvakje (<input type="checkbox">) kan de slideshow worden aangepast voor schermlezers. Als dit aankruisvakje is aangevinkt, worden alle grote afbeeldingen in één keer getoond.

-moz-appearance: checkbox; -webkit-appearance: checkbox; appearance: auto;

Hier staat in feite vier keer hetzelfde: appearance: none;. Waarom dat zo is, staat bij De voorvoegsels -moz-, -ms- en -webkit-. (De reden dat er verschillende waarden staan: er was oorspronkelijk geen specificatie voor dit soort eigenschappen. Vandaar dat elke browser z'n eigen waarden had, gelukkig wel met een voorvoegsel. Inmiddels is er wel een ontwerp-specificatie en die heeft hier als standaardwaarde auto.)

Eerder is dit aankruisvakje bij #schermlezer met appearance: none; vrijwel onzichtbaar gemaakt. (Helemaal verbergen kan niet vanwege een probleem in oudere versies van Android. Daar is meer over te lezen bij Probleem: in TalkBack op Android moet een link en dergelijke binnen het venster staan.)

Hoewel het aankruisvakje dus op het scherm staat, is het bij het testen niet gelukt dit per ongeluk aan te vinken. Kennelijk is een <input> van 1 x 1 px zo klein, dat die (nauwelijks of) niet werkt. Maar mogelijk dat het iemand toch wel lukt om dat voor elkaar te krijgen.

Afbeelding 20: <label> en <input> in Google Chrome, gelijk na het aanvinken

Omdat het aankruisvakje vrijwel volledig is verborgen, krijgt die iemand dan plotsklaps een volledig andere pagina. Waarbij ook niet duidelijk is, hoe dat weer hersteld kan worden. Daarom wordt het vrijwel verborgen aankruisvakje in een gewoon aankruisvakje veranderd, zodra dit is aangevinkt. Bovendien wordt iets hieronder bij #schermlezer:checked + #label-voor-schermlezer ook de tekst in de bijbehorende <label> op het scherm gezet, zoals op de afbeelding is te zien. Nu zal hopelijk duidelijk zijn, hoe je de aanpassingen voor een schermlezer weer uit kunt zetten.

width: 10px; height: 10px; border: black solid 1px;

Breedte en hoogte en een zwart randje.

Niet alle browsers geven zonder meer een standaard-aankruisvakje weer, als appearance: auto; wordt opgegeven. Sommige browsers blijven de eerder opgegeven breedte en hoogte van 1 px gebruiken. Dat wordt hier opgelost.

Ook geven niet alle browsers een zwart randje aan de <input>. <input>'s zijn hopeloze krengen om aan te passen met css. Hopelijk gaat die ontwerp-specificatie dat te zijner tijd oplossen.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. Ook als wordt gescrold, zijn <input> en bijbehorende <label> nog steeds zichtbaar.

top: 5px;

5 px vanaf de bovenkant van het browservenster neerzetten. Eerder is de <input> al op 10 px vanaf rechts neergezet.

#schermlezer:checked + #label-voor-schermlezer

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

#label-voor-schermlezer {display: block; position: absolute; top: -40px; right: 0;}

#schermlezer:checked: het element met id="schermlezer", maar alleen als dit is aangevinkt.

+: het element achter de + moet in de html direct volgen op het element voor de +. In dit geval gaat het om label#label-voor-schermlezer die gelijk op input#schermlezer volgt. Beide elementen moeten ook nog dezelfde ouder hebben. Dat is hier het geval, ze hebben beiden als ouder div#android-temmer.

#label-voor-schermlezer: het element met id="label-voor-schermlezer". De <label> die bij input#schermlezer hoort. In deze <label> zit de tekst 'Maak toegankelijk voor schermlezer'.

De hele selector in normale mensentaal: doe iets met de label#label-voor-schermlezer die in de html op input#schermlezer volgt, maar alleen als de <input> is aangevinkt.

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.

text-align: right;

Tekst rechts uitlijnen.

border: black solid 1px;

Zwart randje.

padding: 3px 60px 3px 3px;

Boven, onder en links wat afstand tussen tekst in en buitenkant van de <label>.

Rechts een grotere padding. Hier staat de bij de <label> horende <input> al. Door rechts een grotere padding te geven, komt de tekst niet onder de <input> te staan, maar ervoor.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. Ook als wordt gescrold, zijn <input> en bijbehorende <label> nog steeds zichtbaar.

top: 0;

Bovenaan het venster van de browser neerzetten.

left: 0;

Eerder is bij #label-voor-schermlezer al right: 0; opgegeven. Hierdoor komt de <label> helemaal rechts in het venster van de browser te staan.

Eerder is ook position: fixed; opgegeven. Een fixed element wordt nooit breder, dan nodig is om de inhoud ervan weer te geven. Die inhoud is hier niet meer dan de korte tekst 'Maak toegankelijk voor schermlezer'. Omdat de <label> niet breder wordt dan deze tekst, worden ook de witte achtergrond en de border niet breder.

Door de <label> ook helemaal links vast te zetten aan het venster van de browser, wordt de <label> even breed als het venster. En daarmee ook de achtergrond en de border.

#schermlezer:checked ~ #wrapper

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#wrapper {background: white; display: flex; height: 98%; overflow-y: hidden; padding-top: 1%;}

#wrapper {height: 150px; overflow-y: visible; float: left; border-bottom: black solid 1px; padding-top: 5px; position: relative;}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

#wrapper {flex-wrap: wrap; width: 310px; height: auto; float: none; margin: 0 auto;}

#schermlezer:checked: het element met id="schermlezer", maar alleen als dit is aangevinkt.

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

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: input#schermlezer voor de ~ en #wrapper na de ~ hebben beide als ouder div#android-temmer.

#wrapper: het element met id="wrapper". Het element waar de hele slideshow in zit.

De hele selector in normale mensentaal: doe iets met de div#wrapper die in de html op input#schermlezer volgt, maar alleen als de <input> is aangevinkt.

flex-wrap: wrap;

Eerder is div#wrapper bij #wrapper met display: flex; veranderd in een zogenaamde 'flex container'. Kinderen van een flex container worden standaard naast elkaar gezet, ook als het blok-elementen zoals een <div> zijn.

Hier wordt opgegeven dat ze, als ze niet naast elkaar passen, onder elkaar moeten worden gezet.

width: auto; height: auto;

⁠Alle eerder opgegeven breedtes en hoogtes terugzetten naar de standaardwaarde.

Dat is bij een blok-element zoals div#wrapper normaal genomen even breed als z'n ouder, en precies hoog genoeg om de inhoud ervan weer te geven.

Hier iets onder wordt div#wrapper echter met position: fixed; vastgezet ten opzichte van het venster van de browser. Dat is in dit geval een simpele manier om ervoor te zorgen dat div#wrapper zich helemaal niets meer aantrekt van z'n voorouders, ook niet van de breedte van die voorouders.

Normaal genomen zou div#wrapper dus even breed worden als z'n ouder. Dat is div#android-temmer, die bij #android-temmer even breed is gemaakt als het venster van de browser. Maar in vensters minimaal 900 px breed en minimaal 600 px hoog, waar de thumbnails links in een kolom boven elkaar staan, is div#android-temmer bij #android-temmer slechts 332 px breed gemaakt. Dat is wat smal voor 'n grote afbeelding.

Maar omdat div#wrapper fixed wordt gepositioneerd ten opzichte van het venster van de browser, is de breedte van de voorouders niet meer van belang. Alleen treedt nu weer een ander probleem op. Een fixed gepositioneerd blok-element wordt niet breder dan nodig is om de inhoud ervan weer te geven. Terwijl div#wrapper even breed als het venster moet worden, want anders kunnen de grote afbeeldingen niet horizontaal gecentreerd worden.

Gelukkig is div#wrapper eerder met display: flex; veranderd in een 'flex container'. De kinderen van een flex container, de 'flex items', worden zoveel mogelijk naast elkaar gezet. Pas als flex items niet meer naast elkaar passen, worden ze op een nieuwe regel gezet. Daarom wordt de flex container toch weer wel even breed gemaakt als z'n ouder, ondanks de fixed positie.

Die 'ouder' is hier alleen niet de ouder, want bij een fixed positie is alles gerelateerd aan het venster van de browser. div#wrapper wordt dus even breed als het venster, ongeacht de breedte van het venster. Precies wat de bedoeling is, want nu zijn de afbeeldingen – als ze horizontaal gecentreerd worden in div#wrapper – gelijk ook horizontaal gecentreerd in het venster.

Ook voor de verticale hoogte heeft de fixed positie gevolgen: er kan niet worden gescrold, als de inhoud van div#wrapper hoger is dan het venster van de browser. Omdat alle grote afbeeldingen onder elkaar staan, is die inhoud altijd hoger. Waardoor het merendeel van de grote afbeeldingen niet te zien zou zijn.

Dit wordt iets hieronder opgelost door div#wrapper aan de boven- en onderkant van het venster vast te zetten met top: 1.4rem; en bottom: 0;. Gelijk hieronder wordt overflow: auto; opgegeven. Hierdoor kan nu verticaal worden gescrold, als de inhoud hoger is dan het venster. Afhankelijk van browser en besturingssysteem kan hierbij aan de rechterkant een verticale scrollbalk verschijnen.

div#wrapper wordt niet helemaal tegen de bovenkant aangezet, maar 1,4 rem eronder. Hierdoor blijft er boven div#wrapper ruimte voor input#schermlezer en label#label-voor-schermlezer, zodat duidelijk is hoe de aanpassingen voor de schermlezer weer kunnen worden uitgevinkt.

Als eenheid bij top wordt de relatieve eenheid rem gebruikt, omdat bij gebruik van een absolute eenheid zoals px de afstand tot de bovenkant niet mee verandert met de lettergrootte. Hierdoor zouden, bij een grotere lettergrootte, input#schermlezer en label#label-voor-schermlezer alsnog onder div#wrapper kunnen verdwijnen. Zoomen kan wel altijd, ongeacht welke eenheid voor de lettergrootte wordt gebruikt.

De rem is ongeveer hetzelfde als de bekendere eenheid em, maar is gebaseerd op de lettergrootte van <html>. Hierdoor is de rem, anders dan de em, overal op de pagina even groot. Ook als de bezoeker de lettergrootte heeft veranderd.

overflow: auto;

Scrollen mogelijk maken. De uitleg staat iets hierboven bij width: auto; height: auto;.

position: fixed;

Vastzetten ten opzichte van het venster van de browser. De uitleg staat iets hierboven bij width: auto; height: auto;.

top: 1.4rem; bottom: 0;

Vastzetten aan boven- en onderkant van het browservenster. De uitleg staat iets hierboven bij width: auto; height: auto;.

#schermlezer:checked ~ #wrapper #titel

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#titel {box-sizing: border-box; min-width: 3em; max-width: 3em; height: 98%; overflow: auto; text-align: center; margin: 0 0 5px 5px; border: black solid 1px;}

#titel {min-width: 4em; max-width: 4em;}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

#titel {width: 300px; max-width: none; margin: 0 auto 5px;}

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

#titel: het element met id="titel". De <div> met de titel.

De hele selector in normale mensentaal: doe iets met div#titel in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is aangevinkt.

width: 100%;

Eerder opgegeven breedtes aanpassen.

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper, die bij #schermlezer:checked ~ #wrapper even breed als het venster van de browser is gemaakt.

max-width: none;

Eerder opgegeven maximumbreedte verwijderen.

height: auto;

Eerder opgeven hoogte verwijderen.

#schermlezer:checked ~ #wrapper h1

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.

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

h1 {font-size: 1.2rem; font-weight: normal; margin-top: 2px; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none;}

h1 {text-align: left; padding-left: 4px;}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

h1 {font-size: 1.7em; text-align: center; margin: 5px 0;}

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

h1: alle <h1>'s. Dat is er hier maar één: de titel zit erin.

De hele selector in normale mensentaal: doe iets met de <h1> in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is aangevinkt.

font-size: 1.7em;

Grotere lettergrootte.

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

text-align: center;

Tekst horizontaal centreren.

margin: 5px 0;

Omdat voor onder en links geen waarde is opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 5px 0 5px 0 in de volgorde boven – rechts – onder – links. Boven en onder kleine marge, links en rechts geen marge.

#schermlezer:checked ~ #wrapper h1 span

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.

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

h1 span {display: block; font-size: 1.3rem; margin-top: 14px; margin-top: 6px; transform: rotate(90deg);}

h1 span {margin-top: 40px; margin-left: -38px;}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

h1 span

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

h1 span: de <span>'s in een <h1>.

De hele selector in normale mensentaal: doe iets met de <span>'s in de <h1> in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is aangevinkt. In deze <span> zit het tweede woord van de titel: huisdieren.

display: inline;

De titel komt op één regel te staan, dus de <span> als inline-element weergeven.

Eerder is bij h1 span de <span> bij met behulp van transform 90° gedraaid. Omdat transform niet werkt op een inline-element, komt de tekst weer gewoon horizontaal te staan.

font-size: inherit;

Eerder is bij h1 span de lettergrootte van de <span> met 'huisdieren' iets groter gemaakt dan de <h1>, waar de <span> in zit. Omdat het hier weer een gewone regel met tekst is, moet de lettergrootte van beide woorden in de titel even groot zijn.

Met het sleutelwoord inherit erft de <span> de lettergrootte van z'n ouder <h1>.

margin: 0;

Alle eerder opgegeven marges verwijderen

#schermlezer:checked ~ #wrapper #uitleg, #schermlezer:checked ~ #wrapper #label-voor-uitleg, #schermlezer:checked ~ #wrapper #uitleg-tekst, #schermlezer:checked ~ #wrapper a, #schermlezer:checked ~ #wrapper .wrap > img

{display: none;}

Deze selectors werken alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes is de uitleg hieronder niet van belang.

Voor deze elementen is eerder tamelijk veel css opgegeven. Omdat deze elementen hier simpelweg worden verborgen, is die eerdere css totaal onbelangrijk. Daarom is die in dit blokje weggelaten.

Hier staan vijf selectors, gescheiden door een komma. Het eerste deel van alle selectors is hetzelfde:

#schermlezer:checked ~ #wrapper: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

Het tweede deel is steeds anders:

#uitleg: de <input> waarmee de uitleg zichtbaar kan worden gemaakt;

#label-voor-uitleg: de bij de <input> hierboven horende <label>

#uitleg-tekst: de <p> met de tekst van de uitleg;

a: alle <a>'s. Dit zijn de links naar de vorige en volgende div.wrap met thumbnail en grote afbeelding;

.wrap > img: alle <img>'s die een direct kind van een div.wrap zijn. Dit zijn alleen de thumbnails, want tussen de <img> met de grote afbeelding en div.wrap zit een <span>. (Een uitgebreider verhaal over de werking van > is te vinden bij .wrap > img.)

Al deze elementen hebben één ding gemeenschappelijk: ze moeten worden verborgen, als wordt gekozen voor de aanpassingen voor een schermlezers.

* De uitleg zegt niets over schermlezers en heeft dus geen enkel nut.

* De links naar vorige en volgende thumbnail en grote afbeelding zijn niet nodig, want de grote afbeeldingen kunnen met de gebruikelijke sneltoetsen van de betreffende schermlezer worden bezocht.

* De thumbnails zijn overbodig, want de grote afbeeldingen zijn al zichtbaar.

display: none;

Verbergen.

#schermlezer:checked ~ #wrapper .wrap

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

.wrap {height: 98%; margin: 0 5px;}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

.wrap {margin: 2px 5px;}

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

.wrap: de elementen met class="wrap". Binnen elke div.wrap zit een thumbnail met de bijbehorende links, grote afbeelding, en dergelijke.

De hele selector in normale mensentaal: doe iets met de div.wrap's in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is aangevinkt.

width: 100%;

Een breedte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper, die bij #schermlezer:checked ~ #wrapper even breed als het venster van de browser is gemaakt. Hierdoor worden de div.wrap's ook even breed als het venster.

Een <div> is een blok-element en wordt daarom standaard even breed als zijn ouder. Toch moet hier een breedte worden opgegeven. Bij #wrapper is div#wrapper met display: flex; veranderd in een zogenaamde 'flex container'. Daardoor zijn de directe kinderen van div#wrapper, waaronder de div.wrap's, veranderd in zogenaamde 'flex items'. En flex items worden standaard niet breder dan nodig is om de inhoud ervan weer te geven.

Door de hier opgegeven breedte worden ze toch even breed als div#wrapper.

(Je had hier ook flex-grow: 1; kunnen gebruiken in plaats van width: 100%;. Daarmee geef je aan dat de flex items alle vrije ruimte in de flex container moeten vullen.)

height: auto;

De standaardwaarde voor de hoogte. Hierdoor wordt de <div> precies hoog genoeg om de inhoud ervan weer te geven.

Eerder was een hoogte van 98% opgegeven. Een hoogte in procenten is normaal genomen ten opzichte van de ouder van het element. Dat is hier div#wrapper, die bij #schermlezer:checked ~ #wrapper ongeveer even hoog als het venster van de browser is gemaakt. Als je de 98% bij div.wrap's niet aanpast, zou de hoogte van elke div.wrap 98% van die hoogte worden, wat veel te hoog is.

margin-top: 30px;

Kleine afstand tussen de div.wrap's, en daarmee tussen de daarin zittende grote afbeeldingen.

#schermlezer:checked ~ #wrapper .foto

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

#checkbox-voor-focus, #voor-schermlezers, #schermlezer, #label-voor-schermlezer, #uitleg, #label-voor-uitleg, #uitleg-tekst, .foto, .vorige-t, .volgende-t, .vorige-a, .volgende-a {display: none;}

.wrap:focus .foto {display: block;}

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

.foto: de <span>'s waarin de grote afbeeldingen zitten.

De hele selector in normale mensentaal: doe iets met de span.foto's in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is aangevinkt.

display: block;

De eerder met display: none; verborgen <span>'s worden getoond.

text-align: center;

Tekst horizontaal centreren.

In de <span>'s zit alleen een <img>. Een <img> is een gewoon inline-element, net zoals tekst. Weliswaar een wat apart inline-element, maar het blijft een inline-element. Daardoor worden met deze regel de <img>'s net als tekst horizontaal gecentreerd binnen de <span>'s.

Omdat hierboven de <span>'s met display: block; worden weergegeven als een blok-element, worden ze automatisch even breed als hun ouder, de div.wrap waar de <span> in zit.

De div.wrap is iets hierboven bij #schermlezer:checked ~ #wrapper .wrap met width: 100%; even breed gemaakt als het venster van de browser. Hierdoor staan de <img>'s met de grote afbeelding altijd horizontaal gecentreerd binnen het venster, ongeacht de breedte van het venster.

#schermlezer:checked ~ #wrapper .foto img

Deze selector werkt alleen in browservensters minimaal 600 px breed en hoog. Voor andere venstergroottes 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.)

img {box-sizing: border-box; height: 100%; border: black solid 1px;}

.foto img {max-width: 96vw; height: auto; max-height: calc(100% - 190px); border: black solid 2px; position: fixed; top: 180px; left: 50%; transform: translateX(-50%);}


Alleen in browservensters minimaal 900 px breed en minimaal 600 px hoog en niet op iOS:

.foto img {max-width: calc(100% - 345px); max-height: 90%; top: 50%; left: calc(50% + 166px); transform: translate(-50%, -50%);}

#schermlezer:checked ~ #wrapper: dit eerste deel is steeds hetzelfde bij alle aanpassingen voor een schermlezer: div#wrapper, maar alleen als input#schermlezer is aangevinkt. Een uitgebreider verhaal is te vinden bij #schermlezer:checked ~ #wrapper.

.foto img: de <img>'s in een element met class="foto". Dit zijn de grote afbeeldingen.

De hele selector in normale mensentaal: doe iets met de <img>'s in een span.foto in div#wrapper, maar alleen als de in de html aan div#wrapper voorafgaande input#schermlezer is angevinkt.

max-width: 90vw;

Maximumbreedte. 1 vw is 1% van de breedte van het browservensters. 90 vw is dus 90% van de breedte. Ook een afbeelding die eventueel breder is dan het venster, wordt hierdoor nooit breder dan het venster.

Als een afbeelding wordt versmald, wordt de hoogte automatisch verhoudingsgewijs evenveel verkleind, zodat geen lachspiegel-effect ontstaat.

max-height: 90vh;

Zelfde verhaal als gelijk hierboven, maar nu in de hoogte. 1 vh is 1% van de hoogte van het browservenster.

position: static; transform: none;

Eerder zijn de grote afbeeldingen op de juiste plaats gezet met position: fixed; en transform: translate();. Hier worden ze als een gewone kolom weergegeven, dus deze eigenschappen zijn niet meer nodig en worden terugveranderd naar de standaardwaarden.

JavaScript

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

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

Als je onderstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

/* afbeelding-039-dl.js */ // ********** Algemene voorbereidingen (function () {
"use strict";
var vensterGrootte, path, pathPlusHash, links, length, checkboxVoorFocus = document.getElementById("checkbox-voor-focus"), i, wraps = document.querySelectorAll(".wrap"), thumbs, linksAfbeelding, wrap_1 = document.getElementById("wrap-1"), actieveWrap;

// ********** Venster moet minimale breedte én hoogte hebben, anders niets doen vensterGrootte = window.matchMedia("(max-width: 599px), (max-height: 599px)"); if (vensterGrootte.matches) { return; }

// ********** Als er een # in het adres zit dit verwijderen en adres zonder # in adresbalk zetten if (location.href.indexOf("#") > -1) { path = location.href.substr(0, location.href.indexOf("#")); location.replace(path); }

// ********** Bij openen pagina horen links naar vorige/volgende thumb bij eerste thumb, dus eerste thumb bovenaan/linksboven venster zetten document.getElementById("wrapper").scrollIntoView(); // iOS en iPadOS ondersteunen het bij input#checkbox-voor-focus gebruikte attribuut autofocus niet checkboxVoorFocus.focus();

// ********** Voorkomen dat elke gevolgde link naar een thumbnail of grote afbeelding in geschiedenis van de browser terechtkomt pathPlusHash = location.pathname + "#"; links = document.getElementsByTagName("a");
length = links.length; for (i = 0; i < length; i++) { if (links[i].href.indexOf(pathPlusHash) > 0) { links[i].addEventListener("click", geenHistory); } }
function geenHistory(e) { location.replace(e.target.href); }

// ********** Zorgen dat ook in Firefox grote afbeelding altijd gesloten kan worden (en in alle browsers dat bij openen pagina eerste div.wrap wordt gemarkeerd als venster wordt aangeraakt of -geklikt) document.getElementsByTagName("main")[0].addEventListener("click", verwerkKlikOpMain);

// ********** Koppelen van allerlei eventlisteners aan allerlei gebeurtenissen // Bij openen van de pagina heeft input#checkbox-voor-focus de focus, daarom moet Tab of Shift+Tab ook hieraan worden gekoppeld, zodat ook de eerste Tab of Shift+Tab goed worden afgehandeld checkboxVoorFocus.addEventListener("keydown", tabToon);
// Eventlistener toevoegen om te kijken of voor aanpassen voor schermlezer wordt gekozen document.getElementById("schermlezer").addEventListener("change", voorSchermlezers);
// Bij sluiten van hulp moet thumb met outline div.wrap weer linksboven worden gezet document.getElementById("uitleg").addEventListener("change", sluitHulp);
// Verschillende eventlisteners koppelen en css verwijderen en toevoegen bij de div.wrap's length = wraps.length; for (i = 0; i < length; i++) { // Bij Tab of Shift+Tab bij vorige/volgende thumbnail commentaar veranderen in html wraps[i].addEventListener("keydown", tabToon); // Als grote afbeelding wordt geopend door klik/aanraking thumbnail id van bijbehorende div.wrap opslaan wraps[i].addEventListener("click", bewaarId); // Links naar vorige/volgende grote afbeelding verbergen wraps[i].classList.add("xverbergx"); // Voorkomen dat Firefox twee gemarkeerde div.wrap's kan tonen wraps[i].classList.remove("firefox"); }
// Eventlistener toevoegen om bij klikken/aanraken thumbnail bij bijbehorende afbeelding commentaar te veranderen in html thumbs = document.querySelectorAll(".wrap > img"); length = thumbs.length; for (i = 0; i < length; i++) { thumbs[i].addEventListener("click", thumbToon); }
// Eventlistener toevoegen om bij klikken/aanraken van links naar vorige/volgende grote afbeelding commentaar bij vorige/volgende afbeelding te veranderen in html, zodat afbeelding getoond kan worden linksAfbeelding = document.querySelectorAll(".vorige-a, .volgende-a"); length = linksAfbeelding.length; for (i = 0; i < length; i++) { linksAfbeelding[i].addEventListener("click", vorigeVolgendeAfb); }

// ********** Verwerken van klik/aanraking leeg deel venster function verwerkKlikOpMain(e) {
// Als er al een id in actieveWrap zit if (actieveWrap) { // Als de tagname 'main' is, is buiten de grote afbeelding, links, en dergelijke aangeraakt/geklikt if (e.target.tagName === "MAIN") { // Door het verwijderen van class 'firefox' worden grote afbeelding en bijbehorende links verborgen, zodat ook in Firefox de grote afbeelding met bijbehorende links wordt verborgen document.getElementById(actieveWrap).classList.remove("firefox"); } else { // Eerste div.wrap actief maken actieveWrap = "wrap-1"; wrap_1.classList.add("xactiefx", "firefox"); } }

// ********** Bij Tab of Shift+Tab commentaar veranderen in gewone html, zodat afbeelding getoond kan worden function tabToon(e) {
var span;
// Alleen de Tab-toets moet worden verwerkt if (e.key === "Tab") {
// Bij openen pagina of als eerste thumb focus heeft if (e.target.id === "checkbox-voor-focus" || e.target.id === "wrap-1") {
// Niet naar vorige/volgende afbeelding gaan, want deze afbeelding wordt nog niet getoond. e.preventDefault();
span = wrap_1.querySelector("span.foto"); if (span.innerHTML.indexOf("<!--") > -1) { // Commentaar in html veranderen, zodat afbeelding kan worden getoond span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } // Links naar vorige/volgende grote afbeelding mogen nu worden getoond wrap_1.classList.remove("xverbergx");
// Bij volgende thumbnail commentaar veranderen in gewone html span = wrap_1.nextElementSibling.querySelector("span.foto"); if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } // Links naar vorige/volgende grote afbeelding mogen nu worden getoond wrap_1.nextElementSibling.classList.remove("xverbergx");
// Eventlisteners zijn niet meer nodig wrap_1.removeEventListener("keydown", tabToon); checkboxVoorFocus.removeEventListener("keydown", tabToon);
// Focus op eerste thumbnail zetten wrap_1.focus();
return; }
// Commentaar bij huidige thumbnail veranderen in gewone html, zodat afbeelding getoond kan worden span = e.target.querySelector("span.foto"); if (span.innerHTML.indexOf("<!--") > -1) { // Niet naar vorige/volgende afbeelding gaan e.preventDefault(); span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }
// Commentaar bij vorige thumbnail veranderen in gewone html, zodat afbeelding getoond kan worden span = e.target.previousElementSibling.querySelector("span.foto"); if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } // Links naar vorige/volgende grote afbeelding mogen nu worden getoond e.target.previousElementSibling.classList.remove("xverbergx");
// Commentaar bij volgende thumbnail veranderen in gewone html, zodat afbeelding getoond kan worden if (e.target.nextElementSibling) { span = e.target.nextElementSibling.querySelector("span.foto"); if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } // Links naar vorige/volgende grote afbeelding mogen nu worden getoond e.target.nextElementSibling.classList.remove("xverbergx"); }
// eventlistener is nu niet meer nodig e.target.removeEventListener("keydown", tabToon); } }

// ********** Aanpassingen voor schermlezer maken function voorSchermlezers(e) {
var span;
// Als is gekozen voor aanpassen voor schermlezer if (e.target.checked) { length = wraps.length; for (i = 0; i < length; i++) { span = wraps[i].querySelector("span.foto"); // Commentaar in html veranderen, zodat afbeelding kan worden getoond if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } // Eventlistener is niet meer nodig, want alle afbeeldingen kunnen worden getoond. Bovendien gebruiken schermlezers de Tab-toets vaak zelf en is het irritant als de werking wordt veranderd wraps[i].removeEventListener("keydown", tabToon); } checkboxVoorFocus.removeEventListener("keydown", tabToon); } else { // Aanpassen voor schermlezer wordt weer uitgevinkt. De simpelste manier om alle aanpassingen weer te verwijderen is het herladen van de pagina location.reload(); } }

// ********** Bij sluiten hulp moet thumbnail met outline weer linksboven worden gezet (of de eerste thumbnail, als die er niet is) function sluitHulp(e) {
// Als de hulp weer wordt gesloten if (! e.target.checked) { // Zet de thumbnail met outline linksboven in het venster document.getElementById(actieveWrap).scrollIntoView(); // Nodig voor Firefox om thumbnail te markeren en bijbehorende links te tonen document.getElementById(actieveWrap).focus(); } }

// ********** id van actieve div.wrap opslaan in variabele actieveWrap function bewaarId(e) {
// class 'xactiefx' en 'firefox' verwijderen bij vorige actieve div.wrap if (actieveWrap) { document.getElementById(actieveWrap).classList.remove("xactiefx", "firefox"); } // Als op een <a> is geklikt, is het laatste deel van het adres (het deel na de '#') de id van de div.wrap die actief wordt. Ook controleren of de <a> de juiste class heeft, want er kunnen nog andere <a>'s aanwezig zijn binnen de wrap if ((e.target.tagName === "A") && (e.target.className.match(/^(vorige|volgende)-(a|t)$/))) { actieveWrap = e.target.hash.substr(1); } else { // Anders het element waarop is geklikt vervangen door de ouder actieveWrap = e.target.parentElement; // Als dat niet de <div> met class 'wrap' is while (! actieveWrap.classList.contains("wrap")) { actieveWrap = actieveWrap.parentElement; } actieveWrap = actieveWrap.id; }
// class 'xactiefx' en 'firefox' toevoegen bij nieuwe actieve div.wrap document.getElementById(actieveWrap).classList.add("xactiefx", "firefox"); } }

// ********** Bij klikken/aanraken thumbnail bij behorende afbeelding commentaar veranderen in gewone html, zodat afbeelding getoond kan worden function thumbToon(e) {
var span = e.target.parentElement.querySelector("span.foto");
// Commentaar in html veranderen, zodat afbeelding kan worden getoond if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }
// Links naar vorige/volgende grote afbeelding mogen nu worden getoond e.target.parentElement.classList.remove("xverbergx");
// eventlistener is nu niet meer nodig e.target.removeEventListener("click", thumbToon); }

// ********** Bij klikken op link bij grote afbeelding commentaar bij vorige/volgende afbeelding veranderen in gewone html, zodat afbeelding getoond kan worden function vorigeVolgendeAfb (e) {
var span;
if (e.target.parentElement.id === "wrap-1") { // Bij de eerste afbeelding moet worden teruggegaan naar de laatste afbeelding span = e.target.parentElement.parentElement.lastElementChild.querySelector("span.foto"); // Links naar vorige/volgende grote afbeelding bij laatste afbeelding mogen nu worden getoond e.target.parentElement.parentElement.lastElementChild.classList.remove("xverbergx"); } else { // Bij alle andere afbeeldingen is er wel een vorige afbeelding span = e.target.parentElement.previousElementSibling.querySelector("span.foto"); Links naar vorige/volgende grote afbeelding mogen nu worden getoond e.target.parentElement.previousElementSibling.classList.remove("xverbergx"); } // Commentaar bij laatste/vorige thumbnail veranderen in gewone html, zodat afbeelding getoond kan worden if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }
if (e.target.parentElement === e.target.parentElement.parentElement.lastElementChild) { // Bij de laatste afbeelding moet worden verdergaan naar de eerste afbeelding span = wrap_1.querySelector("span.foto"); // Links naar vorige/volgende grote afbeelding bij eerste afbeelding mogen nu worden getoond wrap_1.classList.remove("xverbergx"); } else { // Bij alle andere afbeeldingen is er wel een volgende afbeelding span = e.target.parentElement.nextElementSibling.querySelector("span.foto"); // Links naar vorige/volgende grote afbeelding mogen nu worden getoond e.target.parentElement.nextElementSibling.classList.remove("xverbergx"); } // Commentaar bij eerste/volgende thumbnail veranderen in gewone html, zodat afbeelding getoond kan worden if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }
// eventlistener is nu niet meer nodig e.target.removeEventListener("click", vorigeVolgendeAfb); }
}) ();

Als je bovenstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

Inhoud JavaScript

Algemene voorbereidingen

/* afbeelding-039-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.

Het script wordt met behulp van <script src="039-js-dl/afbeelding-039-dl.js"></script> aan de pagina gekoppeld.

(function () {

function: het sleutelwoord waarmee het begin van een functie wordt aangegeven. Een functie is een stukje bij elkaar horende code. (Het haakje helemaal aan het begin wordt iets verderop beschreven.)

(): achter een functie moeten twee haakjes staan. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie, maar bij deze functie gebeurt dat niet.

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie, dat is in dit geval helemaal onderaan in de laatste regel van het script, staat een bijbehorende }.

In die laatste regel staat in dit geval nog meer dan alleen de } die het einde van de code aangeeft: }) ();

}: dit is de eerder genoemde afsluitende }, waarmee het einde van de code in de functie wordt aangegeven.

): dit afsluitende haakje is de tegenhanger van het haakje dat voor de functie staat. Samen zorgen ze ervoor dat de hele functie, met alles erop en eraan, tussen haakjes staat. De reden van deze haakjes wordt iets hieronder besproken.

(): dit is weer gewoon een taalregel van JavaScript. In dit geval moeten er achter de }) nog twee haakjes staan. Een computer is gewoon niet zo slim, anders weet de ziel niet wat er moet gebeuren.

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

We hebben hier een script, waarin alle code binnen een functie is geplaatst. Alle code staat tussen { en }, zodat de computer het begin en einde van de functie kan herkennen.

Een functie is een stukje bij elkaar horende code dat je, zo vaak als je wilt, kunt uitvoeren. Als je de tafel van twaalf op het scherm wilt zetten, hoef je niet twaalf vermenigvuldigingen in JavaScript te schrijven, maar schrijf je 'n functie voor één vermenigvuldiging, die je vervolgens tien keer (op steeds iets andere wijze) uitvoert.

Een functie heeft echter één probleem: de code in de functie wordt pas uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen kan op allerlei manieren gebeuren, bijvoorbeeld als de gebruiker 'n toets indrukt, als de pagina is geladen, als het 13:13 is op vrijdag de dertiende, noem maar op. Maar zonder te worden aangeroepen doet een functie helemaal niets.

In dit script is dat ook zo. Er zit 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 ). 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 'links'. Als nou een ander JavaScript toevallig dezelfde namen zou gebruiken, gaat het gruwelijk mis. Door het hele script in een functie te stoppen, voorkom je dat. Als je hier meer over wilt weten, kun je op internet zoeken naar 'name conflict' of 'name clash'.)

"use strict";

Deze regel zorgt ervoor dat bepaalde slordigheden in de code, die makkelijk tot grote problemen kunnen leiden, niet meer mogen. Een validator (die controleert op fouten in de code) is nu strenger en keurt meer dingen af.

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

De puntkomma aan het eind geeft het eind van de regel aan.

var vensterGrootte,

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.

Deze regel valt in een aantal delen uiteen, die worden gescheiden door een komma. Het eerste deel begint met var. Dit sleutelwoord var wordt automatisch ook voor de andere delen geplaatst, omdat die delen op een komma volgen. Door die komma weet het script dat het hier om bij elkaar horende delen van één regel gaat.

Na het laatste deel staat een puntkomma. Dit is het echte einde van deze regel code. In gewone tekst zou je hier een punt gebruiken.

Het is gebruikelijk zo'n tweede, derde, ... deel op een nieuwe regel te laten beginnen en iets in te laten springen. Zo zie je in één oogopslag dat het sleutelwoord var voor alle delen geldt, dat hier twaalf variabelen worden aangemaakt: in elke regel één.

Pardon? Variwiewatwaar? Ha, leuk dat je het vraagt.

var: het begin van het eerste deel van de eerste regel. Met het sleutelwoord var wordt aangegeven dat elk van de erop volgende woorden 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 var volgen de namen van de variabelen. Als er meerdere variabelen zijn, zoals hier het geval is, worden die gescheiden door een komma. Hier zijn de variabelen 'vensterGrootte', 'path', 'pathPlusHash', 'links', 'length', enzovoort, tot en met 'actieveWrap'. Elke variabele staat óf helemaal alleen op een eigen regel, óf aan het begin van een regel, gelijk gevolgd door een isgelijkteken.

In 'vensterGrootte', 'path, 'pathPlusHash', enzovoort 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 variabelen wordt opgeslagen, kun je hier opgeven of elders in het script. Als het om iets simpels gaat, wordt het vaak hier opgegeven. Als het om iets meer ingewikkelds gaat, of als nog niet bekend is wat moet worden opgeslagen, gebeurt het later.

(Heel vaak zou je in plaats van iets opbergen in deze variabelen iets elke keer dat het nodig is opnieuw kunnen opzoeken. Zo wordt hieronder <body> opgezocht met document.getElementsByTagName("body")[0] en opgeslagen in de variabele 'body'. Je zou ook elke keer <body> opnieuw kunnen opzoeken. Maar dat is een bijzonder slecht idee. De code wordt veel minder leesbaar en vooral: dat opzoeken kost relatief veel tijd. Daarom kun je vaak beter iets één keer opzoeken en het resultaat van die speurtocht opslaan en gebruiken.)

De variabelen krijgen hier hun naam, ze worden hier 'gedeclareerd' of 'aangemaakt'. Dat 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: 'checkboxVoorFocus'. In css zou je dit kunnen schrijven als 'checkbox-voor-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...).

Omdat deze variabelen helemaal bovenaan de bij (function () { beschreven buitenste functie al worden aangemaakt, kunnen deze variabelen overal binnen die functie worden gebruikt. En omdat dit hele script binnen die functie staat, kunnen ze overal in het script worden gebruikt. Later worden ook nog variabelen aangemaakt, die maar in delen van het script zijn te gebruiken.

Hieronder worden de aangemaakte variabelen één voor één doorgenomen.

vensterGrootte,

In deze variabele wordt de grootte van het browservenster opgeslagen.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

path,

In deze variabele wordt het pad van de link opgeslagen. (Het deel van het adres voor de '#'.)

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

pathPlusHash,

In deze variabele wordt het pad van de link opgeslagen plus het teken '#'.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

length,

In deze variabele wordt op verschillende plaatsen de lengte van iets opgeslagen.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

checkboxVoorFocus = document.getElementById("checkbox-voor-focus"),

Als je in de html een andere id dan 'checkbox-voor-focus' hebt gebruikt bij deze <input>, moet je de id ook in bovenstaande regel aanpassen.

Omdat deze variabele op meerdere plaatsen wordt gebruikt, wordt hier gelijk een waarde aan de variabele gegeven. Deze hoeft dan maar één keer opgezocht te worden, maar kan overal in het script worden gebruikt.

checkboxVoorFocus: dit is de variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

=: 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.getElementById("checkbox-voor-focus"): het middelste stukje getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById("checkbox-voor-focus")

Er wordt gezocht naar het element met id="checkbox-voor-focus", oftewel: input#checkbox-voor-focus. Dit is een <input> die zorgt dat bij openen van de pagina gelijk met de pijltjestoetsen door de thumbnails kan worden gescrold. Een uitgebreidere beschrijving van deze <input> staat bij <input id="checkbox-voor-focus" ...

,: de regel eindigt hier niet, er moeten nog meer variabelen worden aangemaakt.

De hele regel samengevat: sla in variabele checkboxVoorFocus het element met id 'checkbox-voor-focus' (input#checkbox-voor-focus) op. Dat wil zeggen dat in variabele checkboxVoorFocus een ongelooflijke hoeveelheid informatie over input#checkbox-voor-focus wordt gestopt, waar het script later gebruik van kan maken. Zo is bijvoorbeeld bekend of de <input> is aangevinkt of niet. En alle css die aan input#checkbox-voor-focus 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 input#checkbox-voor-focus en hun css, attributen, enzovoort, zitten ook in checkboxVoorFocus. Van al deze informatie in checkboxVoorFocus kan het script gebruik maken. En ook kunnen veel van deze dingen worden veranderd door het script, zoals een andere kleur, of een element verwijderen, of juist toevoegen.

Feitelijk is ook input#checkbox-voor-focus in de vorm van een object in checkboxVoorFocus opgeslagen. Naast allerlei informatie die in checkboxVoorFocus wordt opgeslagen, kun je daardoor ook gebruik maken van allerlei methoden (functies), die JavaScript gratis en voor niets toevoegt aan checkboxVoorFocus.

Het script gebruikt checkboxVoorFocus om in browsers op iOS en iPadOS bij openen van de pagina de eerste thumbnail een outline te geven en de bij die thumbnail horende links naar de div.wrap met de vorige en volgende thumbnail te tonen. (iOS en iPadOS ondersteunen het attribuut autofocus niet. Meer daarover bij Bekende problemen (en oplossingen).)

Verder wordt het gebruikt om bij openen van de pagina de eerste Tab-toets goed af te handelen (anders zou de eerst ingedrukte Tab-toets ogenschijnlijk geen effect hebben).

i,

In deze variabele wordt op verschillende plaatsen iets geteld.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

wraps = document.querySelectorAll(".wrap"),

Als je in de html een andere class dan 'wrap' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Omdat deze variabele op meerdere plaatsen wordt gebruikt, wordt hier gelijk een waarde aan de variabele gegeven. Deze hoeft dan maar één keer opgezocht te worden, maar kan overal in het script worden gebruikt.

wraps: dit is de variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

querySelectorAll(".wrap"): dit is de methode uit document die gebruikt moet worden. Met behulp van de methode querySelectorAll() kun je op de pagina zoeken naar bijvoorbeeld alle elementen met een bepaalde class. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: ".wrap".

In dit geval wordt gezocht naar alle elementen met class="wrap". De schrijfwijze bij querySelectorAll() is precies hetzelfde als in css. In een selector bij css wordt .wrap gebruikt voor alle elementen met class="wrap", dus bij querySelectorAll() gebruik je ".wrap".

,: de regel eindigt hier niet, er moeten nog meer variabelen worden aangemaakt.

De hele regel samengevat: zoek alle elementen met class="wrap", en stop die in de variabele met de naam wraps. Als dat is gedaan, kunnen we gaan werken met de inhoud van wraps. En omdat die inhoud feitelijk de div.wrap's binnen de pagina zijn, werken we in werkelijkheid niet met wraps, maar met de div.wrap's.

De div.wrap's worden in wraps opgeslagen in de vorm van objecten. 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'. In elke div.wrap (eigenlijk in elk object dus) zit een enorme hoeveelheid informatie over de betreffende div.wrap, en met behulp van methoden kun je allerlei bewerkingen op één of meer div.wrap's uitvoeren, zoals het toevoegen en verwijderen van classes bij de div.wrap's in de html.

In wraps zitten dus veertien div.wrap's in de vorm van een object. Elk van die div.wrap's heeft een volgnummer, een zogenaamde 'index', zodat je ze apart van elkaar kunt aanroepen en er iets mee doen. Die index staat tussen [] en de telling begint met 0, want daar is een computer nou eenmaal dol op. De eerste div.wrap kan worden aangesproken met wraps[0], de tweede met wraps[1] en de veertiende en laatste met wraps[13].

thumbs,

In deze variabele worden de thumbnails opgeslagen.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

linksAfbeelding,

In deze variabele worden de links naar de vorige en volgende grote afbeelding opgeslagen.

De komma aan het eind geeft aan dat de regel hier niet eindigt: er moeten nog meer variabelen worden aangemaakt.

wrap_1 = document.getElementById("wrap-1"),

Als je in de html een andere id dan 'wrap-1' hebt gebruikt bij het eerste element, waarin een thumbnail met bijbehorende links, afbeelding, en dergelijke zit, moet je de id ook in bovenstaande regel aanpassen.

Omdat deze variabele op meerdere plaatsen wordt gebruikt, wordt hier gelijk een waarde aan de variabele gegeven. Deze hoeft dan maar één keer opgezocht te worden, maar kan overal in het script worden gebruikt.

Deze regel is precies hetzelfde als die bij checkboxVoorFocus = document.getElementById("checkbox-voor-focus"),, alleen wordt nu gezocht naar het element met id="wrap-1", en wordt dat element in wrap_1 opgeborgen. Dit is div#wrap-1, de eerste <div> waarin een thumbnail met bijbehorende links, grote afbeelding, en dergelijke zit.

actieveWrap;

In deze variabele wordt op diverse plaatsen de id van de 'actieve div.wrap' opgeslagen. Dat is de div.wrap, waarvan de erin zittende thumbnail een outline heeft, en de bij die thumbnail horende links naar de div.wrap met de vorige en volgende thumbnail worden getoond. Eventueel wordt ook de grote afbeelding met bijbehorende links van die div.wrap getoond.

Aan het eind van de eerdere regels, waarin een variabele werd aangemaakt, stond steeds een komma: er moest nóg een variabele worden aangemaakt. De puntkomma aan het eind hier geeft het definitie einde van de regel aan, er hoeven geen verder variabelen te worden aangemaakt.

Tot nu toe is alleen een aantal noodzakelijke voorbereidingen uitgevoerd. Vanaf nu gaat het script echt beginnen.

Venstergrootte controleren

Het script moet alleen worden uitgevoerd in browservensters minimaal 600 px breed en hoog. Dat wordt in dit deel gecontroleerd.

vensterGrootte = window.matchMedia("(max-width: 599px), (max-height: 599px)");

vensterGrootte: dit is de variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

window.matchMedia(""): het stukje achter de punt matchMedia is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object window, vandaar dat window ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'window'. 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 window bijvoorbeeld zit heel veel informatie over het browservenster en het scherm, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode matchMedia() uit window kan JavaScript kijken, of het browservenster aan bepaalde eisen voldoet. De eisen waaraan moet worden voldaan, staan tussen de haakjes. Om duidelijk te maken dat het om letterlijke tekst gaat, staan de eisen tussen aanhalingstekens.

"(max-width: 599px), (max-height: 599px)": dit zijn de voorwaarden, waaraan het browservenster moet voldoen: hoogstens 599 px breed (max-width: 599px) of hoogstens 599 px hoog (max-height: 599px). De komma betekent hier 'of': als aan één van de voorwaarden is voldaan, valt het venster binnen de voorwaarden.

Of, omgekeerd, alleen browservensters die minimaal 600 px breed én hoog zijn, voldoen niet aan de voorwaarden.

De schrijfwijze bij matchMedia() is precies hetzelfde als bij een media query in css. Alleen komen er in JavaScript aanhalingstekens om de voorwaarden te staan.

window.matchMedia("(max-width: 599px), (max-height: 599px)") samen: browservensters die niet breder zijn dan 599 px of niet hoger dan 599 px.

;: de puntkomma aan het eind geeft het eind van de regel aan.

Het resultaat van het deel achter de punt wordt in variabele vensterGrootte gestopt. Dat resultaat is weer een object met een aantal methodes en eigenschappen. Zo zit er onder andere een lijst met de opgegeven voorwaarden in.

Hier wordt alleen gebruik gemaakt van de eigenschap matches: als het browservenster aan de eisen voldoet is matches waar (true). Als dat niet zo is, is matches onwaar (false). Hier gelijk onder wordt matches gebruikt.

if (vensterGrootte.matches) {

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.

vensterGrootte: gelijk hierboven bij vensterGrootte = window.matchMedia("(max-width: 599px), (max-height: 599px)"); is in variabele vensterGrootte allerlei informatie over het venster van de browser opgeslagen in de vorm van een object. Hierdoor is voor JavaScript allerlei informatie uit dat object beschikbaar.

matches: in de eigenschap matches van het hierboven genoemde object is opgeslagen, of het browservenster aan de gestelde eisen voldeed of niet. Als het venster aan de eisen voldeed (niet breder of hoger dan 599 px), heeft matches als waarde true, 'waar'. Als dat niet zo is, is de waarde false, 'onwaar'.

{: de code die wordt uitgevoerd, als aan deze if 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 voorwaarde tussen de haakjes is dus alleen waar, als het browservenster niet breder of hoger dan 599 px is. Alleen in dat geval wordt de code binnen de if uitgevoerd.

return;

Deze regel van het script wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

- het venster van de browser is niet breder of hoger dan 599 px.

Het script is alleen bedoeld voor browservensters die minstens 600 px breed en hoog zijn. Als het venster smaller of lager is, moet het script niet worden uitgevoerd. 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. return is onderdeel van de buitenste functie (function (). Deze buitenste functie is vanuit de html 'aangeroepen' met behulp van <script src="039-js-dl/afbeelding-039-dl.js">. '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.

Browservensters minimaal 600 px breed en hoog zien deze return nooit, omdat ze niet aan de voorwaarde van de if voldoen. In deze grotere vensters wordt de rest van het script daarom gewoon uitgevoerd.

De puntkomma aan het eind geeft het eind van de regel aan.

'#' uit adres verwijderen

Als een link naar een div.wrap met een vorige of volgende thumbnail of grote afbeelding is gevolgd, staat in de adresbalk het adres van die link. Als dat bijvoorbeeld de zesde div.wrap was, staat in de adresbalk 'afbeelding-039-dl.html#wrap-6' (meestal zal daar nog iets voor staan).

Als de pagina wordt herladen, krijgt de eerste thumbnail een outline en worden de bij de eerste thumbnail horende links op het scherm gezet. Maar als eerder een link naar bijvoorbeeld div#wrap-6 is gevolgd, wordt de div#wrap-6 met de zesde thumbnail linksboven in het browservenster gezet. Met de verkeerde links, want die horen bij de eerste thumbnail. Ook heeft niet de zesde, maar de eerste thumbnail een outline.

Dit is gewoon standaardgedrag van de browser: als in het adres een '#' gevolgd door een id voorkomt, wordt die id bovenaan het browservenster gezet. Dit deel van het script voorkomt dat standaardgedrag. De '#' en alles daarna wordt uit het adres verwijderd, zodat gewoon het begin van de pagina met de eerste thumbnail linksboven in het browservenster komt te staan.

if (location.href.indexOf("#") > -1) {

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.

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

Een van die objecten is het 'window' object. Daarin zit allerlei informatie, die betrekking heeft op de pagina. De informatie in 'window' is weer opgesplitst in allerlei kleinere objecten. Een van die objecten is 'location'. Hierin zit alles dat met het adres van de pagina te maken heeft: protocol, domeinnaam, naam van de pagina, noem maar op.

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

href: dit is een eigenschap van het hierboven genoemde object location. In href zit het volledige adres van de pagina, inclusief het deel achter de '#', als dat aanwezig is. Dat is het deel dat eventueel verwijderd moet worden.

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 de href voor de punt, 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 href zit. Als het teken '#' in de href zit, wordt de positie van de plaats van het teken '#' in het adres in href geretourneerd door indexOf(). Als het teken '#' niet in het adres van href 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.

path = location.href.substr(0, location.href.indexOf("#"));

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– In het adres van de pagina zit het teken '#'.

Deze regel verwijdert de '#' en alles wat erop volgt uit het adres van de pagina, zodat bij herladen van de pagina de bovenkant van de pagina, en daarmee de eerste thumbnail, linksboven komt te staan.

path: dit is de eerder bij path, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

=: 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.href: hierin zit het volledige adres van de pagina opgeslagen. Een uitgebreidere beschrijving staat iets hierboven bij if (location.href.indexOf("#") > -1) {.

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, location.href.indexOf("#")).

substr() haalt een deel (een 'substring') uit een stukje tekst. Omdat substr() hier achter href 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, in dit geval, het adres van de pagina.)

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

location.href: hierin zit het volledige adres van de pagina opgeslagen. Een uitgebreidere beschrijving staat iets hierboven bij if (location.href.indexOf("#") > -1) {.

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:

'http://example.com/afbeelding-039.html#wrap-1'

staat het teken '#' op de 39e plaats. indexOf("#") levert dan de waarde 38 op.

Het hele stukje location.href.indexOf("#") levert in bovenstaand adres dus uiteindelijk 38 op. Oftewel: dat hele stukje kan worden vervangen door het getal 38.

substr(0, location.href.indexOf("#") wordt dan, met bovenstaand adres, substr(0, 38): een substring beginnend op positie 0 en 38 tekens lang. Dat is precies het deel zonder de '#' en wat daarop volgt:

'http://example.com/afbeelding-039.html'.

location.href.substr(0, location.href.indexOf("#")) samen: dit hele deel na de komma levert uiteindelijk, als het adres 'http://example.com/afbeelding-039.html#wrap-1 is, als resultaat 'http://example.com/afbeelding-039.html' op. Het adres van de pagina, maar dan zonder de '#' en alles wat daar eventueel op volgt.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: haal '#' en alles wat daar eventueel op volgt uit het adres van de pagina, en stop het resterende deel vervolgens in variabele path.

location.replace(path);

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– In het adres van de pagina zit het teken '#'.

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

Een van die objecten is het 'location' object, waarin het adres van de pagina zit.

replace(path): dit is zo'n methode. Het adres in location wordt vervangen door wat tussen de haakjes van replace() zit. Hier is dat variabele path.

Gelijk hierboven is in path het adres van de pagina gestopt, 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 aan het eind geeft het eind van de regel aan.

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 div.wrap met 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.

Bij openen eerste thumbnail linksboven zetten

Bij herladen van de pagina horen de getoonde links bij de eerste thumbnail. Daarom moet deze eerste thumbnail linksboven in het browservenster worden gezet.

document.getElementById("wrapper").scrollIntoView();

Als je in de html een andere id dan 'wrapper' hebt gebruikt bij het element, waar de hele slideshow in zit, moet je de id ook in bovenstaande regel aanpassen.

document.getElementById("wrapper"): het stukje achter de punt getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById("wrapper")

Er wordt gezocht naar het element met id="wrapper", oftewel: div#wrapper. Dit is de <div>, waarbinnen de hele slideshow zit.

scrollIntoView(): dit is nog zo'n methode. Deze methode zet een bepaald element linksboven in het venster van de browser. Omdat hij gelijk volgt op document.getElementById("wrapper"), wordt de methode uitgevoerd op de daarmee gevonden div#wrapper. Als div#wrapper linksboven in het venster wordt gezet, worden ook de daarin zittende div#wrapper-1 en de daar weer in zittende eerste thumbnail linksboven in het venster gezet.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het element met id = "wrapper" op en zet dat linksboven in het browservenster.

checkboxVoorFocus.focus();

iOS en iPadOS ondersteunen het bij input#checkbox-voor-focus gebruikte attribuut autofocus niet, daarom wordt dat hier via JavaScript geregeld.

checkboxVoorFocus: in variabele checkboxVoorFocus is bij checkboxVoorFocus = document.getElementById("checkbox-voor-focus"), in de vorm van een object input#checkbox-voor-focus opgeslagen.

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

focus(): dit is zo'n methode. Het zet de focus op het element voor de punt, in dit geval input#checkbox-voor-focus.

;: de puntkomma aan het eind geeft het eind van de regel aan.

Omdat in de css als selector #checkbox-voor-focus:focus wordt gebruikt om bepaalde dingen te regelen bij het openen van de pagina, is het van belang dat ook op iOS en iPadOS die focus op input#checkbox-voor-focus wordt gezet.

Eventlisteners aanbrengen

document.getElementsByTagName("main")[0].addEventListener("click", verwerkKlikOpMain);

Als je in de html een andere element dan <main> hebt gebruikt om het browservenster te vullen, moet je dat element ook in bovenstaande regel aanpassen.

document.getElementsByTagName("main"): het eerste stukje.

getElementsByTagName is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementsByTagName() uit document kan JavaScript alle elementen van 'n bepaalde soort opzoeken. De tags waarnaar wordt gezocht, staat tussen haakjes:

getElementsByTagName("main")

Jij en ik weten dat er maar één <main> is, maar een browser is nou eenmaal niet zo slim, dus die zoekt trouwhartig naar álle <main>'s. En vindt er uiteraard maar eentje.

[0]: de gevonden tags worden geretourneerd in de vorm van een lijst. Als er bijvoorbeeld honderd <div>'s zouden worden gevonden, heeft deze lijst honderd items. Zo'n item kun je aanspreken door het volgnummer te gebruiken. En omdat computers dol zijn op '0', is het volgnummer van het eerste item '0', en geen '1'.

Uiteraard is er maar één <main> gevonden. Het volgnummer van die <main> is dus '0', het eerste item.

document.getElementsByTagName("main")[0] samen: doe iets met het (eerste) element <main>.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat deel wordt hierboven beschreven en staat voor <main>.

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 'bel 112'.

"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'. Er wordt geluisterd of op de <main> wordt geklikt, of dat de <main> wordt aangeraakt. (Toen dit werd bedacht, kon je alleen klikken, dus de naam 'click' loopt wat achter.)

verwerkKlikOpMain: 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 verwerkKlikOpMain(e) { en zorgt ervoor dat ook in Firefox een grote afbeelding kan worden gesloten, en in alle browsers dat de eerste thumbnail wordt gemarkeerd, als bij openen van de pagina het scherm ergens wordt aangeraakt of -geklikt.

;: de puntkomma aan het eind geeft het eind van de regel aan.

checkboxVoorFocus.addEventListener("keydown", tabToon);

checkboxVoorFocus: bij checkboxVoorFocus = document.getElementById("checkbox-voor-focus"), is het element input#checkbox-voor-focus in de vorm van een object opgeslagen in variabele checkboxVoorFocus. Hierdoor is allerlei informatie uit input#checkbox-voor-focus toegankelijk voor het script, en kan het script allerlei dingen met input#checkbox-voor-focus doen.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat is hier checkboxVoorFocus, de input#checkbox-voor-focus die ervoor zorgt dat gelijk bij openen van met de pijltjestoetsen door de thumbnail kan worden gescrold. (Meer hierover is te vinden bij <input id="checkbox-voor-focus" ...)

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 'bel 112'.

"keydown": 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: 'keydown'. Er wordt geluisterd, welke toets wordt ingedrukt. Dit luisteren gebeurt, zolang input#checkbox-voor-focus focus heeft. (Firefox handelt focus anders af dan de meeste andere browsers, daarom wordt voor Firefox soms :target gebruikt in plaats van :focus, maar ook dan luistert deze eventlistener. Meer over Firefox en focus is te vinden bij Ruziënde browsers.)

tabToon: 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 tabToon(e) { en regelt het tonen van de grote afbeeldingen voor gebruikers van de Tab-toets.

;: de puntkomma aan het eind geeft het eind van de regel aan.

document.getElementById("schermlezer").addEventListener("change", voorSchermlezers);

Als je in de html een andere id dan 'schermlezer' hebt gebruikt bij de <input> voor schermlezers, moet je de id ook in bovenstaande regel aanpassen.

document.getElementById("schermlezer"): het stukje achter de punt getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById("schermlezer")

Er wordt gezocht naar het element met id="schermlezer", oftewel: input#schermlezer. Met dit aankruisvakje kan de slideshow worden aangepast voor schermlezers.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat is hier het element met id="schermlezer", de input#schermlezer die ervoor zorgt dat de slideshow aangepast kan worden voor een schermlezer.

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 'bel 112'.

"change": 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: 'change'. Er wordt geluisterd naar een verandering bij de <input>, naar het aan- of uitvinken van het aankruisvakje.

voorSchermlezers: 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 voorSchermlezers(e) { en regelt het aanpassen van de slideshow voor schermlezers.

;: de puntkomma aan het eind geeft het eind van de regel aan.

document.getElementById("uitleg").addEventListener("change", sluitHulp);

Als je in de html een andere id dan 'uitleg' hebt gebruikt bij de <input> om de hulp te openen, moet je de id ook in bovenstaande regel aanpassen.

Deze regel is precies hetzelfde als die hierboven bij document.getElementById("schermlezer").addEventListener("change", voorSchermlezers);. Alleen wordt hier gezocht naar het element met id='uitleg' (input#uitleg) en wordt hieraan function voorSchermlezers(e) { gekoppeld. Deze functie regelt het sluiten van de uitleg.

length = wraps.length;

length: dit is de eerder bij length, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Dat deze variabele eerder voor iets anders is gebruikt, is geen enkel probleem.

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

wraps: dit is de variabele die bij wraps = document.querySelectorAll(".wrap"), is gevuld met alle div.wrap's op de pagina.

length: de lengte van wraps. Er zitten veertien div.wrap's in de pagina met het voorbeeld, en in wraps zit voor elke div.wrap een apart object. De lengte van wraps is dus 14.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele wraps het aantal div.wrap's op de pagina.

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

Deze for-lus zorgt ervoor, dat de code tussen de { en de } een bepaald aantal keren wordt uitgevoerd. i is een teller, die elke keer dat de code wordt uitgevoerd met 1 wordt verhoogd.

In variabele length is hierboven bij length = wraps.length; het aantal div.wrap's op de pagina gestopt. Dat is in het voorbeeld 14. Zolang i kleiner is dan 14, wordt de code binnen de for-lus uitgevoerd.

Een uitgebreidere beschrijving van een for-lus is te vinden bij de for-lus iets onder length = links.length;.

wraps[i].addEventListener("keydown", tabToon);

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

wraps[i]: in variabele wraps zijn bij wraps = document.querySelectorAll(".wrap"), alle div.wrap's uit de pagina opgeslagen in de vorm van een object. In het voorbeeld zijn veertien div.wrap's aanwezig, dus wraps bevat veertien van die objecten.

[i] regelt, welke div.wrap aan de beurt is. Aan het begin van de for-lus wordt teller i op 0 gezet, en elke keer als de code tussen de {} van de for-lus is uitgevoerd, wordt deze met 1 verhoogd. Tot alle div.wrap's aan de beurt zijn geweest.

De eerste keer staat hier dus eigenlijk wraps[0]: de eerste div.wrap uit wraps (een computer begint vaak met 0 te tellen). Zodra wraps[13], de laatste div.wrap uit het voorbeeld, is afgehandeld, wordt de for-lus verlaten. Alle div.wrap's zijn dan precies één keer aan de beurt geweest.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat is hier wraps[i], de div.wrap 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 'bel 112'.

"keydown": 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: 'keydown'. Er wordt geluisterd, welke toets wordt ingedrukt. Dit luisteren gebeurt, zolang de div.wrap focus heeft.

tabToon: 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 tabToon(e) { en regelt het tonen van de grote afbeeldingen voor gebruikers van de Tab-toets.

;: de puntkomma aan het eind geeft het eind van de regel aan.

wraps[i].addEventListener("click", bewaarId);

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

Deze regel is vrijwel hetzelfde als die hier iets boven bij wraps[i].addEventListener("keydown", tabToon);. Alleen wordt hier geluisterd naar een klik of aanraking en wordt function bewaarId(e) aan de div.wrap gekoppeld. Deze functie zorgt ervoor dat bij openen van een grote afbeelding door het aanraken of -klikken van een thumbnail de id van de bijbehorende div.wrap wordt bewaard.

wraps[i].classList.add("xverbergx");

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

wraps[i]: in variabele wraps zijn bij wraps = document.querySelectorAll(".wrap"), alle div.wrap's uit de pagina opgeslagen in de vorm van een object. In het voorbeeld zijn veertien div.wrap's aanwezig, dus wraps bevat veertien van die objecten.

[i] regelt, welke div.wrap aan de beurt is. Aan het begin van de for-lus wordt teller i op 0 gezet, en elke keer als de code tussen de {} van de for-lus is uitgevoerd, wordt deze met 1 verhoogd. Tot alle div.wrap's aan de beurt zijn geweest.

De eerste keer staat hier dus eigenlijk wraps[0]: de eerste div.wrap uit wraps (een computer begint vaak met 0 te tellen). Zodra wraps[13], de laatste div.wrap uit het voorbeeld, is afgehandeld, wordt de for-lus verlaten. Alle div.wrap's zijn dan precies één keer aan de beurt geweest.

.classList: de div.wrap's zitten in variabele wraps in de vorm van een object. 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'.

In dat object zit ook allerlei informatie. In de eigenschap classList zitten alle classes, die bij de div.wrap die aan de beurt is in de html aanwezig zijn.

add("xverbergx"): add() is zo'n methode. Het voegt een class toe bij de div.wrap die aan de beurt is. De toe te voegen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat.

De naam 'xverbergx' is wat eigenaardig, maar dat verkleint de kans dat een gelijknamige class al in gebruik is. Deze class wordt in de css gebruikt om links naar de vorige en volgende grote afbeelding te verbergen. (Eigenlijk naar de vorige en volgende div.wrap, maar het zichtbare effect is dat ze de vorige of volgende grote afbeelding tonen.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

wraps[i].classList.remove("firefox");

Als je in de html een andere class dan 'firefox' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

Deze regel is vrijwel hetzelfde als wraps[i].classList.add("xverbergx"); gelijk hierboven. Alleen wordt hier geen class toegevoegd, maar de class 'firefox' wordt verwijderd. Dit zorgt ervoor dat Firefox slechts één en geen twee gemarkeerde thumbnails toont.

Als om een of andere reden de class 'firefox' niet aanwezig is, gebeurt er gewoon niets.

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.

thumbs = document.querySelectorAll(".wrap > img");

Als je in de html een andere class dan 'wrap' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

thumbs: dit is de eerder bij thumbs, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

querySelectorAll(".wrap > img"): dit is de methode uit document die gebruikt moet worden. Met behulp van de methode querySelectorAll() kun je op de pagina zoeken naar bijvoorbeeld alle elementen met een bepaalde class. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: ".wrap > img".

In dit geval wordt gezocht naar alle <img>'s die een direct kind zijn van een element met class="wrap". De schrijfwijze bij querySelectorAll() is precies hetzelfde als in css. In een selector bij css wordt .wrap > img gebruikt voor de <img>'s die een direct kind van een element met class="wrap"zijn, dus bij querySelectorAll() gebruik je ".wrap > img".

<div class="wrap"> <img src="039-pics/alpaca-300w.jpg"> </div>

In de code hierboven is de <img> een direct kind van div.wrap, want er zit geen enkel ander element tussen div.wrap en de <img>.

<div class="wrap"> <span> <img src="039-pics/alpaca-300w.jpg"> <span> </div>

In de code hierboven is de <img> geen direct kind van div.wrap, omdat er een <span> tussen zit.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek alle <img>'s die een direct kind van een div.wrap zijn, en stop die in de variabele met de naam thumbs. Omdat alleen de <img>'s met de thumbnails een direct kind van een div.wrap zijn, worden alleen de thumbnails gevonden, en niet de grote afbeeldingen.

Als dat is gedaan, kunnen we gaan werken met de inhoud van thumbs. En omdat die inhoud feitelijk de <img>'s met de thumbnails zijn, werken we in werkelijkheid niet met thumbs, maar met de <img>'s met de thumbnails.

De <img>'s worden in thumbs opgeslagen in de vorm van objecten. 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'. In elke <img> (eigenlijk in elk object dus) zit een enorme hoeveelheid informatie over de betreffende <img>, en met behulp van de methoden kun je allerlei bewerkingen op één of meer <img>'s uitvoeren, zoals het toevoegen en verwijderen van classes bij de <img>'s in de html. Dat gebeurt hier allemaal niet, iets hieronder wordt bij thumbs[i].addEventListener("click", thumbToon); alleen een eventlistener aan elke <img> gekoppeld.

In thumbs zitten dus veertien <img>'s in de vorm van een object. Elk van die <img>'s heeft een volgnummer, een zogenaamde 'index', zodat je ze apart van elkaar kunt aanroepen en er iets mee doen. Die index staat tussen [] en de telling begint met 0, want daar is een computer nou eenmaal dol op. De eerste <img> kan worden aangesproken met thumbs[0], de tweede met thumbs[1] en de veertiende en laatste met thumbs[13].

length = thumbs.length;

length: dit is de eerder bij length, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Dat deze variabele eerder voor iets anders is gebruikt, is geen enkel probleem.

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

thumbs: dit is de variabele die bij thumbs = document.querySelectorAll(".wrap > img"); is gevuld met alle <img>'s met thumbnails op de pagina.

length: de lengte van thumbs. Er zitten veertien <img>'s met thumbnails in de pagina met het voorbeeld, en in thumbs zit voor elke <img> een apart object. De lengte van thumbs is dus 14.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele thumbs het aantal <img>'s met thumbnails op de pagina.

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

Deze for-lus zorgt ervoor, dat de code tussen de { en de } een bepaald aantal keren wordt uitgevoerd. i is een teller, die elke keer dat de code wordt uitgevoerd met 1 wordt verhoogd.

In variabele length is hierboven bij length = thumbs.length; het aantal <img>'s met thumbnails op de pagina gestopt. Dat is in het voorbeeld 14. Zolang i kleiner is dan 14, wordt de code binnen de for-lus uitgevoerd.

Een uitgebreidere beschrijving van een for-lus is te vinden bij de for-lus iets onder length = links.length;.

thumbs[i].addEventListener("click", thumbToon);

Deze regel zit binnen een for-lus en wordt voor elke <img> met een thumbnail in de pagina één keer uitgevoerd.

thumbs[i]: in de variabele thumbs zijn bij thumbs = document.querySelectorAll(".wrap > img"); alle <img>'s met thumbnails op de pagina opgeslagen. (Feitelijk is voor elke <img> een object opgeslagen, waarin allerlei informatie over de <img> zit, zoals de inhoud van het src-attribuut. Van die informatie wordt hier verder geen gebruik gemaakt.)

De teksthaken [] geven aan dat hierin een teller zit. Die teller is hier de variabele i. In de for-lus, waarbinnen deze regel staat, heeft i het volgnummer gekregen van de <img> die aan de beurt is.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat is hier thumbs[i], 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 'bel 112'.

"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'. Er wordt geluisterd of op de <img> wordt geklikt, of dat de <img> wordt aangeraakt. (Toen dit werd bedacht, kon je alleen klikken, dus de naam 'click' loopt wat achter.)

thumbToon: 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 thumbToon(e) {. Deze functie zorgt ervoor dat bij de aangeraakte of -geklikte thumbnail de uitgecommentarieerde grote afbeelding verandert in gewone html, waardoor deze weergegeven kan worden.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: als een thumbnail wordt aangeraakt of – geklikt, voer dan functie 'thumbToon()' uit.

linksAfbeelding = document.querySelectorAll(".vorige-a, .volgende-a");

length = linksAfbeelding.length; for (i = 0; i < length; i++) { linksAfbeelding[i].addEventListener("click", vorigeVolgendeAfb); }

Als je in de html een andere class dan 'vorige-a' en/of 'volgende-a' hebt gebruikt bij de links naar de vorige of volgende grote afbeelding, moet je de classes ook in de bovenste van bovenstaande regels aanpassen.

Deze regels zijn vrijwel hetzelfde als die eerder bij thumbs = document.querySelectorAll(".wrap > img"); en de daarop volgende regels. De verschillen worden hieronder beschreven.

De eventlisteners worden niet gekoppeld aan de <img>'s met de thumbnails, maar aan de links naar de vorige en volgende grote afbeelding. (Eigenlijk aan de links naar de vorige en volgende div.wrap met grote afbeelding.) Er wordt in de bovenste regel met querySelectorAll() dan ook niet gezocht naar ".wrap > img", maar naar ".vorige-a, .volgende-a": de <a>'s met class="vorige-a" en class="volgende-a".

Deze worden niet in variabele thumbs, maar in de eerder bij linksAfbeelding, aangemaakte variabele linksAfbeelding opgeborgen, ook weer in de vorm van een object.

Ten slotte wordt niet de functie thumbToon(); maar de verderop staande function vorigeVolgendeAfb (e) aan de links gekoppeld. Deze functie zorgt ervoor dat bij de grote afbeelding die het doel van de link is, alsmede bij de daaraan voorafgaande en erop volgende grote afbeelding, de uitgecommentarieerde grote afbeelding verandert in gewone html, waardoor deze weergegeven kan worden.

(Er is nog een verschil, maar dat wordt automatisch door het JavaScript afgehandeld. Er zijn veertien thumbnails, maar 28 links met class="vorige-a" of class="volgende-a". Daarom wordt de for-lus niet 14 keer, maar 28 keer doorlopen. Maar omdat het JavaScript het aantal elementen weet, wordt dat aantal automatisch aangepast.)

Aanraken/klikken lege plek afhandelen

Als een lege plek in het browservenster wordt aangeraakt of -geklikt, wordt de grote afbeelding gesloten. De bij de grote afbeelding horende thumbnail houdt z'n outline, en de bij die thumbnail horende links naar de div.wrap met de vorige en volgende thumbnail worden getoond. Twee dingen werken echter niet echt optimaal:

* In Firefox wordt de grote afbeelding niet gesloten, omdat in Firefox soms geen :focus, maar :target in selectors wordt gebruikt. (De reden hiervan is te vinden bij Ruziënde browsers.)

* Als de pagina net is geopend en er wordt een lege plek in het browservenster aangeraakt of ‑geklikt, krijgt geen enkele thumbnail een outline en er worden ook geen links getoond.

De hieronder staande functie lost beide problemen op.

function verwerkKlikOpMain(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 het element <main> ergens wordt aangeraakt of -geklikt. Omdat in het voorbeeld <main> het volledige browservenster beslaat, is dat hetzelfde als het ergens aanraken of -klikken van het venster.

Het luisteren naar een aanraking of klik wordt geregeld bij document.getElementsByTagName("main")[0].addEventListener("click", verwerkKlikOpMain);.

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

verwerkKlikOpMain: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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 aanraken of ‑klikken van <main>. In e zit bijvoorbeeld de hoogte en breedte van <main>. En de achtergrondkleur. En waar <main> precies is aangeraakt of -geklikt. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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 (actieveWrap) {

Deze regel is onderdeel van function verwerkKlikOpMain(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.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waar toen verder niets mee is gedaan. Als deze variabele true ('waar') is, wordt aan de voorwaarde van de if voldaan en wordt de code tussen de bij de if horende {} uitgevoerd. Als de variabele false ('onwaar') is, wordt niet aan de voorwaarde van de if voldaan en wordt de code tussen de {} niet uitgevoerd.

Als een variabele wordt aangemaakt en er wordt verder niets in opgeslagen, zoals hier aan het begin van het script is gebeurd, krijgt deze de waarde 'undefined': ongedefinieerd. Zo'n variabele heeft een acute identiteitscrisis, want hij is niet true en niet false. Gelukkig is daar een oplossing voor: een variabele met als waarde 'undefined' is 'falsy'. Wat de if betreft is zo'n voorwaarde gewoon false en wordt de code tussen de {} dus niet uitgevoerd.

Als de pagina net wordt geopend, heeft actieveWrap als inhoud 'undefined' en zal de code tussen de {} dus niet worden uitgevoerd.

In de regel heeft één van de thumbnails een outline en worden de bij die thumbnail horende links getoond, en eventueel ook de erbij horende grote afbeelding en links. Op een aantal plaatsen in het script wordt de id van de div.wrap, waarbinnen die thumbnail en dergelijke zit, opgeslagen in variabele actieveWrap.

De id van bijvoorbeeld de eerste div.wrap is div#wrap-1. Zodra deze waarde in actieveWrap wordt opgeslagen, is actieveWrap niet meer 'undefined': er zit nu iets in. actieveWrap is nu niet meer 'falsy', maar 'truthy'. (Een variabele is alleen echt true of false als deze de waarde true of false heeft. In alle andere gevallen is de variabele 'truthy' of 'falsy'.)

Zodra er iets in actieveWrap is opgeslagen, is wat de if betreft voldaan aan de voorwaarde en zal de code tussen de {} worden uitgevoerd.

{: de code die wordt uitgevoerd, als aan deze if wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als er iets (in dit geval een id) is opgeslagen in variabele actieveWrap, voer dan de code tussen de {} achter de if uit.

if (e.target.tagName === "MAIN") {

Als je in de html een andere element dan <main> hebt gebruikt om het browservenster te vullen, moet je dat element ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function verwerkKlikOpMain(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Er is iets (een id) opgeslagen in variabele actieveWrap.

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

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of ‑geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

tagName: dit is weer een onderdeel van het hierboven genoemde object target. In tagName zit de naam van de tag, die is aangeraakt of -geklikt. Als dat een link is, is de inhoud van tagName 'A', als het een afbeelding is, is de inhoud, 'IMG', enzovoort. In JavaScript is de naam in tagName altijd in hoofdletters, ongeacht wat er in de html staat.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

"MAIN": de naam van de tag. De naam staat tussen aanhalingstekens, zodat JavaScript weet dat het om een letterlijke naam gaat. En in hoofdletters, omdat de inhoud van tagName ook altijd in hoofdletters is geschreven.

Tenzij je dat voorkomt, wordt een aanraking of klik 'doorgegeven' aan de voorouders van het element dat is aangeraakt of -geklikt. Daardoor komt uiteindelijk ook elke aanraking of klik uit bij <main>. Bijvoorbeeld elke <a> in het voorbeeld is een nakomeling van <main>. Deze code moet echter alleen werken, als op een leeg deel van <main>, als <main> rechtstreeks is aangeraakt of -geklikt. En niet als een <a> of een thumbnail of zo iets is aangeraakt of -geklikt.

De inhoud van tagName bevat de naam van de tag, die daadwerkelijk is aangeraakt of -geklikt. Alleen als rechtstreeks een leeg deel van <main> is aangeraakt of -geklikt, is die naam 'MAIN'. En omdat er maar één <main> is, is dat per definitie de juiste <main>. (Bij bijvoorbeeld een <a> zou je eerst nog moeten uitvogelen, wélke <a> is aangeraakt of -geklikt.)

{: de code die wordt uitgevoerd, als aan deze if wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als ergens een lege plek in <main> is aangeraakt of -geklikt.

document.getElementById(actieveWrap).classList.remove("firefox");

Als je in de html een andere class dan 'firefox' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function verwerkKlikOpMain(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– Er is iets (een id) opgeslagen in variabele actieveWrap.

– Er is een lege plek in <main> aangeraakt of -geklikt.

document.getElementById(actieveWrap): het stukje achter de punt getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById(actieveWrap)

Omdat het deel tussen de haakjes hier niet tussen aanhalingstekens staat, wordt het door JavaScript als een variabele gezien. Er wordt niet naar de id 'actieveWrap' gezocht, maar naar de inhoud van variabele actieveWrap. Die inhoud móét er zijn, want dat was een voorwaarde van if (actieveWrap) om dit deel van de code uit te voeren. Als actieveWrap nog geen inhoud heeft, nog 'undefined' is, wordt het deel tussen de {} bij de } else { lager in de functie uitgevoerd.

De enige inhoud van actieveWrap kan een id van een van de div.wrap's zijn, want dat is het enige wat het script er op een aantal plaatsen in opbergt. Als die id bijvoorbeeld 'wrap-3' is, de id van de derde div.wrap, is de inhoud van actieveWrap 'wrap-3'. getElementById(actieveWrap) wordt dan door het script gelezen als getElementById("wrap-3").

In dat geval wordt div#wrap-3 gevonden door getElementById(actieveWrap), en de informatie en methoden die dit oplevert, horen dan bij div#wrap-3. In het voorbeeld zijn veertien div.wrap's aanwezig met in elk een thumbnail, bijbehorende links, grote afbeelding, en dergelijke. De gevonden id's kunnen in het voorbeeld 'wrap-1' tot en met 'wrap-14' zijn.

classList: de hierboven met behulp van document.getElementById(actieveWrap) gevonden <div> is niet de <div> zelf, maar is de <div> in de vorm van een object. 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'.

In dat object zit ook allerlei informatie. In de eigenschap classList zitten alle classes, die bij de gevonden <div> in de html aanwezig zijn.

remove.("firefox"): remove() is zo'n methode. Het verwijdert een class bij de gevonden <div>. De te verwijderen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat. Dit is een class speciaal voor Firefox, omdat Firefox anders met :focus omgaat dan de meeste andere browsers. (Meer hierover is te vinden bij Ruziënde browsers.)

Voor Firefox staat in de css onder andere onder @supports (-moz-appearance: none) aparte css, waarmee onder andere de grote afbeelding wordt getoond, en de bij die grote afbeelding horende links naar de vorige of volgende grote afbeelding (eigenlijk de links naar de vorige en volgende div.wrap met grote afbeelding).

In die speciaal voor Firefox bedoelde css worden selectors met class 'firefox' gebruikt, om ook in Firefox met behulp van :target de grote afbeelding met bijbehorende links te kunnen tonen, bijvoorbeeld .wrap.firefox:target .vorige-a.

Als er eenmaal een grote afbeelding met bijbehorende links is getoond, blijft in Firefox altijd een grote afbeelding met bijbehorende links zichtbaar. Door de afwijkende manier waarop in Firefox grote afbeelding met bijbehorende links worden getoond, worden deze niet verborgen door het aanraken of -klikken van een leeg deel van het browservenster.

Door stomweg class 'firefox' te verwijderen, werkt de selector niet meer en worden ook in Firefox grote afbeelding en bijbehorende links verborgen bij aanraken van een leeg deel van het browservenster.

Als om een of andere reden class 'firefox' niet aanwezig is in de html, kan deze uiteraard ook niet worden verwijderd. In dat geval gebeurt er hier gewoon helemaal niets. Het levert geen fout of zo op.

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 aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder bij de actieve div.wrap (de div.wrap waarvan de thumbnail een outline heeft en dergelijke) class 'firefox'.

Het hele deel tussen de {} achter de if samengevat: als er een id in actieveWrap zit, en als een lege plek in <main> wordt aangeraakt of -geklikt, verwijder dan class 'firefox' bij de div.wrap met de id uit actieveWrap. Hierdoor worden ook in Firefox grote afbeelding en bijbehorende links verborgen, als een lege plek in <main> wordt aangeraakt of -geklikt.

} else {

Deze regel is onderdeel van function verwerkKlikOpMain(e)

Dit deel van het script wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Er is nog niets opgeslagen in variabele actieveWrap.

Deze else hoort bij if (actieveWrap) {: als variabele actieveWrap een id bevat. Als dat niet zo is, wordt de code tussen de {} achter de else uitgevoerd.

}: dit is de afsluitende accolade van de code die bij de hierboven genoemde if hoort.

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

actieveWrap = "wrap-1";

Als je in de html een andere id dan 'wrap-1' hebt gebruikt bij het eerste element, waarin een thumbnail met bijbehorende links, afbeelding, en dergelijke zit, moet je de id ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function verwerkKlikOpMain(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Er is nog niets opgeslagen in variabele actieveWrap.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

"wrap-1": dit is wat in actieveWrap wordt opgeslagen: de id van de eerste div.wrap. Dat is de div.wrap, waarin de eerste thumbnail met bijbehorende links, grote afbeelding, en dergelijke zit.

;: de puntkomma aan het eind geeft het eind van de regel aan.

Als de pagina wordt geopend, krijgt de eerste thumbnail een outline en worden de bij die thumbnail horende links getoond. Dat gebeurt door de focus met behulp van het attribuut autofocus op input#checkbox-voor-focus te zetten. Als vervolgens gelijk na het openen van de pagina een lege plek in het browservenster wordt aangeraakt of -geklikt, verliest input#checkbox-voor-focus de focus. Daardoor heeft geen enkele thumbnail meer een outline en zijn ook alle links verdwenen.

In het script wordt in variabele actieveWrap bijgehouden, bij welke div.wrap de thumbnail een outline moet krijgen, en welke links getoond moeten worden. Dat gebeurt door in actieveWrap de id van de div.wrap op te slaan. Gelijk na het openen van de pagina zit er echter nog geen id in actieveWrap. Als gelijk na het openen van de pagina een lege plek in het browservenster wordt aangeraakt of -geklikt, wordt daarom hier 'wrap-1', de id van de eerste div.wrap, opgeslagen.

actieveWrap wordt alleen binnen het script gebruikt, gelijk hieronder worden aanpassingen voor de css gemaakt, want daar speelt hetzelfde probleem.

wrap_1.classList.add("xactiefx", "firefox");

Als je in de html een andere class dan 'firefox' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de id ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function verwerkKlikOpMain(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Er is nog niets opgeslagen in variabele actieveWrap.

wrap_1: in variabele wrap_1 is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1, de eerste div.wrap, opgeslagen in de vorm van een object. (In elke div.wrap zit een thumbnail met bijbehorende links, grote afbeelding, en dergelijke.) Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

classList: in dat object zit ook allerlei informatie. In de eigenschap classList zitten alle classes, die bij div#wrap-1 in de html aanwezig zijn.

add("xactiefx", "firefox"): add() is zo'n methode. Het voegt een class toe bij de div.wrap die aan de beurt is. De toe te voegen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat.

In dit geval gaat het om twee classes: 'xactiefx' en 'firefox', gescheiden door een komma.

De naam 'xactiefx' is wat eigenaardig, maar dat verkleint de kans dat een gelijknamige class al in gebruik is. Deze class wordt in de css gebruikt om de juiste thumbnail een outline te geven en om de bij die thumbnail horende links te tonen. Waarom dat nodig is, is bij de regel iets hierboven te vinden bij Als de pagina wordt geopend...

De class 'firefox' is een class speciaal voor Firefox, omdat Firefox anders met :focus omgaat dan de meeste andere browsers. (Meer hierover is te vinden bij Ruziënde browsers.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

Als je wilt kijken, of deze classes inderdaad door het script worden toegevoegd en verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

Tab / Shift+Tab afhandelen

Sommige mensen kunnen of willen de muis niet gebruiken, maar gaan met de Tab-toets links, knoppen, en dergelijke af. In dit deel van het script wordt geregeld dat ook met de Tab-toets de grote afbeeldingen getoond kunnen worden.

Hierbij worden de thumbnails en links helemaal niet gebruikt. Door de Tab-toets in te drukken, worden alle grote afbeeldingen één voor één geopend. Met Shift+Tab wordt een vorige grote afbeelding weer getoond.

Deze functie wordt alleen uitgevoerd, als met de Tab-toets door de grote afbeeldingen wordt gelopen. Als op die manier een grote afbeelding wordt getoond, is de kans vrij groot dat op dezelfde manier een vorige of volgende grote afbeelding gaat worden getoond.

Daarom wordt, als de Tab-toets wordt gebruikt, ook bij de grote afbeeldingen voorafgaand en volgend op de getoonde grote afbeelding het commentaar alvast in html veranderd. Zodra dat gebeurt, zal de browser de afbeeldingen downloaden, ook al worden die nog niet getoond. Hierdoor kunnen ze sneller worden weergegeven.

function tabToon(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 Tab-toets wordt ingedrukt (al dan niet in combinatie met de Shift-toets). Het luisteren of de Tab-toets wordt ingedrukt, wordt geregeld bij checkboxVoorFocus.addEventListener("keydown", tabToon); en bij wraps[i].addEventListener("keydown", tabToon);. (Feitelijk wordt geluisterd, of er 'n toets wordt ingedrukt. Als dat gebeurt, kijkt deze functie eerst, of dat de Tab-toets was. Als dat niet zo is, blijft deze functie lekker in bed liggen en doet helemaal niets.)

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

tabToon: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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. En of dat in combinatie met de Shift-toets was. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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

var span;

Deze regel is onderdeel van function tabToon(e)

Met het sleutelwoord var 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 var volgt de naam van de variabele: span.

De puntkomma aan het eind geeft het eind van de regel aan.

Over wat een variabele precies is, is meer te vinden bij var vensterGrootte,. Omdat variabele span binnen function tabToon() is aangemaakt, is span alleen voor code binnen die functie beschikbaar.

span wordt gebruikt om er de inhoud van de <span> met de grote afbeelding in op te slaan en daar eventueel <!-- en --> uit te verwijderen, zodat het commentaar in gewone html verandert en de afbeelding weergegeven kan worden.

if (e.key === "Tab") {

Deze regel is onderdeel van function tabToon(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: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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.

"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 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 }. In dit geval staat die afsluitende } helemaal aan het eind van function tabToon().

Alleen de Tab-toets moet worden afgehandeld door function tabToon(). Daarom staat alle code van deze functie binnen deze if. Als een andere toets dan de Tab-toets is ingedrukt, wordt niet aan de voorwaarde van de if voldaan en doet de functie verder niets.

if (e.target.id === "checkbox-voor-focus" || e.target.id === "wrap-1") {

Als je in de html een andere id dan 'checkbox-voor-focus' hebt gebruikt bij deze <input>, moet je de id ook in bovenstaande regel aanpassen.

Als je in de html een andere id dan 'wrap-1' hebt gebruikt bij het eerste element, waarin een thumbnail met bijbehorende links, afbeelding, en dergelijke zit, moet je de id ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– De Tab-toets is ingedrukt.

if: dat betekent gewoon 'als': als er aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Het is het hele deel tussen de twee buitenste haakjes achter de if.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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).

target: dit is zo'n stukje informatie uit het hierboven genoemde object: hierin zit allerlei informatie over het element dat focus had, op het moment dat de Tab-toets werd ingedrukt.

id: in dit stukje informatie uit target zit de id van het element.

===: wat hiervoor staat moet precies, echt helemaal precies, hetzelfde zijn, als wat hierachter staat. Om dat echt helemaal volkomen precies hetzelfde aan te geven, worden drie isgelijktekens gebruikt.

"checkbox-voor-focus": de id van input#checkbox-voor-focus. Dit is de <input> die er met behulp van het attribuut autofocus voor zorgt dat bij openen van de pagina gelijk met de pijltjestoetsen door de thumbnails kan worden gescrold. Er is meer over te vinden bij <input id="checkbox-voor-focus" ...

||: twee verticale strepen betekenen in JavaScript 'of'. Omdat dit binnen een if staat, moet óf wat voor de || staat waar zijn, óf wat achter de || staat.

e.target.id === "wrap-1": dit is hetzelfde als target.id === "checkbox-voor-focus" voor de ||, maar nu moet de id 'wrap-1' zijn: div#wrap-1. De eerste div.wrap met een thumbnail en bijbehorende links, grote afbeelding, en dergelijke.

{: de code die wordt uitgevoerd, als aan deze if-voorwaarde wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als bij het indrukken van de Tab-toets input#checkbox-voor-focus of div#wrap-1 de focus had.

Normaal genomen kan een <div> geen focus krijgen, maar door het toevoegen van tabindex="0" bij elke div.wrap kan dat hier wel.

Bij gebruik van de Tab-toets krijgt input#checkbox-voor-focus juist wel focus, maar dat is in dit geval zinloos. Door het toevoegen van attribuut tabindex="-1" aan de <input> wordt dit voorkomen. Bij openen van de pagina heeft de <input> door het toevoegen van attribuut autofocus echter wel focus.

Als na openen van de pagina gelijk de Tab-toets wordt ingedrukt, kan de grote afbeelding nog niet worden weergegeven, omdat de <span> met de afbeelding met <!-- en --> nog commentaar is. Het is nog niet veranderd in gewone html.

Daardoor zou in beide gevallen, als gelijk na openen van de pagina de Tab-toets wordt ingedrukt, de grote afbeelding niet worden getoond. Dat wordt hier voorkomen. Omdat aan het eind van de code binnen deze if de eventlisteners bij input#checkbox-voor-focus en div#wrap-1 worden verwijderd, kan deze code meer één keer worden uitgevoerd. Daarna is het ook niet meer nodig, omdat de <span> met de grote afbeelding in gewone html is veranderd.

e.preventDefault();

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

preventDefault(): dit is zo'n methode uit het hierboven genoemde object. Hiermee wordt de standaardafhandeling geblokkeerd. In het geval van de Tab-toets is dat het gaan naar het volgende element, dat de focus kan krijgen. (Of het vorige element, als Shift+Tab is ingedrukt).

Als dit niet wordt voorkomen, wordt de code hier netjes uitgevoerd, maar vervolgens wordt de tweede (of laatste) grote afbeelding getoond. Want de Tab-toets gaat vrolijk naar het volgende (of vorige) element dat de focus kan krijgen.

;: de puntkomma aan het eind geeft het eind van de regel aan.

span = wrap_1.querySelector("span.foto");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

span: dit is de eerder bij var span; aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

wrap_1: in deze variabele is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1 opgeslagen in de vorm van een object. 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'.

querySelector("span.foto"): dit is de methode uit het hierboven genoemde object die gebruikt moet worden. Met behulp van de methode querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

Omdat voor de punt wrap_1 staat, gaat het om de eerste span.foto binnen div#wrap-1.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het eerste element met class="foto" dat binnen een <span> zit, die weer binnen div#wrap-1 zit. Dit is de <span>, waarbinnen de grote afbeelding zit.

Als dat is gedaan, kunnen we gaan werken met de inhoud van span. En omdat die inhoud feitelijk de span.foto is, werken we in werkelijkheid niet met span, maar met de span.foto met de grote afbeelding.

if (span.innerHTML.indexOf("<!--") > -1) {

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

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.

span: in variabele span is gelijk hierboven bij span = wrap_1.querySelector("span.foto"); in de vorm van een object span.foto opgeslagen. 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'.

(Hier is de in span opgeslagen span.foto de span.foto uit div#wrap-1, omdat in variabele wrap_1 div#wrap-1 is opgeslagen. Op andere plaatsen in het script zal een andere span.foto in span zijn opgeslagen, maar dat maakt voor de werking van deze regels niets uit.)

innerHTML: dit is een van de in span opgeslagen eigenschappen. Het bevat alle html die binnen de in variabele span opgeslagen span.foto zit. Bij openen van de pagina is dat voor bijvoorbeeld de span.foto uit div#wrap-1:

<noscript>

<img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca">

</noscript>

<!-- <img src="039-pics/alpaca-900w.jpg" srcset="039-pics/alpaca-900w.jpg 1x, 039-pics/alpaca-1800w.jpg 2x" alt="Alpaca"> -->

De <span>-tag zelf hoort niet bij innerHTML, alleen wat bínnen de <span> staat. Het deel binnen de <noscript>-tag is hier verder niet interessant, want dat wordt alleen uitgevoerd, als JavaScript uitstaat. (En om even te laten zien, in alle bescheidenheid, welke waanzinnig briljante redenaties schrijver dezes kan maken: als dit script wordt uitgevoerd, staat JavaScript kennelijk niet uit.)

Waar het hier om gaat is het deel dat met behulp van <!-- en --> in commentaar is veranderd en daarom door de browser wordt genegeerd. Hieronder gaat dit commentaar verwijderd worden, waardoor het commentaar in gewone html verandert en de erin zittende <img> weergegeven kan worden.

indexOf("<!--"): dit is zo'n methode. Met indexOf() kan worden gekeken, of een bepaalde tekst binnen een andere tekst staat. De tekst waarnaar wordt gezocht, staat tussen de haakjes. In dit geval is dit <!--, het begin van een commentaar in html. Tussen aanhalingstekens, zodat JavaScript weet dat het om een letterlijke tekst gaat.

Omdat voor indexOf() span.innerHTML staat, wordt gekeken of '<!--' binnen de html binnen de <span> staat.

indexOf() laat met behulp van een getal weten, of de tekst tussen de haakjes wel of niet in het adres binnen html binnen de <span> voorkomt. Als dat het geval is, wordt het volgnummer van de plaats waar '<!--' begint in de html binnen de <span>. Als '<!--' niet in de html binnen de <span> 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 '<!--' niet in de html binnen de <span> zit, retourneert indexOf() het getal -1. Als het geretourneerde getal 0 of groter groter is, staat ergens binnen de html '<!--' en wordt het volgnummer van het begin van '<!--' binnen de html geretourneerd. Soms is het handig om te weten, waar iets precies zit, maar dat is hier niet van belang. Het gaat er hier alleen maar om, óf '<!--' in de html binnen de <span> staat.

{: 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: kijk of in de html binnen de <span> de tag <!--, het begin van commentaar, voorkomt.

Voor een grote afbeelding kan worden getoond, moet eerst het commentaar in gewone html worden veranderd. Het kan echter zijn dat dat al is gebeurd. Afhankelijk van de manier, waarop de grote afbeelding wordt getoond, kan ook bij een direct voorafgaande of volgende grote afbeelding het commentaar al in html zijn veranderd. Om onnodig vervangen van de html – en daarmee mogelijk het opnieuw opmaken van de pagina – te voorkomen, wordt daarom eerst gekeken óf er nog wel commentaar in de <span> zit. Als dat niet zo is, hoeft de html in de <span> niet vervangen te worden.

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", "");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

– In de html binnen variabele span komt '<!--' voor.

Bij span = wrap_1.querySelector("span.foto"); is in variabele span de inhoud van de span.foto uit div#wrap-1 in de vorm van een object opgeslagen. 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'. Alle handelingen hier hebben daarom in werkelijkheid betrekking op die in variabele span opgeslagen span.foto.

(Hier is de in span opgeslagen span.foto de span.foto uit div#wrap-1, omdat in variabele wrap_1 div#wrap-1 is opgeslagen. Op andere plaatsen in het script zal een andere span.foto in span zijn opgeslagen, maar dat maakt voor de werking van deze regels niets uit.)

span.innerHTML: zoals gelijk hierboven bij if (span.innerHTML.indexOf("<!--") > -1) { beschreven zit hierin de html bínnen de span.foto.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

In dit geval heeft dat 'opslaan' een belangrijke bijwerking. Door het aanpassen van span.innerHTML wordt gelijktijdig de html in de in span opgeslagen span.foto aangepast. Waardoor de weergave op het scherm ook wordt aangepast.

innerHTML is een van de eigenschappen die niet alleen door JavaScript gelezen kan worden, maar ook kan worden aangepast.

span.innerHTML: dit is weer dezelfde span.innerHTML als hierboven.

replace("<!--", ""): dit is een van in JavaScript ingebouwde functies: vervang een stukje tekst door een ander stukje.

Voor de komma staat het stukje tekst dat vervangen moet worden: <!--: de tag die een stuk commentaar opent. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke tekst gaat.

Na de komma staat, waardoor deze tekst vervangen moet worden. Ook weer tussen aanhalingstekens. Omdat in dit geval helemaal niets tussen die aanhalingstekens staat, wordt de <!-- vervangen door helemaal niets. Oftewel: gewoon verwijderd.

replace("-->", ""): zelfde verhaal als gelijk hierboven, maar nu voor de --> die een commentaar afsluit.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder '<!--' en '-->' uit de html binnen span.foto en vervang de html binnen span.foto door die nieuwe html.

Hierdoor is het commentaar binnen span.foto veranderd in gewone html en kan de <img> met de grote afbeelding gewoon worden weergegeven.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

wrap_1.classList.remove("xverbergx");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

wrap_1: in deze variabele is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1 opgeslagen in de vorm van een object. 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'.

.classList: in de eigenschap classList van het hierboven genoemde object zitten alle classes, die bij div#wrap-1 in de html aanwezig zijn.

remove("xverbergx"): remove() is zo'n methode. Het verwijdert een class bij het element dat in de vorm van een object in wrap_1 is opgeslagen, bij div#wrap-1.

De te verwijderen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat.

Als om een of andere reden class 'xverbergx' niet aanwezig is, gebeurt er gewoon niets.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder class 'xverbergx' bij de in de vorm van een object in wrap_1 opgeslagen div#wrap-1.

Bij wraps[i].classList.add("xverbergx"); wordt, gelijk bij het openen van de pagina, aan elke div.wrap een class 'xverbergx' toegevoegd. In de css wordt deze class gebruikt om de links naar de vorige en volgende grote afbeelding te verbergen. (Eigenlijk de links naar de vorige of volgende div.wrap met grote afbeelding.)

Omdat deze grote afbeelding nu kan worden getoond, mogen ook de bijbehorende links worden getoond. Ze worden alleen daadwerkelijk getoond, als de bijbehorende grote afbeelding ook wordt getoond. Maar ze kúnnen nu worden getoond.

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.

span = wrap_1.nextElementSibling.querySelector("span.foto");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

De functie, waar deze regel in zit, wordt alleen uitgevoerd, als met de Tab-toets door de grote afbeeldingen wordt gelopen. Als op die manier een grote afbeelding wordt getoond, is de kans vrij groot dat op dezelfde manier een vorige of volgende grote afbeelding gaat worden getoond.

Daarom wordt, als de Tab-toets wordt gebruikt, ook bij de grote afbeeldingen voorafgaand en volgend op de getoonde grote afbeelding het commentaar alvast in html veranderd. Zodra dat gebeurt, zal de browser de afbeeldingen downloaden, ook al worden die nog niet getoond. Hierdoor kunnen ze sneller worden weergegeven.

span: dit is de eerder bij var span; aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

wrap_1: in deze variabele is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1 opgeslagen in de vorm van een object. 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'.

nextElementSibling: het element dat in de html na het element in variabele wrap_1 komt. Beide elementen moeten dezelfde ouder hebben. Omdat het element dat bij het object uit wrap_1 hoort div#wrap-1 is, is dat in dit geval div#wrap-2.

querySelector("span.foto"): dit is de methode uit het hierboven genoemde object die gebruikt moet worden. Met behulp van de methode querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele span het element dat in de html direct op div#wrap-1 volgt en dezelfde ouder heeft.

if (span.innerHTML.indexOf("<!--") > -1) {

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }

Deze drie regels zijn onderdeel van function tabToon(e)

Dit deel van het script wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde(n) is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

Deze drie regels zijn precies hetzelfde als die bij if (span.innerHTML.indexOf("<!--") > -1) {. Een uitgebreide uitleg is daar te vinden.

(Daar wordt de span.foto uit div#wrap-1 in variabele span opgeslagen. Hier gaat het mogelijk om de span.foto uit een andere div.wrap. Dat maakt echter voor de werking van deze regels geen enkel verschil uit, alleen wordt de html van een andere span.foto aangepast.)

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

wrap_1.nextElementSibling.classList.remove("xverbergx");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

wrap_1: in deze variabele is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1 opgeslagen in de vorm van een object. 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'.

nextElementSibling: het element dat in de html na het element in variabele wrap_1 komt. Beide elementen moeten dezelfde ouder hebben. Omdat het element dat bij het object uit wrap_1 hoort div#wrap-1 is, is dat in dit geval div#wrap-2.

.classList: in de eigenschap classList van het hierboven genoemde object zitten alle classes, die bij div#wrap-1 in de html aanwezig zijn.

remove("xverbergx"): remove() is zo'n methode. Het verwijdert een class bij het element dat bij nextElementSibling hoort, in dit geval div#wrap-2.

De te verwijderen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat.

Als om een of andere reden class 'xverbergx' niet aanwezig is, gebeurt er gewoon niets.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder class 'xverbergx' bij het element dat in de html volgt op de in de vorm van een object in wrap_1 opgeslagen div#wrap-1.

Bij wraps[i].classList.add("xverbergx"); wordt, gelijk bij het openen van de pagina, aan elke div.wrap een class 'xverbergx' toegevoegd. In de css wordt deze class gebruikt om de links naar de vorige en volgende grote afbeelding te verbergen. (Eigenlijk de links naar de vorige of volgende div.wrap met grote afbeelding.)

Omdat deze grote afbeelding nu kan worden getoond, mogen ook de bijbehorende links worden getoond. Ze worden alleen daadwerkelijk getoond, als de bijbehorende grote afbeelding ook wordt getoond. Maar ze kúnnen nu worden getoond.

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.

wrap_1.removeEventListener("keydown", tabToon);

checkboxVoorFocus.removeEventListener("keydown", tabToon);

Deze twee regels zijn onderdeel van function tabToon(e)

Deze regels worden alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

Deze twee regels zijn bijna hetzelfde.

De eerste regel is voor div#wrap-1, die in de vorm van een object is opgeslagen in variabele wrap_1.

De tweede regel is voor input#checkbox-voor-focus, die in de vorm van een object is opgeslagen in variabele checkboxVoorFocus.

Aan beide elementen is eerder bij respectievelijk wraps[i].addEventListener("keydown", tabToon); en checkboxVoorFocus.addEventListener("keydown", tabToon); een eventlistener gekoppeld die kijkt, of een toets wordt ingedrukt ('keydown'). Als dat de Tab-toets is, wordt function tabToon(e) uitgevoerd, waarvan ook deze twee regels een onderdeel zijn.

Dit stukje van de functie heeft de <span> met de grote afbeelding in div#wrap-1 en div#wrap-2 in gewone html veranderd, waardoor de grote afbeelding nu altijd wordt weergegeven, als de Tab-toets wordt ingedrukt. Daarom zijn deze eventlisteners nu niet meer nodig.

Met removeEventListener() worden ze verwijderd. Tussen de haakjes komt hetzelfde te staan als bij het koppelen van de eventlisteners. Voor de komma de 'event', de gebeurtenis, waar de eventlistener op moet reageren: 'keydown'. Na de komma de naam van de aan die event gekoppelde functie: 'tabToon'. Nu besteedt de browser geen onnodige energie meer aan het luisteren naar het indrukken van een toets bij deze twee elementen.

(Je moet de soort event en de naam van de functie opgeven bij het verwijderen, omdat aan één element verschillende eventlisteners met verschillende functies kunnen zijn gekoppeld.)

wrap_1.focus();

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

wrap_1: in deze variabele is bij wrap_1 = document.getElementById("wrap-1"), div#wrap-1 opgeslagen in de vorm van een object. 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'.

focus(): dit is zo'n methode: zet de focus op het in wrap_1 opgeslagen element.

;: de puntkomma aan het eind geeft het eind van de regel aan.

Dit deel van de functie toont de eerste grote afbeelding. Die zit in div#wrap-1, met de bijbehorende thumbnail, links, en dergelijke. Door div#wrap-1 de focus te geven, wordt met behulp van css een outline om de thumbnail gezet, en worden de bijbehorende links getoond.

return;

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had de focus.

Het deel van function tabToon() dat moet worden uitgevoerd, als input#checkbox-voor-focus of div#wrap-1 de focus had, is nu uitgevoerd. De rest van de functie wordt alleen uitgevoerd, als de Tab-toets wordt ingedrukt, terwijl deze twee elementen niet de focus hadden.

return: stuurt de uitvoering van het script terug naar de plaats, waar function tabToon() werd aangeroepen, om daar eventueel verder te gaan met de uitvoering van het script. In dit geval is er verder niets uit te voeren, dus stopt het script hier. Tot het eventueel weer ergens opnieuw wordt aangeroepen.

Zonder return zou ook het hieronder staande deel van deze functie worden uitgevoerd, en dat is geen goed idee.

;: de puntkomma aan het eind geeft het eind van de regel aan.

span = e.target.querySelector("span.foto");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

span: dit is de eerder bij var span; aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat de focus had op het moment dat een toets werd ingedrukt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

querySelector("span.foto"): dit is zo'n methode uit het hierboven genoemde object. Met behulp van querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

Omdat voor de punt target staat, gaat het om de span.foto binnen de div.wrap die is aangeraakt of -geklikt.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het eerste element met class="foto" dat binnen een <span> zit, die weer binnen een div.wrap zit. Welke div.wrap dat is, zit in target. Dit is de <span>, waarbinnen de grote afbeelding zit.

Als dat is gedaan, kunnen we gaan werken met de inhoud van span. En omdat die inhoud feitelijk de span.foto is, werken we in werkelijkheid niet met span, maar met de span.foto met de grote afbeelding.

if (span.innerHTML.indexOf("<!--") > -1) {

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

In span is in de vorm van een object een hierboven bij span = e.target.querySelector("span.foto"); een span.foto opgeslagen. Dat is de span.foto die in de div.wrap zat die de focus had, op het moment dat de Tab-toets werd ingedrukt.

Deze regel is precies hetzelfde als die bij if (span.innerHTML.indexOf("<!--") > -1) {, waar een uitgebreidere uitleg is te vinden.

In het kort: als in de html van de <span> de tag <!-- aanwezig is. Oftewel: als er commentaar in de <span> zit. In dat geval wordt de code tussen de {} achter de if uitgevoerd.

e.preventDefault();

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

– De tag <!-- is nog aanwezig in de span.foto van de div.wrap die focus had.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

preventDefault(): dit is zo'n methode uit het hierboven genoemde object. Hiermee wordt de standaardafhandeling geblokkeerd. In het geval van de Tab-toets is dat het gaan naar het volgende element, dat de focus kan krijgen. (Of het vorige element, als Shift+Tab is ingedrukt).

;: de puntkomma aan het eind geeft het eind van de regel aan.

Als een grote afbeelding al eens is getoond, zit er geen commentaar meer in de <span>. Als er nog wel commentaar in zit, is de grote afbeelding in deze <span> nog niet getoond. In dat geval moet eerst de grote afbeelding worden getoond, die hoort bij de div.wrap die focus had op het moment dat de Tab-toets werd ingedrukt.

Standaard gaat de Tab-toets naar de volgende (of vorige) div.wrap. Deze regel zorgt ervoor dat dat niet gebeurt.

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", "");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

– De tag <!-- is nog aanwezig in de span.foto van de div.wrap die focus had.

Deze regel is precies hetzelfde als die bij span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", "");. Een uitgebreide uitleg is daar te vinden.

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

span = e.target.previousElementSibling.querySelector("span.foto");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

span: dit is de eerder bij var span; aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target.previousElementSibling:

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat de focus had op het moment dat een toets werd ingedrukt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

previousElementSibling: dit is zo'n stukje informatie uit het hierboven genoemde target: het bevat het element (ook weer in de vorm van een object) dat in de html voor het element in target zit, en dat dezelfde ouder heeft. Omdat in target één van de div.wrap's zit, is dit per definitie ook een div.wrap.

(Er is altijd een div.wrap aanwezig in de html voorafgaand aan de div.wrap die is aangeraakt of -geklikt. Alleen de eerste div.wrap, div#wrap-1, heeft geen voorafgaande div.wrap, maar die is binnen deze functie eerder al apart afgehandeld bij if (e.target.id === "checkbox-voor-focus" || e.target.id === "wrap-1") {.)

querySelector("span.foto"): dit is zo'n methode uit het hierboven genoemde object. Met behulp van querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

Omdat voor de punt target.previousElementSibling staat, gaat het om de span.foto binnen de div.wrap in de html voorafgaand aan de div.wrap die is aangeraakt of -geklikt.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het eerste element met class="foto" dat binnen een <span> zit, die weer binnen een div.wrap zit, die in de html voor de div.wrap zit die is aangeraakt of -geklikt. Dit is de <span>, waarbinnen de grote afbeelding zit.

Als dat is gedaan, kunnen we gaan werken met de inhoud van span. En omdat die inhoud feitelijk de span.foto is, werken we in werkelijkheid niet met span, maar met de span.foto met de grote afbeelding.

function tabToon(), waar deze regel in zit, wordt alleen uitgevoerd, als met de Tab-toets door de grote afbeeldingen wordt gelopen. Als op die manier een grote afbeelding wordt getoond, is de kans vrij groot dat op dezelfde manier een vorige of volgende grote afbeelding gaat worden getoond.

Daarom wordt, als de Tab-toets wordt gebruikt, ook bij de grote afbeeldingen voorafgaand en volgend op de getoonde grote afbeelding het commentaar alvast in html veranderd. Zodra dat gebeurt, zal de browser de afbeeldingen downloaden, ook al worden die nog niet getoond. Hierdoor kunnen ze sneller worden weergegeven.

if (span.innerHTML.indexOf("<!--") > -1) {

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }

Deze drie regels zijn onderdeel van function tabToon(e)

Deze regels worden alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

Deze drie regels zijn precies hetzelfde als die bij if (span.innerHTML.indexOf("<!--") > -1) {. Een uitgebreide uitleg is daar te vinden.

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven.

Hier wordt het commentaar in html veranderd bij de <span> in de div.wrap voorafgaande aan de div.wrap, waarvan de erin zittende grote afbeelding wordt getoond. Als iemand een grote afbeelding opent met de Tab-toets, is de kans vrij groot dat ook de volgende of de vorige grote afbeelding met de Tab-toets of Shift+Tab getoond moet worden. Door het commentaar in html te veranderen, verandert de in de <span> zittende <img> in een gewone <img>, waardoor deze alvast wordt gedownload en daardoor sneller weergegeven kan worden.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

e.target.previousElementSibling.classList.remove("xverbergx");

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

Deze regel is vrijwel hetzelfde als die bij wrap_1.classList.remove("xverbergx");, waar een uitgebreide beschrijving staat. Het enige verschil: daar staat aan het begin wrap-1: de variabele waarin div#wrap-1 is opgeslagen. Hier staat aan het begin e.target.previousElementSibling: de div.wrap die in de html voorafgaat aan de div.wrap die is aangeraakt of -geklikt. (Een uitgebreider verhaal over wat e.target.previousElementSibling is, staat iets hoger bij span = e.target.previousElementSibling.querySelector("span.foto");.)

De korte samenvatting: verwijder bij de betreffende div.wrap class 'xverbergx'. In de css wordt deze class gebruikt om de links naar de vorige en volgende grote afbeelding te verbergen. (Eigenlijk de links naar de vorige of volgende div.wrap met grote afbeelding.)

Omdat deze grote afbeelding nu kan worden getoond, mogen ook de bijbehorende links worden getoond. Ze worden alleen daadwerkelijk getoond, als de bijbehorende grote afbeelding ook wordt getoond. Maar ze kúnnen nu worden getoond.

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.

if (e.target.nextElementSibling) {

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

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

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of -geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

nextElementSibling: dit is zo'n stukje informatie uit het hierboven genoemde target: het bevat het element (ook weer in de vorm van een object) dat in de html na het element in target komt, en dat dezelfde ouder heeft. Omdat in target één van de div.wrap's zit, is dit per definitie ook een div.wrap.

{: 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 }. In dit geval staat die afsluitende } helemaal aan het eind van function tabToon().

Om de code tussen de {} bij de if uit te voeren, moet het resultaat van het deel tussen de haakjes true (waar) of truthy ('op waar lijkend') zijn. In dit geval staat tussen de haakjes alleen maar e.target.nextElementSibling: de div.wrap die in de html volgt op de div.wrap die is aangeraakt of -geklikt.

Als de aangeraakte of -geklikte div.wrap de laatste div.wrap was, is er geen volgende div.wrap. Als het niet de laatste div.wrap was, is er wel nog een volgende div.wrap. Oftewel: een volgende div.wrap bestaat wel of niet; e.target.nextElementSibling bestaat wel of niet.

Als een volgende div.wrap niet bestaat, levert e.target.nextElementSibling als resultaat de waarde null op: noppes, niets, platzak. Dat is niet echt false (niet waar), maar 'falsy' ('valsachtig'). Voor de if is dat hetzelfde als false (niet waar). De code tussen de {} achter de if wordt niet uitgevoerd.

Als een volgende div.wrap wel bestaat, levert dat de waarde truthy op. Voor de if is dat hetzelfde als true: de code tussen de {} achter de if wordt uitgevoerd.

Als deze controle niet wordt uitgevoerd en er zou geen volgende div.wrap bestaan, wordt geprobeerd de hieronder staande code uit te voeren op een niet bestaand element. Dat levert een fout in het script op, waardoor het script onmiddellijk stopt en mogelijk andere delen van het script ook niet uitgevoerd worden. Een controle op het al dan niet bestaan van een volgende div.wrap voorkomt dit.

span = e.target.nextElementSibling.querySelector("span.foto");

if (span.innerHTML.indexOf("<!--") > -1) { span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); } e.target.nextElementSibling.classList.remove("xverbergx");

Deze vijf regels zijn onderdeel van function tabToon(e)

Deze regels worden alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

– Er zit in de html nog een div.wrap na de div.wrap die is aangeraakt of -geklikt.

Deze vijf regels zijn vrijwel hetzelfde als die bij span = e.target.parentElement.previousElementSibling.querySelector("span.foto");.

Het verschil: daar gaat het om de div.wrap die in de html voor de aangeraakte of ‑geklikte div.wrap zit (previousElementSibling). Hier gaat het om de div.wrap die in de html volgt op de div.wrap die aangeraakt of -geklikt is (nextElementSibling). (Over nextElementSibling is gelijk hierboven bij if (e.target.nextElementSibling) { meer te vinden.)

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven. Verwijder ten slotte bij de betreffende div.wrap class 'xverbergx'.

Hier wordt het commentaar in html veranderd bij de <span> in de div.wrap volgend op de div.wrap, waarvan de erin zittende grote afbeelding wordt getoond. Als iemand een grote afbeelding opent met de Tab-toets, is de kans vrij groot dat ook de volgende of de vorige grote afbeelding met de Tab-toets of Shift+Tab getoond moet worden. Door het commentaar in html te veranderen, verandert de in de <span> zittende <img> in een gewone <img>, waardoor deze alvast wordt gedownload en daardoor sneller weergegeven kan worden.

Als je wilt kijken, of het commentaar echt wordt verwijderd, en of deze class inderdaad door het script wordt verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

e.target.removeEventListener("keydown", tabToon);

Deze regel is onderdeel van function tabToon(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarden is voldaan:

– De Tab-toets is ingedrukt.

input#checkbox-voor-focus of div#wrap-1 had niet de focus.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function tabToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of -geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

removeEventListener("keydown", tabToon) aan elke div.wrap is eerder bij wraps[i].addEventListener("keydown", tabToon); een eventlistener gekoppeld die kijkt, of een toets wordt ingedrukt ('keydown'). Als dat de Tab-toets is, wordt function tabToon(e) uitgevoerd.

Dit stukje van de functie heeft de <span> met de grote afbeelding in de aangeraakte of ‑geklikte div.wrap en de in de html daaraan voorafgaande en eventueel de daarop volgende div.wrap in gewone html veranderd, waardoor de grote afbeelding nu altijd wordt weergegeven, als de Tab-toets wordt ingedrukt. Daarom is deze eventlistener nu niet meer nodig.

Met removeEventListener() wordt hij verwijderd. Tussen de haakjes komt precies hetzelfde te staan als bij het koppelen van de eventlistener. Voor de komma waar de 'event', de gebeurtenis, waar de eventlistener op moet reageren: 'keydown'. Na de komma de naam van de aan die event gekoppelde functie: 'tabToon'. Nu besteedt de browser geen onnodige energie meer aan het luisteren naar het indrukken van een toets bij deze div.wrap.

(Je moet de soort event en de naam van de functie opgeven bij het verwijderen, omdat aan één element verschillende eventlisteners met verschillende functies kunnen zijn gekoppeld.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder bij het element, waarbij werd geluisterd of een toets werd ingedrukt, de eventlistener.

Aanpassingen voor schermlezers

Aan het begin van de pagina kan een aankruisvakje worden aangevinkt, waardoor de weergave wordt aangepast voor een schermlezer. Die aanpassingen worden in dit deel van het script gedaan. Alle grote afbeeldingen worden onder elkaar op het scherm gezet. Verder worden een aantal eventlisteners verwijderd, die mogelijk kunnen botsen met de sneltoetsen van een schermlezer.

Als weer wordt gekozen voor de standaardweergave, verwijdert dit deel van het script alle aanpassingen weer.

function voorSchermlezers(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 input#schermlezer wordt aan- of uitgevinkt. Het luisteren of dit gebeurt, wordt geregeld bij document.getElementById("schermlezer").addEventListener("change", voorSchermlezers);.

voorSchermlezers: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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 aan- of uitvinken van een aankruisvakje. In e zit bijvoorbeeld, of het aankruisvakje is aan- of uitgevinkt. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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

var span;

Deze regel is onderdeel van function voorSchermlezers(e)

Met het sleutelwoord var 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 var volgt de naam van de variabele: span.

De puntkomma aan het eind geeft het eind van de regel aan.

Over wat een variabele precies is, is meer te vinden bij var vensterGrootte,. Omdat variabele span binnen function voorSchermlezers() is aangemaakt, is span alleen voor code binnen die functie beschikbaar.

span wordt gebruikt om er de inhoud van de <span> met de grote afbeelding in op te slaan en daar eventueel <!-- en --> uit te verwijderen, zodat het commentaar in gewone html verandert en de afbeelding weergegeven kan worden.

if (e.target.checked) {

Deze regel is onderdeel van function voorSchermlezers(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: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function voorSchermlezers(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).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over de <input> die werd aan- of uitgevinkt. Ook dat is weer heel veel informatie: het soort element, aangevinkt of niet, grootte, noem maar op.

checked: in de eigenschap checked van het hierboven genoemde object is opgeslagen, of de <input> is aangevinkt of niet.

{: de code die wordt uitgevoerd, als aan deze if wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als input#schermlezer is aangevinkt. In dat geval is gekozen om de weergave aan te passen voor een schermlezer.

length = wraps.length;

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

length: dit is de eerder bij length, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Dat deze variabele eerder voor iets anders is gebruikt, is geen enkel probleem.

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

wraps: dit is de variabele die bij wraps = document.querySelectorAll(".wrap"), is gevuld met alle div.wrap's op de pagina.

length: de lengte van wraps. Er zitten veertien div.wrap's in de pagina met het voorbeeld, en in wraps zit voor elke div.wrap een apart object. De lengte van wraps is dus 14.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele length het aantal div.wrap's op de pagina.

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

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

Deze for-lus zorgt ervoor, dat de code tussen de { en de } een bepaald aantal keren wordt uitgevoerd. i is een teller, die elke keer dat de code wordt uitgevoerd met 1 wordt verhoogd.

In variabele length is hierboven bij length = wraps.length; het aantal div.wrap's op de pagina gestopt. Dat is in het voorbeeld 14. Zolang i kleiner is dan 14, wordt de code binnen de for-lus uitgevoerd.

Een uitgebreidere beschrijving van een for-lus is te vinden bij de for-lus iets onder length = links.length;.

span = wraps[i].querySelector("span.foto");

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

span: dit is de eerder bij var span; aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

wraps[i]: in de variabele wraps zijn bij wraps = document.querySelectorAll(".wrap"), alle div.wrap's op de pagina opgeslagen. 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'.

De teksthaken [] geven aan dat hierin een teller zit. Die teller is hier de variabele i. In de for-lus, waarbinnen deze regel staat, heeft i het volgnummer gekregen van de div.wrap die aan de beurt is.

querySelector("span.foto"): dit is de methode uit het hierboven genoemde object die gebruikt moet worden. Met behulp van de methode querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

Omdat voor de punt wraps[i] staat, gaat het om de span.foto binnen de div.wrap die aan de beurt is.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het eerste element met class="foto" dat binnen een <span> zit, die weer binnen de div.wrap zit die aan de beurt is. Dit is de <span>, waarbinnen de grote afbeelding zit. Omdat deze regel in een for-lus zit, wordt dit achtereenvolgens voor elke div.wrap gedaan.

Als dat is gedaan, kunnen we gaan werken met de inhoud van span. En omdat die inhoud feitelijk de span.foto is, werken we in werkelijkheid niet met span, maar met de span.foto met de grote afbeelding.

if (span.innerHTML.indexOf("<!--") > -1) {

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }

Deze drie regels zijn onderdeel van function voorSchermlezers(e)

Dit deel van het script wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

Deze drie regels zitten binnen een for-lus en worden voor elke div.wrap in de pagina één keer uitgevoerd.

Deze drie regels zijn precies hetzelfde als die bij if (span.innerHTML.indexOf("<!--") > -1) {. Een uitgebreide uitleg is daar te vinden.

(Daar wordt de span.foto uit div#wrap-1 in variabele span opgeslagen. Hier gaat het mogelijk om de span.foto uit een andere div.wrap. Dat maakt echter voor de werking van deze regels geen enkel verschil uit, alleen wordt de html van een andere span.foto aangepast.)

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven. Omdat deze regel in een for-lus zit, wordt dit achtereenvolgens voor elke div.wrap gedaan.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

wraps[i].removeEventListener("keydown", tabToon);

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

Deze regel zit binnen een for-lus en wordt voor elke div.wrap in de pagina één keer uitgevoerd.

wraps[i]: in de variabele wraps zijn bij wraps = document.querySelectorAll(".wrap"), alle div.wrap's op de pagina opgeslagen. 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'.

De teksthaken [] geven aan dat hierin een teller zit. Die teller is hier de variabele i. In de for-lus, waarbinnen deze regel staat, heeft i het volgnummer gekregen van de div.wrap die aan de beurt is.

removeEventListener("keydown", tabToon): removeEventListener() is zo'n methode uit het hierboven genoemde object.

Aan elke div.wrap is eerder bij wraps[i].addEventListener("keydown", tabToon); een eventlistener gekoppeld die kijkt, of een toets wordt ingedrukt ('keydown'). Als dat de Tab-toets is, wordt function tabToon(e) uitgevoerd.

Deze functie heeft voor schermlezers geen nut, omdat hij bedoeld is voor gebruikers van de Tab-toets. Bovendien kan het de werking van de Tab-toets, waar de gebruiker van een schermlezer aan gewend zal zijn, veranderen. Daarom wordt deze eventlistener hier met removeEventListener() verwijderd.

Tussen de haakjes komt hetzelfde te staan als bij het koppelen van de eventlistener. Voor de komma waar de 'event', de gebeurtenis, waar de eventlistener op moet reageren: 'keydown'. Na de komma de naam van de aan die event gekoppelde functie: 'tabToon'.

(Je moet de soort event en de naam van de functie opgeven bij het verwijderen, omdat aan één element verschillende eventlisteners met verschillende functies kunnen zijn gekoppeld.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder de eventlistener die luistert naar het indrukken van een toets op een div.wrap. Omdat deze regel in een for-lus zit, wordt dit achtereenvolgens voor elke div.wrap gedaan.

checkboxVoorFocus.removeEventListener("keydown", tabToon);

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt aangevinkt (aanpassen voor schermlezer).

Deze regel is bijna hetzelfde als die gelijk hierboven bij wraps[i].removeEventListener("keydown", tabToon);. Het enige verschil: daar staat voor de punt wraps[i]: de div.wrap die aan de beurt is. Hier staat voor de punt checkboxVoorFocus: input#checkbox-voor-focus. Het uitgebreide verhaal staat bij de regel hierboven.

Een korte samenvatting: verwijder bij input#checkbox-voor-focus de eerder bij checkboxVoorFocus.addEventListener("keydown", tabToon); aan de <input> gekoppelde eventlistener. Deze heeft voor schermlezers geen nut, maar levert mogelijk wel problemen op.

} else {

Deze regel is onderdeel van function voorSchermlezers(e)

Dit deel van het script wordt alleen uitgevoerd, als input#checkbox-voor-focus wordt uitgevinkt.

Deze else hoort bij if (e.target.checked) {: als input#checkbox-voor-focus wordt aangevinkt. Als dat niet zo is, wordt de <input> uitgevinkt en wordt de code tussen de {} achter de else uitgevoerd.

}: dit is de afsluitende accolade van de code die bij de hierboven genoemde if hoort.

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

location.reload();

Deze regel is onderdeel van function voorSchermlezers(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#schermlezer wordt uitgevinkt (aanpassingen voor schermlezer verwijderen).

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

Een van die objecten is het 'location' object, waarin het adres van de pagina zit.

reload() dit is een van die methoden. Hiermee wordt de pagina opnieuw geladen. Dit is verreweg de makkelijkste manier om alle aanpassingen voor een schermlezer weer te verwijderen.

Het is wel wat onhandig om daarvoor de pagina te herladen, want je gaat ook weer terug naar het begin van de pagina. Maar normaal genomen zal niet worden gewisseld tussen weergave voor een schermlezer of standaardweergave, dus vaak zal dit niet gebeuren.

;: de puntkomma aan het eind geeft het eind van de regel aan.

Sluiten uitleg afhandelen

Linksboven staat in een <label> een vraagteken. Als de bijbehorende input#uitleg wordt aan- of uitgevinkt, wordt een korte uitleg getoond of weer verborgen.

Als de uitleg wordt geopend terwijl een thumbnail al een outline had en de bij die thumbnail horende links en/of grote afbeelding werden getoond, wordt die thumbnail met bijbehorende links en dergelijke bij het sluiten van de uitleg linksboven in het venster van de browser gezet. Dit kan, omdat in variabele actieveWrap op verschillende plaatsen in het script de id van de bij die thumbnail horende div.wrap wordt opgeslagen.

Als er nog geen id in actieveWrap zit, wordt 'wrap-1' (de id van de eerste div.wrap) er door function verwerkKlikOpMain(e) in gestopt, zodra je ergens het browservenster aanraakt of -klikt. input#uitleg is een nakomeling van <main>, en nakomelingen geven een aanraking of klik door aan hun voorouders (tenzij je dat blokkeert). Daarom gebeurt dit ook, als de eerste handeling na openen van de pagina het aanvinken van input#uitleg is.

function sluitHulp(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 uitleg wordt getoond of verborgen (als input#uitleg wordt aan- of uitgevinkt.). Het luisteren naar dat aan- of uitvinken wordt geregeld bij document.getElementById("uitleg").addEventListener("change", sluitHulp);.

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

sluitHulp: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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 aan- of uitvinken van input#uitleg. In e zit bijvoorbeeld, of de <input> is aan- of uitgevinkt. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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

Deze regel is onderdeel van function sluitHulp(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.

!: betekent in JavaScript 'niet'. Hiermee wordt het stukje erachter omgedraaid: dit mag niet waar zijn.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function sluitHulp(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).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over de <input> die werd aan- of uitgevinkt. Ook dat is weer heel veel informatie: het soort element, aangevinkt of niet, grootte, noem maar op.

checked: in de eigenschap checked van het hierboven genoemde object is opgeslagen, of de <input> is aangevinkt of niet.

{: de code die wordt uitgevoerd, als aan deze if wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als input#schermlezer niet is aangevinkt. Oftewel: als input#schermlezer is uitgevinkt. In dat geval is gekozen om de uitleg weer te sluiten en moet eventueel de juiste thumbnail met bijbehorende links en dergelijke linksboven in het browservenster worden gezet.

document.getElementById(actieveWrap).scrollIntoView();

Deze regel is onderdeel van function sluitHulp(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#uitleg wordt uitgevinkt (de uitleg wordt gesloten).

document.getElementById(actieveWrap): het stukje achter de punt getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById(actieveWrap)

Als je naar een letterlijke id zoekt, staat die tussen aanhalingstekens. Hier staat actieveWrap niet tussen aanhalingstekens. Daarom gaat JavaScript ervan uit dat het om een variabele gaat en dat naar de inhoud van die variabele gezocht moet worden.

Die inhoud is de id van één van de div.wrap's. Op een aantal plaatsen in het script wordt zo'n id in actieveWrap opgeborgen. Er wordt nu gezocht naar de div.wrap met die id. Dat is de div.wrap, waarvan de erin zittende thumbnail een outline heeft en waarvan de bijbehorende links zichtbaar zijn, en eventueel de erbij horende grote afbeelding met links.

scrollIntoView(): dit is zo'n methode. Deze methode zet een bepaald element linksboven in het venster van de browser. Omdat hij gelijk volgt op document.getElementById(actieveWrap), wordt de functie uitgevoerd op de div.wrap met de id die in variabele actieveWrap zit. Als die div.wrap linksboven in het venster wordt gezet, wordt ook de daarin zittende thumbnail linksboven in het venster gezet.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek het element met de id die in actieveWrap zit en zet die linksboven in het browservenster.

document.getElementById(actieveWrap).focus();

Deze regel is onderdeel van function sluitHulp(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

input#uitleg wordt uitgevinkt (de uitleg wordt gesloten).

Deze regel is vrijwel hetzelfde als die gelijk hierboven bij document.getElementById(actieveWrap).scrollIntoView();. De uitleg is daar te vinden.

Het enige verschil: het laatste stukje is anders: focus() in plaats van scrollIntoView(). Met focus() wordt de focus op het element (de div.wrap) gezet, waarvan de id is opgeslagen in variabele actieveWrap.

Dit is alleen nodig voor Firefox, omdat die :focus anders afhandelt dan de meeste andere browsers. Zonder deze regel zou Firefox na openen en sluiten van de uitleg geen outline en geen links tonen. Meer over :focus en Firefox is te vinden bij Ruziënde browsers.

id van actieve div.wrap opslaan in variabele actieveWrap

Er is een aantal manieren om een grote afbeelding te tonen of naar een vorige of volgende thumbnail of grote afbeelding te gaan: aanraken van een thumbnail of een link naar een vorige of volgende thumbnail of grote afbeelding volgen. (Ook met de Tab-toets kunnen grote afbeeldingen worden geopend, maar dat is een apart geval en wordt afgehandeld in function tabToon(e)).

Een <img> met een thumbnail en de bij die thumbnail horende <a>'s met de links naar vorige of volgende thumbnail en grote afbeelding zitten allemaal in dezelfde div.wrap. Daardoor zal een aanraking of klik op een thumbnail of link altijd eindigen bij die div.wrap, want als je een nakomeling van een element aanraakt of -klikt, wordt die aanraking of klik doorgegeven aan alle voorouders. Dit maakt het mogelijk om een aanraking of klik van een thumbnail met z'n bijbehorende links in dezelfde functie af te handelen. En dat is wat dit deel van het script doet. Er zijn wel variaties in wat er moet gebeuren, afhankelijk van wat is aangeraakt of -geklikt, maar daar kun je op filteren.

In alle gevallen zal bij aanraken of -klikken van thumbnail of link een thumbnail een outline horen te krijgen en de bijbehorende links (en eventueel grote afbeelding met links) moeten worden getoond. Deze functie slaat de id van de div.wrap, waarin die thumbnail met toebehoren zit, op in variabele actieveWrap, zodat die op diverse plaatsen in het script kan worden gebruikt.

Ook bij aanraken of -klikken van eventuele andere nakomelingen van een div.wrap zal deze functie worden aangeroepen, want een aanraking of klik wordt nou eenmaal altijd doorgegeven aan de voorouders (tenzij je dat blokkeert). Dat gebeurt, als je bijvoorbeeld de grote afbeelding aanraakt of -klikt. In die gevallen heeft deze functie gewoon geen enkel effect.

function bewaarId(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 een thumbnail wordt aangeraakt of -geklikt, of als een link naar een vorige of volgende thumbnail of grote afbeelding wordt gevolgd. (Feitelijk als een nakomeling van een div.wrap wordt aangeraakt of -geklikt, maar alleen de thumbnail en de links hebben enig zichtbaar effect.)

Het luisteren naar die aanraking of klik wordt geregeld bij wraps[i].addEventListener("click", bewaarId);.

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

bewaarId: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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 aanraken of ‑klikken van div.wrap of een van de nakomelingen daarvan. In e zit bijvoorbeeld, welk element precies is aangeraakt. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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 (actieveWrap) {

Deze regel is onderdeel van function bewaarId(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.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waar toen verder niets mee is gedaan. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Als deze variabele true ('waar') is, wordt aan de voorwaarde van de if voldaan en wordt de code tussen de bij de if horende {} uitgevoerd. Als de variabele false ('onwaar') is, wordt niet aan de voorwaarde van de if voldaan en wordt de code tussen de {} niet uitgevoerd.

Als een variabele wordt aangemaakt en er wordt verder niets in opgeslagen, zoals hier aan het begin van het script is gebeurd, krijgt deze de waarde 'undefined': ongedefinieerd. Zo'n variabele heeft een acute identiteitscrisis, want hij is niet true en niet false. Gelukkig is daar een oplossing voor: een variabele met als waarde 'undefined' is 'falsy'. Wat de if betreft is zo'n voorwaarde gewoon false en wordt de code tussen de {} dus niet uitgevoerd.

Als de pagina net wordt geopend, heeft actieveWrap als inhoud 'undefined' en zal de code tussen de {} dus niet worden uitgevoerd.

In de regel heeft één van de thumbnails een outline en worden de bij die thumbnail horende links getoond, en eventueel ook de erbij horende grote afbeelding en links. Op een aantal plaatsen in het script wordt de id van de div.wrap, waarbinnen die thumbnails en dergelijke zitten, opgeslagen in variabele actieveWrap.

De id van bijvoorbeeld de eerste div.wrap is div#wrap-1. Zodra deze waarde in actieveWrap wordt opgeslagen, is actieveWrap niet meer 'undefined': er zit nu iets in. actieveWrap is nu niet meer 'falsy', maar 'truthy'. (Een variabele is alleen echt true of false als deze de waarde true of false heeft. In alle andere gevallen is de variabele 'truthy' of 'falsy'.)

Zodra er iets in actieveWrap is opgeslagen, is wat de if betreft voldaan aan de voorwaarde en zal de code tussen de {} worden uitgevoerd.

{: de code die wordt uitgevoerd, als aan deze if wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze if hoort. Aan het eind van de code bij deze if staat de afsluitende }.

De hele regel samengevat: als er iets (in dit geval een id) is opgeslagen in variabele actieveWrap, voer dan de code tussen de {} achter de if uit.

document.getElementById(actieveWrap).classList.remove("xactiefx", "firefox");

Als je in de html een andere class dan 'firefox' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Er is iets (een id) opgeslagen in variabele actieveWrap.

document.getElementById(actieveWrap): het stukje achter de punt getElementById() is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode getElementById() uit document kan JavaScript een element met een bepaalde id opzoeken. De id waarnaar wordt gezocht, staat tussen haakjes:

getElementById(actieveWrap)

Omdat het deel tussen de haakjes hier niet tussen aanhalingstekens staat, wordt het door JavaScript als een variabele gezien. Er wordt niet naar de id 'actieveWrap' gezocht, maar naar de inhoud van variabele actieveWrap. Die inhoud móét er zijn, want dat was een voorwaarde van if (actieveWrap) om dit deel van de code uit te voeren. Als actieveWrap nog geen inhoud heeft, nog 'undefined' is, wordt het deel tussen de {} achter de if niet uitgevoerd.

De enige inhoud van actieveWrap kan een id van een van de div.wrap's zijn, want dat is het enige wat het script er op een aantal plaatsen in opbergt. Als die id bijvoorbeeld 'wrap-3' is, de id van de derde div.wrap, is de inhoud van actieveWrap 'wrap-3'. getElementById(actieveWrap) wordt dan door het script gelezen als getElementById("wrap-3").

In dat geval wordt div#wrap-3 gevonden door getElementById(actieveWrap), en de informatie en methoden die dit oplevert, horen dan bij div#wrap-3. In het voorbeeld zijn veertien div.wrap's aanwezig met in elk een thumbnail, bijbehorende links, grote afbeelding, en dergelijke. De gevonden id's kunnen in het voorbeeld 'wrap-1' tot en met 'wrap-14' zijn.

classList: de hierboven met behulp van document.getElementById(actieveWrap) gevonden <div> is niet de <div> zelf, maar is de <div> in de vorm van een object. In dat object zit ook allerlei informatie. In de eigenschap classList zitten alle classes, die bij de gevonden <div> in de html aanwezig zijn.

remove.("xactiefx", "firefox"): remove() is zo'n methode. Het verwijdert één of meer classes bij de gevonden <div>. De te verwijderen classes staan tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat. Dit zijn twee classes die zorgen dat de juiste thumbnail een outline krijgt, en dat de bij die thumbnail horende links worden getoond. ('firefox' is een class speciaal voor Firefox, omdat Firefox anders met :focus omgaat dan de meeste andere browsers. Meer hierover is te vinden bij Ruziënde browsers.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek de div.wrap met de id die in variabele actieveWrap zit, en verwijder bij die div.wrap de classes 'xactiefx' en 'firefox'.

In actieveWrap zit de id van de div.wrap, waarvan de erin zittende thumbnail is aangeraakt of -geklikt, of waarvan één van de erin zittende links is gevolgd. Omdat mogelijk naar een vorige of volgende thumbnail moet worden gegaan, moeten de outline bij die thumbnail en de bij die thumbnail horende links worden weggehaald. Door het verwijderen van de classes 'xactiefx' en 'firefox' werkt css met die classes in de selector niet meer, waardoor dit precies is, wat er gebeurt.

Door later bij de nieuwe div.wrap met de thumbnail die een outline moet krijgen en waarvan de links getoond moeten worden en dergelijke deze classes toe te voegen, werken die selectors bij die div.wrap wel.

(Als je een thumbnail aanraakt terwijl deze al een outline en dergelijke heeft, worden de classes bij de bijbehorende div.wrap verwijderd. Maar later worden door het script diezelfde classes weer toegevoegd. Dat is 'n overbodige handeling en dat zou je kunnen voorkomen. Maar dat maakt de code 'n stuk ingewikkelder en dit gaat zo snel, dat het absoluut onmerkbaar is.)

Als je wilt kijken, of deze classes inderdaad door het script worden verwijderd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

if ((e.target.tagName === "A") && (e.target.className.match(/^(vorige|volgende)-(a|t)$/))) {

Deze regel is onderdeel van function bewaarId(e)

Is dat nou niet leuk griezelen? Dit gaat écht op programmeren lijken.

Zoals meestal: als je dit in stukjes opsplitst, blijkt het (hopelijk) minder ingewikkeld dan je op het eerste oog zou zeggen. Uiteindelijk heeft JavaScript (net zoals alle programmeertalen) heel strikte taalregels, waardoor het uiteindelijk meestal toch veel duidelijker blijkt te zijn dan een antwoord van premier Rutte op z'n wekelijkse persconferentie. Die gebruikt vaak heel veel woorden om niets te zeggen, hier worden eigenlijk heel weinig 'woorden' gebruikt om tamelijk veel heel duidelijk te zeggen.

Deze if kijkt of er een <a> is aangeraakt of -geklikt, en als dat zo is, of die dan een bepaalde class heeft. In dat geval moet een vorige of volgende thumbnail een outline krijgen en moeten de daarbij horende links (en eventueel de grote afbeelding met links) worden getoond.

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. Dat is hier vrij veel:

(e.target.tagName === "A") && (e.target.className.match(/^(vorige|volgende)-(a|t)$/))

Maar dat maakt voor het principe niets uit.

De voorwaarde valt in twee delen uiteen, gescheiden door &&:

(e.target.tagName === "")

en

(e.target.className.match(/^(vorige|volgende)-(a|t)$/))

In het eerste deel (e.target.tagName === "A") wordt gekeken, of het aangeraakte of -geklikte element een <a> is. Dit eerste deel staat in z'n geheel tussen haakjes, maar dat is voornamelijk voor de leesbaarheid.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function bewaarId(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).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of ‑geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

tagName: dit is weer een onderdeel van het hierboven genoemde object target. In tagName zit de naam van de tag, die is aangeraakt of -geklikt. Als dat een link is, is de inhoud van tagName 'A', als het een afbeelding is, is de inhoud, 'IMG', enzovoort. De naam in tagName is in JavaScript altijd in hoofdletters, ongeacht wat er in de html staat.

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

"A": de naam van de tag. De naam staat tussen aanhalingstekens, zodat JavaScript weet dat het om een letterlijke naam gaat. En in hoofdletters, omdat de inhoud van tagName in JavaScript ook altijd in hoofdletters is geschreven.

Tenzij je dat voorkomt, wordt een aanraking of klik 'doorgegeven' aan de voorouders van het element dat is aangeraakt of -geklikt. Daardoor komt uiteindelijk ook elke aanraking van of klik op een <a> uit bij de div.wrap, waar de <a> in zit. Verder niet, want bij wraps[i].addEventListener("click", bewaarId); is de eventlistener die function bewaarId() aanroept gekoppeld aan de div.wrap's, dus verder dan de div.wrap wordt het niet doorgegeven richting voorouders.

De inhoud van tagName bevat de naam van de tag, die daadwerkelijk is aangeraakt of -geklikt. Alleen als rechtstreeks een link, een <a>, is aangeraakt of -geklikt, is die naam 'A'. Dat is hier van belang, want als een <a> is aangeraakt of -geklikt, moet een vorige of volgende thumbnail een outline krijgen, en moeten de bij die thumbnail horende links (en eventueel de grote afbeelding met links) worden getoond.

e.target.tagName === "A" samen: er moet rechtstreeks een <a> zijn aangeraakt of -geklikt.

&&: dit is de manier, waarop je in JavaScript 'en' aangeeft: wat voor de && staat moet waar zijn, én wat achter de && staat moet waar zijn.

In het tweede deel (e.target.className.match(/^(vorige|volgende)-(a|t)$/)) wordt gekeken of de aangeraakte of -geklikte <a> de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t' heeft. Dit tweede deel staat in z'n geheel tussen haakjes, maar dat is voornamelijk voor de leesbaarheid.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function bewaarId(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of -geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

className: dit is weer een onderdeel van het hierboven genoemde object target. In className zit de class van de tag, die is aangeraakt of -geklikt. Als er meerdere classes zijn, zitten die er allemaal in, gescheiden door een spatie.

Voor de <a>'s naar de div.wrap met een vorige thumbnail bijvoorbeeld is de inhoud van className 'vorige-t'.

match(): dit is zo'n methode uit het hierboven genoemd object. Het kijkt of wat er tussen de haakjes bij match(), voorkomt in wat er in de tekst voor match() staat.

Slimmeriken merken nu natuurlijk op dat er helemaal geen tekst voor match() staat. Minder domme slimmeriken weten echter dat je e.target.className eigenlijk moet vervangen door de inhoud ervan: de class (of classes) bij het aangeraakte of -geklikte element. Voor de <a>'s naar de div.wrap met een vorige thumbnail bijvoorbeeld wordt dat 'vorige-t'.In dat geval staat daar dus eigenlijk "vorige-t".match(): er wordt gekeken of de tekst tussen de haakjes bij match() voorkomt in 'vorige-t'.

(/^(vorige|volgende)-(a|t)$/)): dit is dan die tekst tussen de haakjes van match(). Voor mensen heeft het meer weg van een vloekende stripfiguur, maar JavaScript kan hier prima mee uit de voeten.

Tussen de haakjes bij match() staat geen gewone tekst, maar een zogenaamde 'reguliere expressie'. JavaScript kan hier tekst van maken. En wel allerlei soorten tekst. Hier staat eigenlijk 'vorige-a' of 'volgende-a' of 'vorige-t' of 'volgende-t'. Dat zijn alle mogelijk classes die een link naar een div.wrap met een vorige of volgende thumbnail of grote afbeelding kan hebben. Alleen zijn deze vier mogelijke classes opgeschreven in de vorm van een 'reguliere expressie'. Dat is korter dan ze voluit schrijven, en het is ook gewoon lolliger. Geef toe: op verjaarfeestjes maak je hier meer indruk mee dan met een eindeloze serie 'of' 'of' 'of'. (Op een bepaald soort verjaarfeestjes dan. Op de meeste verjaarfeestjes wordt vermoedelijk de Crisisdienst gebeld, als je in Reguliere Expressies gaat praten.)

In dit geval zijn er maar vier mogelijke classes, maar je door het toevoegen van slechts enkele tekens kun je dit uitbreiden naar 52 classes, om maar iets te noemen. Of álle classes behalve eentje. Over reguliere expressies zijn bibliotheken vol geschreven. Het is eigenlijk een taal op zichzelf. Reguliere expressies zijn niet alleen in JavaScript, maar in allerlei talen te gebruiken. Op de pagina met links zijn er onder het kopje Gereedschap → Reguliere expressies ('Regular Expressions') links over dat onderwerp te vinden.

De reguliere expressie opgehakt in stukjes:

/: in JavaScript begint en eindigt een reguliere expressie met 'n schuine streep. Daar is dus verder weinig bijzonders aan: gewoon een taalregel.

^: met dit symbool wordt aangegeven dat wat hierachter staat aan het begin van een woord moet staan. Omdat met een class (of meerdere classes, gescheiden door een spatie) wordt vergeleken, moet minstens één class dus beginnen met wat hierop volgt.

(vorige|volgende): de haakjes geven aan dat dit deel bij elkaar hoort. Het hiervoor staande teken ^ heeft daarom betrekking op het geheel tussen de haakjes.

De woorden 'vorige' en 'volgende' hebben geen bijzondere betekenis binnen een reguliere expressie, ze worden gewoon letterlijk genomen. (Een aantal tekens heeft een bijzondere betekenis, zoals het hierboven gebruikte ^ en het tussen de haakjes staande |. Als je op dat soort tekens wilt zoeken, gelden aparte regels.)

Tussen vorige en volgende staat het teken |. Dit betekent in een reguliere expressie 'of'. Hier staat dus eigenlijk 'vorige' of 'volgende'. Er mogen geen spaties voor of na 'vorige' of 'volgende' staan, want dan wordt ook op die spaties gezocht, bijvoorbeeld op 'vorige ' in plaats van 'vorige'.

^(vorige|volgende) samen: één van de classes moet beginnen met 'vorige' of 'volgende'.

-: dit teken heeft geen bijzondere betekenis in een reguliere expressie. Het moet daarom letterlijk aanwezig zijn.

^(vorige|volgende)- samen: 'vorige' of 'volgende' gevolgd door '-'.

(a|t): de haakjes geven weer aan dat dit deel bij elkaar hoort. De letters a en t worden weer letterlijk genomen. De | betekent weer 'of'. De letter 'a' of 't' moet aanwezig zijn.

^(vorige|volgende)-(a|t) samen: één van de classes moet beginnen met 'vorige' of 'volgende', gevolgde door '-', gevolgd door een 'a' of een 't'.

$: wat hiervoor staat, moet aan het eind van een woord staan. Omdat wat hiervoor staat tussen haakjes staat, heeft de $ betrekking op de groep (a|t), op de letters 'a' en 't'. Eén van deze letters moet aan het eind van een woord staan.

^(vorige|volgende)-(a|t)$ bij elkaar: één van de classes moet beginnen met 'vorige' of 'volgende', gevolgd door '-', gevolgd door aan het eind van de class een 'a' of 't'. De enig mogelijke combinaties hiervan zijn 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'. Meer combinaties zijn gewoon niet mogelijk. Oftewel: alleen elementen met één van deze vier classes komen in aanmerking.

/^(vorige|volgende)-(a|t)$/: voor de volledigheid nog 'n keer hetzelfde als hier gelijk boven, maar nu met de schuine strepen aan begin en eind. Die hebben dus verder geen enkele invloed op de reguliere expressie zelf, ze geven alleen het begin en het eind van de reguliere expressie aan.

De hele regel samengevat: kijk of een <a> is aangeraakt of -geklikt, en als dat zo is, kijk dan of die <a> een class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t' heeft. Als dat zo is, voer dan de code tussen de {} achter de if uit.

De controle op de class is nodig, omdat er andere <a>'s aanwezig kunnen zijn. In het voorbeeld is dat niet zo, maar er zouden bijvoorbeeld links naar een uitgebreide beschrijving in Wikipedia of zoiets aanwezig kunnen zijn. Die links moeten genegeerd worden, want die hebben niets met de navigatie tussen de thumbnails en/of de grote afbeeldingen te maken.

actieveWrap = e.target.hash.substr(1);

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Het aangeraakte of -geklikte element is een <a> met een class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function bewaarId(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of ‑geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

hash: dit is een stukje informatie uit het hierboven genoemde object. Deze regel wordt alleen uitgevoerd, als op een <a> is geklikt met een class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'. Die <a>'s zijn per definitie een link naar een vorige of volgende div.wrap binnen de pagina, naar een zogenaamd anker. Dat betekent dat de link móét eindigen op een id: de id van het anker.

De link naar de tweede thumbnail vanaf de eerste thumbnail bijvoorbeeld is (ingekort weergegeven):

<a class="volgende-t" href="#wrap-2" (...) >→</a>

In hash zit het deel uit de href dat begint met '#' en alles daarachter. In bovenstaande link is dat dus '#wrap-2'. Oftewel: de id die hoort bij de tweede div.wrap.

(Hoewel je in de html normaal genomen bij een anker alleen de '#' en wat daarop volgt in de href zet, maakt de browser daar achter de schermen een volledig adres van, inclusief domein, pad, en dergelijke hash bevat altijd alleen het deel daarvan met de '#' en wat daarop volgt. Dus ook als een langer adres is gebruikt in de href, blijft dit werken.)

substr(1): dit is zo'n methode uit het hierboven genoemde object. Omdat het achter e.target.hash staat, heeft het daar betrekking op: substr(1) wordt toegepast op de inhoud van e.target.hash, op het deel van de href met de '#' en wat daarop volgt.

De 1 tussen de haakjes wil zeggen: gebruik alleen het tweede teken en wat daarop volgt. Het tweede teken, want een computer begint vaak met 0 te tellen. Voor de computer is (1) dus wat wij het tweede teken zouden noemen.

In het voorbeeld hier gelijk boven bij hash is de inhoud van e.target.hash '#wrap-2'. Als je daarvan alleen het tweede teken en wat daarop volgt neemt, houdt je 'wrap-2' over. En is dat niet wonderschoon? Dat is precies de id van de div.wrap waar naartoe wordt gelinkt.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele actieveWrap het deel uit de href van de aangeraakte of -geklikte <a> dat volgt op de '#'.

} else {

Deze regel is onderdeel van function bewaarId(e)

Dit deel van het script wordt alleen uitgevoerd, als een aangeraakt of -geklikt element geen <a> is, of als die <a> niet de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t' heeft.

Deze else hoort bij if ((e.target.tagName === "A") && (e.target.className.match(/^(vorige|volgende)-(a|t)$/))) {: als een <a> is aangeraakt of -geklikt en als die <a> de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t' heeft. Als dat niet zo is, wordt de code hieronder uitgevoerd.

}: dit is de afsluitende accolade van de code die bij de hierboven genoemde if hoort.

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

Dit deel van het script wordt uitgevoerd, als het aangeraakte of -geklikte element geen <a> is met de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'. Oftewel: dit deel van het script wordt altijd uitgevoerd, tenzij een link naar een div.wrap met een vorige of volgende thumbnail of grote afbeelding is gevolgd.

Dus ook als je een thumbnail (dat is een <img>) of de grote afbeelding (ook een <img>) aanraakt of -klikt. In het voorbeeld ontbreken andere elementen binnen de div.wrap's, maar als die er zijn, geldt daar hetzelfde voor.

actieveWrap = e.target.parentElement;

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Het aangeraakte of -geklikt element is geen <a>, of die <a> heeft niet de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets moet worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,.

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

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function bewaarId(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).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of ‑geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

parentElement: dit is een stukje informatie uit het hierboven genoemde object. Hierin zit de ouder van het aangeraakte of -geklikte element, dat in target zit, ook weer in de vorm van een object.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: stop in variabele actieveWrap de ouder van het element dat is aangeraakt of -geklikt.

while (! actieveWrap.classList.contains("wrap")) {

Als je in de html een andere class dan 'wrap' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Het aangeraakte of -geklikt element is geen <a>, of die <a> heeft niet de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'.

while: zolang de voorwaarde tussen de buitenste haakjes achter while waar is, wordt de code tussen de {} achter while steeds opnieuw herhaald. Tussen de {} staat code, die iets verandert, net zolang tot de voorwaarde niet meer waar is.

De voorwaarde staat tussen haakjes, dat is gewoon een van de taalregels van JavaScript.

Het kan zijn dat al gelijk niet aan de gestelde voorwaarde wordt voldaan, nog voordat de code tussen de {} achter while is uitgevoerd. In dat geval wordt die code nooit uitgevoerd. Het kan ook zijn dat de code tienduizend keer moet worden uitgevoerd (dat is dan niet erg efficiënt, maar het kán).

Waar het om gaat: de code wordt net zolang uitgevoerd, totdat de voorwaarde tussen de haakjes niet meer waar is.

!: betekent in JavaScript 'niet'. Hiermee wordt het stukje erachter omgedraaid: dit mag niet waar zijn. Daarmee wordt de while dus feitelijk omgedraaid: voer de code tussen de {} achter while uit, zolang de voorwaarde níét waar is.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets kan worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Hier gelijk boven bij actieveWrap = e.target.parentElement; is dat opbergen gebeurd: de ouder van het element dat is aangeraakt of -geklikt is in de vorm van een object opgeborgen in variabele actieveWrap.

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

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

classList: dit is een stukje informatie uit het hierboven genoemde object. In classList zitten alle classes, die in het element in actieveWrap in de html aanwezig zijn.

contains("wrap"): contains() is zo'n methode uit het hierboven genoemde object. contains() kan voor meerdere doeleinden worden gebruikt, afhankelijk van waarachter het staat. Als het achter classList staat, kan het worden gebruikt om te kijken, of een bepaalde class aanwezig is.

De class waarnaar gezocht moet worden, staat tussen de haakjes bij contains(). Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke tekst gaat.

contains() zoekt naar een exacte overeenkomst. Als je zoekt naar de class 'wrap', voldoet bijvoorbeeld de class 'wrapper' niet.

{: de code die wordt uitgevoerd, als aan de voorwaarde bij while wordt voldaan, wordt tussen accolades gezet. Het script weet dan, wat bij deze while hoort. Aan het eind van de code bij deze while staat de afsluitende }.

De hele regel samengevat: herhaal de code tussen de {} achter while zolang het element in actieveWrap geen class 'wrap' heeft.

Uiteindelijk moet in actieveWrap de id van één van de veertien div.wrap's worden opgeborgen. Zolang het element in actieveWrap geen class 'wrap' heeft, is het geen div.wrap. En kan in dat element dus ook niet de id van de div.wrap worden gevonden.

Bij wraps[i].addEventListener("click", bewaarId); is de eventlistener die function bewaarId() aanroept, gekoppeld aan alle div.wrap's. Als je een nakomeling van een div.wrap aanraakt of -klikt, zoals een <img>, wordt de aanraking of klik doorgegeven naar de voorouders, dus uiteindelijk komt die aanraking of klik bij de div.wrap uit. Oftewel: een element met de class 'wrap'.

Op dat moment wordt niet meer aan de voorwaarde van de while voldaan (het element in actieveWrap mag geen class 'wrap' hebben) en wordt de code tussen de {} achter de while dus niet meer uitgevoerd.

In het voorbeeld zitten maar enkele elementen in elke div.wrap. Maar deze constructie werkt ook bij meer elementen, ook als die genest zijn, zoals een onderschrift met een <em> binnen een <i> binnen een <span>. Zolang er geen element met class="wrap" wordt gevonden, wordt gewoon naar de ouder gegaan.

Dat is ook de reden dat je niet iets als 'ga twee niveaus omhoog' kunt zeggen: je weet niet hoeveel elementen er tussen de div.wrap en het aangeraakte of -geklikte element zitten. En als er later in de html elementen zouden worden bijgevoegd, hoef je het script niet te veranderen.

actieveWrap = actieveWrap.parentElement;

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Het aangeraakte of -geklikt element is geen <a>, of die <a> heeft niet de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'.

Deze regel zit binnen een while-lus en wordt uitgevoerd, zolang het element in actieveWrap niet de class 'wrap' heeft.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets kan worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Iets hoger bij actieveWrap = e.target.parentElement; is hier in de vorm van een object de ouder van het aangeraakte of -geklikte element in gestopt.

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

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

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

actieveWrap: dit is precies hetzelfde object (element), als wat voor het isgelijkteken staat en dat hierboven wordt beschreven.

parentElement: dit is een stukje informatie uit het hierboven genoemde object. Hierin zit de ouder van het aangeraakte of -geklikte element, dat in target zit, ook weer in de vorm van een object.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: vervang het element (object) in variabele actieveWrap door de ouder van het element. Ook dit wordt weer in de vorm van een object opgeslagen in variabeleWrap.

Zodra het in variabeleWrap opgeslagen element een class 'wrap' heeft, wordt niet meer aan de voorwaarde van de while-lus voldaan, en wordt deze regel daarom niet meer uitgevoerd.

actieveWrap = actieveWrap.id;

Deze regel is onderdeel van function bewaarId(e)

Deze regel wordt alleen uitgevoerd, als aan deze eerder gestelde voorwaarde is voldaan:

– Het aangeraakte of -geklikt element is geen <a>, of die <a> heeft niet de class 'vorige-a', 'volgende-a', 'vorige-t' of 'volgende-t'.

Als deze regel wordt uitgevoerd, is de while-lus hierboven verlaten, omdat kennelijk een element met een class 'wrap' is gevonden.

actieveWrap: dit is de eerder bij actieveWrap, aangemaakte variabele, waarin iets kan worden opgeborgen. Over wat een variabele is, is meer te vinden bij var vensterGrootte,. Iets hoger bij actieveWrap = actieveWrap.parentElement; is hier in de vorm van object één van de div.wrap's in gestopt. Om precies te zijn: de div.wrap, waarin het aangeraakte of -geklikte element zit.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

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

actieveWrap: dit is precies hetzelfde object (element), als wat voor het isgelijkteken staat en dat hierboven wordt beschreven.

id: dit is een onderdeel van het hierboven genoemde object. In id zit de id van het element dat in de vorm van een object in variabele actieveWrap is opgeslagen.

De hele regel samengevat: vervang de div.wrap die in actieveWrap is opgeslagen door alleen de id van die div.wrap.

En dat is precies, wat we nodig hebben. Aan de hand van die id kan het script op verschillende plaatsen de juiste thumbnail een outline geven en de juiste links bij die thumbnail, en eventueel de juiste grote afbeelding met links, tonen.

document.getElementById(actieveWrap).classList.add("xactiefx", "firefox");

Als je in de html een andere class dan 'firefox' hebt gebruikt bij de elementen, waar elke thumbnail met bijbehorende afbeelding, links, en dergelijke in zit, moet je de class ook in bovenstaande regel aanpassen.

Deze regel is onderdeel van function bewaarId(e)

Deze regel is de tegenhanger van die aan het begin van de functie document.getElementById(actieveWrap).classList.remove("xactiefx", "firefox");. De uitgebreide uitleg is daar te vinden.

Het enige verschil: daar werden de classes 'xactiefx' en 'firefox' met remove() verwijderd, hier worden ze met add() weer toegevoegd.

De korte uitleg: voeg bij het element met de id die in variabele actieveWrap is opgeslagen de classes 'xactiefx' en 'firefox' toe.

Als je wilt kijken, of deze class inderdaad door het script wordt toegevoegd, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

Aanraken/klikken thumbnail afhandelen

Als een thumbnail wordt aangeraakt, moet de bijbehorende grote afbeelding worden getoond. Dat regelt dit deel van het script.

function thumbToon(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 een thumbnail wordt aangeraakt. Het luisteren naar die aanraking of klik wordt geregeld bij thumbs[i].addEventListener("click", thumbToon);.

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

thumbToon: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(e): die haakjes horen nou eenmaal zo na 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 aanraken of ‑klikken van een thumbnail. In e zit bijvoorbeeld, welk element precies is aangeraakt. En nog 'n waanzinnige hoop andere informatie, waarvan hier verder vrijwel niets wordt gebruikt. Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

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

var span = e.target.parentElement.querySelector("span.foto");

Deze regel is onderdeel van function thumbToon(e)

var: met het sleutelwoord var 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.

span: gelijk na var volgt de naam van de variabele: span. Over wat een variabele precies is, is meer te vinden bij var vensterGrootte,. Omdat variabele span binnen function thumbToon() is aangemaakt, is span alleen voor code binnen die functie beschikbaar.

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function thumbToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of ‑geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

parentElement: dit is een stukje informatie uit het hierboven genoemde object. Hierin zit de ouder van het aangeraakte of -geklikte element, dat in target zit, ook weer in de vorm van een object.

Omdat deze functie alleen wordt aangeroepen, als een thumbnail wordt aangeraakt of -geklikt, is de ouder van het aangeraakte of -geklikte element per definitie een div.wrap. Dat is een van de Voorwaarden van het JavaScript waaraan html en css moeten voldoen. (Je kunt daar wel omheen werken, maar dat maakt het script weer ingewikkelder.)

querySelector("span.foto"): dit is de methode uit het hierboven genoemde object die gebruikt moet worden. Met behulp van de methode querySelector() kun je op de pagina zoeken naar het eerste element dat aan een bepaalde eis voldoet, bijvoorbeeld het hebben van een bepaalde class.. Tussen de haakjes staat, waarnaar gezocht moet worden. Dat staat tussen aanhalingstekens, zodat het script weet dat het hier om een letterlijke tekst gaat: "span.foto".

In dit geval wordt gezocht naar alle elementen met class="foto" binnen een <span>. De schrijfwijze bij querySelector() is precies hetzelfde als in css. In een selector bij css wordt span.foto gebruikt, dus bij querySelector() gebruik je "span.foto".

Omdat voor de punt target.parentElement staat, gaat het om de eerste span.foto binnen de ouder van het element in target, om de ouder van de aangeraakte of -geklikte thumbnail.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: zoek in de ouder van de aangeraakte of -geklikte thumbnail de eerste span.foto op en stop die, in de vorm van een object, in variabele span.

if (span.innerHTML.indexOf("<!--") > -1) {

span.innerHTML = span.innerHTML.replace("<!--", "").replace("-->", ""); }

Deze drie regels zijn onderdeel van function thumbToon(e)

Deze drie regels zijn precies hetzelfde als die bij if (span.innerHTML.indexOf("<!--") > -1) {. Een uitgebreide uitleg is daar te vinden.

(Daar wordt de span.foto uit div#wrap-1 in variabele span opgeslagen. Hier gaat het mogelijk om de span.foto uit een andere div.wrap. Dat maakt echter voor de werking van deze regels geen enkel verschil uit, alleen wordt de html van een andere span.foto aangepast.)

De korte samenvatting: kijk of in de html binnen de <span> '<!--' voorkomt. Als dat zo is, verwijder dan uit die html '<!--' en '-->'. Nu is het commentaar in gewone html veranderd en kan de grote afbeelding binnen die <span> worden weergegeven.

Als je wilt kijken, of het commentaar inderdaad door het script wordt veranderd in html, moet je niet in de gewone broncode kijken, maar in de Gegenereerde code.

e.target.parentElement.classList.remove("xverbergx");

Deze regel is onderdeel van function thumbToon(e)

e.target.parentElement: zoals iets hierboven bij var span = e.target.parentElement.querySelector("span.foto"); beschreven, is dit de ouder van de aangeraakte of -geklikte thumbnail. Dat is altijd een div.wrap, die in de vorm van een object hierin is opgeslagen.

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

.classList: in de eigenschap classList van het hierboven genoemde object zitten alle classes, die bij de betreffende div.wrap in de html aanwezig zijn.

remove("xverbergx"): remove() is zo'n methode. Het verwijdert een class bij de betreffende div.wrap: de div.wrap waarin de aangeraakte of -geklikte thumbnail zit.

De te verwijderen class staat tussen de haakjes. Tussen aanhalingstekens, want dan weet JavaScript dat het om een letterlijke naam gaat.

Als om een of andere reden class 'xverbergx' niet aanwezig is, gebeurt er gewoon niets.

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder bij de div.wrap, waarin de aangeraakte of -geklikte thumbnail zit, class 'xverbergx'.

Bij wraps[i].classList.add("xverbergx"); wordt, gelijk bij het openen van de pagina, aan elke div.wrap een class 'xverbergx' toegevoegd. In de css wordt deze class gebruikt om de links naar de vorige en volgende grote afbeelding te verbergen. (Eigenlijk de links naar de vorige of volgende div.wrap met grote afbeelding.)

Omdat deze grote afbeelding nu kan worden getoond, mogen ook de bijbehorende links worden getoond. Ze worden alleen daadwerkelijk getoond, als de bijbehorende grote afbeelding ook wordt getoond. Maar ze kúnnen nu worden getoond.

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.

e.target.removeEventListener("click", thumbToon);

Deze regel is onderdeel van function thumbToon(e)

e: in e zit een zogenaamd object, waarin allerlei informatie zit. Bij function thumbToon(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 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'.

Een uitgebreidere omschrijving van dit soort objecten is te vinden bij (e).

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over het exacte element dat werd aangeraakt of -geklikt. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

removeEventListener("click", thumbToon): aan elke <img> met een thumbnail is eerder bij thumbs[i].addEventListener("click", thumbToon); een eventlistener gekoppeld die luistert naar een aanraking of klik.

Deze functie heeft de <span> met de grote afbeelding die bij de aangeraakte of -geklikte thumbnail hoort in gewone html veranderd, waardoor de grote afbeelding nu altijd kan worden weergegeven.

Met removeEventListener() wordt hij verwijderd. Tussen de haakjes komt precies hetzelfde te staan als bij het koppelen van de eventlistener. Voor de komma waar de 'event', de gebeurtenis, waar de eventlistener op moet reageren: 'click'. Na de komma de naam van de aan die event gekoppelde functie: 'thumbToon'. Nu besteedt de browser geen onnodige energie meer aan het luisteren naar het aanklikken of -klikken van een thumbnail.

(Je moet de soort event en de naam van de functie opgeven bij het verwijderen, omdat aan één element verschillende eventlisteners met verschillende functies kunnen zijn gekoppeld.)

;: de puntkomma aan het eind geeft het eind van de regel aan.

De hele regel samengevat: verwijder bij de <img> met de thumbnail, waarbij werd geluisterd naar een aanraking of klik, de eventlistener.