Skip links en inhoudsopgave

Responsieve en toegankelijke tabel met (in bredere vensters) vaste kolomkoppen - uitleg

Laatst aangepast: .

Afbeelding 1: de tabel in een breder en in een smaller venster

Korte omschrijving

In bredere browservensters kan de tabel worden gescrold, terwijl de kolomkoppen blijven staan. In smallere vensters staan de gegevens niet naast elkaar in kolommen, maar onder elkaar, voorafgegaan door de naam van het gegeven.

BELANGRIJK

Deze uitleg hoort bij het voorbeeld dat in de download zit. Het voorbeeld uit de download verschilt iets van het voorbeeld hier op de site. In de download ontbreekt bijvoorbeeld de navigatie voor de site. Ook in de kopregels zit vaak wat verschil. Daarnaast kunnen er nog andere (meestal kleine) verschillen zijn.

Als je deze uitleg leest naast de broncode van het voorbeeld op de site, kan het dus bijvoorbeeld zijn dat 'n <h1> uit de css bij 'n <h2> uit de html hoort. Maar het gaat niet om hele grote, fundamentele afwijkingen.

Als je dit lastig vindt, downloadt dan de hele handel (ga terug naar het voorbeeld en kies daar voor downloaden). In de download zit 'n voorbeeld dat wel naadloos aansluit op de uitleg in de download.

Alles op deze site kan vrij worden gebruikt, met twee 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.

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

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

Opmerkingen

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

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

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

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

Achterliggend idee

De tabel moest aan een aantal eisen voldoen. Allereerst moest de tabel toegankelijk zijn voor schermlezers en dergelijke. De kolomkoppen moesten, bij scrollen van de tabel, zichtbaar blijven. De tabel moest werken op de desktop, op touchscreens, in smalle en brede browservensters, kortom: in principe overal. Dat blijkt simpeler gezegd dan gedaan.

Op internet is een grote hoeveelheid oplossingen te vinden voor een tabel met vaste kolomkoppen. Als er veel verschillende oplossingen zijn, is dat meestal een goede indicatie dat er geen oplossing is die echt altijd goed werkt. Dat bleek ook hier het geval.

Het overgrote deel van wat op internet is te vinden, is niet toegankelijk voor schermlezers en dergelijke. Daarmee vielen die gelijk af.

Dat zoveel oplossingen niet toegankelijk zijn voor schermlezers, heeft met het verleden te maken. Een (goede) schermlezer behandelt een tabel op een aparte manier. Aan het begin van de tabel wordt gemeld dat hier een tabel begint, eventueel met aantal rijen en kolommen. Bij het voorlezen van de kolomkoppen wordt gemeld dat het om koppen gaat. Bij elke cel wordt de kolomkop herhaald, en eventueel het nummer van de rij en van de kolom.

Alle schermlezers doen dit weer op een iets andere manier, maar maar ze behandelen een tabel wel allemaal op een voor gebruikers van schermlezers bruikbare manier. De manier waarop schermlezers dit doen, kan ook door de gebruiker zelf nog wat worden ingesteld.

(Met deze extra meldingen is het – voor mij – eigenlijk al onmogelijk om een tabel te begrijpen. Ik neem aan dat gebruikers van een schermlezer daar meer handigheid in hebben. Maar zonder deze extra meldingen is een tabel echt volstrekt ontoegankelijk: alle gegeven worden gewoon rij voor rij zonder enige onderbreking achter elkaar voorgelezen.)

Maar wat een schermlezer precies doet met een tabel, is verder niet zo interessant. Het gaat erom dat een tabel als een tabel wordt behandeld. De precieze afhandeling is dan aan de schermlezers en de door de gebruiker daarin aangebrachte instellingen.

In het verleden werden tabellen op zeer grote schaal gebruikt om een pagina mee op te maken. Niet voor tabulaire gegevens, maar om tekst, headers, footers, afbeeldingen, noem maar op, op de juiste plaats te krijgen. Css bestond nog niet, dus dit wat de enige manier om een pagina op te maken. Soms waren die tabellen wel tien of meer lagen diep genest. Het was geen uitzondering dat voor losse woorden aparte cellen werden gebruikt om ze op de juiste plaats te krijgen. (Trouwens een nachtmerrie om te onderhouden. Als je linksboven iets veranderde, donderde er rechtsonder iets van je scherm. Wordt er nog wel 'ns gillend wakker van.)

Omdat niet elke website wordt bijgehouden, zijn er nog miljoenen en miljoenen sites in omloop, waarin tabellen voor opmaak zijn gebruikt. Als schermlezers elke tabel stomweg als tabel zouden behandelen, zouden ook op dat soort sites rijnummer, kolomnummers, en dergelijke worden voorgelezen. Sites met dat soort tabellen zijn al slecht toegankelijk, maar dat wordt er niet beter op, als dat ook nog 'ns wordt opgeleukt met niet ter zake doende meldingen over die tabel.

Daarom proberen schermlezers in te schatten, of een tabel is gebruikt voor opmaak of voor echte tabulaire data. Alleen die laatste tabel wordt als tabel behandeld. Inmiddels kun je met aparte codes aangeven dat een tabel voor opmaak wordt gebruikt, maar dat is pas 'n aantal jaren mogelijk. Dus voor oudere sites heeft dat geen nut. Voor oudere sites blijven schermlezers aangewezen op 'raden', of het om een tabel voor opmaak of een tabel met tabulaire data gaat.

Verschillende schermlezers doen dat op verschillende manieren. Zo blijkt bij bijvoorbeeld VoiceOver onder andere het gebruik van position bij <thead> automatisch te leiden tot het niet langer herkennen van de tabel als tabel met tabulaire data.

Om de tabel te kunnen scrollen, terwijl de kolomkoppen blijven staan, zou je <tbody> display: block; kunnen geven. Helaas is dat ook voor sommige schermlezers reden de tabel niet meer goed te herkennen.

Het is dus niet zo dat alleen wijzigingen in de html schermlezers op een dwaalspoor kunnen brengen. Ook css kan veroorzaken dat een schermlezer een tabel niet meer als een echte tabel met tabulaire data herkent.

Dit ligt zelfs zo gevoelig dat een extra @media-regel nodig is. In browservensters met een maximale breedte van 759 px worden <td>'s en <tr>'s als blok-element weergegeven, en de <thead> wordt verborgen. Deze elementen worden in die smallere vensters dus niet als onderdelen van een tabel weergegeven.

Ook als dit later voor bredere vensters voor deze elementen weer wordt veranderd naar weergave als onderdeel van een tabel, blijken sommige schermlezers de tabel niet meer te herkennen als een tabel met tabulaire data.

Normaal genomen is de css voor smallere browservensters geldig voor álle vensters. Voor bredere vensters pas je die css dan later aan. In dit geval zou dat onder andere inhouden dat de <td>'s, <th>'s en <thead> weer als onderdelen van een tabel worden weergegeven. Maar omdat sommige schermlezers de tabel nu toch niet meer als een tabel met tabulaire data herkennen, werkt dat hier dus niet.

Daarom wordt de veranderde weergave van <td>, <tr> en <thead> met een extra @media-regel beperkt tot vensters die maximaal 759 px breed zijn. Bredere browservensters negeren die andere weergave nu volkomen. Met die extra @media-regel voor smallere vensters blijkt de werking van schermlezers op bredere vensters niet meer verstoord te worden.

Op zich is het goed te begrijpen dat schermlezers zo kritisch zijn vanwege alle oudere sites, waarin tabellen zijn gebruikt voor opmaak. Maar tegenwoordig kun je WAI-ARIA-codes toevoegen aan de html, waarmee je specifiek voor schermlezers kunt aangeven dat bijvoorbeeld een <th> echt een kolomkop is. Ook daarbij gaan schermlezers nogal eens de fout in. Geen van de geteste schermlezers blijkt alle WAI-ARIA-codes voor tabellen te herkennen. Met als resultaat dat alle schermlezers uiteindelijk een tabel, waaraan iets meer dan cosmetische dingen als de breedte van kolommen of lijntjes en zo is veranderd, niet meer als tabel met tabulaire data zien. En dus niet meer behandelen als tabel. Waarmee de tabel volstrekt ontoegankelijk is geworden.

Het toevoegen van WAI-ARIA-codes was dus ook geen oplossing.

Het hierboven staande verhaal was ook de reden van de ontoegankelijkheid van vrijwel alle op internet gevonden oplossingen: er was te veel veranderd in de html en/of css voor de tabel. (En wel toegankelijke oplossingen waren weer niet geschikt voor mobieltjes, of konden niet worden gescrold op iOS.)

De enige oplossing die in alle schermlezers werkt: de tabel gewoon een volstrekt normale tabel laten zijn, zonder enige wijziging in de html. En de css voor de tabel beperken tot dingen als lijntjes, lettergrootte en kolombreedte.

Een normale tabel klinkt wel leuk en aardig, maar een gewone tabel scrolt dus volledig, inclusief de kolomkoppen. Daarom is een tweede stel kolomkoppen aangebracht. De kolomkoppen die je in het voorbeeld ziet, hebben helemaal niets met de tabel te maken.

De tabel staat in div#wrapper, een <div> met een bepaalde hoogte. Hierdoor kan de tabel binnen deze <div> worden gescrold. Inclusief de kolomkoppen.

div#wrapper, met daarin de tabel, zit weer in <main>. Binnen <main> zit onder andere ook nog een div#kolomkop. In div#kolomkop zitten nogmaals de kolomkoppen, maar nu in gewone <span>'s. Deze div#kolomkop is boven de 'echte' <thead> gezet en scrolt niet mee. De kolomkoppen uit div#kolomkop zijn de koppen die je op het scherm ziet.

Een tabel (en de onderdelen daarvan) behandelt andere lettergroottes anders dan een <div> (en eventueel daarin zittende elementen). Dat geldt ook voor de kolomkoppen. Daarom is het van essentieel belang dat de <th>'s in de <thead> zoveel mogelijk hetzelfde zijn als de <span>'s in div#kolomkop. Niet alleen de tekst, maar ook dingen als lettergrootte. Bij een grotere letter zorgen de <th>'s dan voor voldoende ruimte boven de tabel, zodat de kolomkoppen uit div#kolomkop niet over de bovenste rij(en) van de tabel heen komen te staan. Hoe dit precies wordt gedaan, is te vinden bij <span>VOOR&shy;NAAM</span>... voor de html en bij #kolomkop span voor de css.

Schermlezers kunnen nu de echte <thead> gebruiken, terwijl de 'valse' kolomkoppen bij scrollen blijven staan. Omdat schermlezers niets aan die valse koppen hebben, worden die met behulp van aria-hidden verborgen.

Hiermee is de hele tabel scrollbaar gemaakt, terwijl de kolomkoppen blijven staan. Op iOS echter werkt dit niet. Als op iOS de inhoud van een <div> kan worden gescrold, gebeurt dat uiterst houterig. Om scrollen soepel te laten verlopen op iOS, is een aparte css-eigenschap nodig: -webkit-overflow-scrolling.

Daar valt nog wel mee te leven, maar dit superieure bestuurssysteem heeft nog een klein ander probleempje: als wordt ingezoomd (vergroot), kan gewoon helemaal niet meer worden gescrold binnen een <div>. Dat deel van de tabel dat niet op het scherm staat, is gewoon helemaal onbereikbaar. Zelfs bij de kleinste vergroting kan niet meer worden gescrold. Scrollen binnen een <div> op iOS levert al jaren problemen op en internet staat er vol mee, maar het is volstrekt onduidelijk of Apple dit ooit op gaat lossen, omdat Apple vrijwel nooit iets naar buiten brengt over bugs, plannen, en dergelijke.

Dit is op geen enkele manier op te lossen. Daarom is bovenaan de tabel een aankruisvakje (<input type="checkbox">) aangebracht. Als dat wordt aangevinkt, wordt de volledige tabel weergegeven. Scrollen binnen de <div> is dan niet meer nodig, omdat de hele pagina nu wordt gescrold. Waarmee ook op iOS bij zoomen de hele tabel zichtbaar is. Alleen scrollen de kolomkoppen nu ook mee, dat valt niet te voorkomen.

(Je zou het kunnen 'oplossen' door zoomen onmogelijk te maken. Dat kan door een toevoeging aan de <meta name="viewport">-tag. Dat is echter een ongelooflijk slechte 'oplossing', omdat je daarmee zoomen voor de hele pagina onmogelijk maakt. Mensen die iets slechter zien, kunnen daardoor de pagina niet meer goed gebruiken.)

Het aankruisvakje boven de tabel is ook fors aangepast. De <input> kan niet binnen de bijbehorende <label> staan, omdat anders de truc niet werkt. Helaas ziet een aankruisvakje dat niet binnen de bijbehorende <label> staat er in de verschillende browsers nogal anders uit. Daarom is het aankruisvakje onzichtbaar gemaakt en vervangen door een eigengemaakt aankruisvakje. (Meer over waarom de <input> niet binnen de <label> kan staan, is te vinden bij <input id="toon" type="checkbox" aria-hidden="true">.)

Overigens kan deze mogelijkheid om de hele tabel te zien ook op andere systemen handig zijn, dus in dit geval is van de nood min of meer een deugd gemaakt.

Tot nu toe is er een tabel die toegankelijk is en vaste kolomkoppen heeft. Met een kleine kunstgreep voor het onvolprezen iOS. En met een grotere kunstgreep, ook voor iOS, voor de vaste kolomkoppen.

Voor die vaste kolomkoppen zijn een extra <div> en zeven extra <span>'s (één voor elke kolomkop) nodig. Veel mensen vinden dat je geen extra <div>'s en dergelijke op deze manier mag toevoegen. Veel anderen, waaronder ik, zien er het bezwaar niet van. Die extra <div>'s elementen leveren vaak problemen op in schermlezers en dergelijke, maar dat wordt hier voorkomen door de extra <div> te verbergen voor schermlezers en dergelijke.

In smallere browservensters doemt nog een ander probleem op: er is stomweg geen ruimte voor zoveel kolommen naast elkaar. Voor deze tabel ligt de grens ongeveer op 760 px, dat is de minimaal benodigde breedte. (Dat is niet helemaal toevallig, want die 760 kom je op tal van plaatsen op de site tegen. Binnen deze grens vallen ook de meeste tablets, vandaar.)

Als het venster van de browser smaller is dan 760 px, wordt de <thead> (en daarmee de daarin zittende <th>'s met de kolomkoppen) verborgen. De <tr>'s en <td>'s van de tabel worden als blok-elementen weergegeven. Hierdoor komt elke <td> op een nieuwe regel te staan. Met behulp van ::before wordt bij elke <td> links het soort gegeven vermeld. Rechts staat het gegeven zelf.

De kolomkoppen hoeven nu niet meer vast te worden gezet, want er zijn geen kolomkoppen meer. Deze manier van weergeven is wel minder overzichtelijk dan een echte tabel, maar dat komt omdat zo'n schermpje nou eenmaal kleiner en daardoor minder bruikbaar voor een tabel is.

(Op internet zijn trouwens ook nog andere technieken te vinden, zoals het weglaten van minder belangrijke kolommen, of het door de bezoeker laten kiezen van de weer te geven kolommen. Het zal aan de soort tabel liggen, welke techniek het best is.)

Voor de toegankelijkheid heeft het weglaten van de kolomkoppen geen nadelige gevolgen, omdat inmiddels (vrijwel) elke schermlezer met behulp van ::before gemaakte tekst voorleest. Ook in deze smallere browservensters wordt voor elk gegeven eerst netjes het soort gegeven vermeld. (Dit geldt in ieder geval voor TalkBack op Android en VoiceOver op iOS, waarmee het grootste deel van de kleinere vensters al is gedekt. Windows Phone en Windows Mobile zijn niet getest, omdat Microsoft geen Nederlandstalige stem levert. En een Engelstalige stem Nederlands laten lezen, dat werkt echt voor geen meter.)

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.

In dit voorbeeld worden hyphens en -webkit-overflow-scrolling gebruikt.

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.

hyphens

Op dit moment moet je nog het volgende schrijven:

{-ms-hyphens: ...; -webkit-hyphens: ...; hyphens: ...;}

In de toekomst kun je volstaan met:

{hyphens: ...;}

-webkit-overflow-scrolling

Dit is een apart geval, omdat het hier niet om een eigenschap gaat die uiteindelijk in een standaard gaat komen. Het is een uitbreiding van Apple, waarmee je aan kunt geven of scrollen op een touchscreen vloeiend of schoksgewijs moet gebeuren. Andere browsers hebben dit niet nodig. Daarom is hier de vorm zonder voorvoegsel niet nodig.

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

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

Voorlopig zijn we echter nog niet van deze voorvoegsels af. Als je ze gebruikt, gebruik dan álle varianten, en eindig met de variant zonder voorvoegsel, zoals die uiteindelijk ooit gebruikt gaat worden. Als je alleen de -webkit-variant gebruikt, ben je in feite 'n onbetaalde reclamemaker voor Apple. (Dit geldt dus niet voor het hierboven genoemde -webkit-overflow-scrolling, want hiervan bestaat alleen maar een -webkit-versie.)

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.

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

Door het op de goede manier gebruiken van semantische elementen, kunnen zoekmachines, schermlezers, enz. 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>

Hierbinnen staat de belangrijkste inhoud van de pagina (in dit voorbeeld is dat alleen de tabel met toebehoren).

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 de nieuwe elementen als <main>. Dat is een al veel langer bestaande code, die schermlezers en dergelijke wel herkennen. Voor een <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 een <main> inmiddels herkennen.

WAI-ARIA-codes

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

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

aria-hidden

Met behulp van aria-hidden="true" kan een deel van de code worden verborgen voor schermlezers en dergelijke, zodat dit niet wordt voorgelezen. Op de normale weergave op het scherm heeft dit verder geen enkele invloed.

In dit voorbeeld wordt aria-hidden op twee plaatsen gebruikt.

De tabel heeft kolomkoppen, die in een gewone <thead> staan. Op het scherm staat boven die <thead> een div#kolomkoppen met 'nep' kolomkoppen. Die 'nep' koppen scrollen niet mee met de tabel. De hele <thead> met inhoud zie je dus helemaal niet. Maar voor een schermlezer maakt dat geen verschil. Ook al zie je de <thead> niet, hij wordt wel voorgelezen.

Om te voorkomen dat schermlezers twee keer de kolomkoppen voorlezen, wordt daarom div#kolomkoppen verborgen voor schermlezers:

<div id="kolomkop" aria-hidden="true">

Nu wordt deze <div>, en alles wat daarin zit, genegeerd door schermlezers.

Boven de tabel zitten een aankruisvakje (<input type="checkbox">) en de daarbij horende <label>. Als het aankruisvakje is aangevinkt. wordt de volledige tabel zichtbaar. Voor een schermlezer is dat echter niet nodig. Ook al is een deel van de tabel met behulp van css verborgen, een schermlezer leest dat deel toch gewoon voor. Dit aankruisvakje is daardoor voor schermlezers overbodig en wekt alleen maar verwarring. Met behulp van aria-hidden worden <input> en bijbehorende <label> verborgen:

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

<label for="toon" aria-hidden="true">Toon hele tabel</label>

De code aanpassen aan je eigen ontwerp

Toegankelijkheid en zoekmachines

Het eerste deel van deze tekst is voor alle voorbeelden hetzelfde. Eventueel specifiek voor dit voorbeeld geldende dingen staan verderop onder het kopje Specifiek voor dit voorbeeld.

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:

Specifiek voor dit voorbeeld

Getest in

Laatst gecontroleerd op 26 augustus 2016

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

Eventuele opmerkingen over de toegankelijkheid specifiek voor dit voorbeeld staan hierboven bij Toegankelijkheid en zoekmachines onder het kopje Specifiek voor dit voorbeeld.

Dit voorbeeld is getest op de volgende systemen:

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 resoluties groter dan 800x600 is ook in- en uitzoomen en – voor zover de browser dat kan – een kleinere en grotere letter getest. Er is ingezoomd en vergroot tot zover de browser kan, maar niet verder dan 200%.

Er is getest met behulp van muis en toetsenbord, behalve op de iPad, Android, Windows Phone en Windows 10 Mobile, waar een touchscreen is gebruikt. Op Windows 8.1 en 10 is getest met een touchscreen, met een combinatie van toetsenbord en touchpad, en met een combinatie van toetsenbord en muis.

Als dat relevant is, is op de desktop ook getest, als JavaScript uitstaat. Eventuele problemen staan hierboven bij Toegankelijkheid en zoekmachines onder het kopje Specifiek voor dit voorbeeld. (Op iOS, Android, Windows Phone 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.)

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

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

WebbIE is een browser die gericht is op mensen met een handicap. Er is getest op Windows 7.

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

TalkBack is een in Android ingebouwde schermlezer. Er is getest in combinatie met Chrome.

VoiceOver is een in iOS en OS X ingebouwde schermlezer. Er is getest in combinatie met Safari op iOS en OS X.

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

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 opmerkingen over de toegankelijkheid specifiek voor dit voorbeeld staan hierboven bij Toegankelijkheid en zoekmachines onder het kopje Specifiek voor dit voorbeeld.

Alleen op de hierboven genoemde systemen en browsers is getest. Er is dus niet getest op bijvoorbeeld 'n Blackberry. De kans is (heel erg) groot dat dit voorbeeld niet (volledig) werkt op niet-geteste systemen en apparaten. Om het wel (volledig) werkend te krijgen, zul je vaak (kleine) wijzigingen en/of (kleine) aanvullingen moeten aanbrengen, bijvoorbeeld met JavaScript.

Er is ook geen enkele garantie dat iets werkt in een andere tablet of smartphone dan hierboven genoemd, omdat fabrikanten in principe de software kunnen veranderen. Dit is anders dan op de desktop, waar browsers altijd (vrijwel) hetzelfde werken, zelfs op verschillende besturingssystemen. Iets wat in Android browser werkt, zal in de regel overal werken in die browser, maar een garantie is er niet. De enige garantie is het daadwerkelijk testen op een fysiek apparaat. En aangezien er duizenden mobiele apparaten zijn, is daar eigenlijk geen beginnen aan.

De html is gevalideerd met de validator van w3c, de css ook. Als om een of andere reden niet volledig gevalideerd kon worden, wordt dat bij Bekende problemen (en oplossingen) vermeld.

Nieuwe browsers test ik pas, als ze uit het bèta-stadium zijn, omdat er anders 'n redelijke kans is dat ik 'n bug zit te omzeilen, 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 kan ik niet beantwoorden, en het melden van fouten in niet-geteste browsers heeft ook geen enkel nut. (Melden van fouten, problemen, enz. in wel geteste browsers: graag!)

Bekende problemen (en oplossingen)

Waarop en hoe is getest, kun je gelijk hierboven vinden bij Getest in.

Als je hieronder geen oplossing vindt voor een probleem dat met dit voorbeeld te maken heeft, kun je op het forum proberen een oplossing te vinden voor je probleem. Om forumspam te voorkomen, moet je je helaas wel registreren, voordat je op het forum een probleem kunt aankaarten.

Oudere versies van Android browser en UC browser op Android in vensters minimaal 760 px breed

De lijntjes tussen de kolommen staan verkeerd en de laatste kolomkop is te smal

Omdat in smallere browservensters helemaal geen kolomkoppen en kolommen aanwezig zijn, speelt dit probleem daar niet.

Afbeelding 2: de lijntjes tussen de kolommen staan iets verkeerd

De breedte van kolommen en tabellen wordt op een aparte manier berekend door de browser, anders dan bij een <div> of zo. Browsers hebben daar (helaas) ook enige vrijheid in. Inmiddels is het redelijk hetzelfde, maar nog niet in alle browsers.

Oudere versies van Android browser en UC browser op Android maken alle kolommen iets breder dan ze zouden moeten zijn. Hierdoor is er in de laatste kolom minder ruimte, waardoor deze kolom te smal wordt. Op de afbeelding is dat duidelijk te zien: de gegevens in de laatste kolom worden op twee regels gezet, terwijl dat in alle andere browsers op één regel past.

Omdat de breedte van de kolommen in de tabel afwijkt van de breedte van de kolomkoppen in div#kolomkop, staan de lijntjes tussen de kolomkoppen niet gelijk met de lijntjes tussen de kolommen, zoals op de afbeelding is te zien. Feitelijk staan de lijntjes tussen de koppen goed, maar is de breedte van de kolommen verkeerd.

Alles werkt verder gewoon, maar het is minder mooi. Dit is trouwens vrij makkelijk te corrigeren door de breedte van de kolommen aan te passen, maar dan gaat het mis in alle andere browsers.

Oudere versies van Android browser

Bij aanvinken van het keuzevakje verandert de weergave van de tabel niet

Oudere versies van Android browser hebben problemen met de selector #toon:checked ~ #wrapper. Dat komt door het gebruik van de ~ in de selector.

Deze selector zorgt voor het volledig of gedeeltelijk weergeven van de tabel. Als input#toon wordt aan‑ of uitgevinkt, wordt de tabel volledig of gedeeltelijk weergegeven. In deze browsers zie je de verandering pas, als de browser van portret- in landschapsstand wordt gedraaid, of omgekeerd. Dat dwingt de browser namelijk om de pagina opnieuw te tekenen. Als alleen het keuzevakje wordt aangevinkt, wordt de pagina niet opnieuw getekend.

Je zou #toon:checked ~ #wrapper kunnen vervangen door #toon:checked + label + #wrapper, ware het niet dat deze minkukel met de + dezelfde problemen heeft.

Hier is verder niets aan gedaan, omdat deze browsers (gelukkig) aan het uitsterven zijn. Als je dat wilt, is het echter eenvoudig op te lossen. Zet bovenin de css de volgende regel:

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

Voeg in de css voor #wrapper, de <div> waarbinnen de tabel wordt gescrold, de volgende regel toe:

-webkit-animation: bugfix infinite 1s;

(Normaal genomen is het een bijzonder slecht idee iets alleen voor op webkit gebaseerde browsers te doen. Maar omdat dit probleem alleen speelt in de op webkit gebaseerde Android browser, hoeven andere browsers niet ook opgezadeld te worden met deze ongein.)

De combinatie van deze beide regels is volslagen onzin, maar het werkt. Wat hier staat: ga eindeloos door met het over een periode van 1 seconde veranderen van de grootte van de linker-padding van 0 naar 0. Een mens begrijpt gelijk dat dit zinloos is, een browser niet. En het resultaat is dat het aankruisvakje nu werkt.

Omdat er in werkelijkheid helemaal niets verandert, kost dit ook nauwelijks extra batterijvermogen of zo.

Dolphin op Android

De tabel kan horizontaal iets worden verschoven

Feitelijk een onbenullig iets dat hier alleen staat, omdat ik altijd álle problemen probeer te beschrijven.

De hele tabel kan horizontaal ongeveer 2 px worden verschoven. Ik neem aan dat ook dit met het berekenen van de breedte van de tabel heeft te maken. Er valt verder niets weg of zo en alles werkt, dus nauwelijks een echt probleem te noemen.

De lijntjes tussen de kolomkoppen zijn niet allemaal even breed

Sommige lijntjes tussen de kolomkoppen zijn niet 1, maar 2 px breed. Aangezien dit de enige browser is, neem ik aan dat Dolphin wat slordig is met afronden of zo. Omdat dit verder niet echt ernstig is, is hier verder niet uitgebreid naar gekeken.

iOS in vensters minimaal 760 px breed

Bij zoomen kan de tabel niet worden gescrold

Omdat scrollen van de tabel alleen kan in browservensters van minimaal 760 px breed, speelt dit probleem niet in smallere vensters.

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

De tabel staat binnen een div#wrapper met een hoogte van 400 px. Door die <div> overflow-y: auto; te geven, kan de hele tabel worden gescrold.

Helaas werkt scrollen binnen een <div> niet meer, zodra er ook maar iets wordt ingezoomd (vergroot). Het deel van de tabel dat niet op het scherm staat, is dan gewoon volledig onbereikbaar. Dit soort problemen bij scrollen binnen een <div> speelt al jaren, het internet staat er vol mee. Omdat Apple vrijwel nooit iets meedeelt over bugs of ergens op reageert, is volstrekt onduidelijk, of Appel dit ooit gaat repareren.

Het is in dit geval opgelost door boven de tabel een aankruisvakje (<input type="checkbox">) neer te zetten. Als dat wordt aangevinkt, wordt de hoogte bij div#wrapper weggehaald, waardoor de hele tabel zichtbaar wordt. Nu wordt niet meer binnen de <div> gescrold, maar scrolt gewoon de hele pagina. Misschien geen heel fraaie oplossing, maar het is beter dan een deel van de tabel te laten verdwijnen.

(Je zou het ook kunnen oplossen door zoomen onmogelijk te maken. Dat kan door een simpele toevoeging aan de <meta name="viewport">-tag. Dat is echter een ongelooflijk slechte oplossing, omdat je daarmee zoomen voor de hele pagina onmogelijk maakt. Mensen die iets slechter zien, kunnen daardoor de pagina niet meer goed gebruiken.)

Het kan trouwens zijn dat de tabel plotseling wel fantastisch scrolt, ook als wordt gezoomd. Zo'n één op de honderd keer of zoiets scrolt 't kreng opeens, gezoomd en wel, als een antilope op de Afrikaanse savanne.

Zoomen en andere lettergrootte

Bij zoomen staan de lijntjes niet meer helemaal goed

Bij in- en uitzoomen komen in sommige browsers de lijntjes rondom koppen en kolommen iets verkeerd te staan. Dat komt door afrondingsverschillen en is niet op te lossen. Overigens levert dit verder geen enkel probleem op, en mensen die zoomen zullen hieraan gewend zijn.

Bij een grotere letter staan soort gegeven en gegeven over elkaar

Afbeelding 3: tekst uit verschillende kolommen staat over elkaar heen

In browservensters met een maximale breedte van 759 px staat de soort gegeven voor het gegeven zelf. Als de letters worden vergroot tot meer dan ongeveer 130%, komen soort en langere gegevens in het midden over elkaar heen te staan, omdat ze niet meer naast elkaar passen. Op de afbeelding is dat het geval met de middelste regel: 'ACHTERNAAM' en 'met de Korte Achternaam' staan gedeeltelijk over elkaar heen.

Dit doet zich eigenlijk alleen voor, als je een smaller venster imiteert op de desktop. Op de afbeelding is in Firefox op Linux een browservenster van 320 px breed gesimuleerd, met een lettergrootte van 130%. In het echt vind je dit soort smallere vensters eigenlijk alleen op mobiele apparaten, en daar zullen mensen eerder zoomen dan de lettergrootte veranderen.

In een normaal venster op de desktop zal de tabel altijd als tabel worden weergegeven, en bij die weergave doet zich dat probleem niet voor, ook niet bij zoomen.

(Overigens kan in vrijwel alle mobiele browser alleen worden gezoomd, waardoor dit zich hoe dan ook niet voor zal doen.)

Wijzigingen

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

26 augustus 2016:

Nieuw opgenomen.

Inhoud van de download en licenties

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

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

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

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

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

positioneren-114-dl.html: de pagina met het voorbeeld.

positioneren-114.pdf: deze uitleg (aangepast aan de inhoud van de download).

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

114-css-dl:

positioneren-114-dl.css: stylesheet voor positioneren-114-dl.html.

HTML

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

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

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

<!DOCTYPE html>

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

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

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

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

<html lang="nl">

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

<meta charset="utf-8">

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

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

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

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

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

Mobiele apparaten variëren enorm in breedte. En dat is een probleem. Sites waren, in ieder geval tot voor kort, gemaakt voor desktopbrowsers. En die hebben, in vergelijking met bijvoorbeeld een smartphone, heel brede browservensters. Hoe moet je op 'n smartphone een pagina weergeven, die is gemaakt voor de breedte van een desktop? Je kunt natuurlijk wachten tot álle sites zijn omgebouwd voor smartphones, tablets, enz., maar dan moet je waarschijnlijk heel erg lang wachten.

Mobiele browsers gokken erop dat een pagina een bepaalde breedte heeft. Safari voor mobiel bijvoorbeeld gaat ervan uit dat een pagina 980 px breed is. De pagina wordt vervolgens zoveel versmald dat hij binnen het venster van het apparaat past. Op een iPhone wordt de pagina dus veel smaller dan op een iPad. Vervolgens kan de gebruiker inzoomen op het deel van de pagina dat hij of zij wil zien.

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

Nieuwe sites of pagina's kunnen echter wel rekening houden met de veel kleinere vensters van mobiele apparaten. Op deze pagina bijvoorbeeld wordt de tabel in smallere vensters anders weergegeven dan in bredere vensters.

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

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

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

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

<main>

Binnen dit element staat de belangrijkste inhoud van de pagina. Op deze pagina is dat alleen de tabel met toebehoren. Op een 'echte' pagina zal dat meestal meer zijn. In dat geval moet <main> worden gebruikt voor álle belangrijke inhoud, niet alleen voor de tabel.

In dit voorbeeld is <main> ook gebruikt om bijvoorbeeld bij de tabel horende dingen als de <h1> en de kolomkoppen uit div#kolomkop te positioneren. Als <main> daar niet voor gebruikt kan worden, omdat er meer inhoud op de pagina staat, kan de tabel met toebehoren binnen een eigen <div> worden geplaatst. Die <div> kan dan, op dezelfde manier als hier met <main> het geval is, worden gebruikt voor positioneren en dergelijke. (In feite is <main> niet meer dan een doodgewone <div> met een speciale semantische betekenis.)

<div id="kolomkop" aria-hidden="true">

Binnen deze <div> staan de kolomkoppen die niet meescrollen met de tabel. Deze kolomkoppen hebben feitelijk helemaal niets met de tabel te maken: ze worden van buiten de tabel over de 'echte' kolomkoppen uit <thead> neergezet.

Binnen <thead> zijn de gewone kolomkoppen van de tabel ook aanwezig. Er zijn dus feitelijk twee sets kolomkoppen: een met 'echte' kolomkoppen en een met 'nep' kolomkoppen. Een schermlezer zou beide voorlezen, wat hoogst verwarrend is. Daarom wordt aria-hidden="true" toegevoegd aan div#kolomkop. Nu wordt div#kolomkop (en alles wat daarin staat) genegeerd door schermlezers.

Op het scherm zie je de niet-meescrollende ' nep' kolomkoppen, terwijl schermlezers gewoon gebruik kunnen maken van de 'echte' kolomkoppen uit <thead>. Dat is belangrijk, omdat bij het voorlezen van de tabel – afhankelijk van schermlezer en instellingen – de kolomkop wordt herhaald.

<span>VOOR&shy;NAAM</span><span>ACHTER&shy;NAAM</span><span>ADRES</span><span>POST&shy;CODE</span><span>STAD</span><span>GEBO&shy;REN</span><span>LID SINDS</span>

Deze onleesbare html staat binnen div#kolomkop. De eigenlijke tekst erin is precies hetzelfde als die van de <th>'s met de kolomkoppen binnen <thead>. Deze <span>'s worden van buiten de tabel over de 'echte' kolomkoppen uit <thead> neergezet. De 'echte' kolomkoppen scrollen gewoon met de tabel mee, maar deze 'nep' kolomkoppen blijven staan. Voor het oog lijkt het er dan op, dat de kolomkoppen niet meescrollen.

Het zou makkelijker zijn als de <thead> zelf gewoon vastgezet kon worden. Dat kan ook wel, alleen wordt de tabel dan niet meer door schermlezers herkend als tabel. Hierdoor wordt alleen de inhoud van de tabel als één lange woordenbrij voorgelezen, wat volstrekt onbegrijpelijke wartaal oplevert. (Meer over het herkennen van een tabel door schermlezers is te vinden bij Achterliggend idee.)

Omdat de <thead> zelf niet kan worden vastgezet, is een imitatie van de kolomkoppen gemaakt, die over de 'echte kolomkoppen' in de <th>'s wordt neergezet. Dat imitatie is, wat in deze <span>'s zit.

De breedte en hoogte van kolommen, cellen, en dergelijke wordt op een aparte manier berekend, die afwijkt van wat bij <div>'s, <span>'s en dergelijke gebeurt. Daarom moeten deze 'nep' kolomkoppen proberen op dezelfde manier te werken als de echte kolomkoppen uit de <th>'s. Om die reden moeten html en css voor de <th>'s en voor deze <span>'s grotendeels precies hetzelfde zijn. Hier wordt de html beschreven, de css wordt uitgebreid beschreven bij #kolomkop span.

<th>'s kunnen in de html gewoon allemaal op een nieuwe regel worden gezet. Maar alle <span>'s moeten op één regel staan, wat veel onoverzichtelijker is. Een <span> is een inline-element, net zoals gewone tekst. Als een inline-element op een nieuwe regel wordt gezet, wordt automatisch een spatie toegevoegd tussen de <span>'s. Waarmee de <span>'s samen 6 spaties breder zouden worden dan de <th>'s . Door alle <span>'s achter elkaar te zetten, wordt die spatie voorkomen en wordt de opticien blij, want het levert volstrekt onleesbare code op.

Er zijn twee manieren om de breedte van een tabel te bepalen: afhankelijk van de inhoud van de cellen (table-lay-out: auto;, de standaardinstelling), of door het zelf opgeven van een breedte aan kolommen en tabel (table-layout: fixed;). Hier is de tweede methode gebruikt, omdat het anders onmogelijk is de <span>'s even breed te houden als de kolommen van de tabel. Waardoor de koppen uit de <span>'s en de lijntjes tussen de <span>'s niet meer goed boven de kolommen zouden staan.

Afbeelding 4: zonder afbreekteken vallen de kopjes gedeeltelijk weg bij een grotere letter

Maar een woord als 'ACHTERNAAM' wordt nu al snel te breed voor de <span>, als de lettergrootte wordt verhoogd. Hierdoor zou het laatste deel van 'ACHTERNAAM' wegvallen achter de <span> met 'ADRES'. Op de afbeelding hiernaast zijn de letters tot 200% vergroot, waardoor alle iets langere woorden gedeeltelijk onder hun rechterbuur verdwijnen.

De onderste afbeelding laat het resultaat zien, als in langere woorden tussen de lettergrepen een koppelteken &shy; wordt geplaatst: ACHTER&shy;NAAM. Als 'ACHTERNAAM' niet meer binnen de <span> past, wordt afgebroken tussen 'ACHTER' en 'NAAM', waarbij &shy; automatisch een koppelteken neerzet. Zolang 'ACHTERNAAM' gewoon past, zie je geen koppelteken.

Bij de andere iets langere woorden werkt dit op precies dezelfde manier.

(Je zou ook met hyphens automatisch afbreken kunnen aanzetten voor deze <span>'s. Maar dit werkt niet in alle browsers, en omdat het hier om slechts enkele woorden gaat, is gekozen voor het handmatig aanbrengen van &shy;. hyphens wordt wel gebruikt binnen sommige kolommen, omdat je anders bij bijvoorbeeld elke voornaam op de juiste plaatsen &shy; zou moeten neerzetten.)

<th scope="col">VOOR&shy;NAAM</th>

<th scope="col">ACHTER&shy;NAAM</th> <th scope="col">ADRES</th> <th scope="col">POST&shy;CODE</th> <th scope="col">STAD</th> <th scope="col">GEBO&shy;REN</th> <th scope="col">LID SINDS</th>

Binnen de <th> staat de kolomkop. In smallere browservensters wordt deze verborgen en vervangen door het soort gegeven aan het begin van elke regel te zetten. In bredere vensters zijn de <th>'s wel aanwezig, omdat ze door schermlezers worden gebruikt. Maar ook daar zie je ze niet, omdat ze worden verborgen onder 'nep' kolomkoppen. Dat geeft de mogelijkheid om de 'nep' kolomkoppen te laten staan, terwijl de tabel wordt gescrold.

Omdat de 'nep' kolomkoppen over de 'echte' koppen uit de <th>'s worden neergezet, moeten beide er precies hetzelfde uitzien. Hier is meer over te vinden bij <span>VOOR&shy;NAAM</span>... iets hierboven, waar de html voor de 'nep' kolomkoppen wordt beschreven.

Om te zorgen dat de 'nep' kolomkoppen goed afbreken, wordt gebruik gemaakt van &shy; tussen de lettergrepen. Omdat de koppen in de <th>'s zich hetzelfde moeten gedragen als de 'nep' koppen, moet hier op dezelfde plaatsen &shy; worden gebruikt als bij de 'nep' koppen.

Het attribuut scope="col" laat schermlezers weten dat de kop in de <th> bij een kolom hoort, zodat die dit kunnen gebruiken bij het voorlezen van de tabel.

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

<label for="toon" aria-hidden="true">Toon hele tabel</label>

Op iOS kan de tabel niet worden gescrold, zodra wordt ingezoomd (vergroot). Zodra je zoomt, hoe weinig dat ook is, kan de tabel niet meer worden gescrold. Hierdoor is alleen het deel van de tabel dat bij het zoomen al zichtbaar was bereikbaar. De rest van de tabel is gewoon verdwenen. Dit gebeurt in álle browsers op iOS, omdat Apple andere browsers voor de weergave verplicht gebruik te maken van het kennelijk – in de ogen van Apple – superieure Safari. Waardoor álle browsers dus zijn gezegend met dit probleem.

Scrollen binnen <div> levert al jaren problemen op iOS op. Of het ooit wordt opgelost, is onduidelijk. Het probleem speelt al vele jaren en internet staat vol met klachten en bugrapporten, maar Apple reageert daar vrijwel nooit op.

Je zou zoomen onmogelijk kunnen maken met een kleine toevoeging aan de <meta viewport>-tag, maar dat geldt dan gelijk voor de hele pagina en is rampzalig voor mensen die slechter kunnen zien.

Daarom is boven de tabel een aankruisvakje (<input type= checkbox">) aangebracht. Als dat wordt aangevinkt, wordt de hele tabel zichtbaar. Dan kan ook op iOS worden gezoomd, zonder dat het grootste deel van de tabel verdwijnt.

De tabel staat in een div#wrapper die een hoogte van 400 px heeft en overflow-y: auto;. Hierdoor wordt scrollen van de tabel mogelijk. Als je het aankruisvakje aanvinkt, wordt de hoogte bij div#wrapper verwijderd, waardoor de hele tabel zichtbaar wordt.

Voor het verwijderen van de hoogte wordt in de css de selector #toon:checked ~ #wrapper gebruikt. Hierin is #toon het aankruisvakje.

In deze selector komt een ~ voor, de 'general sibling combinator' (een Nederlandse vertaling is er niet). Deze ~ werkt alleen, als input#toon en div#wrapper dezelfde ouder hebben. Die gemeenschappelijke ouder is hier <main>. Dit betekent echter dat input#toon niet binnen de bij de <input> horende <label> kan worden gezet, omdat de ouder van de <input> dan niet meer <main>, maar <label> is.

De <label> wordt met behulp van het for-attribuut aan input#toon gekoppeld, waardoor de werking precies hetzelfde is, als wanneer <input> binnen <label> zou staan. Dat geldt echter niet voor het uiterlijk. Als <input> binnen <label> staat, plaatst elke browser het aankruisvakje van de <input> netjes naast de tekst in de <label>. Maar als <input> buiten <label> staat, maken ze er een zootje van. Bij de een staat het aankruisvakje veel te hoog, bij de ander veel te laag. Als je het dan bij de ene goed zet, staat het bij de ander nog veel verkeerder.

Daarom wordt het echte aankruisvakje van de <input> verborgen en vervangen door een eigengemaakt aankruisvakje, dat bij #toon + label::before wordt gemaakt met behulp van een met ::before gemaakt pseudo-element. Omdat dit pseudo-element bij <label> hoort, is dit wel overal op de juiste hoogte neer te zetten.

Schermlezers hebben geen boodschap aan deze hele constructie, die lezen gewoon hoe dan ook de hele tabel voor, of die nu zichtbaar is of niet. (Dat geldt niet als iets wordt verborgen met behulp van display: none; of visibility: hidden;, maar dat is hier niet het geval.) Voor schermlezers is deze constructie daarom overbodig en alleen maar verwarrend.

Daarom is aan <label> en <input> de WAI-ARIA-code aria-hidden="true" toegevoegd. Nu worden <label> en <input> door schermlezers volledig genegeerd.

CSS

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

Omdat deze site nou eenmaal (voornamelijk) op css is gericht, wordt hieronder álle css besproken.

Technisch gezien is er geen enkel bezwaar om de css in 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 stylesheetss, 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 stylesheetss. De uitgebreide versie waarin je dingen uitprobeert, verandert, enz., 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. Als je op internet zoekt naar 'css' en 'compress' of 'comprimeren', vind je tal van sites, waar je dat automatisch kunt laten doen.

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

css voor alle vensters

/* positioneren-114-dl.css */

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

body

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

background: #ff9;

Achtergrondkleurtje.

color: black;

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

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

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

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

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

margin: 0; padding: 0;

Slim om te doen vanwege verschillen tussen browsers.

main

Alle <main>'s. Dat is er maar eentje. De belangrijkste inhoud van de pagina staat hierin. In dit voorbeeld is dat de tabel met toebehoren.

display: block;

Oudere browsers kennen <main> niet. Onbekende elementen worden standaard als inline-element weergegeven. Daarom wordt hier expliciet gezegd dat dit een blok-element is.

margin-bottom: 20px;

Kleine marge aan de onderkant, omdat anders niet goed duidelijk is dat het einde van de tabel is bereikt.

h1

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

background: #eee;

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

font-size: 1.3em;

Lettergrootte iets kleiner maken dan de standaardgrootte bij een <h1>, want die is wel héél enthousiast.

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;

Van zichzelf is een <h1> vet. Dat wordt hier veranderd naar normaal.

text-align: center;

Tekst horizontaal centreren.

margin: 10px auto 0;

Omdat voor links geen waarde is ingevuld, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 10px auto 0 auto in de volgorde boven – rechts – onder – links. Boven een kleine marge voor wat afstand tot de bovenkant van het browservenster.

Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor staat de <h1> altijd horizontaal gecentreerd, ongeacht de breedte van zijn ouder.

Deze manier van horizontaal centreren van een blok-element werkt alleen, als het te centreren element een breedte heeft. Bij h1, #wrapper wordt voor smallere browservensters een breedte van 400 px aan de <h1> gegeven, dus dat is geregeld.

De ouder van de <h1> is <main>, een blok-element. Een blok-element wordt normaal genomen even breed als z'n ouder. Dat is het blok-element <body>, die normaal genomen ook weer even breed wordt als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk staat de <h1> hierdoor altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht hoe breed dit is.

(In vensters breder dan 760 px wordt <main> bij main 756 px breed gemaakt en horizontaal gecentreerd binnen het browservenster. Omdat de <h1> bij h1 in die bredere vensters even breed wordt gemaakt als z'n ouder <main>, wordt daardoor in bredere vensters ook de <h1> gecentreerd.)

border: black solid 1px;

Zwart randje.

padding: 5px 0;

Omdat voor onder en links geen waarde is opgegeven, krijgen die automatisch dezelfde waarde als boven en rechts. Hier staat dus eigenlijk 5px 0 5px 0 in de volgorde boven – rechts – onder – links. Boven en onder kleine afstand tussen de tekst in en de rand rondom de <h1>.

#wrapper

Het element met id="wrapper". Binnen deze <div> staat de tabel. Door deze <div> een hoogte te geven en overflow-y: auto;, kan de tabel binnen deze <div> worden gescrold.

background: white;

Witte achtergrond. Dit is de achtergrond die je ziet in de witte regels binnen de tabel.

Er is nog een andere reden voor deze witte achtergrond. Een aantal scrollbalken laat de kleur van de achtergrond door de scrollbalk heen komen. Dat zou hier de gele achtergrond van de pagina zijn. De scrollbalk hoort bij div#wrapper, dus als je die <div> een achtergrondkleur geeft, wordt die achtergrondkleur ook bij de scrollbalk gebruikt.

color: black;

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

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

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

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

margin: 0 auto;

Omdat voor links geen waarde is ingevuld, krijgt links automatisch dezelfde waarde als rechts. Hier staat dus eigenlijk 0 auto 0 auto in de volgorde boven – rechts – onder – links. Boven en onder geen marge.

Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor staat de #wrapper, en daarmee ook de daarin zittende tabel, altijd horizontaal gecentreerd, ongeacht de breedte van zijn ouder.

Deze manier van horizontaal centreren van een blok-element werkt alleen, als het te centreren element een breedte heeft. Bij h1, #wrapper wordt voor smallere browservensters een breedte van 400 px aan #wrapper gegeven, dus dat is geregeld.

De ouder van de div#wrapper is <main>, een blok-element. Een blok-element wordt normaal genomen even breed als z'n ouder. Dat is het blok-element <body>, die normaal genomen ook weer even breed wordt als z'n ouder <html>. Omdat <html> het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk staat de div#wrapper hierdoor altijd horizontaal gecentreerd binnen het venster van de browser, ongeacht hoe breed dit is. En daarmee ook de in #wrapper zittende tabel.

(In vensters breder dan 760 px wordt <main> bij main 756 px breed gemaakt en horizontaal gecentreerd binnen het browservenster. Omdat div#wrapper bij #wrapper in die bredere vensters even breed wordt gemaakt als z'n ouder <main>, wordt daardoor in bredere vensters ook de <h1> gecentreerd.)

border: black solid 1px;

Randje rondom. Omdat de tabel de hele div#wrapper vult, lijkt dit randje rondom de tabel te staan.

border-top: none;

De boven div#wrapper zittende <h1> heeft al een border aan de onderkant. Als deze <div> een border aan de bovenkant krijgt, komen die twee borders tegen elkaar aan te staan en worden ze samen 2 px breed.

In bredere browservensters veroorzaakt deze border ook problemen, omdat daar de <span>'s met de kolomkoppen een border aan de onderkant hebben.

Omdat deze border aan de bovenkant dus kennelijk alleen maar ellende kan voortbrengen, wordt hij gewoon helemaal verwijderd.

table

Alle <table>'s. Dat is er hier maar eentje.

border-spacing: 0;

Geen ruimte vrijlaten voor borders tussen de cellen.

width: 100%;

Een breedte in procenten is altijd ten opzichte van de ouder van het element. Dat is hier div#wrapper. Bij h1, #wrapper krijgt #wrapper een breedte van 400 px voor smallere browservensters. Voor bredere vensters wordt #wrapper bij #wrapper even breed gemaakt als <main>, wat daar 756 px is.

De breedte van een tabel wordt op een aparte manier berekend, en niet elke browser doet dat op precies dezelfde manier. Als aan de tabel een breedte in px wordt opgegeven, levert dat verschillen tussen browsers op. Wat wel in elke browser hetzelfde werkt: de tabel even breed maken als z'n ouder. Dat is wat hier gebeurt. Aan die ouder kan dan een breedte worden gegeven, en daar luistert de tabel dan wel braaf naar.

td

Alle <td>'s. Alle tabel-cellen.

font-size: 0.8em;

Iets kleinere letter, zodat er wat meer in de tabel past. Omdat de letter wat klein is, is het extra belangrijk dat zoomen en een grotere lettergrootte goed werken, zodat mensen die iets slechter zien, alles toch kunnen lezen.

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.

css voor vensters maximaal 759 px breed

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

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

De css die binnen deze media query staat, geldt alleen voor vensters die maximaal 759 px breed zijn. In deze bredere venster worden de kolomkoppen van de tabel verborgen. De cellen komen elk op een nieuwe regel te staan, met daarvoor het soort gegeven.

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

screen: deze regel geldt alleen voor schermweergave.

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

(max-width: 759px): het venster mag maximaal 759 px breed zijn. Is het venster breder, dan wordt de css die binnen deze media-regel staat genegeerd.

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

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

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

Verderop volgt nog een media query voor browservensters die minimaal 760 px breed zijn. Normaal genomen zou de media query hierboven voor vensters maximaal 759 px breed niet nodig zijn. Je geeft gewoon css op die voor alle browservensters werkt, en voor vensters minimaal 760 px breed pas je dat dan later aan.

In dit geval werkt dat echter niet. Als je <td>, <tr>, <thead> en dergelijke meer aanpast dan alleen wat randjes, lettergrootte, en dergelijke, herkennen schermlezers de tabel niet meer als een tabel met tabulaire data. Waardoor de tabel in bredere vensters volstrekt ontoegankelijk wordt. Als je bijvoorbeeld een <td> hier weergeeft als blok-element en die dan later voor bredere vensters weer in een table-cell verandert, blijven sommige schermlezers de tabel niet meer als tabel zien. (Meer hierover is te lezen bij Achterliggend idee.)

Daarom worden de aanpassingen voor smallere vensters in een eigen media query gezet. Het blijkt dat de tabel dan in bredere vensters door schermlezers wel wordt herkend als een tabel met tabulaire data.

h1, #wrapper

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

h1 {background: #eee; color: black; font-size: 1.3em; font-weight: normal; text-align: center; margin: 10px auto 0; border: black solid 1px; padding: 5px 0;}

#wrapper {background: white; color: black; margin: 0 auto; border: black solid 1px; border-top: none;}

Alle <h1>'s en het element met id="wrapper". Er is maar één <h1>: de belangrijkste kop. Binnen div#wrapper staat de tabel.

width: 400px;

Breedte.

Eerder is bij h1 en #wrapper met margin gezorgd dat deze elementen altijd horizontaal gecentreerd staan binnen het venster van de browser. Die manier van centreren van een blok-element werkt alleen, als het te centreren blok-element een breedte heeft.

max-width: 99%;

Een breedte in procenten is altijd ten opzichte van de ouder van het element. Dat is bij <h1> en div#wrapper beide <main>, een blok-element. Een blok-element wordt normaal genomen automatisch even breed als z'n ouder. Dat is hier <body>, ook weer een blok-element, dat dus ook weer even breed wordt als z'n ouder <html>. Ook <html> is een blok-element. Omdat dit het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk krijgen de <h1> en div#wrapper dus maximaal 99% van de breedte van het venster.

Hier gelijk boven is een breedte van 400 px opgegeven. Dat is te breed voor browservensters die smaller dan 400 px zijn: een deel van de tabel zou dan buiten het venster komen te staan, of de pagina wordt bij openen verkleind weergegeven. Door de breedte te beperken tot maximaal 99% van de breedte van het venster, wordt dit voorkomen.

Waarom geen 100%? Rondom de <h1> en #wrapper staat een border van 1 px breed. Als de <h1> en #wrapper 100% breed worden, valt die border net buiten het venster in browservensters smaller dan 400 px. Die ene procent verschil zorgt voor precies genoeg ruimte om de border te kunnen zien. Dat ziet er net iets mooier en minder gedrongen uit.

#kolomkop, #toon, label, thead

De elementen met id="kolomkop", id="toon" alle <label>'s en alle <thead>'s.

div#kolomkop is de <div> met de 'nep' kolomkoppen, die niet meescrollen met de tabel. input#toon is de <input type="checkbox">, waarmee de hele tabel kan worden getoond.

Er is maar één <label> in dit voorbeeld: de <label> bij input#toon.

Er is maar één <thead> in dit voorbeeld. Daarbinnen zitten de <th>'s met de 'echte' kolomkoppen van de tabel.

display: none;

In smallere browservensters worden de kolomkoppen helemaal niet gebruikt, daarom worden ze verborgen. De tabel is altijd in z'n geheel zichtbaar, dus het aankruisvakje en bijbehorend <label> zijn overbodig.

Al deze elementen worden hier verborgen. Omdat dat met display: none; gebeurt, worden ze ook verborgen voor schermlezers.

(#kolomkop, #toon en de <label> waren al met aria-hidden verborgen voor schermlezers, maar ze moeten ook verborgen worden voor 'gewone' browsers. Voor schermlezers is dat voor deze drie elementen wat dubbelop, maar dat maakt niets uit).

In deze smallere vensters wordt bij td::before met behulp van een pseudo-element de soort gegeven voor het gegeven gezet. Dat vervangt de kolomkoppen uit <thead>. Daarmee zijn de kolomkoppen overbodig geworden. Veel schermlezers zouden, als de kolomkoppen niet worden verborgen, de kolomkoppen én de voor elk gegeven staande soort bij elk gegeven voorlezen. Dat is dubbelop en wordt hier voorkomen.

tr

Alle <tr>'s. Alle rijen in de tabel.

display: block;

Een rij uit een tabel wordt standaard met display: table-row; weergegeven. Dat levert een aantal aparte eigenschappen op, die normaal genomen heel nuttig zijn. Maar niet in smalle browservensters, want daar is te weinig ruimte voor een hele serie kolommen naast elkaar.

Door de <tr> als blok-element weer te geven, wordt het min of meer een gewone <div>. Hierdoor kan bijvoorbeeld aan elke <tr> een border aan de onderkant worden gegeven.

border-bottom: black solid 2px;

Zwarte rand aan de onderkant. Omdat deze zwarte rand onder elke <tr> staat, geeft dit een duidelijke afscheiding tussen de verschillende rijen in de tabel: aan het einde van elke persoon staat een duidelijke zwarte streep.

td

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

td {font-size: 0.8em;}

Alle <td>'s. Alle tabel-cellen.

display: block;

Een cel uit een tabel wordt standaard met display: table-cell; weergegeven. Dat levert een aantal aparte eigenschappen op, die normaal genomen heel nuttig zijn. Maar niet in smalle browservensters, want daar is te weinig ruimte voor een hele serie kolommen naast elkaar.

Door de <td> als blok-element weer te geven, wordt het min of meer een gewone <div>. Hierdoor komt elke <td> op een nieuwe regel te staan. Bovendien kan een aantal eigenschappen worden gebruikt, die bij een gewone <td> niet gebruikt kunnen worden.

De <td>'s worden nu, omdat ze een soort blok-element zijn geworden, automatisch even breed als hun ouder. Die ouder is een <tr>, die iets hierboven ook in een blok-element is veranderd en dus ook weer even breed wordt als z'n ouder <table>. De <td>'s vullen hierdoor elk de volle breedte van de tabel, wat voldoende ruimte oplevert om links van het gegeven uit de <td> het soort gegeven neer te kunnen zetten.

text-align: right;

Tekst rechts uitlijnen. Hierdoor komt er links voldoende ruimte om het soort gegeven neer te kunnen zetten.

border-bottom: #ddd solid 1px;

Lichtgrijs lijntje onder elke <td>.

padding: 6px;

Wat ruimte tussen de tekst in en de buitenkant van de <td>.

position: relative;

Hier gelijk onder wordt met behulp van ::before een pseudo-element bij elke <td> gemaakt, waarin het soort gegeven staat. Dit wordt links van het gegeven zelf neergezet. Om dit pseudo-element te kunnen positioneren ten opzichte van de <td>, moet de <td> zelf een positie hebben. Omdat verder niets voor top en dergelijke wordt opgegeven, heeft dit verder geen enkele invloed op de <td> zelf.

td::before

Met behulp van ::before wordt bij elke <td> een pseudo-element gemaakt, waarin het soort gegeven komt te staan. In deze smallere vensters zijn de kolomkoppen verborgen, maar dit heeft dezelfde functie als bij een echte tabel de kolomkoppen. Schermlezers lezen dit ook gewoon voor, waardoor de tabel ook toegankelijk blijft voor gebruikers van schermlezers.

Voor elk soort gegeven moet een andere tekst worden geplaatst. Voor de cellen uit de eerste kolom is dat bijvoorbeeld 'voornaam:'. Dat wordt iets hieronder voor elke aparte soort geregeld. Maar de meeste eigenschappen zijn voor alle pseudo-elementen hetzelfde, dus die kunnen hier in één keer voor alle pseudo-elementen worden opgegeven.

font-variant: small-caps;

Als kleine hoofdletters weergeven.

Stel je hier niet te veel van voor. Normaal genomen zijn dit helemaal geen échte kleine hoofdletters, want die moeten speciaal worden ontworpen. In sommige fonts zijn die ingebouwd, maar niet elke browser kan daarmee omgaan.

Meestal zijn dit gewone hoofdletters, die verkleind worden weergegeven. Wat dus heel iets anders is dan een speciaal ontworpen font. Elke rechtgeaarde typograaf gruwt hiervan.

position: absolute;

Om het pseudo-element op de juiste plaats neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Dat is hier de <td>, waar het pseudo-element bij hoort.

top: 6px;

Op deze hoogte staat de tekst uit het pseudo-element even hoog als de tekst uit de <td>.

left: 6px;

6 px vanaf links neerzetten.

td:nth-of-type(1)::before

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

td::before {font-variant: small-caps; position: absolute; top: 6px; left: 6px;}

td: gewoon een <td>.

:nth-of-type(1): het zoveelste element van een bepaalde soort, in dit geval een <td>.

Bij de telling doen alleen de <td>'s met dezelfde ouder mee. Binnen elke <tr> zitten zeven <td>'s. Deze selector spreekt alleen de eerste <td> binnen elk van die <tr>'s aan.

::before: met behulp hiervan wordt een pseudo-element gemaakt, waarbinnen de soort gegeven komt te staan.

De meeste opmaak voor de pseudo-elementen is al eerder opgegeven. Hier hoeft alleen nog maar de tekst voor het pseudo-element opgegeven te worden.

content: "voornaam:";

De inhoud van het pseudo-element. Voor elke eerste <td> binnen elke <tr> komt 'voornaam:' te staan. Dit vervangt de kolomkop 'VOORNAAM', die in bredere browservensters boven de kolom met voornamen staat.

td:nth-of-type(2)::before

{content: "achternaam:";} td:nth-of-type(3)::before {content: "adres:";} td:nth-of-type(4)::before {content: "postcode:";} td:nth-of-type(5)::before {content: "stad:";} td:nth-of-type(6)::before {content: "geboren:";} td:nth-of-type(7)::before {content: "lid sinds:";}

Dit is de inhoud van de pseudo-elementen bij elke tweede tot en met zevende <td> binnen elke <tr>. Het werkt precies hetzelfde als gelijk hierboven bij td:nth-of-type(1)::before. Er zijn twee verschillen: het volgnummer van de <td> en de tekst binnen het pseudo-element.

css voor vensters minimaal 760 px breed

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

De opbouw van deze regel staat beschreven bij @media screen and (max-width: 759px). Er is één verschil: het browservenster moet minimaal 760 px: breed zijn (min-width: 760px). In vensters van minimaal deze breedte wordt de tabel als een echte tabel weergegeven. De tabel kan worden gescrold, terwijl de kolomkoppen blijven staan.

main

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

main {display: block; margin-bottom: 20px;}

Alle <main>'s. Dat is er maar eentje. De belangrijkste inhoud van de pagina staat hierin. In dit voorbeeld is dat de tabel met toebehoren.

In deze bredere browservensters worden kolomkoppen getoond. De kolomkoppen die zichtbaar zijn, zijn niet de 'echte' kolomkoppen uit de <th>'s, maar een soort imitatie daarvan die over de echte kolomkoppen heen wordt gezet. Daardoor kunnen die 'nep' koppen blijven staan, terwijl de tabel zelf wordt gescrold. (Als de 'echte' kolomkoppen worden vastgezet, is de tabel volstrekt ontoegankelijk voor schermlezers. Meer daarover is te lezen bij Achterliggend idee.)

Omdat verschillende onderdelen ten opzichte van elkaar op de juiste plaats moeten worden neergezet, wordt hier <main> gebruikt als gemeenschappelijke voorouder. Door verschillende elementen te positioneren ten opzichte van <main>, worden ze indirect ook ten opzichte van elkaar gepositioneerd.

In dit voorbeeld staat binnen <main> alleen de tabel met toebehoren. Op een 'echte' pagina staat mogelijk meer belangrijke inhoud en kan <main> hier mogelijk niet voor worden gebruikt. In dat geval kan <main> door een gewone <div> worden vervangen. In feite is <main> ook niet meer dan een gewone <div>, maar dan een <div> met een speciale Semantische betekenis.

width: 756px;

Breedte.

margin: 0 auto 20px;

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

Boven geen marge. Onder een marge van 20 px voor wat afstand tot de onderkant van het browservensters. Anders is niet goed duidelijk dat het eind van de tabel is bereikt.

Links en rechts auto, wat hier hetzelfde betekent als evenveel. Hierdoor staat <main>, en daarmee alles binnen <main>, altijd horizontaal gecentreerd binnen de ouder van <main>, het blok-element <body>. Een blok-element wordt normaal genomen even breed als z'n ouder. De ouder van <body> is <html>. Omdat dit het buitenste element is, wordt dit normaal genomen even breed als het venster van de browser. Uiteindelijk staat <main> horizontaal gecentreerd binnen het venster, ongeacht hoe breed dit venster is.

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

position: relative;

Om nakomelingen van <main> te kunnen positioneren ten opzichte van <main>, moet <main> zelf een positie hebben. Omdat verder niets wordt opgegeven voor top en dergelijke, heeft dit verder geen enkele invloed op <main> zelf.

main::after

Afbeelding 5: scrollbalk op touchscreen en desktop

In bredere browservensters kan de tabel worden gescrold. In browsers op de desktop is daardoor in div#wrapper een scrollbalk aanwezig. Het zou mooi zijn, als links van die scrollbalk een lijntje zou komen te staan. Dat is feitelijk vooral nuttig op touchscreens, omdat daar meestal een scrollbalk ontbreekt. Op touchscreens zit dan een lege ruimte op de plaats van de scrollbalk. Op die lege ruimte loopt de gestreepte achtergrond van de tabel gewoon door. Door naast die lege ruimte een lijntje te zetten, ziet het er gelijk 'n stuk beter uit.

Op de afbeelding is links van de stippellijn een touchscreen te zien, rechts een desktopbrowser met scrollbalk. Bij beide maakt het lijntje het gelijk 'n stuk netter.

Je zou denken dat zo'n lijntje vrij simpel is te plaatsen. Dat is echter niet zo. Browsers denken niet allemaal hetzelfde over de breedte van een tabel, dus naast de tabel kan het lijntje niet. Bovendien moet het lijntje doorlopen naast de kolomkop met 'lid sinds'. Hierdoor werkte ook een truc met iets als een inwendige box-shadow bij div#wrapper niet, want #wrapper loopt niet zover door.

Uiteindelijk bleek de oplossing een pseudo-element bij <main>, waarin een border wordt gezet. Dat lijntje dat je ziet, is de border van het pseudo-element.

content: "";

Hoewel er geen tekst of zoiets in het pseudo-element staat, is dit toch nodig. Dat er geen tekst in staat, geef je aan door niets tussen de aanhalingstekens te zetten.

width: 13px;

Het pseudo-element wordt iets hieronder tegen de rechterkant van <main> neergezet. De border komt aan de linkerkant van het pseudo-element. Met deze breedte komt de border dus 13 px links van de rechterkant van <main> te staan. Dat lijkt een goede breedte. (De breedte van scrollbalk in div#wrapper is niet in elke browser exact hetzelfde, dus ook dit was weer een kwestie van even uitproberen.)

height: 100%;

Een hoogte in procenten wordt normaal genomen gemeten ten opzichte van de ouder van het element. Hier is dat <main>, waar dit pseudo-element bij hoort. Met deze hoogte loopt het lijntje van de onderkant tot de bovenkant van <main>.

De tabel loop ook tot de onderkant van <main>, aan de onderkant staat het dus goed. Aan de bovenkant van <main> staat echter de <h1> met 'Ledenlijst'. Het lijntje komt ook daaroverheen te staan. Dat wordt opgelost door later bij h1 aan de <h1> een hogere z-index te geven.

border-left: black solid 1px;

Zwarte border links. Dit is het enige dat zichtbaar is van dit pseudo-element.

position: absolute;

Om het pseudo-element op de juiste plaats te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Dat is hier <main>, waar dit pseudo-element bij hoort.

Een pseudo-element is van zichzelf een inline-element. Hierdoor zijn eigenschappen als breedte niet te gebruiken. Door het pseudo-element absoluut te positioneren verandert het in 'n soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.

right: 0;

Helemaal rechts in <main> zetten.

bottom: 0;

Helemaal onderin <main> zetten. In combinatie met de hierboven gegeven hoogte van 100% vult het pseudo-element nu de hele hoogte van <main>.

z-index: 10;

De bovenkant van het lijntje verdwijnt net achter div#kolomkop, waarin de 'nep' kolomkoppen staan. Een hogere z-index verhelpt dat.

Een z-index werkt alleen in sommige omstandigheden. Eén van die omstandigheden is een absolute positie. Die heeft het pseudo-element iets hierboven gekregen, dus dat is geregeld.

h1

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

h1 {background: #eee; color: black; font-size: 1.3em; font-weight: normal; text-align: center; margin: 10px auto 0; border: black solid 1px; padding: 5px 0;}

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

width: 100%;

Een breedte in procenten is altijd ten opzichte van de ouder van het element. Dat is hier <main>. De <h1> wordt dus altijd even breed als <main>. Bij main heeft <main> een breedte van 756 px gekregen is ook geregeld dat <main> altijd horizontaal gecentreerd in het browservenster staat. Omdat de <h1> binnen <main> staat en even breed is als <main>, is hiermee ook gelijk de breedte en het horizontaal centreren van de <h1> geregeld.

position: relative;

Nodig om hier gelijk onder een z-index aan de <h1> te kunnen geven. Omdat geen top en dergelijke worden opgegeven, heeft dit verder geen invloed op de positie van de <h1>.

z-index: 20;

Hogere z-index.

Bij main::after is een pseudo-element gemaakt, dat voor een lijntje links van de scrollbalk (op de meeste touchscreens links van de lege ruimte) zorgt. Dat lijntje loopt echter door over de <h1>. Door de <h1> een hogere z-index te geven, staat de <h1> weer boven het pseudo-element en daarmee boven het lijntje.

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

#kolomkop

Het element met id="kolomkop". Binnen deze <div> zitten de <span>'s met de 'nep' kolomkoppen. Dit zijn de kolomkoppen die bij scrollen van de tabel niet meescrollen. Deze 'nep' kolomkoppen worden over de 'echte' kolomkoppen uit de <th>'s neergezet. (De 'echte' kolomkoppen uit de <th>'s kunnen niet worden vastgezet, omdat dat problemen oplevert in schermlezers. Hierover is meer te lezen bij Achterliggend idee.)

width: 741px;

Normaal genomen wordt een blok-element zoals een <div> automatisch even breed als z'n ouder. Iets hieronder wordt deze <div> echter absoluut gepositioneerd, en dan is dat niet meer zo. Een absoluut gepositioneerde <div> wordt niet breder dan nodig is om de inhoud van de <div> weer te kunnen geven.

Maar ook als de <div> wel even breed zou worden als ouder <main>, zou dat in dit geval niet goed uitkomen. <main> heeft bij main een breedte van 756 px gekregen. Maar aan de rechterkant van <main> staat een scrollbalk of, op een touchscreen, een lege ruimte. div#kolomkop moet daar niet overheen komen te staan. Daarom wordt aan de <div> een breedte van 741 px gegeven.

Bij main::after is aan de rechterkant van <main> een pseudo-element met een breedte van 13 px en een border links van 1 px neergezet. Met een breedte van 741 px (en een border links van 1 px) loopt div#kolomkop precies door tot de border van het met ::after gemaakte pseudo-element. (En samen zijn ze precies even breed als <main>: 1 + 741 + 1 + 13 = 756 px.)

border-left: black solid 1px;

Links een zwart randje. Dit sluit precies aan op de bij h1 en #wrapper opgegeven rand om de <h1> en div#wrapper.

position: relative;

Om de nakomelingen van div#wrapper te kunnen positioneren ten opzichte van de <div>, moet de <div> zelf een positie hebben. Omdat verder niets bij top en dergelijke wordt opgegeven, heeft dit verder geen invloed op de <div> zelf.

z-index: 10; Afbeelding 6: aankruisvakje om hele tabel te tonen

Boven de tabel staat een aankruisvakje (<input type="checkbox">), waarmee desgewenst de hele tabel kan worden weergegeven. Dat aankruisvakje is niet goed neer te zetten in de diverse browsers. Als het in de ene browser goed staat, staat het in de andere veel te laag. Daarom wordt het verborgen en vervangen door een zelfgemaakt aankruisvakje (dat is op de afbeelding het bovenste aankruisvakje).

Bij #toon wordt de <input> absoluut gepositioneerd, zodat deze geen invloed heeft op de rest van de lay-out. Maar hiermee is het aankruisvakje van de <input> nog niet onzichtbaar. Toevallig staat het aankruisvakje van de <input> op dezelfde hoogte als div#kolomkop. Op de afbeelding staat het aankruisvakje van de <input> links van 'VOORNAAM' schaamteloos de orde te verstoren.

Verbergen met display: none; is geen goed idee, want dan wordt de <input> onbereikbaar voor mensen die de Tab-toets gebruiken om links, tekstvelden, aankruisvakjes, en dergelijke te bezoeken. Maar door aan div#kolomkop een hogere z-index te geven, komt deze boven de <input> te staan, waarmee de <input> onzichtbaar is.

(Doordat div#kolomkop de <input> afdekt, is deze ook niet meer gevoelig voor aanraken of aanklikken. Alleen het zelfgemaakte aankruisvakje en de bijbehorende <label> reageren daarop. Precies de bedoeling.)

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

#kolomkop span

De <span>'s binnen het element met id="kolomkop".

Binnen deze <span>'s zitten de 'nep' kolomkoppen, die bij scrollen van de tabel niet meescrollen. Deze 'nep' kolomkoppen worden over de 'echte' kolomkoppen uit de <th>'s neergezet. (De 'echte' kolomkoppen uit de <th>'s kunnen niet worden vastgezet, omdat dat problemen oplevert in schermlezers. Hierover is meer te lezen bij Achterliggend idee.)

Er zijn zeven <span>'s: voor elke kolomkop eentje. Veel eigenschappen zijn voor alle <span>'s hetzelfde, die kunnen hier in één keer worden opgegeven.

div#kolomkop wordt van buiten de tabel bovenop de 'echte' kolomkoppen uit de <th>'s gezet. Dit levert een probleem op: als de lettergrootte wordt verhoogd, waardoor de koppen niet meer op één regel passen, komen de 'nep' kolomkoppen uit de <span>'s over de bovenste rij(en) uit de tabel heen te staan.

Bij 'echte' kolomkoppen, zoals die in een <th> staan, speelt dit probleem niet. Als daarbij de lettergrootte wordt verhoogd en de koppen niet meer op één regel passen,, wordt de rest van de tabel gewoon naar beneden verplaatst, als dat nodig is. Maar omdat de 'nep' kolomkoppen niets met de tabel te maken hebben, werkt dat mechanisme hier niet.

Dat is de reden dat er toch <th>'s met 'echte' kolomkoppen aanwezig zijn. Als je de 'echte' kolomkoppen precies even groot maakt als de 'nep' kolomkoppen uit de <span>'s, zorgen de 'echte' kolomkoppen voor het verplaatsen van de tabel, als dat nodig is. Dat ze worden afgedekt door de 'nep' kolomkoppen uit de <span>'s, maakt daarbij niets uit.

Als je nu de lettergrootte verhoogt, zie je dat bij de 'nep' kolomkoppen. De daaronder verborgen 'echte' kolomkoppen worden evenveel vergroot, en die zorgen voor het verplaatsen van de tabel, als dat nodig is. Omdat lettergrootte, breedte, en dergelijke precies hetzelfde zijn bij 'echte' en 'nep' koppen, passen ze op hetzelfde moment niet meer op één regel. (Hiervoor moet ook de html deels hetzelfde zijn. Over de html is meer te vinden bij <span>VOOR&shy;NAAM</span>..., hier wordt alleen de css beschreven.)

Afbeelding 7: kolomkopjes staan bij grotere letter over kolom heen

Dit is ook de reden dat eigenschappen als lettergrootte, border, en dergelijke hier tot de essentiële css worden gerekend: ze moeten precies hetzelfde zijn als bij de koppen in de <th>'s, anders heb je kans dat een 'nep' kolomkop als 'ACHTERNAAM' niet meer op één regel past dan de 'echte' kolomkop 'ACHTERNAAM' uit de <th>. Als de <span>'s en de <th>'s en de daarin zittende tekst precies hetzelfde zijn, zullen ze op hetzelfde moment afbreken.

Op de afbeelding hierboven hebben de 'echte' kolomkoppen uit de <th>'s een kleinere lettergrootte gekregen dan de 'nep' kolomkoppen uit de <span>'s. De letters zijn vergroot, waardoor de 'nep' kolomkoppen in de <span>'s worden afgebroken, omdat ze te breed zijn geworden. Maar de 'echte' kolomkoppen in de <th>'s zijn nog niet afgebroken, want die passen nog op één regel.

De tabel wordt daardoor niet naar beneden verplaatst, want de <th>'s zijn nog niet hoger geworden. De <span>'s zijn wel hoger geworden, maar die hebben geen invloed op de tabel. Hierdoor komen de 'nep' kolomkoppen uit de <span>'s boven de eerste rij uit de tabel te staan. Waardoor de eerste persoon uit de tabel bij een grotere lettergrootte plotsklaps grotendeels spoorloos is.

Als de lettergrootte in de <th>'s (en andere eigenschappen) echter hetzelfde zijn als die in de <span>'s, zullen de <th>'s op hetzelfde moment meer ruimte nodig hebben als de <span>'s, en dus op het juiste moment de tabel omlaag verplaatsen.

Afbeelding 8: nepkoppen en echte koppen zichtbaar gemaakt

Op de afbeelding hiernaast zijn de letters weer fors vergroot. De grijze achtergrond bij de <span>'s is weggehaald, zodat de daaronder zittende 'echte' kolomkoppen uit de <th>'s zichtbaar worden. Omdat die echter voor het grootste deel precies onder de 'nep' koppen uit de <span>'s zitten, zou je ze echter nog steeds niet zien. Daarom is rondom de koppen uit de <th>'s een rood randje gezet.

De zwarte letters op de afbeelding zijn de 'nep' koppen uit de <span>'s. De roodomrande letters zijn de 'echte' koppen uit de <th>'s. De zwarte letters zijn wat je op het scherm ziet, de normaal genomen onzichtbare roodomrande letters zorgen voor het omlaag zetten van de tabel, als dat nodig is.

Je kunt op de afbeelding ook duidelijk zien dat de tekst in de <span>'s zich anders gedraagt dan de tekst in de <th>'s. 'ADRES' en 'STAD' passen op één regel. De zwarte tekst uit de <span>'s blijft gewoon bovenin de <span> staan. De roodomrande tekst echter staat in een <th>, en tekst in een <th> wordt normaal genomen automatisch verticaal gecentreerd. Zodra één <th> hoger wordt, worden alle <th>'s in dezelfde <tr> hoger. En komen 'ADRES' en 'STAD' uit de <th>'s lager te staan, want die staan nu verticaal gecentreerd in een hogere <th>. Als je aan de css voor de <th>'s text-align: top; toe zou voegen, staan ook de 'echte' en 'nep' 'ADRES' en 'STAD' precies over elkaar heen. (Dat is trouwens niet nodig, want het maakt niets uit als die korte woorden op verschillende hoogte staan.)

Ook de breedte is van belang. In een smal element zullen woorden eerder afbreken dan in een breed element. Daarom moet ook de breedte van de <span>'s en de <th>'s precies hetzelfde zijn. Omdat ook borders daarbij een rol spelen, zijn ook die precies hetzelfde. Dat is veiliger dan bij de <th>'s de border weglaten en de breedte iets groter maken, omdat je dan kans loopt op afrondingsverschillen bij browsers, waardoor 'n kolom mogelijk net 'n px breder of smaller wordt. En 'n lijntje dan net 'n px verkeerd komt te staan, of 'n 'nep' kolomkop net iets eerder of later wordt afgebroken dan 'n 'echte' kop.

De <thead> met de <th>'s staat boven de echte tabel met de rijen gegevens. Voor die <thead> met inhoud wordt automatisch voldoende ruimte bovenaan de tabel vrijgemaakt. Dat geldt echter niet voor de <span>'s met de 'nep' kolomkoppen, want die hebben niets met de tabel te maken.

Daarom moet ook de hoogte van de <th>'s hetzelfde zijn als de hoogte van de <span>', en krijgen de <th>'s dezelfde regelhoogte en padding als de <span>'s.

background: #ddd;

Grijze achtergrond.

color: black;

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

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

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

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

width: 126px;

Drie van de zeven <span>'s krijgen deze breedte. Voor de andere vier wordt de breedte verderop aangepast.

font-size: 0.75em;

Kleinere letter.

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

line-height: 18px;

Omdat aan de <span>'s geen hoogte is gegeven, is dit ook gelijk de hoogte van de <span>. Tekst wordt normaal genomen automatisch halverwege de regelhoogte gezet. Vandaar dat een regelhoogte hier handiger is dan een gewone hoogte: nu staat de tekst verticaal gecentreerd binnen de <span>'s.

text-align: center;

Tekst horizontaal centreren.

Omdat dit geen invloed heeft op de hoogte of breedte van de <span>'s, kan dit de juiste plaatsing van <span>'s en tabel niet verstoren. Daarom wordt dit niet tot de essentiële css gerekend.

border: black solid;

Zwarte rand. Gelijk hieronder wordt de breedte opgegeven.

border-width: 0 1px 1px 0;

Boven en links geen border, rechts en onder een border van 1 1px. Kleur en stijl zijn gelijk hierboven al opgegeven.

De borders links en rechts hebben invloed op de breedte van de <span>'s. Daarom moeten de borders rechts en links bij <span>'s en <th>'s hetzelfde zijn. En dat is ook de reden dat de borders rechts en links in dit geval tot de essentiële css wordt gerekend.

De border van 1 px aan de onderkant heeft geen echte invloed op de positie van <span>'s of tabel, daarvoor is de border te smal. Maar zevers van muggen en liefhebbers van vleselijke omgang met mieren mogen wat mij betreft gerust ook die border tot de essentiële css rekenen.

padding: 4px 0;

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

Omdat de padding invloed heeft op de hoogte, moet die bij de 'nep' kolomkoppen uit de <span>'s hetzelfde zijn als bij de 'echte' kolomkoppen in de <th>'s. Als je bij de <th>'s de padding weg zou laten, worden die 8 px lager dan de <span>'s. Waardoor de tabel 8 px hoger zou komen te staan, want de <span>'s hebben geen invloed op de hoogte van de tabel. Daarom wordt de padding hier tot de essentiële css gerekend.

position: absolute;

Om de <span>'s op de juiste plaats neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Dat is hier div#kolomkop, de ouder van de <span>'s.

Bij de <th>'s wordt de plaats van de <th> bepaald door de breedte van de <th>'s. Dat werkt niet bij de <span>'s. Daarom worden deze met behulp van een absolute positie op dezelfde plaats gezet als de <th>'s. (De breedte van de <th>'s bepaalt ook de breedte van de kolommen, waardoor ook de lijntjes tussen de 'nep' koppen goed boven de lijntjes tussen de kolommen komen te staan.)

Een <span> is van zichzelf een inline-element. Daardoor zijn eigenschappen als breedte niet te gebruiken. Door de <span>'s absoluut te positioneren, veranderen ze in 'n soort blok-element, waardoor dit soort eigenschappen wel is te gebruiken.

#kolomkop span:nth-of-type(2)

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

#kolomkop span {background: #ddd; color: black; width: 126px; font-size: 0.75em; line-height: 18px; text-align: center; border: black solid; border-width: 0 1px 1px 0; padding: 4px 0; position: absolute;}

#kolomkop: het element met id="kolomkop". De <div> waar de <span>'s met de 'nep' kolomkoppen in zitten.

span: de <span> binnen #kolomkop.

:nth-of-type(2): het zoveelste element van een bepaalde soort, in dit geval een <span>. Bij de telling doen alleen de <span>'s met dezelfde ouder mee. Binnen div#kolomkop zitten zeven <span>'s.

Deze selector spreekt alleen de tweede <span> binnen #kolomkop aan. Binnen deze <span> staat 'ACHTERNAAM'.

right: 487px;

Op 487 px van de rechterkant van div#kolomkop zetten.

Meestal wordt een element vanaf links neergezet, als dat kan. Waarom? Daarom. Er is geen echte reden voor. (Dat kan trouwens totaal anders liggen in talen die van rechts naar links worden geschreven.) In dit geval echter verslikt Firefox op Android zich echter als een dronken zeeman in een Febo-frikandel, als left wordt gebruikt: de <span>'s komen verkeerd te staan. Neerzetten vanaf rechts werkt wel. Weer 'ns 'n aardige illustratie waarom testen in zoveel mogelijk browsers handig is. Elke browser heeft nou eenmaal z'n bugs en eigenaardigheden.

Dit gaat niet om een heel grote bug, maar als het zo simpel is op te lossen als hier, is er natuurlijk niets tegen om dat te doen.

#kolomkop span:nth-of-type(3)

{right: 360px;} #kolomkop span:nth-of-type(4) { width: 72px; right: 287px;} #kolomkop span:nth-of-type(5) {width: 124px; right: 162px;} #kolomkop span:nth-of-type(6) {width: 82px; right: 79px;} #kolomkop span:nth-of-type(7) {width: 79px; right: -1px;}

Precies hetzelfde verhaal als hierboven bij #kolomkop span;nth-of-type(2), maar nu voor de derde tot en met zevende <span>. De afstand tot rechts is anders, zodat ze netjes naast elkaar komen te staan. Bij de vierde, vijfde, zesde en zevende <span> wordt ook de breedte aangepast, omdat die boven kolommen met een andere breedte staan.

De zevende (en laatste) <span> komt helemaal rechts te staan. Althans: 1 px meer naar rechts dan je zou denken. De rechterborder van deze <span> komt dan precies onder het bij main::after opgegeven verticale lijntje te staan, waardoor je daar geen dubbele lijn ziet.

#toon

Het element met id="toon". Dit is de <input type="checkbox">, waarmee gekozen kan worden om de tabel volledig weer te geven.

position: absolute;

Deze <input> kan niet binnen de bijbehorende <label> worden gezet. (De reden daarvan is te vinden bij <input id="toon" type="checkbox" aria-hidden="true">.)

Daardoor staat het aankruisvakje van deze <input> in sommige browsers veel hoger of lager dan de in de <label> zittende tekst. Om dit te corrigeren, wordt niet het echte aankruisvakje gebruikt, maar een eigengemaakt.

De <input> kan niet worden verborgen met display: none;, want dan is hij niet meer bereikbaar voor bezoekers, die de Tab-toets (moeten of willen) gebruiken om links, tekstvelden, <input>, en dergelijke af te gaan.

De simpelste manier om te zorgen dat de <input> de rest van de lay-out niet verstoort, is een absolute positie. De rest van de lay-out negeert de <input> nu gewoon.

Daarmee is de <input> nog niet onzichtbaar: hij staat boven div#kolomkop. Door #kolomkop bij #kolomkop een hogere z-index te geven, wordt de <input> echt onzichtbaar.

#toon + label

De <label> die in de html direct op het element met id="toon" volgt.

#toon is een <input type="checkbox">, waarmee de tabel volledig kan worden weergegeven. label is de bij die <input> horende <label>.

background: white;

Witte achtergrond.

border: black solid 1px;

Zwart randje.

border-radius: 7px;

Ronde hoeken.

padding: 3px 5px 2px 24px;

Aan alle kanten wat ruimte tussen tekst in en rand van de <label>.

Links is een veel grotere ruimte. Hierdoor komt links van de tekst in <label> voldoende ruimte om het bij #toon + label::before gemaakte aankruisvakje neer te zetten.

position: absolute;

Om de <label> op de juiste plaats neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Dat is hier <main>.

top: 5px;

Op deze hoogte staat de <label> verticaal in het midden van de <h1> met 'Ledenlijst'.

left: 4px;

De <label> staat over de <h1> met 'Ledenlijst'. Iets links van de buitenkant van die <h1> neerzetten.

z-index: 20;

De <label> staat op dezelfde hoogte als de <h1> met 'Ledenlijst. De <h1> heeft bij h1 een z-index van 20 gekregen. Daarom moet de <label> ook een z-index van minstens 20 krijgen. Anders staat de <h1> over de <label> heen, waardoor de <label> niet zichtbaar is.

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

#toon + label::before

Met behulp van ::before wordt, bij de <label> die in de html direct op het element met id="toon" volgt, een pseudo-element gemaakt. Dit pseudo-element wordt gebruikt om een eigengemaakt aankruisvakje weer te geven.

Deze <label> hoort bij <input id="toon" type="checkbox">. Als de <input> binnen de bijbehorende <label> staat, zorgt de browser ervoor dat het aankruisvakje van de <input> goed staat ten opzichte van de tekst in de <label>. In dit geval kan de <input> niet binnen de bijbehorende <label> staan, omdat verderop de selector #toon:checked ~ #wrapper wordt gebruikt. Het element voor de ~ in die selector moet dezelfde ouder hebben als het element na de ~. input#toon en div#wrapper hebben beide als ouder <main>. Als input#toon echter binnen de <label> wordt gezet, is <label> de ouder van input#toon en werkt de selector niet meer.

Omdat de <input> niet binnen de bijbehorende <label> staat, zet de ene browser het bij de <input> horende aankruisvakje te hoog, de andere browser zet het te laag. Corrigeren kan niet, want als het bij de een goed staat, staat het daardoor bij de ander nu véél te hoog of te laag. Daarom wordt dit aankruisvakje verborgen.

Met behulp van ::before wordt bij de <label> een pseudo-element gemaakt, waarin een eigengemaakt aankruisvakje wordt neergezet. Omdat dit pseudo-element wel bij de <label> hoort, is dit wel goed neer te zetten binnen elke browser.

content: "";

Hoewel er geen tekst of zoiets in het pseudo-element staat, is dit toch nodig. Dat er geen tekst in staat, geef je aan door niets tussen de aanhalingstekens te zetten.

background: #eee;

Achtergrond lichtgrijs.

width: 10px; height: 10px;

Breedte en hoogte.

overflow: hidden;

Als de <input> is aangevinkt, wordt binnen dit zelfgemaakte aankruisvakje een 'X' gezet. Die 'X' is in de verschillende browsers niet altijd precies even groot, waardoor in sommige browsers delen van de 'X' buiten het vakje komen te staan. Hiermee worden deze uitstekende delen verborgen.

border: black solid 1px;

Zwart randje.

border-radius: 2px;

Beetje afgeronde hoeken. Het zijn kleine hoekjes, maar dit maakt het aankruisvakje toch net iets vriendelijker.

position: absolute;

Om het pseudo-element op de juiste plaats neer te kunnen zetten. Er wordt gepositioneerd ten opzichte van de eerste voorouder die zelf een positie heeft. Dat is hier de <label>, waarbij dit pseudo-element hoort.

Een pseudo-element is van zichzelf een inline-element. Daardoor zijn eigenschappen als breedte en hoogte niet te gebruiken. Door het absoluut te positioneren verandert het in 'n soort blok-element, waardoor ook dit soort eigenschappen is te gebruiken.

bottom: 6px;

Op deze hoogte staat het aankruisvakje goed ten opzichte van de tekst in de <label>. Als het vakje met behulp van top wordt neergezet, levert dit verschillende hoogtes op in de diverse browsers. Vreemd genoeg werkt bottom wel overal goed.

left: 7px;

Op deze breedte staat het vakje netjes binnen de <label>.

#toon:focus + label::before

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

#toon + label::before {content: ""; background: #eee; width: 10px; height: 10px; overflow: hidden; border: black solid 1px; border-radius: 2px; position: absolute; bottom: 6px; left: 7px;}

#toon:focus: als het element met id="toon" focus heeft.

+: het na de + staande element <label> moet in de html direct volgen op het voor de + staande element #toon. Het is de <label> die bij het aankruisvakje om de hele tabel te tonen hoort.

label::before: het bij <label> met behulp van ::before gemaakte pseudo-element. Met behulp van dit pseudo-element wordt het standaard-aankruisvakje van de <input> vervangen door een eigengemaakt aankruisvakje.

In normale mensentaal: als de <input> met het aankruisvakje om de hele tabel te tonen focus heeft, doe dan iets met het bij de daarop volgende <label> gemaakte pseudo-element, het aankruisvakje.

Sommige mensen gebruiken de Tab-toets om links, tekstvelden, aankruisvakjes, en dergelijke langs te lopen. Omdat ze geen muis kunnen gebruiken, of ook wel omdat dit vaak veel sneller werkt dan de muis. Als een element wordt bereikt, heeft dat element 'focus'. Een link bijvoorbeeld die focus heeft, wordt gevolgd als op op Enter wordt gedrukt. In een tekstvak dat focus heeft, kan tekst worden ingevoegd. Als een <input type="checkbox"> focus heeft, kan deze worden aan‑ of uitgevinkt met de spatiebalk.

Voor toetsenbordgebruikers is het belangrijk te weten, welk element focus heeft. Anders weten ze niet, welk element ze met een toets aansturen. Daarom geeft elke browser dit aan door rondom het element een kadertje te zetten. Dat is ook zo bij het aankruisvakje dat standaard bij een <input type="checkbox"> wordt getoond.

Maar dat standaard-aankruisvakje is hier vervangen door een eigengemaakt aankruisvakje. En dat krijgt geen kadertje, als de bijbehorende <input> focus heeft. Want de browser weet helemaal niet dat dat aankruisvakje bij een <input> hoort. Hierdoor weet een toetsenbordgebruiker niet dat de <input> focus heeft en kan worden bediend met de spatiebalk. Daarom wordt hier voor een eigengemaakt kadertje gezorgd, als de <input> focus heeft.

In sommige browsers op touchscreens verschijnt dit kadertje ook, als het aankruisvakje wordt aangeraakt. Maar dat heeft verder geen enkel effect, dus maakt dat weinig uit.

outline: cyan solid 2px;

Lichtblauw kadertje. Er wordt een outline gebruikt en geen border, omdat de border al in gebruik is genomen bij #toon + label::before.

(Een andere reden om een outline te gebruiken en geen border: een outline neemt geen ruimte in. Bij gebruik van een border van 2 px breed, verschuift het aankruisvakje 2 px naar beneden en naar rechts, als de border verschijnt en weer verdwijnt. Bij een outline blijft het pseudo-element gewoon staan waar het staat.)

#wrapper

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

#wrapper {background: white; color: black; margin: 0 auto; border: black solid 1px; border-top: none;}

Het element met id="wrapper". Binnen deze <div> staat de tabel. Door deze <div> in bredere browservensters een hoogte en overflow-y: auto; te geven, kan de tabel binnen deze <div> worden gescrold.

width: 100%;

Een breedte in procenten is altijd ten opzichte van de ouder van het element. Dat is hier <main>. Allerlei onderdelen van deze pagina zijn gepositioneerd ten opzichte van <main>, en daardoor indirect ook ten opzichte van elkaar. Door #wrapper even breed te maken als <main>, wordt ook #wrapper aan <main> gekoppeld, waardoor dit positioneren makkelijker gaat.

height: 400px;

Hoogte.

overflow-x: hidden;

Op de desktop zie je een scrollbalk als een element te hoog is voor z'n ouder, zoals hier bij de in #wrapper zittende tabel het geval is. Op een touchscreen zie je die scrollbalk niet. Dat levert een aantal problemen op. Een onderdeel van de oplossing is de padding-right: 20;, die bij th:last-of-type aan de laatste kolom wordt gegeven.

Dit levert echter op de desktop een horizontale scrollbalk op, omdat de tabel nu te breed is voor #wrapper. Op iOS kan de hele tabel nu een heel eind naar links en rechts worden bewogen, en op Android kan dat enkele px. Geen probleem, maar het is wel wat vreemd. En die horizontale scrollbalk is gewoon foeilelijk, en bovendien volledig overbodig.

Door de overflow in horizontale richting met behulp van deze regel gewoon af te kappen, verdwijnen al deze problemen.

overflow-y: auto;

Normaal genomen is de inhoud van een element altijd zichtbaar, ook als die inhoud niet binnen het element past. Normaal genomen is dat ook, wat je wilt. Daarom is de standaardinstelling van overflow (en overflow-y) visible. Mogelijk wordt de lay-out wat verstoord, maar er verdwijnt in ieder geval geen tekst of zo.

Met de waarde auto wordt dit veranderd. Als de inhoud van het element, hier de tabel, binnen het element past, gebeurt er niets. Als de inhoud te groot is voor het element, verschijnt een scrollbalk. Omdat dit bij overflow-y staat, gebeurt dit alleen in verticale richting.

Op een touchscreen verschijnt geen scrollbalk. Daar is een met lijntjes afgezette lege ruimte op de plaats, waar op de desktop de scrollbalk staat.

-webkit-overflow-scrolling: touch;

Het veelgeprezen besturingssysteem iOS van Appel heeft een klein probleempje, dat al jaren bestaat. Of het ooit wordt opgelost, is onbekend, want Apple reageert nauwelijks of niet op het ontelbare aantal klachten en bugrapporten over dit probleem (en aanverwante problemen).

Als je op iOS scrolt binnen een <div>, gaat dat ongelooflijk houterig. Het is feitelijk onbruikbaar, zo slecht werkt het. Door deze eigenschap wordt het scrollen net zo vloeiend als elders op iOS. Alleen heeft deze eigenschap nogal wat bijwerkingen. Voor zover ik heb kunnen nagaan, waren er geen bijwerkingen in dit voorbeeld. Maar internet staat vol met verhalen over verdwijnende tekst en wat al niet, als deze eigenschap wordt gebruikt. Ik weet niet helemaal zeker, of er echt geen bijwerkingen zijn, want je komt op internet echt de meest bizarre problemen tegen bij gebruik van deze eigenschap. En omdat Apple zwijgt als het graf, is het ook niet mogelijk echt goed op problemen te controleren. Je kunt bij het testen van iets niet álle potentiële bizarre bijwerkingen in álle mogelijke situaties verzinnen.

#toon:checked ~ #wrapper

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

#wrapper {background: white; color: black; margin: 0 auto; border: black solid 1px; border-top: none;}

#wrapper {width: 100%; height: 400px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch;}

#toon:checked: als het element met id="toon" is aangevinkt. Dit is de <input type="checkbox">, waarmee de hele tabel zichtbaar kan worden gemaakt.

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

De enige voorwaarde is verder dat het voor en het na de ~ staande element dezelfde ouder hebben. Dat is hier het geval: #toon voor de ~ en #wrapper na de ~ hebben beide als ouder <main>.

#wrapper: de <div> waar de tabel in zit.

In gewone mensentaal: doe iets met div#wrapper als het aankruisvakje bij input#toon is aangevinkt.

height: auto;

Bij #wrapper heeft div#wrapper een hoogte van 400 px gekregen en overflow-y: auto;. Hierdoor kan, als de in #wrapper zittende tabel hoger is dan 400 px, de tabel worden gescrold, terwijl de kolomkoppen stil blijven staan.

Op iOS kan echter, zodra ook maar een heel klein beetje wordt ingezoomd (vergroot), niet meer worden gescrold. Daardoor is alleen het deel van de tabel zichtbaar, dat al op de pagina staat, als wordt gezoomd. De rest van de tabel is volledig onbereikbaar.

Daarom wordt boven de tabel een aankruisvakje neergezet, waarmee de volledige tabel kan worden weergegeven. Dan kan ook in dit dure en daarom dus ongetwijfeld superieure besturingssysteem worden gezoomd én gescrold. De kolomkoppen blijven dan weliswaar niet meer stilstaan, maar er verdwijnt in ieder geval niet een groot deel van de tabel.

Door gewoon simpelweg de eerder opgegeven hoogte van 400 px te veranderen in de standaardwaarde auto, wordt de hele tabel zichtbaar. Wat eventueel niet binnen het venster van de browser past, is nu gewoon met scrollen zichtbaar te maken, omdat de tabel met de pagina mee scrolt.

#toon:checked + label::before

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

#toon + label::before {content: ""; background: #eee; width: 10px; height: 10px; overflow: hidden; border: black solid 1px; border-radius: 2px; position: absolute; bottom: 6px; left: 7px;}

#toon:focus + label::before {outline: cyan solid 2px;}

#toon:checked: als het element met id="toon" is aangevinkt. Dit is het keuzevakje, waarmee de hele tabel zichtbaar kan worden gemaakt.

+: het na de + staande element <label> moet in de html direct volgen op het voor de + staande element #toon. Het is de <label> die hoort bij het aankruisvakje om de hele tabel te tonen.

label::before: het bij <label> met behulp van ::before gemaakte pseudo-element. Met behulp van dit pseudo-element wordt het standaard-aankruisvakje van de <input> vervangen door een eigengemaakt aankruisvakje.

In normale mensentaal: als de <input> met het aankruisvakje om de hele tabel te tonen is aangevinkt, doe dan iets met het bij de daarop volgende <label> gemaakte pseudo-element, het eigengemaakte aankruisvakje.

content: "X";

Zet een 'X' in het vakje.

Bij een <input type="checkbox"> zit een standaard-aankruisvakje. Dat is hier echter niet bruikbaar en wordt daarom bij #toon + label::before vervangen door een eigengemaakt aankruisvakje in een met behulp van ::before gemaakt pseudo-element.

In een standaard-aankruisvakje is op een of andere manier te zien dat het is aangevinkt. Dat is in een eigengemaakt aankruisvakje niet het geval. Daarom wordt hier, als de <input type="checkbox"> is aangevinkt, een 'X' in het eigengemaakte aankruisvakje gezet.

De 'X' steekt in sommige browsers hier en daar buiten het aankruisvakje uit. Dat wordt verholpen door het bij #toon + label::before opgegeven overflow: hidden;. Overigens zie je, als je heel goed kijkt, dat deze 'X' niet in alle browsers perfect in het midden van het vakje staat. In de meeste browsers staat de 'X' wel in het midden, maar het blijft 'n soort compromis. Dit is trouwens wel een melding van de afdeling Als Je Het Niet Weet, Zie Je Het Niet.

color: green;

Groene voorgrondkleur. Dit is onder andere de kleur van de tekst. Hier is de tekst alleen de 'X'.

line-height: 10px;

Tekst wordt standaard halverwege de regelhoogte gezet. Met deze regelhoogte staat de 'X' het best verticaal gecentreerd.

table

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

table {border-spacing: 0; width: 100%;}

Alle tabellen. Dat is er hier maar één.

table-layout: fixed;

De breedte van kolommen en tabel kan op twee manieren worden bepaald. De standaardwaarde is auto, waarbij de browser de breedte bepaalt. Wat (te) simpel gesteld, bepaalt hierbij in principe de breedste cel in een kolom de breedte van een kolom, en alle kolommen samen de breedte van een tabel.

Met de waarde fixed bepalen de bij de tabel en de afzonderlijke kolommen opgegeven breedte de breedte van kolommen en tabel. Dat is te zeggen: tot op zekere hoogte, want browsers hebben een zekere vrijheid om de breedte van de tabel aan te passen, als de kolommen daar niet helemaal in passen. En (uiteraard) doen ze dit allemaal net even anders.

De breedte van de tabel is bij table opgegeven: 100%. Dat wil zeggen: even breed als de ouder van de tabel, wat hier div#wrapper is. div#wrapper heeft bij #wrapper ook een breedte van 100% gekregen en wordt daarmee even breed als z'n ouder <main>, die bij main een breedte van 756 px heeft gekregen. Hiermee wordt ook de tabel 756 px breed.

Omdat diverse onderdelen ten opzichte van <main> zijn gepositioneerd, komt het hier goed uit om de tabel even breed als <main> te maken. Maar ook als dat niet zo zou zijn, kan aan de tabel zelf geen breedte worden opgegeven. Als je aan een <table> een breedte opgeeft, wordt die breedte bij verschillende browsers net iets verschillend uitgelegd. Dat is normaal genomen geen probleem, maar hier wel.

Boven de 'echte' kolomkoppen uit de <th>'s staan 'nep' kolomkoppen uit div#kolomkop. Die moeten netjes boven de kolommen staan, en de lijntjes tussen de 'nep' koppen moeten aansluiten op de lijntjes tussen de kolommen.

De breedte van div#kolomkop wordt in alle browsers op dezelfde manier berekend, want dat is een doodgewone <div> met <span>'s erin. Maar als de breedte van de kolom of de tabel iets afwijkt, krijg je de 'nep' koppen niet op de juiste plaats boven de kolommen.

Als je echter geen breedte aan de tabel opgeeft, maar aan het element, waarin de tabel staat (hier is dat via #wrapper uiteindelijk <main>), is de breedte van de tabel wel hetzelfde in alle browsers. Dat is de reden dat hier niet rechtstreeks een breedte aan de tabel is gegeven.

(De breedte voor de kolommen wordt bepaald door de hieronder bij de diverse <th>'s opgegeven breedte. Je zou verwachten dat een bepaalde breedte van de zeven <th>'s in alle browsers dezelfde breedte voor de tabel oplevert, maar dat is dus niet zo.)

margin: 0; padding: 0;

Verschillende browsers hebben verschillende standaardinstellingen. Die worden hier hetzelfde gemaakt.

th

Alle <th>'s. Dat zijn er hier zeven. Binnen elke <th> staat een kolomkop.

De kolomkop binnen een <th> is van essentieel belang voor de toegankelijkheid van tabellen voor schermlezers en dergelijke. Maar in dit voorbeeld moeten de kolomkoppen in bredere browservensters blijven staan, als de tabel wordt gescrold. En dat kan niet, want dan herkennen schermlezers de tabel niet meer als tabel.

Daarom zijn over de 'echte' kolomkoppen in de <th>'s 'nep' kolomkoppen neergezet. Deze 'nep' kolomkoppen scrollen niet mee. Het zijn de 'nep' kolomkoppen die je ziet, de 'echte' kolomkoppen zijn verborgen onder deze 'nep' kolomkoppen.

De 'nep' kolomkoppen staan in zeven <span>'s, die weer binnen div#kolomkop zitten. Bij een grotere lettergrootte komen de in de <span>'s zittende kolomkoppen over de bovenste rij in de tabel te staan, omdat ze feitelijk niets met de tabel te maken hebben. Bij 'echte' <th>'s worden, als dat nodig is, de <th>'s hoger en wordt de tabel omlaag verplaatst. De <span>'s worden ook wel hoger, als dat nodig is, maar de tabel wordt niet verplaatst.

Om dit op te lossen zijn de <th>'s en de <span>'s zoveel mogelijk hetzelfde gemaakt. Je ziet de kolomkoppen uit de <span>'s, maar de onzichtbare <th>'s zorgen voor voldoende ruimte boven de tabel.

Waarom dit niet simpel kan worden opgelost door bijvoorbeeld <tbody> te laten scrollen en <thead> niet, is te lezen bij Achterliggend idee. Hoe de html van de <span>'s en de <th>'s min of meer hetzelfde wordt gemaakt is te vinden bij <span>VOOR&shy;NAAM</span>... De css wordt grotendeels al uitgebreid besproken bij #kolomkop span. Hieronder wordt daarom vaak daarnaar verwezen.

Kort samengevat komt het erop neer, dat de <span>'s (en de inhoud daarvan) en de <th>'s (en de inhoud daarvan) grotendeels hetzelfde moeten zijn. Dat is ook de reden dat hieronder dingen als lettergrootte en borders tot de essentiële css worden gerekend, wat meestal niet het geval is: ze moeten exact hetzelfde zijn als bij de <span>'s.

color: transparent;

Voorgrondkleur doorzichtig, waarmee de kolomkoppen onzichtbaar zijn.

De kolomkoppen in de <th>'s staan weliswaar precies onder die uit de <span>'s en zijn in principe niet zichtbaar, maar mogelijk zijn er toch exotische situaties denkbaar, waarin de koppen uit de <th>'s onder de <span>'s zouden uitsteken. In dat geval zie je ze toch niet, omdat ze doorzichtig zijn.

width: 126px;

Zelfde breedte als bij #kolomkop span aan de <span>'s is gegeven.

Omdat bij table table-layout: fixed; is opgegeven aan de tabel, bepaalt de breedte die hier aan de <th>'s wordt gegeven de breedte van de kolommen. De breedte van de <span>'s uit div#kolomkop is aan deze breedte aangepast.

font-size: 0.75em;

Zelfde lettergrootte als bij #kolomkop span aan de <span>'s is gegeven.

font-weight: normal;

Standaard is tekst in een <th> vet. De kolomkoppen uit de <span>'s zijn niet vet, dus hier ook niet.

line-height: 18px;

Zelfde regelhoogte als bij #kolomkop span aan de <span>'s is gegeven.

border-right: transparent solid 1px;

Ook de <span>'s hebben rechts een border. Alleen wordt de border hier, voor de zekerheid, doorzichtig gemaakt. Mocht er dan 'n border onder de <span>'s uit glippen, dan zie je er toch niets van.

padding: 4px 0;

Zelfde padding als bij #kolomkop span aan de <span>'s is gegeven.

th:nth-of-type(4)

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

th {color: transparent; width: 127px; font-size: 0.75em; font-weight: normal; line-height: 18px; padding: 4px 0;}

Het zoveelste element van een bepaalde soort, in dit geval een <th>. Bij de telling doen alleen de <th>'s met dezelfde ouder mee. Binnen <thead> zitten zeven <th>'s. Deze selector spreekt alleen de vierde <th> binnen de <thead> aan. Daarin staat de vierde kolomkop: 'POSTCODE'.

width: 72px;

Zelfde breedte als de vierde <span> bij #kolomkop span:nth-of-type(4) heeft gekregen.

Omdat bij table table-layout: fixed; is opgegeven aan de tabel, bepaalt de breedte die hier aan de vierde <th> wordt gegeven de breedte van de vierde kolom. De breedte van de vierde <span> uit div#kolomkop is aan deze breedte aangepast.

th:nth-of-type(5)

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

th {color: transparent; width: 127px; font-size: 0.75em; font-weight: normal; line-height: 18px; padding: 4px 0;}

Het zoveelste element van een bepaalde soort, in dit geval een <th>. Bij de telling doen alleen de <th>'s met dezelfde ouder mee. Binnen <thead> zitten zeven <th>'s. Deze selector spreekt alleen de vijfde <th> binnen de <thead> aan. Daarin staat de vijfde kolomkop: 'STAD'.

width: 124px;

Zelfde breedte als de vijfde <span> bij #kolomkop span:nth-of-type(5) heeft gekregen.

Omdat bij table table-layout: fixed; is opgegeven aan de tabel, bepaalt de breedte die hier aan de vijfde <th> wordt gegeven de breedte van de vijfde kolom. De breedte van de vijfde <span> uit div#kolomkop is aan deze breedte aangepast.

th:nth-of-type(6)

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

th {color: transparent; width: 127px; font-size: 0.75em; font-weight: normal; line-height: 18px; padding: 4px 0;}

Het zoveelste element van een bepaalde soort, in dit geval een <th>. Bij de telling doen alleen de <th>'s met dezelfde ouder mee. Binnen <thead> zitten zeven <th>'s. Deze selector spreekt alleen de zesde <th> binnen de <thead> aan. Daarin staat de zesde kolomkop: 'GEBOREN'.

width: 82px;

Zelfde breedte als de zesde <span> bij #kolomkop span:nth-of-type(6) heeft gekregen.

Omdat bij table table-layout: fixed; is opgegeven aan de tabel, bepaalt de breedte die hier aan de zesde <th> wordt gegeven de breedte van de zesde kolom. De breedte van de zesde <span> uit div#kolomkop is aan deze breedte aangepast.

th:last-of-type

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

th {color: transparent; width: 127px; font-size: 0.75em; font-weight: normal; line-height: 18px; padding: 4px 0;}

De laatste <th>. Dat is hier de zevende <th>. Bij het bepalen van de laatste <th> doen alleen de <th>'s met dezelfde ouder mee. Binnen <thead> zitten zeven <th>'s. Deze selector spreekt alleen de laatste <th> binnen de <thead> aan. Daarin staat de zevende kolomkop: 'LID SINDS'.

Bij de andere <th>'s is een volgnummer gebruikt: th:nth-of-type(5) voor de vijfde <th>, bijvoorbeeld. Hier is als 'volgnummer' het begrip 'laatste van deze soort' (in het Engels: 'last-of-type') gebruikt. Deze laatste <th> wijkt namelijk iets af van de andere, en is specifiek doordat het de laatste is. Door hier deze afwijkende selector te gebruiken is de kans hopelijk groter dat ik over 17 jaar (of iemand anders die dit ooit bekijkt) denkt: 'Hé, hier is een ander soort selector gebruikt. Zou dat 'n reden hebben? Even opletten.' Maar er is niets op tegen, als iemand liever th:nth-of-type(7) zou gebruiken.

width: 79px;

Zelfde breedte als de zevende <span> bij #kolomkop span:nth-of-type(7) heeft gekregen.

Omdat bij table table-layout: fixed; is opgegeven aan de tabel, bepaalt de breedte die hier aan de zevende <th> wordt gegeven de breedte van de kolom. De breedte van de zevende <span> uit div#kolomkop is aan deze breedte aangepast.

padding-right: 20px;

Deze regel hoort echt bij de laatste <th>, of dat nou de derde of de dertiende is.

De breedte die in deze kolom beschikbaar is voor tekst, wordt niet veranderd, want de padding komt naast die breedte te staan. Die breedte blijft dus 79 px.

Bij table heeft de tabel een breedte van 100% gekregen, even breed als z'n ouder div#wrapper. Maar als de tabel hoger is dan #wrapper, komt in div#wrapper een scrollbalk te staan. Waardoor de feitelijk beschikbare breedte in div#wrapper minder wordt dan de tabel nodig heeft.

Door deze padding wordt de tabel 20 px breder, zonder dat de tekst in de zevende kolom meer ruimte krijgt. Want die moet hetzelfde blijven als in de zevende <span>. Die padding schept ruimte voor de scrollbalk in div#wrapper, de tabel schuift als het ware onder de scrollbalk. Touchscreens hebben in de regel geen scrollbalk, daar is deze ruimte leeg. Om dat wat mooier te maken is de ruimte daarin met lijntjes afgezet.

Die breedte van 20 px is gevonden door uitproberen. Waarom die breedte in alle browsers goed werkt, is me eerlijk gezegd 'n raadsel. 19 px en 21 px werkt trouwens ook goed, maar als je te veel afwijkt van die 20, ontstaan er problemen, zoals niet precies op elkaar aansluitende lijntjes.

tr:nth-of-type(even)

Met :nth-of-type wordt het volgnummer van een bepaald element aangegeven, hier een <tr>. even betekent toevallig hetzelfde als in het Nederlands: elke even <tr>. Hierbij tellen alleen de <tr>'s met dezelfde ouder mee. In dit voorbeeld zitten alle <tr>'s in dezelfde tabel. Deze selector geldt dus voor elke even <tr> binnen de tabel.

background: #e9e9e9;

Achtergrond lichtgrijs kleuren.

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.

td

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

td {font-size: 0.8em;}

Alle <td>'s. Alle cellen uit de tabel.

border-right: #ccc solid 1px;

Rechts een lichtgrijs randje.

padding: 6px 0 6px 3px;

Wat afstand tussen de tekst in en de rand van de cel.

tr:first-child td:last-child

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

td {font-size: 0.8em;}

td {border-right: #ccc solid 1px; padding: 6px 0 6px 3px;}

tr:first-child: elke <tr> die het eerste kind van z'n ouder is. Elke eerstgeborene, zogezegd. Er zijn hier twee <tr>'s, die daaraan voldoen. Binnen de <thead> zit een <tr> die het eerste kind van <thead> is, en binnen <tbody> zit een hele serie <tr>'s, waarvan de eerste het eerste kind van <tbody> is.

td:last-child: elke <td> die het laatste kind van z'n ouder is. Alleen in de <tr>'s in <tbody> zitten <td>'s, de <tr> in <thead> heeft geen <td>'s.

In normale mensentaal: de laatste <td> in een <tr> die zelf eerste kind is. Alleen de laatste <td> in de eerste <tr> in de tabel, gelijk onder de kolomkoppen, komt hiervoor in aanmerking.

border-top: black solid 1px; Afbeelding 9: lege ruimte rechts van kolomkoppen niet en wel weggewerkt

Op de linkerafbeelding is rechts van de laatste kolom een lege ruimte te zien. Op de desktop staat daar een scrollbalk, maar die mist op de meeste touchscreens. Die lege ruimte kan redelijk onopvallend worden weggewerkt, als er wat lijntjes omheen worden gezet.

Aan de onderkant van de <span>'s met de 'nep' kolomkoppen is een zwart lijntje gezet. Op de afbeelding is daar een stukje van te zien onder de letters 'INDS'. Maar die <span>'s eindigen links van de scrollbalk. Op de desktop is dat geen probleem, maar op de meeste touchscreens blijft daardoor rechts een lege ruimte over, zoals op de linkerafbeelding is te zien.

Het is mooier als het lijntje onder de <span>'s helemaal tot rechts doorloopt. Daarom krijgt de laatste cel van de eerste rij aan de bovenkant een border. Deze cel loopt wel tot helemaal rechts door, omdat er bij td:last-of-type links van die cel een padding van 20 px wordt toegevoegd. In die padding kan weliswaar geen tekst staan, maar een border loopt gewoon door naast een padding, zoals op de rechterafbeelding is te zien.

td:last-of-type

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

td {font-size: 0.8em;}

td {border-right: #ccc solid 1px; padding: 6px 0 6px 3px;}

tr:first-child td:last-child {border-top: black solid 1px;}

De laatste <td>. Dat is hier de zevende <td>. Bij het bepalen van de laatste <td> doen alleen de <td>'s met dezelfde ouder mee. Binnen elke <tr> zitten zeven <td>'s. Deze selector spreekt alleen de laatste <td> binnen de <tr> aan. Dat zijn de <td>'s in de kolom 'LID SINDS'.

padding-right: 20px;

Bij th:last-of-type heeft de laatste <th> rechts een padding van 20 px gekregen, om ruimte te maken voor de scrollbalk rechts van de tabel. (De uitgebreidere uitleg is bij th:last-of-type te vinden.)

De zevende (en laatste) <th> is 79 px breed. Dat hoort ook de breedte van de in de zevende kolom staande <td>'s te zijn. Een aantal browsers telt de padding van 20 px echter op bij de breedte van de <td>'s in de zevende kolom. Hierdoor kan de laatste 20 px van de inhoud van de laatste <td>'s onder de scrollbalk verdwijnen.

Door ook aan deze <td>'s rechts een padding te geven van 20 px, wordt dit opgelost.

td:nth-of-type(1), td:nth-of-type(2), td:nth-of-type(3), td:nth-of-type(5)

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

td {font-size: 0.8em;}

td {border-right: #ccc solid 1px; padding: 6px 0 6px 3px;}

Het zoveelste element van een bepaalde soort, in dit geval een <td>. Bij de telling doen alleen de <td>'s met dezelfde ouder mee. Binnen elke <tr> zitten zeven <td>'s. Deze selector spreekt alleen de eerste, tweede, derde en vijfde <td> binnen elke <tr> aan.

-ms-hyphens: auto; -webkit-hyphens: auto; hyphens: auto;

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

Binnen de cellen in de eerste, tweede, derde en vijfde kolom is de kans vrij klein dat een woord te lang is voor de cel. Maar áls zo is, verdwijnt dat te lange woord rechts gedeeltelijk onder de volgende kolom.

Binnen deze kolommen staan woorden die min of meer op een normale manier zijn af te breken. Min of meer, want bij voor‑ of achternamen kan juist afbreken nog wel 'ns lastig zijn, zeker voor een browser.

Voor het geval een woord in een van deze kolommen toch te lang zou zijn, wordt hier automatisch afbreken aangezet. Mogelijk wordt een woord dan verkeerd afgebroken, maar dat is beter dan dat het gedeeltelijk verdwijnt. Niet alle browsers ondersteunen dit, en je moet ook de bij de taal horende afbreekregels hebben geïnstalleerd. Maar waar het werkt, is dat meegenomen.

td:nth-of-type(4), td:nth-of-type(6), td:nth-of-type(7)

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

td {font-size: 0.8em;}

td {border-right: #ccc solid 1px; padding: 6px 0 6px 3px;}

td:last-of-type {padding-right: 20px;}

tr:first-child td:last-child {border-top: black solid 1px;}

Het zoveelste element van een bepaalde soort, in dit geval een <td>. Bij de telling doen alleen de <td>'s met dezelfde ouder mee. Binnen elke <tr> zitten zeven <td>'s. Deze selector spreekt alleen de vierde, zesde en zevende <td> binnen elke <tr> aan.

word-wrap: break-word; overflow-wrap: break-word;

Binnen de cellen in de vierde, zesde en zevende kolom staan de postcode, de geboortedatum en de datum, vanaf wanneer iemand lid is. Dit zijn gegevens die niet op een normale manier tussen lettergrepen zijn af te breken, zoals bij de andere kolommen iets hierboven met hyphens gebeurt. Maar als de lettergrootte wordt verhoogd, kunnen ze wel te breed voor de kolom worden, waardoor het laatste deel van het gegeven zou verdwijnen.

Met deze regel wordt aangegeven dat de tekst in deze cellen, als die te breed wordt, op een willekeurige plaats mag worden afgebroken. Anders dan bij hyphens worden geen koppeltekens geplaatst op de afbreekpunten.

Er staan twee eigenschappen. Alle browsers hadden namelijk word-wrap al geïmplementeerd, toen w3c besloot dat overflow-wrap een betere naam was. (Vraag mij niet dat soort beslissingen uit te leggen, want dan ga ik beslist overmatig veel alcohol drinken.) In alle browsers werkt word-wrap, in een groeiend aantal browsers werkt overflow-wrap. Daarom worden ze beide gebruikt. De uiteindelijke standaardvorm wordt, zoals altijd, achteraan gezet.