Skip links en inhoudsopgave

Bij aanraken van, hoveren over of tabben naar een gearceerd woord opent een illustratie - uitleg

Laatst aangepast: .

Afbeelding 1: gearceerde woorden met bijbehorende afbeelding

Korte omschrijving

Als de cursor boven een van de gearceerde woorden komt, een van de gearceerde woorden wordt aangeraakt, of als je met de Tab-toets naar een van de gearceerde woorden gaat, opent een bij de woorden horende kleine afbeelding.

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

Opmerkingen

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

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

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

Achterliggend idee

De tekst is 'n gewone normale tekst (nou ja, normaal, wat heet...). In die tekst zijn bepaalde woorden gearceerd. Die woorden staan in 'n <span>, en als je over die span hovert wordt een kleine afbeelding zichtbaar, die bij dat woord hoort. Als er niet wordt gehoverd, staat die afbeelding links buiten het scherm geparkeerd.

Dat was het oorspronkelijke voorbeeld, zoals dat werkte op desktopcomputers met een muis.

Met een muis of touchpad werkt het nog grotendeels hetzelfde. Alleen wordt nu gebruik gemaakt van het relatief nieuwe position: sticky; waardoor de afbeeldingen niet meer regelmatig gedeeltelijk buiten het venster van de browser komen te staan.

In lage browservensters, zoals op smartphones, werkt dit niet echt lekker. Daarom wordt in lage vensters de afbeelding in het midden van het venster geplaatst.

Voor gebruikers van de Tab-toets is een extra (lege) <span> voor de afbeelding gezet. Door deze <span> tabindex="0" te geven, kan de <span> focus krijgen, waardoor de erachter zittende afbeelding getoond kan worden. In principe zou je ook de afbeelding zelf tabindex="0" kunnen geven, maar dan noemen sommige schermlezers de afbeelding. Wat bij 37 afbeeldingen dus wat bezwaarlijk is.

Als de <span> met de gemarkeerde woorden een tabindex krijgt, melden sommige schermlezers iets als 'klikbaar'. Alleen een lege <span> wordt bij het voorlezen door alle geteste schermlezers volledig genegeerd.

Als een afbeelding wordt geopend door hoveren over of aanraken van een gemarkeerd woord, is duidelijk bij welk woord de afbeelding hoort. Maar bij gebruik van de Tab-toets is dat niet duidelijk. Normaal genomen is een element dat de focus heeft te herkennen aan een kadertje. Hier ontbreekt dat kadertje echter, want de <span> met focus is leeg.

Daarom staan de gemarkeerde woorden, de lege <span> en de afbeelding in een gezamenlijke buitenste <span>. Nieuwere browsers ondersteunen de pseudo-class :focus-within: als een nakomeling van een element focus heeft, kun je het element met behulp van css aanpassen. Zodra de lege <span> focus heeft, kan de buitenste <span> een kadertje krijgen.

Voor browsers die :focus-within niet ondersteunen wordt een klein beetje JavaScript gebruikt om hetzelfde effect te krijgen.

Dan zijn er nog wat aanpassingen voor verschillende browsers en systemen nodig. Deze worden bij de uitleg van de code besproken.

Voorwaarden waaraan html en css moeten voldoen

Voor gebruikers van de Tab-toets is niet duidelijk, bij welke gemarkeerde tekst een afbeelding hoort. Daarom wordt met behulp van de pseudo-class :focus-within de bij een afbeelding horende gemarkeerde tekst voorzien van een kadertje, zodra de afbeelding wordt getoond.

Voor browsers die dit niet ondersteunen, wordt gebruik gemaakt van een beetje JavaScript. Om dit script goed te laten werken, moet de html aan twee simpele voorwaarden voldoen.

Structuur

De gemarkeerde tekst staat in een <span> met class="markeer". Die <span> moet altijd direct voor de <span> met class="pict" staan (de <span> die focus kan krijgen).

Als je dit verandert, moet je ook het script (en de css) aanpassen.

Id's en classes

De <span> die focus kan krijgen, moet een class="pict" hebben. Als je dit wilt aanpassen, moet je dat ook in het script (en in de css) aanpassen.

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

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

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

De belangrijkste browsers hebben elk een eigen voorvoegsel:

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

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

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

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

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

In dit voorbeeld worden user-select en position: sticky; gebruikt.

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

position: sticky;

Op dit moment moet je nog het volgende schrijven:

position: -webkit-sticky; position: sticky;

In de toekomst kun je volstaan met:

position: sticky;

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

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

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

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

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 alleen de tekst met de illustraties.

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

<main role="main">

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

WAI-ARIA-codes

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

Er word in dit voorbeeld één WAI-ARIA-code gebruikt: aria-haspopup.

aria-haspopup

In de <span> met de gemarkeerde woorden wordt aria-haspopup gebruikt:

<span class="markeer" aria-haspopup="true">

In Internet Explorer 11 en Edge op touchscreens opent de pop-up bij aanraking niet. 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" opent de pop-up gewoon.

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.

Tabindex en Tab-toets

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

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

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

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

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

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

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

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

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

tabindex="-1"

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

In dit voorbeeld wordt tabindex="-1" niet gebruikt.

tabindex="0"

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

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

In dit voorbeeld wordt tabindex="0" gebruikt om te zorgen dat een <span> focus kan krijgen:

<span class="pict" tabindex="0">

Hierdoor kunnen gebruikers van de Tab-toets deze <span> focus geven, waarna de bijbehorende afbeelding kan worden getoond.

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.

In dit voorbeeld is dit niet van belang, want de afbeeldingen geven alleen maar wat aanvullende informatie. Bovendien worden de afbeeldingen allemaal getoond, als css uitstaat, ontbreekt of onvolledig is geïmplementeerd.

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. Omdat deze combinatie niet wordt gebruikt in dit voorbeeld, spelen deze problemen niet. Een aanraking op een touchscreen werkt in dit geval hetzelfde als hoveren met een muis.

Alleen op iOS levert dit problemen op. Als je een gemarkeerd woord aanraakt, worden de illustraties alleen gewoond in Firefox, Opera Mini en Microsoft Edge. In andere browsers worden de illustraties stomweg niet getoond. Om dit op te lossen, wordt een klein stukje JavaScript gebruikt.

Omdat een iPad of iPhone zonder JavaScript weinig meer is dan een duur en groot uitgevallen horloge, zal JavaScript vrijwel nooit uitstaan. In steeds meer mobiele browsers kan JavaScript bovendien helemaal niet worden uitgezet.

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

In dit voorbeeld is dit niet van belang, want de afbeeldingen geven alleen maar wat aanvullende informatie. Bovendien worden de afbeeldingen allemaal getoond, als css uitstaat, ontbreekt of onvolledig is geïmplementeerd.

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 voor gebruikers van de Tab-toets een lege <span> aangebracht tussen elke gemarkeerde tekst en de bijbehorende afbeelding:

<span class="pict" tabindex="0">

Hierdoor kunnen de afbeeldingen ook bij gebruik van de Tab-toets worden getoond. Maar omdat deze <span> leeg is, staat er geen kadertje rondom de <span>, als deze focus heeft. Het is daardoor niet duidelijk, bij welk gemarkeerd woord een afbeelding hoort.

Daarom wordt met behulp van de pseudo-class :focus-within een kadertje rondom de bij de afbeelding horende gemarkeerde woorden getekend. Voor browsers die :focus-within niet ondersteunen, wordt dat kadertje met behulp van wat JavaScript getekend.

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

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 28 mei 2019.

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

Dit voorbeeld is getest op de volgende systemen:

Desktopcomputers

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

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

Linux (Kubuntu 18.04 LTS, 'Bionic Beaver') (2560 x 1080 px, resolution: 96 ppi):
Firefox, Opera en Google Chrome, in grotere en kleinere browservensters.

Laptops

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

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

Tablets

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

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

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

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

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

Android 8.1.0 ('Oreo') (1920 x 1200 px, resolution: 144 ppi):
Dolphin, Samsung Internet, UC Browser, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

Smartphones

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

Android 4.1.2 ('Jelly Bean') (800 x 480 px, resolution: 144 ppi):
Chrome, UC Browser, Firefox en Opera for Android (alle portret en landschap).

Android 8.1.0 ('Oreo') (1280 x 720 px, resolution: 192 ppi):
Dolphin, Samsung Internet, UC Browser, Firefox, Microsoft Edge en Chrome (alle portret en landschap).
Opera Mini (besparingen uitgeschakeld) portret en landschap.

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

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

Er is getest met behulp van muis en toetsenbord, behalve op 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 en 8.1.0.

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

ChromeVox is een schermlezer in de vorm van een extensie bij Google Chrome. Er is getest op een systeem met Kubuntu Linux 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 één of meer problemen worden besproken, staat in een breed rood kadertje een korte samenvatting daarvan. Bij een onderwerp over toegankelijkheid zijn er soms geen problemen, maar alleen aanpassingen. In dat geval staat in een smal groen kadertje 'Geen problemen'.

Zonder JavaScript

Probleem: zonder JavaScript worden de afbeeldingen niet getoond op iOS.

Op iOS worden bij aanraken van het scherm de afbeeldingen alleen getoond in Firefox, Opera Mini en Microsoft Edge. In andere browsers worden de afbeeldingen niet getoond.

Om dit op te lossen wordt een klein stukje JavaScript gebruikt. Een groot probleem is dit niet, want zonder JavaScript is een iPad of iPhone weinig meer dan een groot en duur uitgevallen horloge. In steeds meer mobiele browsers kan JavaScript bovendien helemaal niet worden uitgeschakeld.

Probleem: in UC browser, Internet Explorer en Edge is zonder JavaScript voor gebruikers van de Tab-toets niet te zien, bij welke woorden een afbeelding hoort.

Voor gebruikers van de Tab-toets is niet duidelijk, welke afbeelding bij welke gemarkeerde woorden hoort. Dit wordt opgelost door het gebruik van pseudo-class :focus-within, met behulp waarvan een kadertje wordt getekend om de woorden die bij de getoonde afbeelding horen.

UC browser, Internet Explorer en Edge ondersteunen :focus-within niet. Daarom wordt dit geïmiteerd met behulp van wat JavaScript. Zonder JavaScript werkt alles gewoon, maar het kadertje ontbreekt in de genoemde browsers.

Zonder css

Geen problemen.

De afbeeldingen zijn gewone <img>'s, die worden verborgen met behulp van css. Zonder css staan de afbeeldingen gewoon in de tekst. Het ziet er niet uit, maar dat is logisch zonder css.

Zonder afbeeldingen

Geen problemen.

Het zal je verbazen, maar zonder afbeeldingen zijn er geen afbeeldingen.

Omdat de <img>'s een witte achtergrond hebben gekregen, wordt bij hoveren en dergelijke de alt-tekst getoond boven een witte achtergrond, op de plaats waar anders de afbeelding zou komen te staan.

Je zou de alt-tekst ook weg kunnen laten. Maar als een schermlezer dan op een of andere manier toch een afbeelding tegenkomt, gaat die vaak de naam van het bestand voorlezen. Als er een alt-tekst staat, wordt de alt-tekst voorgelezen.

Gebruikers Tab-toets

Probleem: in UC browser, Internet Explorer en Edge is zonder JavaScript voor gebruikers van de Tab-toets niet te zien, bij welke woorden een afbeelding hoort.

Voor gebruikers van de Tab-toets is niet duidelijk, welke afbeelding bij welke gemarkeerde woorden hoort. Dit wordt opgelost door het gebruik van pseudo-class :focus-within, met behulp waarvan een kadertje wordt getekend om de woorden die bij de getoonde afbeelding horen.

UC browser, Internet Explorer en Edge ondersteunen :focus-within niet. Daarom wordt dit geïmiteerd met behulp van wat JavaScript. Zonder JavaScript werkt alles gewoon, maar het kadertje ontbreekt in de genoemde browsers.

Tekstbrowsers

Geen problemen.

Lynx zet de alt-tekst bij de <img>'s binnen de gewone tekst.

WebbIE toont alleen de tekst, zonder alt-tekst.

Schermlezers

Alle geteste schermlezers lezen de tekst zonder onderbrekingen voor.

Daarvoor is wel een lege <span class="pict" tabindex="0"></span> nodig.

In principe zou je de afbeelding ook kunnen laten openen door gebruikers van de Tab-toets, door de tabindex="0" bij de <img> te zetten. Maar dan kondigt TalkBack op Android 8 (en mogelijk ook latere versies, maar daar wordt nog niet op getest) bij elke <img> aan: "Dubbelklik om te activeren". Als je dat 37 keer hoort tijdens het voorlezen van de tekst, wordt je daar vermoedelijk niet heel vrolijk van. Door de tabindex="0" in een volledig lege <span> te zetten, wordt dit voorkomen.

In dit geval leveren de <img>'s nauwelijks echte informatie. Als dat wel zo zou zijn, is deze constructie volkomen ongeschikt, omdat schermlezers dan informatie gaan missen. De <img>'s hebben weliswaar een alt-tekst, maar die wordt in deze constructie niet voorgelezen. Maar ook als die alt-tekst wel zou worden voorgelezen, is dat geen goed idee, want dan wordt het voorlezen van de tekst 37 keer onderbroken.

(Er zijn twee redenen om die hele korte alt-tekst te laten staan: als de afbeeldingen niet worden geladen, wordt de alt-tekst weergegeven. En in bepaalde situaties kan een schermlezer toch de <img> noemen. Zonder alt-tekst wordt dan vaak de naam van het bestand voorgelezen.)

Zoomen en lettergroottes

Probleem: in browservensters lager dan 630 px kan de afbeelding bij inzoomen buiten het venster komen te staan.

Apparaten met zulke lage browservensters zullen voornamelijk smartphones zijn.

In browservensters lager dan 630 px worden de afbeeldingen in het midden van het venster weergegeven. Bij inzoomen (vergroten) kan tekst, en daardoor ook de afbeelding, buiten het venster komen te staan. Door te scrollen kan de afbeelding alsnog gewoon worden bekeken.

In sommige browsers kan de tekst worden vergroot. In dat geval blijft de tekst, en daarmee ook de afbeelding, gewoon binnen het browservenster staan.

Probleem: op mobiele apparaten met browservensters minimaal 630 px hoog kan bij inzoomen de afbeelding buiten het venster komen te staan.

Dit soort apparaten zullen voornamelijk tablets zijn. Als op een desktop wordt ingezoomd (vergroot), wordt bij een bepaalde vergroting overgeschakeld naar de weergave voor browservensters lager dan 630 px. Op een tablet gebeurt dit echter niet.

De afbeelding wordt altijd binnen het browservenster weergegeven met behulp van position: sticky;. Bij inzoomen werkt dat niet meer en kan de afbeelding (gedeeltelijk) buiten het venster komen te staan. Door te scrollen kan de afbeelding alsnog gewoon worden bekeken.

Android browser en UC browser op de tablet met Android 4.12 met gewone resolutie

Probleem: de afbeeldingen worden niet getoond.

Ze weigeren gewoon iets te doen. Ook dreigen met acuut en grof geweld hielp niet. Vreemd genoeg werkt UC browser op dezelfde versie van Android wel gewoon op de tablet met dubbele resolutie.

Omdat deze versie van Android heel snel aan het uitsterven is, is hier verder niets aan geprobeerd te doen. Hierbij speelde ook een rol dat de afbeeldingen geen wezenlijke informatie toevoegen.

In het verleden werd dit op deze site vaak opgelost met een beetje extra css. Android browser en oudere versies van UC browser hadden vaak problemen met selectors waarin een + en/of een ~ zaten. Bovenin de stylesheet werd dan de flauwekulregel

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

neergezet. Een flauwekulregel, omdat padding-left van 0 px naar 0 px wordt veranderd.

Elders in de css stond dan de regel:

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

Voer deze animatie eindeloos één keer per seconde uit. Door deze twee regels werkte de selector dan plotsklaps wel. Wel even zoeken naar het element, waar deze regel moet staan, want niet altijd was dat het meest voor de hand liggende element.

Dit kun je dus eventueel nog steeds doen. Op deze site wordt dit niet meer gedaan, omdat browsers met dit probleem nog nauwelijks worden gebruikt. (In oudere voorbeelden staat het vaak nog wel.)

Het vervelende van deze extra css is dat élke browser die eigenschappen met de prefix -webkit- kan lezen deze code gebruikt. Ook browsers als Firefox, Google Chrome, Microsoft Edge en Safari voeren deze code uit, terwijl dat helemaal niet nodig is.

:focus-within valideert niet

Probleem: de css-validator van w3c geeft een foutmelding bij :focus-within.

De pseudo-class :focus-within is relatief nieuw. De specificatie, waarin deze pseudo-class staat, is nog pas in een ontwerpfase. In theorie zou de werking van :focus-within daardoor nog (meer of minder) kunnen veranderen. Die kans is echter vrij klein.

In browsers die :focus-within niet ondersteunen, wordt dit geïmiteerd met behulp van wat JavaScript. Als je alle risico's wilt uitsluiten, kun je dit script voor alle browsers laten werken:

  • Verwijder uit de css de regel .buiten:focus-within .markeer {outline: green dotted 3px;}
  • Verander in het script de regel

    var focusWithin = probeer(), picts, len, x;

    in

    var picts, len, x;
  • Verwijder uit het script de regel if (focusWithin) return;
  • Verwijder uit het script het hele deel

    function probeer() { try { document.querySelector(":focus-within"); } catch (error) { return false; } return true; }

Nu werkt het script in alle browsers, omdat niet meer wordt getest of :focus-within wordt ondersteund.

UC browser op Windows en iOS en Internet Explorer

Probleem: de afbeeldingen kunnen (iets) buiten het browservenster komen te staan.

Omdat deze browsers position: sticky; niet ondersteunen, kunnen de afbeeldingen (gedeeltelijk) buiten het browservenster komen te staan. Door scrollen kunnen ze weer binnen het venster worden gezet.

Wijzigingen

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

:

Nieuw opgenomen.

10 april 2009:

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

28 mei 2019:

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 ode en dergelijke zitten. Voor eventuele schade die door gebruik van materiaal uit deze download ontstaat, in welke vorm dan ok, 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 oorbeeld (en de bijbehorende uitleg en dergelijke) op welke manier dan ook te verspreiden, zonder daarbij duidelijk te ermelden dat voorbeeld, uitleg, en dergelijke afkomstig zijn van www.css-voorbeelden.nl en dat daar altijd de nieuwste versie s te vinden. Dit is om te voorkomen dat er verouderde versies worden verspreid.

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

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

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

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

037-css-dl:

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

037-pics:

De 36 in het voorbeeld gebruikte thumbnails.

HTML

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

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

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

<!doctype html>

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

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

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

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

<html lang="nl">

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 wordt de pagina nooit breder dan het venster en staan de afbeeldingen altijd binnen het venster.

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.

<span class="buiten">

<span class="markeer" aria-haspopup="true">eerste lay-out</span> <span class="pict" tabindex="0"></span> <img src="037-pics/menu-1.jpg" alt="Lay-out 1"> </span>

Dit is de html voor het tonen van de eerste afbeelding. Voor de andere afbeeldingen is de code vrijwel hetzelfde: alleen de tekst in span.markeer, de naam van de afbeelding en de alt-tekst zijn anders.

Anders dan in het voorbeeld zelf zijn de elementen hier over meerdere regels neergezet. Dat is hier duidelijker. In het voorbeeld zou net nauwelijks duidelijker zijn, omdat daar ook nog veel meer gewone tekst in staat. Bovendien zou het heel erg veel extra regels opleveren.

<span class="buiten">: binnen deze <span> staat onder andere de gemarkeerde tekst, hier is dat 'eerste lay-out'. Zodra een element binnen span.buiten focus heeft, komt rondom span.markeer met de gemarkeerde tekst te staan een randje te staan. Hierdoor kunnen gebruikers van de Tab-toets zien, bij welke tekst de afbeelding hoort.

Omdat dit alleen gebeurt als een element binnen span.buiten focus heeft, verschijnt het randje alleen bij gebruik van de Tab-toets. Bij hoveren over of aanraken van de gemarkeerde woorden is het ook niet nodig, want dan is duidelijk bij welke gemarkeerde tekst de getoonde afbeelding hoort.

Meer hierover is te vinden bij .buiten:focus-within .markeer.

<span class="markeer" aria-haspopup="true">eerste lay-out</span>: binnen deze <span> staat de gemarkeerde tekst. Als over deze tekst wordt gehoverd of als deze wordt aangeraakt, verschijnt een bijbehorende afbeelding. De tekst in deze <span> is het enige altijd zichtbare deel van deze hele constructie.

aria-haspopup="true" is een zogenaamde WAI-ARIA-code. Het is nodig voor Internet Explorer en Edge op touchscreens, omdat anders de afbeelding niet wordt getoond.

<span class="pict" tabindex="0"></span>: met de Tab-toets kunnen links, tekstvelden, en dergelijke worden afgelopen. Dit is belangrijk voor mensen die de muis niet goed kunnen gebruiken (en soms werkt het ook veel sneller dan de muis).

Een <span> kan normaal genomen niet worden bezocht met de Tab-toets, maar door toevoeging van tabindex="0" kan dit wel. Met behulp van de selector .pict:focus + img kan de afbeelding zichtbaar worden gemaakt, als span.pict focus heeft.

Het zou nogal wat html en css uitsparen als tabindex="0" gewoon bij de <img> gezet zou kunnen worden. TalkBack op Android 8 (en mogelijk ook in latere versies, maar daar wordt (nog) niet in getest) leest dan echter bij elke <img> 'Dubbelklik om te activeren' voor. (Andere schermlezers lezen de tekst zonder onderbrekingen voor.)

Als een tekst bij het voorlezen 37 keer wordt onderbroken met zo'n toch wat weinig opwindende tekst, leidt dat mogelijk tot een woedekoliek. Wat weer hogere zorgkosten, Kamervragen en een boze minister oplevert, dus het is beter dat te voorkomen.

Alleen als de <span> volledig leeg is, wordt deze door TalkBack genegeerd. Ook span.markeer of span.buiten waren daardoor niet te gebruiken, want ook deze zijn niet volledig leeg. De enige oplossing bleek een aparte, volledig lege <span> te zijn.

(In dit geval moeten de afbeeldingen worden verborgen voor schermlezers, omdat ze nauwelijks informatie opleveren. Als de afbeeldingen wel informatie bevatten, is deze techniek volledig ongeschikt.)

Het JavaScript gebruikt class 'pict' voor browsers die de pseudo-class :focus-within niet ondersteunen. Als je de naam van deze class wijzigt, moet je dat ook in het script doen.

<img src="037-pics/menu-1.jpg" alt="Lay-out 1">: dit is een gewone afbeelding, die alleen op verzoek wordt getoond.

Normaal genomen zou je alt="" gebruiken om aan te geven dat de afbeelding door schermlezers genegeerd mag worden. Hier is toch een korte alt-tekst aangebracht, omdat in bepaalde omstandigheden een schermlezer de afbeelding toch noemt. Zonder alt-tekst wordt dan meestal de naam van het bestand voorgelezen.

CSS

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

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

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

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

              div#header-buiten {
            position: absolute;
            right: 16px;
            width: 100%;
            height: 120px;
            background: yellow;
        }

        div p {
            margin-left: 16px;
            height: 120px;
            text-align: center;
        }

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

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

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

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

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

css voor alle vensters

/* tekst-037-dl.css */

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

body

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

background: #ff9;

Achtergrondkleurtje.

color: black;

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

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

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

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

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

font-size: 110%;

Iets groter dan standaard. 't Zal de leeftijd zijn, maar ik vind de standaardgrootte wat te klein.

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

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. (Hier is dat alleen de flauwekultekst met de afbeeldingen.)

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;

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.

width: 800px;

Breedte. Een maximumbreedte voorkomt te lange, slecht leesbare regels.

max-width: 95%;

Hier gelijk boven is een breedte van 800 px opgegeven. Als het venster van de browser smaller is, moet je hierdoor horizontaal scrollen om alles te kunnen zien. Daarom wordt hier de breedte beperkt tot maximaal 95%.

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

Hierdoor wordt <main> uiteindelijk nooit breder dan 95% van de breedte van het browservenster, ongeacht de breedte van het venster.

line-height: 2em;

Tamelijk grote regelafstand. In de tekst zitten gemarkeerde woorden, die moeten worden aangeraakt om een afbeelding te openen. Door een grotere regelafstand zijn deze gemarkeerde woorden op een touchscreen makkelijk te raken.

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

margin: 20px auto 0;

Omdat voor links geen waarde is opgegeven, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 20px auto 0 auto in de volgorde boven – rechts – onder – links. Boven een kleine afstand tot de bovenkant van het venster van de browser.

Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor staat <main> altijd horizontaal gecentreerd binnen z'n ouder <body>, ongeacht de breedte van <body>.

Zoals iets hierboven bij max-width beschreven is <body> altijd even breed als het venster van de browser. Hierdoor staat <main>, en daarmee de tekst en dergelijke erin ook, altijd horizontaal gecentreerd binnen het venster, ongeacht de breedte van het venster.

border: black solid 1px;

Zwart randje.

padding: 5px;

Kleine afstand tussen buitenkant van en tekst in <main>.

h1

Alle <h1>'s. Dat is er maar één: de belangrijkste kopregel van de pagina staat erin.

font-size: 1.4em;

Van zichzelf is een <h1> wel heel enthousiast groot. Daarom wordt hier de lettergrootte iets verkleind.

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

font-weight: normal;

Standaard is een <h1> vet. Hier wordt dat veranderd naar normaal.

text-align: center;

Tekst horizontaal centreren.

.buiten

Alle elementen met class="buiten". In elke span.buiten staat onder andere een stukje gemarkeerde tekst en de daarbij horende afbeelding.

border: transparent solid 1px;

Onzichtbaar randje rondom de <span>. Dit is geen onderdeel van een goocheltruc of zoiets, maar lost een probleem in Google Chrome en Opera op.

Als een nieuwe regel toevallig binnen een stuk gemarkeerde tekst begint, staat een deel van de tekst aan het eind van de bovenste regel. Als over dat deel van de tekst wordt gehoverd, moet de bijbehorende afbeelding gewoon worden getoond.

In Google Chrome en Opera echter wordt de afbeelding wel getoond, maar verdwijnt gelijk weer. En dat in een tempo van tientallen keren per seconde. Bij hoveren over de gemarkeerde tekst in de onderste regel gaat het wel goed.

Een doorzichtige rand rondom de hele constructie blijkt dit op te lossen. Mogelijk zijn er nog wel meer oplossingen mogelijk, maar omdat span.buiten toch al aanwezig is, kan deze worden gebruikt voor deze simpele oplossing.

.markeer

Alle elementen met class="markeer". Dit zijn de <span>'s, waarin de gemarkeerde tekst zit.

background: #fe8;

Afwijkende achtergrondkleur.

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.

border: black dotted 1px;

De tekst die een afbeelding tevoorschijn doet komen, heeft hierboven een afwijkende kleur gekregen. Voor mensen die moeite hebben met kleuren, is dit mogelijk niet duidelijk genoeg. Daarom wordt rondom de tekst ook nog een stippellijntje gezet.

padding: 2px;

Door de <span> een kleine padding te geven, wordt deze wat breder en hoger. Hierdoor wordt de markering wat duidelijker.

img

Alle afbeeldingen.

background: white;

Witte achtergrond.

Normaal genomen zie je de achtergrondkleur van de afbeelding niet. Maar elke <img> heeft een korte alt-tekst. Deze wordt getoond, als de afbeelding om een of andere reden niet wordt weergegeven. Door de afbeelding een witte achtergrond te geven, is de tekst altijd goed leesbaar.

color: black;

Voorgrondkleur zwart. Dit is onder andere de kleur van de alt-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: none;

Afbeelding verbergen. Door display: none; te gebruiken is de afbeelding volledig verborgen voor schermlezers. In dit geval is dat prima, omdat de kleine thumbnails nauwelijks informatie geven. Als de afbeelding wel informatief is, is het geen goed idee deze te verbergen voor schermlezers.

width: 119px; height: 106px;

Alle afbeeldingen hebben dezelfde breedte en hoogte. Het spaart behoorlijk wat html uit, als je die breedte en hoogte één keer opgeeft in de css. Omdat de afbeeldingen normaal genomen niet worden getoond, is het ook niet nodig om er ruimte voor te reserveren bij het opbouwen van de pagina.

margin: -53px 0 0 -60px;

Iets hieronder wordt de linkerbovenhoek van de afbeeldingen met position: fixed;, top: 50%; en left: 50%; precies in het midden van het venster van de browser geplaatst.

De afbeelding is 106 px hoog. Als de afbeelding met een negatieve marge van -53 px naar boven wordt gezet, komt de afbeelding verticaal precies in het midden van het venster van de browser te staan.

De afbeelding is 119 px breed, dus met een negatieve marge naar links van -60 px komt de afbeelding ook horizontaal in het midden van het browservenster te staan.

In browservensters minimaal 630 px hoog wordt de positie van de afbeelding later op een andere manier bepaald. Die manier werkt echter niet in kleinere vensters, omdat de afbeelding dan heel vaak (grotendeels) buiten het venster zou komen te staan.

border: black solid 1px;

Zwart randje rondom de afbeelding.

pointer-events: none;

Deze css-eigenschap moet niet worden verward met Pointer Events uit JavaScript. Hier is heel handig dezelfde naam voor gekozen, maar dit heeft helemaal niets te maken met deze css-eigenschap.

Deze eigenschap zorgt ervoor dat aanraken van of hoveren over de <img> volledig wordt genegeerd. Als de <img> wordt aangeraakt of als erover wordt gehoverd, wordt dit doorgegeven aan het element onder de <img>. Dit is nodig voor Microsoft Edge op een touchscreen.

Afbeelding 2: afbeelding staat gedeeltelijk over gemarkeerde tekst

Op de illustratie is het gemarkeerde woord 'imagemap' te zien met de bijbehorende afbeelding. De afbeelding staat gedeeltelijk over het gemarkeerde woord heen. Omdat de afbeelding doorzichtig is gemaakt, is het hele gemarkeerde woord toch te zien.

Als je op Edge het gemarkeerde woord aanraakt op een plaats, waar later de afbeelding boven komt te staan, sluit de afbeelding gelijk weer. Op de illustratie staat 'ima' niet onder de afbeelding. Als je 'ima' aanraakt, blijft ook op Microsoft Edge de afbeelding geopend. Maar als je 'gemap' aanraakt, het deel waarover de afbeelding komt te staan, sluit de afbeelding gelijk weer.

Het gemarkeerde woord 'imagemap' reageert op :hover. Dat wordt op een touchscreen omgezet naar een aanraking. Als de afbeelding wordt geopend, raak je het gemarkeerde woord onder de afbeelding niet langer aan, want daar raak je nu de afbeelding aan. Dit is precies zoals het hoort en gebeurt in alle browsers.

Oorspronkelijk werd dit opgevangen met img:hover {display: block;}: ook als je over de <img> hovert of deze aanraakt, blijft de <img> geopend. Dit bleek echter op Microsoft Edge tot het hierboven probleem te leiden. Door de <img> volledig ongevoelig voor aanraken (of hoveren) te maken, wordt dit opgelost.

Hier zit één klein nadeel aan. Bij hoveren zou de afbeelding eigenlijk getoond moeten blijven, zolang de cursor boven de <img> staat. Nu sluit de <img> echter, zodra de cursor de gemarkeerde tekst verlaat. Een echt probleem blijkt dit echter niet te zijn.

position: fixed; top: 50%; left: 50%;

De linkerbovenhoek van de afbeelding in het midden van het browservenster neerzetten. Met behulp van de iets hierboven opgegeven negatieve marges komt de afbeelding uiteindelijk precies in het midden van het venster te staan. Het hele verhaal is bij die marges te vinden.

z-index: 100; Afbeelding 3: zonder z-index wordt de afbeelding deels afgedekt

Elementen worden normaal genomen op het scherm gezet in de volgorde, waarin ze in de html voorkomen. Op de illustratie staat een afbeelding die tamelijk onderin een <p> staat. Hierdoor komt tekst uit de <p> daaronder over de afbeelding te staan.

Door de <img> een z-index te geven, komt de afbeelding toch boven de latere <p> te staan.

Een z-index werkt alleen in bepaalde omstandigheden. Eén van die omstandigheden is een fixed positie. Die is hierboven gegeven, dus dat is geregeld.

(In browservensters minimaal 630 px breed wordt de<img> relatief of sticky gepositioneerd, en ook dan werkt een z-index.)

.buiten:focus-within .markeer

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.

.markeer {background: #fe8; color: black; border: black dotted 1px; padding: 2px;}

Alle elementen met class="buiten", maar alleen als een nakomeling van dat element focus heeft.

In elke span.buiten zit onder andere een <span class="pict" tabindex="0">. Met behulp van de Tab-toets kunnen links, tekstvelden, en dergelijke worden bezocht. Het bezochte veld heeft focus: een link kan worden gevolgd door het indrukken van Enter, in een tekstveld kan tekst worden ingevoerd, enzovoort.

Normaal genomen kan een <span> geen focus krijgen. Maar door toevoeging van het attribuut tabindex="0" kan dat wel. Dit is het geval bij span.pict. Omdat deze <span> verder helemaal leeg en daardoor onzichtbaar is, kan de <span> alleen focus krijgen door de Tab-toets te gebruiken. Aanraken van of hoveren over een onzichtbare <span> is wat lastig, vandaar. Deze selector kan dus alleen maar werken, als de Tab-toets wordt gebruikt.

Omdat span.pict binnen span.buiten zit, gaat de pseudo-class :focus-within werken, zodra span.pict de focus heeft. Op dat moment kan er dus css worden uitgevoerd bij span.buiten.

outline: green dotted 3px;

Groen gestippeld kadertje rondom span.buiten zetten.

Als je een gemarkeerd woord aanraakt of erover hovert, weet je dat de getoonde afbeelding daarbij hoort. Voor gebruikers van de Tab-toets is dat niet duidelijk. Daarom wordt, als een afbeelding wordt getoond, een groen gestippeld kadertje rondom de gemarkeerde woorden gezet.

Dit kadertje komt rondom span.buiten te staan en daarmee dus ook om alles wat ín span.buiten zit. Dat is span.markeer met de gemarkeerde tekst. Dat komt goed uit, want dat is precies de tekst, waarom het kadertje moet komen te staan.

Slimmeriken denken nou natuurlijk: "ho, wacht even, er zit toch nog veel meer in span.buiten?" (Om minderwaardigheidsgevoelens en andere enge dingen te voorkomen: als je dat niet hebt gedacht, ben je niet dom. Je bent dan zo superslim dat je gelijk de eindconclusie hebt gevonden en deze stap hebt overgeslagen.)

Buiten de tekst zit in span.buiten nog span.pict. Die staat inderdaad ook binnen dat kadertje, maar omdat span.pict leeg is en dus onzichtbaar, heeft die geen invloed op de grootte van het kadertje.

Dan zit er nog de <img> in, maar die is fixed of absoluut gepositioneerd, als deze selector werkt. En een fixed of absoluut gepositioneerd element heeft geen invloed op de grootte van z'n ouder.

Kortom: het kadertje komt precies rondom de gemarkeerde tekst die bij de afbeelding hoort.

Voor browsers die het relatief nieuwe :focus-within niet ondersteunen wordt dit met behulp van een beetje JavaScript gesimuleerd.

.markeer:hover

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.

.markeer {background: #fe8; color: black; border: black dotted 1px; padding: 2px;}

.buiten:focus-within .markeer {outline: green dotted 3px;}

Alle elementen met class="markeer", maar alleen als daarover wordt gehoverd of, op touchscreens, bij aanraking van het element. In deze <span>'s staan de gemarkeerde woorden.

-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 de gemarkeerde tekst iets langer wordt aangeraakt, opent de afbeelding niet, maar wordt de tekst geselecteerd. Dit maakt het heel moeilijk de afbeelding te openen. (Heel moeilijk, want bij precies de juiste aanraking is de afbeelding toch te zien. Maar het vangen van een vlo met de blote hand is 'n stuk makkelijker.)

Als je de tekst niet kunt selecteren, kun je die ook niet kopiëren en dergelijke. Maar dat geldt alleen als je alleen precies zo'n gemarkeerd stukje wilt selecteren. Als je van iets voor tot iets achter de gemarkeerde tekst selecteert, wordt ook de tussenliggende gemarkeerde tekst geselecteerd, want daar hover je dan niet over. Het selecteren van één extra spatie voor en na de gemarkeerde tekst is al genoeg, om ook de gemarkeerde tekst te kunnen selecteren.

.markeer:hover + span + img, .pict:focus + img

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.

img {background: white; display: none; width: 119px; height: 106px; margin: -53px 0 0 -60px; border: black solid 1px; pointer-events: none; position: fixed; top: 50%; left: 50%; z-index: 100;}

De eerste selector, die voor de komma:

.markeer:hover: alle elementen met class="markeer", maar alleen als daarover wordt gehoverd (of als deze worden aangeraakt). Dit zijn de <span>'s, waarin de gemarkeerde tekst staat.

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

span: omdat deze <span> direct op span.markeer moet volgen, kan dit alleen span.pict zijn.

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

img: de <img> met de afbeelding.

De hele eerste selector in normale mensentaal: doe iets met de <img> die direct op span.pict en span.markeer volgt, maar alleen als over span.markeer wordt gehoverd (of als deze wordt aangeraakt).

De tweede selector, die na de komma:

.pict:focus: alle elementen met class="pict", maar alleen als die focus hebben. Dit zijn de lege <span>'s die voor gebruikers van de Tab-toets zijn aangebracht.

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

img: de <img> met de afbeelding.

De hele tweede selector in normale mensentaal: doe iets met de <img> die direct op span.pict volgt, maar alleen als span.pict focus heeft.

display: block;

Bij img is de <img> met display: none; verborgen. Nu wordt hij zichtbaar gemaakt.

css voor vensters minimaal 630 px hoog

@media screen and (min-height: 630px)

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

De css die binnen deze media query staat, geldt alleen voor browservensters die minimaal 630 px hoog zijn. In deze hogere vensters wordt de afbeelding niet meer in het midden van het venster gezet, maar meer in de buurt van de bijbehorende tekst.

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

screen: deze regel geldt alleen voor schermweergave.

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

(min-height: 630px): het browservenster moet minimaal 630 px hoog zijn. Is het venster lager, 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 query valt een bijbehorende afsluitende }. Die zijn in de regel hierboven weggevallen, maar het geheel ziet er zo uit:

@media screen and (min-height: 630px) { body {color: silver;} (...) rest van de css voor deze @media-regel (...) footer {color: gold;} }

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

Als je nou 'n mobieltje hebt met een resolutie van – ik roep maar wat – 1024 x 768 px, dan geldt deze media query soms toch niet voor dat mobieltje. Terwijl dat toch echt meer dan 630 px hoog 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’). (Vaak wordt foutief de eenheid dpi (’dots per inch’) gebruikt. Die eenheid is voor printers.)Als dat mobieltje een resolutie van 192 ppi heeft, 192 pixels per inch, zijn de pixels ervan twee keer zo klein als op een origineel beeldscherm. Er zijn per inch twee keer zoveel schermpixels aanwezig.

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

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

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

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

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

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

.markeer:hover + span + img, .pict:focus + img

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 {background: white; display: none; width: 119px; height: 106px; margin: -53px 0 0 -60px; border: black solid 1px; pointer-events: none; position: fixed; top: 50%; left: 50%; z-index: 100;}

.markeer:hover + span + img, .pict:focus + img {display: block;}

Deze selector werkt precies hetzelfde als die bij .markeer:hover + span + img, .pict:focus + img, waar een uitgebreidere beschrijving staat. Als over span.markeer (de <span>'s met de gemarkeerde tekst) wordt gehoverd of als deze wordt aangeraakt, of als span.pict focus heeft, doe dan iets met de daaropvolgende <img>.

display: inline;

Bij img is de <img> met display: none; verborgen. Nu wordt hij zichtbaar gemaakt.

De <img> wordt als een inline-element zichtbaar gemaakt. Daardoor wordt de <img> niet op een nieuwe regel gezet, wat met display: block; wel zou gebeuren. De <img> gedraagt zich nu min of meer als normale tekst.

De afbeelding was eerst verborgen met display: none;. Als die afbeelding nu opeens tussen de tekst wordt gezet, neemt die ruimte in. Daardoor zou een deel van de tekst moeten worden verplaats om ruimte te maken. Dat zou uiterst storend zijn. Bovendien wordt soms ook span.markeer verplaatst, de <span> die bij hoveren over en aanraken van de <span> de <img> zichtbaar maakt. Daardoor zouden ook hoveren en aanraken niet meer goed werken.

Je zou dit op kunnen lossen door de <img> absoluut te positioneren. Op die manier is het in de vorige versie opgelost. Maar dat heeft als nadeel dat de afbeelding makkelijk (gedeeltelijk) buiten het browservenster kan komen te staan. Met behulp van het relatief nieuwe position: sticky; kan dit grotendeels worden voorkomen, maar dan neemt de afbeelding wel ruimte in.

Het probleem van de ruimte wordt gelijk hieronder met behulp van negatieve marges opgelost. Hierdoor kunnen display: inline; én position: sticky; worden gebruikt en hebben we het beste uit twee werelden.

margin: -54px -61px -54px -60px;

Boven en onder een negatieve marge van 54 px, rechts van 61 px en links van 60 px.

Iets hoger wordt opgegeven dat de <img> als inline-element moet worden weergegeven. Een marge aan de boven- en onderkant heeft geen effect op inline-elementen zoals een <span>, <a>, <i>, en dergelijke. Ook een <img> is een inline-element en dus zouden marge aan boven- en onderkant geen effect hebben. Maar dat is niet zo.

Een <img> is een bijzonder soort inline-element: een 'replaced element'. De tag <img> wordt vervangen door iets anders: de afbeelding. Een marge aan boven- en onderkant werkt wel op een replaced element, ook als dit een inline-element is.

Zoals hierboven bij display: inline; beschreven is de <img> eerst verborgen en neemt dus geen ruimte in. Maar bij tonen neem de afbeelding opeens wel ruimte in, waardoor de tekst zou moet en opschuiven.

De afbeeldingen zijn 119 px breed en 106 px hoog. Daar komt aan alle kanten nog een border van 1 px bij, dus de breedte wordt 121 px en de hoogte 108 px.

Als aan een element een marge wordt gegeven, wordt het element daadwerkelijk van plaats veranderd. Als aan de bovenkant een negatieve marge van 54 px aan de <img> wordt gegeven, wordt de <img> daadwerkelijk 54 px naar boven verplaatst. Omdat de <img> daadwerkelijk wordt verplaatst, wordt tekst en dergelijke die onder de <img> staat, ook 54 px naar boven verplaatst.

Aan de onderkant wordt ook een negatieve marge van 54 px gegeven. Een negatieve marge aan de onderkant verplaatst alles eronder 54 px naar boven.

De <img> zelf wordt dus 54 px naar boven verplaatst, en alle tekst en dergelijke eronder ook. Samen is dag 2 x 54 = 108 px. Precies de hoogte van de <img> met border. Oftewel: de 108 px hoogte die de <img> bij verschijnen inneemt, worden gecorrigeerd door de negatieve marges. Het lijkt alsof de <img> geen hoogte inneemt.

Precies hetzelfde geldt voor de negatieve marges rechts en links. De marge links verplaatst de <img> 61 px naar links, en de marge rechts verplaatst de tekst rechts van de <img> nog 'ns 60 px naar links. Samen de breedte van de <img> met z'n border.

De <img> neemt dus wel degelijk gewoon ruimte in, maar daar zie je niets van, omdat de negatieve marges dat wegmoffelen.

position: relative;

Hier gelijk onder wordt position: sticky; gebruikt. UC browser op Windows en iOS9 en Internet Explorer ondersteunen dat niet. Daardoor zou het eerder bij img opgegeven position: fixed; van kracht blijven. Dat werkt daar goed, omdat ook top: 50%; en left: 50%; zijn opgegeven.

Iets hieronder worden echter top: 10px; en left: 10px; opgegeven. Dat betekent dat de <img> nu ergens in de linkerbovenhoek van het browservenster zou komen te staan. Daarom wordt hier een relatieve positie opgegeven. Browsers die position: sticky; niet ondersteunen gebruiken de hier opgegeven relatieve positie, en browsers die het wel ondersteunen gebruiken de position: sticky; hieronder, want die staat lager in de css.

(Iets hieronder worden niet alleen nieuwe waarden voor top en left opgegeven, maar ook right: 10px; en bottom: 10px;. In combinatie met position: fixed; is dat een onmogelijke opgave, daarom worden right en bottom iets hieronder genegeerd. Overigens worden die ook genegeerd bij deze relatieve positie, want je kunt een afbeelding niet gelijktijdig naar links én rechts én boven én onder verplaatsen. Althans: dat kan wel, maar dat levert een aantal forse scheuren op.)

position: -webkit-sticky; position: sticky;

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

Dit is een relatief nieuwe eigenschap. Normaal genomen is dit hetzelfde als position: relative;. Hier gelijk onder worden top, right, bottom en left opgegeven, allemaal met 10 px. De <img> wordt dus 10 px naar onder, naar links, naar boven en naar rechts verplaatst. Dat kan natuurlijk niet, en daarom worden right en bottom genegeerd.

Voor een sticky positie is de eerste voorouder die een blok-element is van belang. Dat is de <p>, waar de <img> (en nog wat tekst) in staat. Zodra deze <p> door scrollen buiten het browservenster komt te staan, zorgt de sticky positie ervoor dat de <img> binnen het venster blijft staan. Althans: tot op zekere hoogte. Als je de <p> ver genoeg naar boven of naar beneden scrolt, zal uiteindelijk de <img> ook boven of onder het venster komen te staan.

Hier gelijk onder wordt opgegeven dat de <img> in principe altijd op minimaal 10 px van de boven-, rechter-, onder- en linkerkant van het venster van de browser moet komen te staan. In principe, want als je genoeg scrolt, zal de <img> uiteindelijk verdwijnen. Als je wilt dat de <img> altijd zichtbaar is, moet je een fixed positie gebruiken (zoals in dit voorbeeld bij vensters minder dan 630 px hoog inderdaad is gebruikt).

Hopelijk wordt dit wat duidelijker met behulp van onderstaande illustraties. De <p> waar de <img>'s bij horen heeft een gestippelde rand, zodat duidelijk is, hoe groot de <p> precies is. De vier <img>'s binnen de <p> op de illustratie zijn alle vier zichtbaar gemaakt. In werkelijkheid kan er steeds maar één zichtbaar zijn.

Afbeelding 4
Afbeelding 4: pp bovenstaande afbeelding staat de <p> ergens midden op het scherm. Er is voldoende ruimte aan boven- en onderkant, dus de <img>'s staan op hun ' natuurlijke' hoogte.
Afbeelding 5
Afbeelding 5: de <p> is nu zover naar boven gescrold, dat een deel van de <p> boven het venster van de browser verdwijnt. Maar de bovenste drie <img>'s staan alle drie op 10 px vanaf de bovenkant van het venster en zijn dus nog volledig zichtbaar. Dit is het effect van position: sticky; in combinatie met top: 10px;. Normaal genomen zou een deel van de <img>'s nu aan de bovenkant van het venster zijn verdwenen.
De onderste afbeelding staat nog gewoon op z'n 'natuurlijke' hoogte, omdat die nog niet binnen de grens van 10 px vanaf de bovenkant van het browservenster staat.
Afbeelding 6
Afbeelding 6: Nu is de <p> zover naar boven gescrold, dat de <img>'s aan de bovenkant niet meer volledig zichtbaar zijn. Zonder position: sticky; zou dat al veel eerder zijn gebeurd.

Aan de onderkant werkt dit precies hetzelfde. De <img> staat op z'n 'natuurlijke' hoogte. Zodra de <p> te laag wordt gescrold, komt de <img> op 10 px van de onderkant van het venster van de browser te staan. Als er laag genoeg wordt gescrold, zal de <img> uiteindelijk ook aan de onderkant verdwijnen.

Ook in de breedte werkt dit op dezelfde manier. <main> is 800 px breed, maar nooit breder dan 95% van de breedte van het browservenster.

Afbeelding 7
Afbeelding 7: links is het venster van de browser breder dan <main> en kan de <img> dus aan de rechterkant wat uitsteken. Rechts is het venster smaller en staat de <img> meer naar links, zodat deze nog in z'n geheel is te zien.
top: 10px; right: 10px; bottom: 10px; left: 10px;

In principe de <img> altijd op 10 px vanaf alle kanten van het venster van de browser houden. Dit wordt iets hierboven bij position: sticky; veel uitgebreider beschreven.

JavaScript

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

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

Als je onderstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

Als je bovenstaande code ergens aanraakt of ‑klikt, ga je rechtstreeks naar de bijbehorende uitleg.

Dit script bestaat uit twee delen.

Het JavaScript voor iOS bestaat uit één simpele regel. Zonder deze regel worden de afbeeldingen op iOS alleen in Firefox, Opera Mini en Microsoft Edge getoond.

Het overige JavaScript is iets langer. Sommige mensen gebruiken niet de muis of aanraken om naar een link, tekstveld, en dergelijke te gaan, maar de Tab-toets. Bijvoorbeeld omdat ze moeite hebben een muis te gebruiken, maar soms is het gewoon ook veel sneller dan een muis.

Als een element met de Tab-toets wordt bezocht, heeft dat element focus. Dat wordt aangegeven door een kadertje rondom het element. Dat is belangrijk, want anders is niet duidelijk, welk element focus heeft. Bij focus wordt een link gevolgd door op Enter te drukken, in een tekstveld kan tekst worden ingevoerd, enzovoort.

In dit voorbeeld kunnen de afbeeldingen ook met behulp van de Tab-toets worden getoond.

Als iemand ergens op de pagina gemarkeerde tekst aanraakt of erover hovert, is duidelijk bij welke tekst de getoonde afbeelding hoort. Maar bij gebruik van de Tab-toets is dat onduidelijk. Daarom wordt bij .buiten:focus-within .markeer met behulp van :focus-within een kadertje getekend rondom de tekst, waarbij de getoonde afbeelding hoort.

Voor browsers die het relatief nieuwe :focus-within niet ondersteunen, wordt dat in dit script geïmiteerd.

Als dit script op meerdere pagina's wordt gebruikt, kan het ook als een extern script worden gebruikt: <script src="pad-naar-script/script.js"></script>

In dat geval moet <script> helemaal onderaan de pagina worden 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.

<script></script>

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

JavaScript nodig voor iOS

document.getElementsByTagName("body")[0].addEventListener("touchstart", function () {return null; });

document.getElementsByTagName("body"): 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("body")

Jij en ik weten dat er maar één <body> is, maar een browser is nou eenmaal niet zo slim, dus die zoekt trouwhartig naar álle <body>'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 <body> gevonden. Het volgnummer van die <body> is dus '0', het eerste item.

document.getElementsByTagName("body")[0] samen: doe iets met het element <body>.

addEventListener: voeg aan <body> een 'eventlistener' toe. Dat is een ding dat luistert, of er iets gebeurt. Zo'n gebeurtenis kan een aanraking van het scherm zijn, een liefdevolle aai van de muis, een ram op het toetsenbord, van alles.

"touchstart": in dit geval wordt naar het begin van een aanraking van het touchscreen geluisterd. Dat is nog niet zo interessant, het wordt pas echt opwindend wat er ná die eerste, inleidende aanraking gaat gebeuren... Vinden zij elkaar, of wordt het moord- en doodslag?

function () {return null; }: Helaas, ze vinden elkaar niet én het wordt geen moord- en doodslag.

function() is gewoon een soort aankondiging dat er iets gaat gebeuren. Erachter tussen de accolades staat, wat er gaat gebeuren: return null;. Doe niets. Helemaal niets. Vanavond niet, schat, ik heb hoofppijn.

Overige JavaScript (voor kadertje)

De rest van het script zorgt voor een kadertje rondom de gemarkeerde tekst die bij de getoonde afbeelding hoort.

(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 kijkt of de browser de css-pseudo-class :focus-within ondersteunt. 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 'focusWithin' en 'len'. 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'.

De bovenste regel van dit script staat buiten deze functie, die wordt dus automatisch uitgevoerd. Het hierboven genoemde risico voor een conflict bestaat daar niet, omdat in die regel geen variabelen worden gebruikt.)

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

var focusWithin = probeer(),

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 dertien 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 'focusWithin', 'picts', 'len' en 'i'. Elke variabele staat óf helemaal alleen op een eigen regel, óf aan het begin van een regel, gelijk gevolgd door een isgelijkteken.

In 'focusWithin', 'picts', 'len', en 'x'. 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 hier opgegeven. Als het om iets meer ingewikkelds gaat, of als nog niet bekend is wat moet worden opgeslagen, gebeurt het later.

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 een functie 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: 'focusWithin'. In css zou je dit schrijven als 'focus-within', 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...).

Hieronder worden de aangemaakte variabelen één voor één doorgenomen.

focusWithin = probeer(),

Het tweede deel van de eerste regel. Hier wordt de eerste variabele aangemaakt.

focusWithin: dit is de variabele, waarin iets moet worden opgeborgen.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

probeer(): dit is, wat in focusWithin moet worden opgeslagen. Het is een aanroep van een zogenaamde 'functie': een stukje bij elkaar horende code dat vaker kan worden uitgevoerd. Dat uitvoeren doe je door het aan te roepen met de naam van de functie: probeer(). De haakjes achter zo'n aanroep zijn een verplichte taalregel, anders weet de browser niet dat het om een functie gaat. (Je kunt tussen de haakjes ook informatie doorgeven aan de functie, maar dat gebeurt hier niet.)

Bij het vullen van focusWithin wordt deze functie al aangeroepen en dus uitgevoerd. De functie zelf staat een heel eind verderop, maar dat maakt niets uit. Functie probeer() kijkt, of de browser de css-pseudo-class :focus-within ondersteunt. Als dat zo is, retourneert de functie de waarde true (waar). Als :focus-within niet wordt ondersteund, wordt de waarde false (niet waar) geretourneerd.

Die waarde true of false wordt in variabele focusWithin opgeslagen en kan later worden gebruikt om te kijken, welke code moet worden toegepast: als focusWithin waar (true) is, doe dan dit. Als focusWithin niet waar (false) is, doe dan dat.

,: de regel eindigt hier niet, er moeten nog meer variabelen worden aangemaakt.

De hele declaratie: sla in variabele focusWithin het resultaat van functie probeer() op: true of false.

picts,

In deze variabele wordt voor elke <img> in de html een zogenaamd object opgeslagen: een verzameling informatie en dergelijke over elke afbeelding, zoals breedte en hoogte. Met zo'n object kun je ook allerlei dingen doen, zoals css toevoegen.

Je zou dat opslaan van objecten ook hier al kunnen doen, omdat het tamelijk eenvoudig is, maar omdat dit een uitleg is, gebeurt dat pas later.

Over wat een variabele is, is meer te vinden bij var focusWithin = probeer(),

len,

In deze variabele wordt later het aantal <img>'s opgeslagen.

Over wat een variabele is, is meer te vinden bij var var focusWithin = probeer(),

i;

i: deze variabele wordt op een aantal plaatsen als teller gebruikt. Aan het begin van een berekening wordt er 'n getal in gestopt. Het is gebruikelijk om dit soort tellers de naam 'i' te geven. Dat soort gewoontes maakt het makkelijker code van anderen te lezen. Hier wordt de variabele alleen aangemaakt, er wordt pas iets in gestopt, als er geteld moet worden.

Over wat een variabele is, is meer te vinden bij var var focusWithin = probeer(),

;: de regel waarin variabelen worden aangemaakt, eindigt hier. Aan het eind van de eerdere regels, waarin een variabele werd aangemaakt, stond steeds een komma: er moest nog een variabele worden aangemaakt. De puntkomma geeft het definitie einde van de regel aan, er hoeven geen verder variabelen te worden aangemaakt.

Tot nu toe is alleen een aantal noodzakelijke gegevens opgevraagd en opgeborgen. Vanaf nu gaat het script echt beginnen.

if (focusWithin) return;

if: dat betekent gewoon 'als': als aan de voorwaarde hierachter is voldaan. Die voorwaarde staat tussen haakjes, omdat dat nou eenmaal zo hoort. Als aan de voorwaarde wordt voldaan, wordt de code achter de voorwaarde uitgevoerd.

focusWithin: het deel tussen de haakjes. Dat is hier maar één woord, maar dat kan veel meer zijn.

Om te bepalen of aan de voorwaarde wordt voldaan, wordt gekeken naar de inhoud van variabele focusWithin. Die inhoud is eerder bepaald bij var focusWithin = probeer(),. Daar werd de uitslag van functie probeer() in focusWithin gestopt: als de browser de css-pseudo-class :focus-within ondersteunt, is de uitslag true ('waar') en bevat focusWithin de waarde true. Als de browser :focus-within niet ondersteunt, is de uitslag false ('onwaar') en bevat focusWithin de waarde false.

Oftewel: als de browser :focus-within ondersteunt, is de voorwaarde voor de if waar en wordt rest van de regel uitgevoerd.

Als de browser :focus-within niet ondersteunt, is de voorwaarde voor de if niet waar en wordt de rest van de regel niet uitgevoerd.

return;: Met het sleutelwoord return wordt de functie afgebroken: de rest van de code wordt gewoon niet uitgevoerd. De ; geeft weer het eind van de regel aan.

Deze regel kijkt dus, of de browser :focus-within ondersteunt. Als dat zo is, als aan de voorwaarde voor de if wordt voldaan, wordt return Als de browser :focus-within niet ondersteunt, wordt return niet uitgevoerd. En wordt de rest van de code dus uitgevoerd.

Deze regel staat binnen de functie die begint met (function () {. Dit is de buitenste functie (je kunt functies ook in elkaar nesten). Als deze functie wordt verlaten, is er voor het script verder niets meer te doen.

En dat is precies de bedoeling: als de browser :focus-within ondersteunt, is dit hele script overbodig.

Een if kan enorm ingewikkeld worden, zowel de voorwaarde als de eventueel uit te voeren code. Om de code voor mensen leesbaar te houden wordt een if met de bijbehorende code daarom meestal over een (groot) aantal regels uitgesplitst. Omdat deze if superkort is, is het in dit geval duidelijker alles op dezelfde regel te zetten.

picts = document.getElementsByClassName("pict");

picts: dit is de variabele, waarin iets moet worden opgeborgen. Deze variabele is al eerder bij picts, aangemaakt, nu wordt er iets in opgeborgen.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

document.getElementsByClassName("pict"): het middelste stukje getElementsByClassName 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 getElementsByClassName() uit document kan JavaScript alle elementen met 'n bepaalde class opzoeken. De class waarnaar wordt gezocht, staat tussen haakjes:

getElementsByClassName("pict")

De elementen met de gevraagde class 'pict' worden geretourneerd in de vorm van een lijst. In het voorbeeld zitten 37 <span>'s met class="pict", dus er worden 37 elementen gevonden en in de lijst gestopt.

Als je de in de html de classnaam 'pict' verandert, moet je die ook in deze regel veranderen.

;: de ; geeft weer het eind van de regel aan.

De hele regel: er worden 37 elementen in variabele picts gestopt: elke <span> met class="pict" uit de html van het voorbeeld.

Van elk van deze 37 elementen wordt in variabele picts een ongelooflijke hoeveelheid informatie gestopt, waar het script later gebruik van kan maken. Zo zit bijvoorbeeld alle css, die aan elk van die <span>'s is gegeven, ook in picts. Maar niet alleen de in de stylesheet opgegeven css, ook alle standaardwaarden, en ook alle css die van voorouders wordt geërfd. Alle eventuele nakomelingen van elke <span> en hun css, attributen, enzovoort, zitten ook in picts. Van al deze informatie in picts 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 elk van de 37 <span>'s in de vorm van een object in picts opgeslagen. Naast allerlei informatie die in elk item wordt opgeslagen, kun je daardoor ook gebruik maken van allerlei methoden (functies), die JavaScript gratis en voor niets toevoegt aan picts.

Het script gebruikt picts om bepaalde css toe te voegen als een span met class="pict" focus krijgt, en deze weer te verwijderen als de <span> de focus weer verliest. Die css zorgt voor een kadertje rondom de gemarkeerde woorden, die bij de getoonde afbeelding horen.

Je kunt 'n bepaald item uit de lijst aanspreken door het volgnummer te gebruiken. En omdat computers dol zijn op '0', is het volgnummer van het eerste item '0', en geen '1'. picts[0] is de eerste in picts opgeslagen <span>, picts[7] is de achtste in picts opgeslagen <span>.

len = picts.length;

len: bij len, is deze variabele al aangemaakt, maar er is nog niets mee gedaan. Hier gaat er iets in worden opgeborgen.

=: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat moet worden opgeslagen.

picts: bij picts = document.getElementsByClassName("pict"); is elke <span class="pict"> in de vorm van een eigen object opgeslagen in picts. In het voorbeeld zijn dit er 37, dus er zitten 37 objecten in picts.

length: variabele picts heeft zelf ook wat informatie, onder andere het aantal items dat erin is opgeslagen. Dat aantal zit in de eigenschap length. Omdat er 37 objecten – één voor elke <span> met class="pict" – in zitten, is de lengte van picts 37.

De hele regel: sla in len het aantal objecten in picts – oftewel: het aantal <span>'s met class="pict" – op. Omdat dat aantal eerder door het script is vastgesteld, wordt dit aantal automatisch aangepast, als het aantal <span>'s met class="pict" in de pagina verandert door het toevoegen of verwijderen van 'n <span> met class="pict" in de html.

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

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

Hier eerst het deel dat ervoor zorgt alle 37 <span>'s met class="pict" worden behandeld.

(: met dit haakje opent het deel dat ervoor zorgt, dat alle 37 <span>'s aan de beurt komen. Aan het eind van de regel staat de bijbehorende ), waardoor het script weet dat dit het einde van dit deel van de code is.

i = 0;: hiermee geef je in JavaScript aan dat in de voor het isgelijkteken staande variabele het resultaat van wat achter het isgelijkteken staat, moet worden opgeslagen. In dit geval is dat resultaat heel simpel: het is het getal 0.

De variabele i is al eerder aangemaakt bij i, en kan daarom hier nu gewoon worden gebruikt.

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

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

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

Elke keer als een <span> is afgehandeld, 1 optellen bij 'i'. Omdat programmeurs liederlijk lui zijn, wordt 'er 1 bij optellen' afgekort tot ++.

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

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

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

(...)

Als i 36 is, betekent i++: i wordt 36 + 1 ('37' dus). En meer dan 37 wordt het niet, vanwege redenen die gelijk hieronder staan.

i < len;: het middelste deel.

i bevat een getal. Als er nog geen enkele <span> is verwerkt, is dat getal 0, want dat is hierboven opgegeven.

Het teken < betekent: kleiner dan.

In len is iets hoger bij len = picts.length; het aantal <span>'s met class="pict" opgeslagen. In het voorbeeld is dat 37. Eigenlijk staat hier dus: i < 37;

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

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

Als niet meer aan deze voorwaarde wordt voldaan, als teller i niet meer kleiner is dan de 37 in len, stop dan met het uitvoeren van de code die tussen de {} staat. En omdat 37 even groot is als het aantal <input>'s in het menu dat moet worden bekeken, geeft dit een mogelijkheid om elke <input> één keer te verwerken. En er, als ze allemaal verwerkt zijn, mee te stoppen.

): dit haakje hoort bij de ( gelijk achter for. Tussen deze twee haakjes staat de code, die ervoor zorgt dat elk niveau in het menu aan de beurt komt.

{: het laatste teken op de regel. Hiermee geef je aan dat hierna de code volgt die uitgevoerd moet worden. Tot nu toe is alleen gezorgd dat met elk item uit picts, met elke <span> met class="pict", íéts moet gebeuren, maar nog niet wát. Dat wát volgt na deze {. Na de code die moet worden uitgevoerd staat nog een afsluitende }. Hiermee geef je aan dat het uitvoerende deel van de code hier stopt.

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

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

Aan het begin heeft i de waarde 0. Na de eerste ronde heeft i de waarde 1. Na de tweede ronde heeft i de waarde 2. Na de derde ronde heeft i de waarde 3. Na de 36e ronde heeft i de waarde 37. Waarmee i niet meer kleiner is dan de 37 in len, het aantal <spans>'s.

Omdat niet meer aan de voorwaarde i < len wordt voldaan, wordt gestopt met het uitvoeren van de code tussen de {}. En dat komt goed uit, want alle <span class="pict">'s' zijn precies allemaal één keer aan de beurt gekomen. Niet meer, niet minder. Bij allemaal is de tussen de {} staande code één keer toegepast.

(Wat mogelijk wat verwarrend is: het script begint te tellen met 0. De eerste <span> heeft dus volgnummer 0, en de 36e <span> heeft volgnummer 37. Ook al stopt de for-lus bij 36, er worden dus toch 37 <input>'s bekeken.)

De code tussen de haakjes in gewone taal: zet teller i op 0 als de for de eerste keer wordt uitgevoerd. Herhaal de for zolang teller i lager is dan de 37 in len. Verhoog teller i elke keer als de for wordt uitgevoerd met 1.

picts[i].addEventListener("focus", tekenRandje);

Deze regel zit binnen een for-lus en wordt voor elke <span> met class="pict" één keer uitgevoerd.

pics[i]: in de variabele pics zijn bij picts = document.getElementsByClassName("pict"); alle <span>'s met class="pict" opgeslagen. (Feitelijk is voor elke <span> een object opgeslagen, waarin allerlei informatie over de <span> zit, zoals de bijbehorende css.)

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 <span> die aan de beurt is.

addEventListener: er wordt een zogenaamde 'eventlistener' gekoppeld aan het voor de punt staande deel. Dat is hier picts[i], de <span> 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'.

"focus": 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: 'focus'. Er wordt geluisterd of de <span> focus krijgt.

tekenRandje: 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 tekenRandje(e) { en zorgt voor een randje rondom de gemarkeerde tekst die bij de getoonde afbeelding hoort.(Probeer op dit moment vooral niet de logica van wel of geen aanhalingstekens te begrijpen. Het makkelijkste is om dat soort dingen maar gewoon te accepteren. Nederlands heeft ook zo z'n eigenaardigheden...)

;: aan het eind van elke regel staat een puntkomma. Daarmee geef je aan dat de regel is afgelopen. In een gewoon boek zou je hier een punt gebruiken, om aan te geven dat een nieuwe zin begint.

De hele regel nog eens in gewone mensentaal: als een <span> met class="pict" focus krijgt, voer dan functie 'tekenRandje ()' uit.

picts[i].addEventListener("blur", verwijderRandje);

Deze regel zit binnen een for-lus en wordt voor elke <span> met class="pict" één keer uitgevoerd.

Deze regel werkt precies hetzelfde als die iets hierboven bij picts[i].addEventListener(focus, "tekenRandje");. De enige verschillen: hier wordt geluisterd naar 'blur': de <span> verliest de focus weer. Als dat gebeurt, wordt functie verwijderRandje(e) uitgevoerd: haal het randje rondom de gemarkeerde tekst weer weg.

function probeer() {

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 variabele focusWithin wordt aangemaakt bij var focusWithin = probeer(),

De code kijkt, of de browser de css-pseudo-class :focus-within ondersteunt. Als dat zo is, retourneert de functie de waarde true, die dan in variabele focusWithin wordt opgeslagen. Als :focus-within niet wordt ondersteund, retourneert de functie de waarde false.

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

probeer: de naam van de functie. Als het beestje geen naam heeft, kun je het ook niet aanroepen en heb je er dus niets aan. (Dit klopt niet helemaal. JavaScript kent ook equivalenten van 'hé!', 'hé, jij daar!', 'hé, jij daar in die zwarte jas', en dergelijke, maar die worden hier niet gebruikt. En het is zo al ingewikkeld genoeg.)

(): die haakjes horen nou eenmaal zo na de naam van een functie. Behalve dat het gewoon zo hoort, kun je hier ook van alles in stoppen om door te geven aan de code in het binnenste van de functie. Dat gebeurt hier niet.

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }.

try {

Deze regel is onderdeel van function probeer() {

Binnen een try-blok staat code, die mogelijk een fout kan opleveren. Er wordt geprobeerd ('try') de code uit te voeren. Als dat lukt, wordt gewoon de rest van de code uitgevoerd. Maar als een fout wordt gegenereerd, wordt de code na } catch (error) { uitgevoerd.

Een 'fout' moet je niet al te letterlijk nemen. In dit geval wordt gekeken, of de browser de css-pseudo-class :focus-within ondersteunt. Als dat niet zo is, wordt een fout gegenereerd.

document.querySelector(":focus-within");

Deze regel is onderdeel van function probeer() {

Deze regel is onderdeel van een try-blok.

Het middelste stukje querySelector is een zogenaamde 'functie'. Een functie is een stukje in de browser ingebakken code, waarmee je iets kunt doen. Deze functie is te vinden in het object document, vandaar dat document ervoor staat.

Een object is een bij elkaar horende verzameling van functies en andere code. Een van die objecten heeft de naam 'document'. Bij een object werkt een functie alleen een klein beetje anders dan een gewone functie, daarom heet een functie uit een object 'methode'.

JavaScript heeft een groot aantal ingebakken objecten, waar je gebruik van kunt maken. In document bijvoorbeeld zit heel veel informatie over de pagina, en er zitten heel veel methodes in om met die informatie te kunnen werken.

Met de methode querySelector() uit document kan JavaScript een bepaalde selector opzoeken. De selector waarnaar wordt gezocht, staat tussen haakjes:

querySelector(":focus-within")

Als :focus-within niet wordt ondersteund door de browser, wordt een fout gegenereerd. In dat geval wordt die fout ('error') iets hieronder gevangen ('catch') en wordt de code achter } catch (error) { uitgevoerd. Die code retourneert false en dat wordt dan opgeslagen in variabele focusWithin. (Want de functie probeer(), waar dit try-blok onderdeel van is, is aangeroepen bij het aanmaken van variabele focusWithin bij var focusWithin = probeer(),.)

Als geen fout wordt gegenereerd, als :focus-within door de browser wordt ondersteund, wordt de code achter } catch error { niet uitgevoerd. Er wordt dan doorgegaan met de eerste regel na het catch-blok: return true;: in focusWithin wordt nu true opgeslagen.

} catch (error) {

Deze regel is onderdeel van function probeer() {

De in het catch-blok zittende code wordt alleen uitgevoerd, als door het bijbehorende try-blok een fout is gegenereerd (als de browser de css-pseudo-class :focus-within niet ondersteunt).

}: dit is de afsluitende accolade van de code van het try-blok. De openings-accolade staat bij try {.

catch (error): catch is gewoon het sleutelwoord dat aangeeft dat hier de fout wordt afgehandeld. Tussen de haakjes wordt een zogenaamd fout-object doorgegeven, waarvan de naam 'error' is. Je mag het ook 'wilhelmina' noemen in plaats van 'error', maar 'error' is min of meer een standaardnaam. Zo houd je je code 'n beetje leesbaar. Niet alleen voor jezelf, maar ook voor anderen.

In error zit een hele serie informatie over de fout, maar die wordt hier verder niet gebruikt.

{: deze accolade geeft aan dat hier de code begint die moet worden uitgevoerd, als zich een fout heeft voorgedaan. Aan het eind van deze code staat weer een afsluitende }.

return false;

Deze regel is onderdeel van function probeer() {

Dit deel van het script wordt alleen uitgevoerd, als het try-blok een fout heeft gegenereerd (als de browser de css-pseudo-class :focus-within niet ondersteunt).

return zorgt ervoor dat de rest van functie probeer() niet meer wordt uitgevoerd. Omdat er false achter staat, wordt de waarde false geretourneerd naar de aanroepende code. Die waarde false wordt in variabele focusWithin opgeslagen. (Want de functie probeer(), waar deze regel onderdeel van is, is aangeroepen bij het aanmaken van variabele focusWithin bij var focusWithin = probeer(),.)

return true;

Deze regel is onderdeel van function probeer() {

Dit deel van het script wordt alleen uitgevoerd, als het try-blok geen fout heeft gegenereerd (als de browser de css-pseudo-class :focus-within ondersteunt).

Als het try-blok geen fout heeft gegenereerd, wordt deze regel uitgevoerd: functie probeer() retourneert de waarde true. Die waarde true wordt in variabele focusWithin opgeslagen. (Want de functie probeer(), waar deze regel onderdeel van is, is aangeroepen bij het aanmaken van variabele focusWithin bij var focusWithin = probeer(),.)

function tekenRandje(e) {

Ook deze functie is, zoals elke functie, weer een stukje bij elkaar horende code. Maar anders dan de buitenste functie wordt deze niet automatisch uitgevoerd. (Waarom de buitenste functie wel automatisch wordt uitgevoerd, is te vinden bij (function () {.)

De code in deze functie wordt alleen uitgevoerd, als de functie wordt aangeroepen. Dat aanroepen gebeurt, als een <span> met class="pict" focus krijgt. Het luisteren naar dat focus krijgen wordt geregeld bij picts[i].addEventListener("focus", tekenRandje);.

De code in deze functie regelt, wat er gebeurt, als een <span> met class="pict" focus krijgt. Als de <span> de focus krijgt, wordt een kadertje rondom de bijbehorende gemarkeerde tekst gezet. Dit gebeurt door het toevoegen van een beetje css aan de <span> in de html. Als je die toevoeging wilt bekijken moet je niet de gewone code, maar de Gegenereerde code bekijken.

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

tekenRandje: 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 (over dat aanroepen later meer). In dit geval wordt deze functie aangeroepen, als een <span> met class="pict' focus heeft gekregen. In e zit bijvoorbeeld de hoogte en breedte van de <span>. En nog 'n waanzinnige hoop andere informatie, waarvan het overgrote deel hier verder niet wordt gebruikt.

Los hiervan voegt JavaScript aan e allerlei methodes toe: functies binnen het object, waarmee je allerlei dingen kunt doen.

Heel formeel is e eigenlijk geen object, maar is e een parameter, iets dat wordt doorgegeven aan de functie, zodat het binnen die functie gebruikt kan worden. e is de naam van het object, en de inhoud van e is een object. Om het object iets te kunnen vragen, of het iets te laten doen, moet het beestje 'n naam hebben: e.

De naam e voor het object is niet verplicht, maar 'n soort afspraak, zodat code makkelijker door anderen is te begrijpen. Maar als je het object niet e, maar 'hetIsStervenskoud' wilt noemen, is daar technisch geen enkel bezwaar tegen. Het is dan wel verstandig een cursus zelfverdediging te volgen, voor het geval iemand anders ooit je code moet bekijken.

(e is een afkorting van 'event', gebeurtenis. De functie reageert op een gebeurtenis, in dit geval het krijgen van focus door de <span>. In e zit het object dat bij díé gebeurtenis hoort. Bij bijvoorbeeld een muisklik krijg je een ander object met andere informatie dan bij het indrukken van een toets.)

{: geeft het begin van de code binnen de functie aan. Aan het eind van de functie staat een bijbehorende }.

e.target.previousSibling.setAttribute("style", "outline: green dotted 3px;");

Deze regel is onderdeel van function tekenRandje(e) {

e: het bij function tekenRandje(e) { aan de code in de functie doorgegeven object, waarin onder andere allerlei informatie zit over de <span> die focus heeft.

target: dit is een onderdeel van het hierboven genoemde object e. In target zit, in de vorm van een object, informatie over de <span> met class="pict" die focus heeft. Ook dat is weer heel veel informatie: het soort element, eventuele tekst erin, nakomelingen, wat de ouder is, noem maar op.

previousSibling: het vorige element in de html. In het hierboven genoemde target zit, in de vorm van een object, de <span> met class="pict" die focus heeft. Omdat previousSibling achter target staat, gaat het om het element vóóŕ het element dat in target zit. Dat is altijd een <span> met class="markeer", waarin gemarkeerde tekst zit, want in de <html> volgt altijd een <span> met class="pict" op een <span> met class="markeer".

Omdat deze twee <span>'s altijd gelijk op elkaar volgen in de html, weet je altijd zeker dat de <span> met gemarkeerde tekst bij de <span> met focus hoort. En dus bij de getoonde afbeelding.

Ook deze <span> met class="markeer" is weer beschikbaar in de vorm van een object, waardoor veel informatie over de <span> beschikbaar is. En allerlei methoden uit het object gebruikt kunnen worden.

setAttribute(): dit is zo'n methode. Eigenlijk is dit een gewone functie, maar omdat het om een functie uit een object gaat, werkt die soms net iets anders dan een gewone functie. Daarom wordt een functie in een object een 'methode' genoemd.

Met setAttribute() kun je een attribuut aan een element in de html toevoegen. Dat kan van alles zijn: tabindex, een WAI-ARIA-code, noem maar op. Tussen de haakjes komen de naam en de waarde van het attribuut te staan.

("style", "outline: green dotted 3px;"): voor de komma staat tussen aanhalingstekens de naam van het attribuut dat wordt toegevoegd, achter de komma staat tussen aanhalingstekens de waarde van het attribuut.

In dit geval levert dat style="outline: green dotted 3px;" op.

De hele regel in normale mensentaal: zet een groene gestippelde rand rondom de gemarkeerde tekst in de <span>, die gelijk voor de <span> met focus staat.

Als je de toegevoegde css wilt bekijken moet je niet in de gewone code, maar in de Gegenereerde code kijken.

function verwijderRandje(e) {

e.target.previousSibling.removeAttribute("style");

Deze functie en de regel eronder werken precies hetzelfde als de hierboven beschreven function tekenRandje(e) {.

De enige verschillen:

* Deze functie wordt aangeroepen als de <span> met class="pict" die focus heeft die focus weer verliest. Het aanroepen van deze functie wordt geregeld bij picts[i].addEventListener("blur", verwijderRandje);

* De naam van de functie. Deze functie heet 'verwijderRandje'.

* In plaats van setAttribute wordt hier removeAttribute gebruikt met tussen de haakjes "style": het attribuut 'style' wordt hier weer verwijderd. De waarde 'outline: green dotted 3px;' is hier niet nodig, want als 'style' wordt verwijderd, wordt automatisch ook de bijbehorende waarde verwijderd.