Skip links en inhoudsopgave

Als je dit artikel afdrukt, wordt automatisch een printvriendelijke versie afgedrukt.
Dit artikel is van toepassing op Firefox, Safari, Google Chrome, Opera en Internet Explorer 7 en later.
Links in dit artikel, vooral links naar andere sites, kunnen verouderd zijn. Op de pagina met links vind je steeds de meest recente links.
Tekst die tussen << en >> staat, is een link naar internet.
Tekst die omgeven is door een lijn (meestal een stippellijn), verwijst naar een andere plaats binnen het artikel. Op de site zijn dit ankers, maar dat werkt nou eenmaal niet op papier.
Laatst herzien op .

Algemene opmerkingen

In dit artikel heb ik het alleen over simpele selectors: alleen namen van elementen, id's en classes, en combinaties daarvan. En als toetje de universele selector. Alles wat ingewikkelder is dan dat, komt mogelijk ooit nog eens in 'n ander artikel aan bod, maar niet hier.

Pseudo-elementen en pseudo-classes worden hier alleen besproken, voor zover ze van belang zijn voor de specificiteit. De werking ervan wordt hier verder niet besproken.

Als je nou denkt: wat heb ik er dan aan?, dan is dit artikel waarschijnlijk niet erg nuttig voor jou. Als je daarentegen bij een pseudo-class alleen aan Marx, die leuke klassenleraar of een enge tropische ziekte kunt denken, dan vind je in dit artikel misschien wel wat nuttige informatie.

Wat is nou eigenlijk het nut van css?

Een pagina met html is opgebouwd uit elementen als een <p>, een <div>, een <h2>, enz. De soort element wordt aangegeven met behulp van een tag. Deze begint altijd met < en eindigt altijd met >. Daartussen staat de soort element. (De meeste elementen hebben ook een eind-tag. Deze begint met </, maar die zijn verder niet interessant voor css.)

De meeste elementen hebben standaard-instellingen. Zo heeft een <p> automatisch een marge aan de boven- en aan de onderkant. Daar is geen css voor nodig, deze marge is er automatisch, zodra je een <p> gebruikt.

Als deze standaard-instellingen niet bevallen, kun je die veranderen. Vroeger kon dat alleen met html. Als je een andere lettergrootte wilde, moest je voor elk element die andere lettergrootte opgeven met het element <font>. Er bestond een groot aantal van dat soort elementen, die eigenlijk alleen maar voor de opmaak dienden.

Dat leverde een enorme hoeveelheid extra code op en maakte sites ook moeilijk toegankelijk voor spraakbrowsers en dergelijke. Het onderhoud van zo'n site is een nachtmerrie.

Als je expert bent op het gebied van de paringsdans van de Kleine Wilde Amazone-mier, wil je daar natuurlijk een website over maken. En omdat je 'n échte expert bent, krijgt die site honderden pagina's. Vanzelfsprekend heeft de expert hele goede ogen, want anders zou hij de paringsdans van de kleine miertjes niet hebben kunnen bestuderen. Hij gebruikt dus geen zwarte letter, maar een mooi kleurtje op een frisse achtergrond. Lekker apart.

Waarop het klachten regent van bezoekers. Die willen wel lezen, maar hebben zelf nooit paringsdansen van kleine mieren bekeken, dus hebben ze moeite met de mooie lichtblauwe letters op de frisse grijze achtergrond. De letters moeten dus 'n andere kleur krijgen.

Op elk van die honderden pagina's staat enkele tientallen keren de letterkleur aangegeven. Dat is op de hele site enkele duizenden keren. Die allemaal veranderd moeten worden. Daar gaan de goede ogen en het goede humeur van de expert.

En dan heb ik het alleen nog maar over de letterkleur gehad...

Het zou fantastisch zijn, als er een manier bestond, waarmee je op één centrale plaats die letterkleur (en allerlei andere dingen natuurlijk, de hele opmaak) zou kunnen veranderen. Die manier bestaat: css. Met behulp van css kun je vanuit één (of enkele) centrale bestanden het uiterlijk van een hele site aansturen. Zo'n bestand heet een stijlbestand, in het Engels stylesheet.

Naast het veel makkelijker onderhoud wordt de site ook veel toegankelijker voor zoekmachines, spraakbrowsers, en dergelijke. Bovendien wordt de site sneller geladen, omdat de bestanden veel en veel kleiner zijn.

Om allerlei redenen, waarvan er 'n paar hierboven staan, wordt het gebruik van html voor opmaak al meer dan tien jaar afgeraden. In html5, wat op het ogenblik wordt ingevoerd, wordt het niet meer afgeraden: het is ronduit verboden.

html, css, enz. werken via internationale standaarden. Voor html en css worden die bijgehouden door w3c. Als je zeker wilt weten dat je site ook in de toekomst bekeken kan worden, is het belangrijk volgens die standaarden te werken. En html voor opmaak valt buiten die standaarden.

Nog even een waarschuwing. css is bedoeld voor de lay-out, de opmaak, van de pagina. Er horen geen inhoudelijke dingen in te staan. Inhoud hoort in de html. Net zoals JavaScript alleen gebruikt hoort te worden voor extra's. Als de echte informatie van de site in de html staat, en niet afhankelijk is van css en/of JavaScript en dergelijke, is de pagina ook toegankelijk voor schermlezers en dergelijke.

Helemaal haalbaar is dit niet, want het zal nooit mogelijk zijn een volledig voor blinden toegankelijke slideshow te maken, om maar iets te noemen. Maar je kunt het wel zo goed mogelijk proberen. Overigens is de spider van een zoekmachine (die indexeert je site) redelijk te vergelijken met een blinde, dus het is ook in je eigen belang om een site toegankelijk te maken.

Je moet om nog een andere reden niet volledig op css (of JavaScript, of ...) vertrouwen. css, JavaScript, en dergelijke kunnen worden uitgezet. Gebruikers kunnen eigen css hebben opgegeven of zelf JavaScript tussenvoegen, die jouw css overrulet. Bijvoorbeeld omdat ze moeite hebben met contrast en dus bepaalde kleuren afdwingen.

Dat is allemaal niet zo'n probleem, als je er maar enigszins rekening mee houdt. Een site is nou eenmaal iets totaal anders dan een gedrukt stuk papier, waar niets meer aan veranderd kan worden, nadat het is gedrukt. Ook zonder css (en zonder JavaScript, zonder...) hoort een site nog alle feitelijke inhoud toegankelijk te maken. Tekst, links, essentiële afbeeldingen, enz. moeten nog steeds zichtbaar zijn en werken. Alleen extra's mogen verdwijnen.

Hoe koppel je bepaalde css aan bepaalde elementen?

Als ik de opmaak wil bepalen met behulp van css, moet ik op een of andere manier bepaalde css aan bepaalde elementen koppelen. Als ik de gewone tekst zwart wil hebben, maar alle <h2>'s oranje, moet ik dat onderscheid kunnen maken. En misschien wil ik wel de meeste <h2>'s oranje, maar eentje gifgroen.

Inline-style

Soms zie je css gebruikt worden op dezelfde manier als in het verleden de opmaak-tags van html:

<p style="color: black; background: white;">

Bij elke tag staat de css die bij die tag hoort: een inline-style. Een Nederlandse naam hiervoor bestaat, voor zover ik weet, niet. Omdat de css gelijk achter de tag staat, hoef je verder niet aan te geven, bij welk html-element de css hoort.

Dat is dan ook het enige voordeel. Als je iets wilt veranderen, moet je dat nog steeds op achtduizend plaatsen doen. De bestanden zijn nog steeds groter dan nodig. Bovendien kunnen dingen als :hover nooit werken met een inline-style. Enz., enz.

Een inline-style kan wel uiterst handig zijn bij het maken van een pagina, om even snel iets uit te proberen, of om erachter te komen waarom de foto van ome Piet pertinent weigert naast z'n echtgenote tante Klazina te blijven staan, maar steeds bovenop de foto van de buurvrouw springt. (Disclaimer: dit is slechts een verzonnen voorbeeld. Elke overeenkomst met de werkelijkheid berust op toeval.)

Stijlregels

Met uitzondering van de methode hierboven, waarbij de css gelijk achter de bijbehorende tag staat, moet op een of andere manier worden duidelijk gemaakt, bij welk element bepaalde css hoort. Dit gebeurt met behulp van een stijlregel (in het Engels style rule). Bij zowel een stijlblok in de head van een pagina als bij een extern stijlbestand gebeurt dit op dezelfde manier.

Een stijlregel is altijd op dezelfde manier opgebouwd:

p {color: black; background: white;}

Tussen de accolades staat de eigenlijke css. Dat zijn altijd één of meer eigenschappen met bijbehorende waarde of waarden. Vóór de openings-accolade staat de eigenlijke selector. Deze bepaalt, waarvoor de css geldt, en om dat deel gaat het hier. De css die tussen de accolades staat, heeft geen invloed op waar de css voor bedoeld is. De selector selecteert, waar de css voor geldt.

Enkelvoudige selector

In bovenstaande regel is de selector de p. Deze hoort bij het element <p>. De selector is gewoon de naam van het element, zonder de < en >. Bij een <p> houd je dan dus een p over, bij een <div> div, bij een <h1> h1, enz.

Omdat in de bovenstaande regel alleen een p staat, heet dit een enkelvoudige selector. Enkelvoudig, omdat er maar één element wordt genoemd. Selectors kunnen enorm ingewikkeld worden, dit is de meest simpele vorm.

Deze selector is heel eenvoudig. Dat is lekker makkelijk, maar het nadeel is dat hij heel grof is. Omdat er geen enkele beperking is, geldt deze selector voor álle <p>'s. Zonder uitzondering. Heel vaak is dat te grof, omdat bijvoorbeeld niet alle tekst dezelfde kleur moet krijgen, dezelfde lettergrootte, enz.

Meerdere selectors

Het komt vaak voor dat meerdere elementen dezelfde css moeten krijgen:

p {color: black; background: white;} h1 {color: black; background: white;} div {color: black; background: white;}

Dit is maar heel weinig css, maar als er ook nog een border, outline, enz. bij komen, wordt dit natuurlijk onwijs veel. Gelukkig kun je in één stijlregel meerdere selectors gebruiken:

p, h1, div {color: black; background: white;}

In de onderste regel staat precies hetzelfde als in de drie afzonderlijke regels daarboven. De drie selectors worden gewoon achter elkaar gezet, vóór de eerste accolade, gescheiden door een komma. Je kunt zo een onbeperkt aantal selectors dezelfde css geven.

Let er wel op dat je de komma niet vergeet. Zonder komma krijg je een Gecombineerde selector, zoals gelijk hieronder wordt beschreven, en dat is heel wat anders dan twee afzonderlijke selectors.

Gecombineerde selector

Ook in Nederland wordt deze selector vaak met zijn Engelse naam aangeduid: descendant selector. 'Descendant' betekent 'afstammeling'. Bij selectors, html, en dergelijke wordt een indeling gebruikt, die heel erg lijkt op die van een familie.

div h2 {font-size: 1.2em;}

In bovenstaande regel is de selector div h2. De standaard-lettergrootte van een <h2> wordt verkleind tot 1,2 em. Maar dit geldt hier niet voor élke <h2>. Omdat h2 volgt op een div en een spatie, geldt het hier alleen voor een <h2> die binnen een <div> staat.

De h2 is een 'descendant', een afstammeling, van de div. <h2>'s die buiten een <div> staan, worden niet beïnvloed door deze regel, omdat ze niet aan de voorwaarde van de selector voldoen: ze zijn geen afstammeling van een <div>.

Op deze manier is het mogelijk om, met twee stijlregels, alle <h2>'s rood te maken, behalve als ze binnen een <div> staan. Die maak je met behulp van een tweede regel groen:

h2 {color: red;} div h2 {color: green;}

Door op zo'n manier elementen te combineren krijg je al iets meer mogelijkheden. Dit kan in theorie heel diep gaan:

div p span strong abbr {...}

Een <abbr>, maar alleen als die binnen een <strong> staat, maar alleen als die <strong> binnen een <span> staat, maar alleen als die <span> binnen een <p> staat, maar alleen als die <p> binnen een <div> staat. Alleen als aan ál deze voorwaarden is voldaan, geldt deze regel.

Toch zijn deze gecombineerde selectors nog steeds vaak veel te grof. Want wat als je alleen elke tweede <h2> binnen een <div> groen wilt maken? Of alleen de zevende, maar niet in élke <div>?

Daarvoor moeten we het element in een class indelen of het 'n naam geven, een identiteit.

Class en id

Er zijn drie manieren om een naam te geven aan een element: name, class en id(entity). Daarvan hebben er twee met css te maken. De derde heeft helemaal niets met css te maken, maar wordt daar vaak voor misbruikt. Wat allerlei problemen oplevert.

Name

Het gebruik van name wordt al meer dan tien jaar afgeraden. Het is helemaal nooit bedoeld geweest voor css. Tot de dag van vandaag kom je het regelmatig tegen bij een link, zodat die link als anker kan worden gebruikt. Een anker is een plaats ergens binnen een pagina, waar naartoe kan worden gelinkt:

<a href="#" name="hiero">hiero</a>

En dan elders op de pagina (of in iets andere vorm op een andere pagina):

<a href="#hiero">hiero</a>

Door op 'hiero' in de onderste regel te klikken, schiet de browser naar het anker in de bovenste regel. Dit wordt al meer dan tien jaar afgeraden. Wat mij betreft komt hier na de revolutie vijf jaar mosselen opduiken in Zeeland op te staan. Met zuurstofflessen, want ik ben de rotste niet.

Ik noem name hier, omdat nog steeds met grote regelmaat op forums de vraag verschijnt, waarom css niet werkt. Onbegrijpelijk, want 'het is toch goed aan name gekoppeld'. Simpel antwoord: omdat het er helemaal niets mee te maken heeft. En het gebruik van name voor ankers wordt dus al meer dan tien jaar afgeraden. Naar een id kun je ook een anker maken, en nog veel meer.

name mag alleen nog worden gebruikt bij het verzenden van bepaalde onderdelen van een formulier (<form>) naar de server. De server heeft dat nodig om bepaalde onderdelen van het formulier te kunnen verwerken. Daarnaast wordt name nog gebruikt bij <map> en <param>. Afgezien van deze drie toepassingen is er geen enkele reden om name te gebruiken. Nergens voor. Nooit. Denk aan de mosselen.

Class

Een class is te vergelijken met een schoolklas. Een schoolklas bestaat uit een (groot) aantal kinderen. Een class bestaat uit een (groot) aantal elementen. En zoals kinderen uit een klas hetzelfde krijgen te leren, krijgen die elementen allemaal precies dezelfde css. (Voordat woeste ouders beginnen te klagen: bij kinderen wordt dat hopelijk wel enigszins individueel aangepast.)

Om een element lid te maken van een class, moet je het element in de html indelen in een class:

<p class="tekst">Deze <p> zit in de class 'tekst'</p>

De bijbehorende stijlregel:

p.tekst {color: black; background: white;}

Om aan te geven dat tekst de naam van een class is, wordt die in de css voorafgegaan door een punt. p.tekst wil dus zeggen: alle <p>'s die de class-naam 'tekst' hebben. Dat kan één <p> zijn, maar het kunnen er ook driehonderd of drieduizend zijn (hoewel er dan waarschijnlijk betere manieren zijn, maar dat voert hier te ver).

Je mag zo vaak als je wilt dezelfde class gebruiken op een pagina, ook met verschillende soorten elementen. Als je ook een <h1> met dezelfde kleuren hebt als de p.tekst hierboven, komt die er gewoon bij te staan in de css:

p.tekst, h1.tekst {color: black; background: white;}

De verschillende selectors worden gescheiden door een komma, net als bij meerdere selectors zonder class.

Ook de gecombineerde of descendant selector kun je gebruiken:

p.tekst, div h2.tekst {color: black; background: white;}

Deze css is geldig voor álle <p>'s met class="tekst". Daarnaast is hij geldig voor de <h2>'s met class="tekst", maar alleen als die binnen een <div> staan. Dit werkt precies hetzelfde als bij alle gecombineerde selectors.

Ook een stijlregel als de volgende is mogelijk:

.tekst {color: black; background: white;}

Nu wordt élk element, ongeacht welke soort het is, met een class="tekst" aangesproken. De punt geeft aan dat het om een class gaat, en omdat er geen enkel element voor staat, worden automatisch álle elementen verondersteld.

Selectors met een class kunnen ook gewoon worden gecombineerd met selectors met een id. Pardon, een id?

id

Als de hele klas een musical aan het zingen is, is het fantastisch als ze allemaal netjes de maat houden en zo. Hoe meer ze hetzelfde doen, hoe beter het is. De klas gedraagt zich, en moet zich gedragen, als een class.

Maar als er een spreekbeurt gehouden moet worden, is het wat onhandig als de hele klas naar voren rent en hetzelfde verhaal begint op te dreunen. Bij een spreekbeurt horen we liever een individu, iemand met een eigen persoonlijke identiteit, een id, niet de hele class.

Om degene die de spreekbeurt gaat houden aan te kunnen spreken, moet die een naam, een eigen identiteit, hebben. Bij kinderen uit een klas is dat een jongens- of meisjesnaam, bij css heet dat een id.

id is een afkorting van identity. Het belangrijkste verschil met een class is dat een id maar één keer gebruikt mag worden op een pagina. Ook al zijn het verschillende elementen, ze mogen toch niet dezelfde id hebben.

Anders dan bij kinderen met dezelfde naam uit één klas, kun je bij een technische taal prima regelen dat er geen twee id's met dezelfde naam op één pagina staan: gewoon verbieden. Kinderen met dezelfde naam kun je ook wel verbieden, maar dat levert toch al snel ernstige ethische en juridische problemen op.

Het is vrij makkelijk om twee keer dezelfde id te gebruiken op één pagina. Soms gaat dat goed, soms levert dat een volledig verstoorde pagina op. Daar is weinig zinnigs over te zeggen: het ligt eraan, waar de dubbele id toevallig staat, aan de gebruikte browser, enz. Voor het voorkomen van dit soort fouten is een validator ideaal.

Een validator controleert een taal op syntactische fouten: fouten tegen de regels. 'Intelligente' fouten worden meestal niet gevonden. Een afschuwelijke kleurencombinatie wordt enthousiast gevalideerd. Maar iets als een dubbele id vinden, daar is een validator erg goed in.

html kan worden gevalideerd op validator.w3.org.

css kan worden gevalideerd op jigsaw.w3.org/css-validator.

Om een element een id te geven, moet je het in de html gewoon de naam van een id geven:

<p id="tekst">Hier staat, o verrassing, tekst</p>

De bijbehorende stijlregel:

p#tekst {color: black; background: white;}

Om aan te geven dat tekst de naam van een id is, wordt het in de css voorafgegaan door een #. p#tekst wil dus zeggen: de <p> die de id 'tekst' heeft. Dat kan per definitie maar één <p> per pagina zijn. Ook andere elementen op dezelfde pagina mogen niet diezelfde id hebben. Op andere pagina's van dezelfde site mag wel weer dezelfde id worden gebruikt.

Je kunt selectors met een class en met een id gewoon combineren in dezelfde stijlregel. Als je ook een <h1> met dezelfde kleuren hebt als de p#tekst hierboven, komt die er gewoon bij te staan in de css:

p#tekst, h1 {color: black; background: white;}

Dit werkt precies hetzelfde als bij alle meerdere selectors.

Ook de gecombineerde of descendant selector kun je gebruiken:

p.tekst, div#header h2.tekst {color: black; background: white;}

Deze css is geldig voor álle <p>'s met class="tekst". Daarnaast is hij geldig voor de <h2>'s met class="tekst", maar alleen als die binnen een <div> staan. Die <div> moet bovendien een id="header" hebben.

<h2>'s binnen een <div> met bijvoorbeeld een id="footer" (div#footer), of een <div> zonder id, worden niet aangesproken door deze regel. Dit werkt verder precies hetzelfde als bij alle gecombineerde selectors.

Ook een stijlregel als de volgende is mogelijk:

#tekst {color: black; background: white;}

Nu wordt het element met id="tekst", ongeacht welke soort het is, aangesproken. De # geeft aan dat het om een id gaat, en omdat er geen enkel element voor staat, maakt de soort element niet uit. Maar ook hier mag maar één element op een pagina dezelfde id hebben. Zoiets is handig om bijvoorbeeld alle div#footer's op alle pagina's dezelfde css te geven.

Wanneer een class en wanneer een id?

Daar is weinig over te zeggen, wat niet wegneemt dat er complete bibliotheken over vol zijn geschreven door classici en idealisten.

Het belangrijkste verschil is dat je een id maar één keer mag gebruiken op een pagina, en een class zo vaak als je wilt.

Verder heeft een id meer specifiteit, maar dat speelt nauwelijks een rol bij het kiezen voor id of class.

Een echt verschil is het gebruik als anker. Een anker is een plaats binnen een pagina, waar naartoe kan worden gelinkt vanuit dezelfde pagina of vanaf een andere pagina. Elk element met een id kan als anker worden gebruikt:

<h2 id="hier-moet-u-wezen">Wat kom je hier doen?</h2>

Naar deze <h2> kan vanaf elke plek op de pagina worden gelinkt:

<a href="#hier-moet-u-wezen">Klik hier</a>

Vanaf een andere plek op internet zet je er de naam van de pagina en eventueel ook nog de naam van de site bij.

Als een element ook als anker wordt gebruikt, kun je dus beter een id dan een class gebruiken.

Verder is het eigenlijk vooral een kwestie van voorkeur. Ik gebruik bijvoorbeeld een id bij de inhoudsopgave bovenaan de pagina's met uitleg. Een inhoudsopgave mag maar één keer op de pagina staan. Daarom lijkt een id hier meer op z'n plaats. En omdat ik html en css ontzettend vaak valideer, krijg ik gelijk een melding als ik per ongeluk twee keer de id 'inhoud' zou gebruiken.

Veel sitebouwers gebruiken ook id's als 'header' en 'footer'. De hele header staat in een <div> met id="header", en de footer in een <div> met id="footer". Dat maakt het een stuk makkelijker om de weg te vinden in de stylesheet (het bestand met de css).

(html5 komt met een hele serie nieuwe elementen met namen als header, footer, menu, en dergelijke. Dit maakt in de toekomst het gebruik van dingen als een <div> met id="header" overbodig. Belangrijkste is dat pagina's hierdoor veel beter toegankelijk gaan worden voor spraakbrowsers en dergelijke, en dus ook voor zoekmachines. Alle browsers ondersteunen deze nieuwe elementen al, behalve Internet Explorer 8 en eerder. Voor deze oudere browsers kunnen deze elementen bruikbaar worden gemaakt door het gebruik van wat JavaScript.)

Namen van classes en id's

Er zijn wat regels, waaraan namen móéten voldoen, omdat er anders fouten kunnen optreden. Daarnaast zijn er wat dingen, waarvan het handig is om er rekening mee te houden. Dat zijn geen verplichte dingen, maar dingen die helpen misverstanden, slecht leesbare code, en dergelijke te voorkomen.

Geldige namen

Een naam moet met een hoofd- of kleine letter beginnen. Na die beginletter mogen cijfers, verbindingsstreepjes ('-', hyphen in het Engels) en onderstrepingen ('_', underscore in het Engels) worden gebruikt.

Daarnaast mogen ook dubbele punten en punten worden gebruikt. Maar dit kun je beter niet doen, omdat bijvoorbeeld een JavaScript-bibliotheek als jQuery daar problemen mee zou hebben.

In feite is het dus heel simpel: beginnen met een letter en alleen letters, cijfers, verbindingsstreepje en onderstreping gebruiken.

Persoonlijk gebruik ik altijd kleine letters in namen voor een class of id. In het verleden, en mogelijk nog steeds, maakten sommige browsers onderscheid tussen kleine letters en hoofdletters. Er zijn op dat gebied ook verschillen tussen Windows en alle andere besturingssystemen. En de meeste computertalen maken ook verschil tussen hoofd- en kleine letters.

Hoe het tegenwoordig precies zit, weet ik niet. Om problemen te vermijden gebruik ik gewoon alleen kleine letters. Je kunt ook alleen hoofdletters gebruiken. Of een combinatie. Als het maar steeds hetzelfde is, dan weet je zeker dat er geen problemen ontstaan in een of andere exotische browser of een buitenaards besturingssysteem.

Een veel gebruikte combinatie van hoofd- en kleine letters heet in goed Nederlands CamelCase (een Nederlandse vertaling bestaat, voor zover ik weet, niet). Hierbij begint elk deel van een naam met een hoofdletter. Iets als hoofdtitel wordt dan hoofdTitel of HoofdTitel.

Persoonlijk vind ik het beter om áltijd kleine letters te gebruiken, omdat je al snel een hoofdletter kunt vergeten. En hoofd-titel is volgens mij minstens even duidelijk als HoofdTitel. Maar goed, dit is voornamelijk een kwestie van persoonlijke voorkeur en van zorgvuldig werken.

Beschrijvende namen

Het is geen goed idee de naam van een html-tag te gebruiken als naam voor een class of id. In het verleden gaf dat problemen in sommige browsers. Daarnaast is het hoogst verwarrend. Het verschil tussen body en .body in een stijl is maar één puntje verschil in schrijfwijze. Maar in de uitwerking is het het verschil tussen het element <body>, dus de hele pagina, en de class "body", dus alleen de elementen met een class="body". Een wereld van verschil.

Het gebruik van dit soort namen voor classes en id's maakt css uiterst moeilijk te begrijpen en is vragen om fouten. Het missen van één rottig puntje kan eindeloze zoekpartijen naar een fout opleveren.

Een hele leuke, die steeds terugkomt op forums, is het gebruik van namen als "groene-knop" en "rode-knop". Als je deze code leest, is nog wel te begrijpen welk element er wordt bedoeld. Alleen, als je later besluit om 'rood' en 'groen' om te wisselen, omdat dat toch mooier is, zit je dus tot in de eeuwigheid met code die eigenlijk alleen voor een kleurenblinde nog is te volgen. Elke keer moet je eraan denken dat div#groene-knop de rode knop is, en omgekeerd.

Een aardige variant op bovenstaande is die met namen als "span-1", "span-2", (...), "span-327". Over zeven maanden weet je echt niet meer, wat voor soort span span-37 is. Terwijl je over zeven maanden nog wel weet wat de bedoeling van <span class="waarschuwing"> is. En heb ook een beetje medelijden met anderen die jouw code moeten lezen, zoals bij vragen op een forum het geval is...

Het is dus beter om geen naam te kiezen, die voor iets staat dat later veranderd kan worden, zoals een kleur of een positie. De naam 'rechter-kolom' lijkt in eerste instantie prima. Tot je je realiseert dat het met css kinderlijk eenvoudig is om de rechterkolom aan de linkerkant neer te zetten, omdat dat toch beter blijkt te zijn. Hier kan zelfs kleurenblindheid geen redding meer bieden.

Betere namen beschrijven wat het element doet, het zijn beschrijvende namen. Een naam als 'sidebar' is goed, want sidebar zal altijd aan de zijkant staan. De kans dat een sidebar plotsklaps in het midden wordt neergezet, is niet heel erg groot. 'Menu', 'header', 'footer' zijn ook goede namen. De kans dat een header opeens footer wordt, is ook niet zo heel erg groot.

<code class="html">(...)</code> en <code class="css">(...)</code> zijn prima. De tag maakt duidelijk dat het code is, en de class maakt duidelijk wat voor soort code het is. En met behulp van css kun je eventueel het verschil accentueren. De kans dat html plotsklaps in css verandert, is ook niet heel erg groot.

De combinatie van de juiste html-tags en de juiste namen van id's en classes maakt code echt 'n heel stuk begrijpelijker. En voorkomt mede daardoor conflicten tussen verkeerd gebruikte namen.

Conflicten tussen namen van classes en id's kun je ook helpen voorkomen door namen in te delen in groepen. Zo begint op mijn eigen site - tenzij dat echt niet anders kan - elke id die als anker binnen de pagina wordt gebruikt met 'a-'. Die begin 'a-' is gereserveerd voor deze 'lokale' id's. Id's die op meerdere pagina's kunnen worden gebruikt, beginnen nooit met 'a-'.

Dit voorkomt dat ik voor een anker een id kies, die al wordt gebruikt in een externe stylesheet. En dat voorkomt dus weer eindeloze zoekpartijen: ik begrijp het niet, ik heb hier alleen maar 'n anker, en er verschijnt opeens een marge aan de bovenkant... Dat soort dingen. (Ja, klopt, hier spreekt de afdeling Schade en Schande en Wijs worden.)

Terzijde: als je in de code van mijn eigen site kijkt, zie je dat ook ik soms zondig. Een van de redenen is dat ik net een echt mens lijk. Daarnaast is het soms heel lastig een echt beschrijvende naam te vinden. Ik gebruik bijvoorbeeld <code>. Met behulp van css heb ik die een bepaalde opmaak gegeven. De onderste regel moet echter een grotere marge krijgen.

Met de nieuwste css-selectors zou dat waarschijnlijk wel zonder extra class-naam te regelen zijn, maar die worden nog niet goed ondersteund. Dus geef ik de onderste regel een class="onder".

<code class="css"> en <code class="html"> klopt meestal op mijn site. Maar soms smokkel ik en gebruik 'css' in plaats van 'html'. Om nou voor die hele enkele keer dat 'css' beter uitkomt (niet vet en nog wat kleine verschillen), terwijl het om html gaat, wéér 'n hele class of iets soortgelijks te gaan verzinnen... Maar er zijn goede argumenten om dat wel te doen, geef ik direct toe.

(Aanvulling: omdat nieuwere delen van de site inmiddels inderdaad gebruik maken van nieuwere selectors en dergelijke, zijn er steeds meer pagina's op de site, waarop bovenstaande dingen niet meer voorkomen.)

Bij de lay-out-voorbeelden op mijn site gebruik ik namen als 'links' en 'rechts' voor de kolommen. Maar hier zijn dat beschrijvende namen: het gaat om voorbeelden waarbij de linkerkolom per definitie altijd links blijft staan, want anders zou het geen voorbeeld van een linkerkolom meer zijn.

Universele selector *

Ook in Nederlandse teksten wordt de universele selector vaak op z'n Engels 'universal selector' genoemd.

Deze selector is een geval apart. Zoals de naam al aangeeft, selecteert hij álle elementen:

* {margin: 0; padding: 0;}

zal bij álle elementen de marge en padding weghalen.

Dit lijkt een goed idee, omdat je dan niets meer hebt te maken met de standaard-instellingen van browsers, die soms wat verschillen. Toch is het dat meestal niet.

Als je de marge en padding bij álle elementen weghaalt, haal je die bij wel heel erg veel elementen weg, ook waar er geen verschil is tussen browsers. En je moet het dan ook weer overal terugzetten.

Dit gaat nog meer spelen, als je erfelijke eigenschappen als regelhoogte erin opneemt:

* {line-height: 1.2em;}

Normaal genomen wordt een eigenschap als regelhoogte automatisch door kinderen geërfd van de ouders. Normaal genomen is die overerving ook precies, wat je wilt. Maar als je met behulp van de universele selector in de css de regelhoogte instelt, wint dat van de overerving. Elke vorm van css overrulet nou eenmaal de overerving. Je moet dan de regelhoogte voor élk element, elke keer opnieuw, instellen.

Als een <div> twintig <p>'s bevat, kun je normaal genomen de regelhoogte instellen met div {line-height: 1.5em;}. Elke <p> binnen deze <div> zal nu deze regelhoogte van 1,5 em erven. Maar als je de regelhoogte met de universele selector instelt op 1,2 em, overrulet die regelhoogte van 1,2 em de bij de <div> opgegeven regelhoogte. Je moet dan voor élke <p>, alle twintig, die regelhoogte op gaan geven. Dat kost enorm veel extra css, veel meer dan het voordeel dat de universele selector oplevert.

Als je de standaard-instellingen van browsers gelijk wilt krijgen, is het beter om een zogenaamde 'reset style' te gebruiken. (Een Nederlandse naam ken ik niet.) Als je daarnaar zoekt op internet, vind je verschillende goede. Zo'n reset style stelt de standaard-instellingen op een gecontroleerde manier in. Overigens wordt ook over het gebruik van reset styles nogal verschillend gedacht. Zelf gebruik ik ze niet, maar er zijn ook mensen die ze bij elke site gebruiken. Net wat je prettig vindt.

Omdat de universele selector zo enorm botst met het mechanisme van overerving, kan hij beter niet zonder meer in z'n eentje worden gebruikt. Maar de universele selector is wel bruikbaar als onderdeel van een langere selector:

div#content * a {color: red;}

Deze css maakt de tekst van een link rood. Maar niet van alle links, er moet aan twee voorwaarden worden voldaan.

div#content bepaalt dat de link binnen div#content, een <div> met id="content", moet staan. Maar het geldt niet voor álle links binnen div#content.

De * geeft aan dat de link een kind van een ander element moet zijn, wat zelf weer een kind is van div#content. Het maakt niet uit wat voor element er tussen de link en de <div> staat, als er maar een element is. De <a> moet een kleinkind van de div#content zijn, geen rechtstreeks kind.

Deze css geldt dus niet voor <div><a ...>, want er staat hier geen element tússen de <div> en de <a>: de link is een direct kind van div#content.

Maar hij geldt wel voor <div><h2><a ...> en ook voor <div><p><a ...>, want nu staat er een element tussen div#content en de <a>: de <a> is nu een kleinkind van div#content. En omdat de universele selector * is gebruikt, maakt het niet uit dat in het ene geval een <h2> en in het andere geval een <p> de ouder van de link is.

Je kunt dit in principe net zo ingewikkeld maken, als je wilt:

body * ul ul * * span

levert een groene tekst op in de laatste span bij:

<body><div><ul><li><ul><li><strong><span>(...)

en ook bij:

<body><ul><li><ul><li><ul><li><span><span>(...)

maar niet bij:

<body><div><ul><li><strong><span>(...)

Veel zul je dit niet gebruiken. De reden daarvoor heb je mogelijk al gemerkt: je moet de regels hierboven zeven keer lezen en hebt bijkans een telraam nodig om uit te tellen, hoe het werkt. Maar het is nuttig te weten dat er zoiets als een universele selector bestaat, en dat die soms handig kan zijn.

(Terzijde: in oudere css zie je vaak * html aan het begin van een regel staan. Dit is de zogenaamde 'star hack'. <html> is het buitenste, hoogste element van een pagina, per definitie. Internet Explorer 6 had echter een of ander denkbeeldig element daarbuiten. Als je dus * html in je css gebruikte, werkte dit alleen in browsers die een element buiten de html hadden. En dat waren alleen Internet Explorer 6 en eerder.

Deze hack maakte gebruik van een fout in Internet Explorer 6 om css speciaal voor die browser te geven. En dat was hard nodig, wat het onding stikte werkelijk van de bugs en afwijkingen van de standaard. Word er nog wel 'ns gillend wakker van, als ik zwaar getafeld heb.

Internet Explorer 7 had minder fouten en afwijkingen, maar nog steeds behoorlijk wat. Een van de fouten die eruit was gehaald, was dat vreemde buitenste element. De hack werkte dus niet meer. Maar was wel nog steeds nodig voor een aantal fouten en afwijkingen, dat er helaas nog wel in zat. Paniek alom. En het kostte vreselijk veel werk om de css aan te passen aan Internet Explorer 7, want dat moest handmatig gebeuren op élke plaats, waar deze hack was gebruikt.

Ik ga daar hier verder niet echt diep op in, maar dit is de reden dat je veel beter conditional comments kunt gebruiken. Een hack kan worden gerepareerd, conditional comments zijn opzettelijk aangebracht en blijven daarom gewoon werken, zoals ze zijn bedoeld.)

Pseudo-element

Naast de gewone elementen als <div>, <p>, <h1>, zoals die in html voorkomen, bestaan er ook nog zogenaamde pseudo-elementen. Van een pseudo-element zie je niets in de html zelf. Een pseudo-element wordt aangemaakt in de css. Maar het gedraagt zich alsof het een echt element is.

Een van de meer gebruikte pseudo-elementen is :first-line. Dit kan bijvoorbeeld zo gebruikt worden in een stijlregel:

p:first-line {font-size: 1.1em;}

Van elke <p> krijgt de eerste regel een iets grotere letter. Maar alleen de éérste regel. Je zou hetzelfde kunnen bereiken door de eerste regel in een eigen span te zetten. Die span geef je dan een iets grotere letter. Dat geldt eigenlijk voor álle pseudo-elementen: je zou met html hetzelfde kunnen bereiken.

Maar niet helemaal.

Om te beginnen zou je dan, om bij :first-line te blijven, elke eerste regel in een span moeten zetten, en mogelijk die span ook nog een class moeten geven. Ontzettend veel extra werk. Als de lettergrootte door de gebruiker wordt veranderd, verandert de lengte van de eerste regel en krijgt dus iets meer of minder dan alleen de eerste regel een iets grotere letter. Als je 'n woord toevoegt of weghaalt, moet je de span ook weer aanpassen.

Het voordeel van een pseudo-element is dat dit geen extra html vergt. En het past zich automatisch aan bij een andere lettergrootte, meer of minder tekst, enz. Veel en veel minder werk dus en ook nog 'ns veel flexibeler.

css 2.1 kent de volgende pseudo-elementen: :after, :before, :first-letter en :first-line.

css3 voegt daar nog een aantal pseudo-elementen aan toe. Die worden nog niet breed ondersteund, maar in de toekomst zul je ze steeds vaker gaan zien, dus voor de volledigheid zet ik ze er hier ook maar bij: ::alternate, ::choices, ::line-marker, ::marker, ::outside, ::repeat-index, ::repeat-item, ::selection, ::slot() en ::value.

Om ze makkelijk te kunnen onderscheiden van pseudo-classes beginnen pseudo-elementen in css3 met ::. Pseudo-classes worden begonnen met een enkele :. In css 2.1 begonnen beide met een enkele :. Uiteindelijk moeten ook :after, :before, :first-letter en :first-line begonnen worden met ::, maar het zal nog jaren en jaren duren voor dat echt goed wordt ondersteund. Voorlopig kan gewoon de enkele : worden gebruikt voor deze oudere pseudo-elementen.

Wat je verder met pseudo-elementen kunt doen, gaat ver buiten de bedoeling van dit artikel. Voor wat betreft de css is een pseudo-element volledig hetzelfde als een gewoon element, zoals dat in html voorkomt.

Pseudo-class

Een pseudo-class wordt, anders dan een echte class, niet toegekend in de html. In de html zie je er helemaal niets van. Maar in de css kun je ze gewoon gebruiken, ze worden automatisch toegekend. Ze hebben te maken met de plaats van een element in de opbouw van de html, met waar geklikt of gehoverd wordt, of met een bepaalde voorwaarde waar ze al dan niet aan voldoen.

De pseudo-class :hover bijvoorbeeld treedt op, als over een element wordt gehoverd:

a:hover {color: orange; background: blue;}

Bij hoveren over een link wordt de voorgrondkleur, en dus de tekst, oranje en de achtergrond blauw.

Een ook regelmatig gebruikte pseudo-class is :first-child. Stel dat je alle <p>'s wilt laten inspringen, behalve de eerste <p> binnen een <li>. Dat kan heel simpel:

li p:first-child {text-indent: 0;}

Bij een <p> binnen een <li> moet de tekst in de eerste regel niet inspringen. Maar alleen als de <p> het eerste kind is van de <li>, als er niet eerst een ander element binnen de <li> voor de <p> staat. Dus alleen als in de html staat: <li><p>. Volgende <p>'s springen gewoon weer in.

Pseudo-classes sparen enorm veel code uit, omdat je niet handmatig allerlei elementen een class moet geven. Ze zijn ook uiterst flexibel. Als er een nieuwe <p> wordt tussengevoegd gelijk na de <li>, is die de nieuwe :first-child en springt niet in. De voormalige :first-child springt nu gewoon in, want die is nu niet meer het eerste kind. Gelukkig gaat dit wat probleemlozer dan soms bij échte families het geval is.

"Piet, doe jij even open, er wordt gebeld." "Hallo pa, leuk je te leren kennen, ik ben je eerstgeborene." Wordt toch niet echt overal zonder meer enthousiast op gereageerd. html-elementen zijn wat dit soort dingen betreft beslist soepeler dan mensen. Ook veel saaier trouwens, dat wel. Onmenselijk saai.

Sommige dingen zijn zonder pseudo-classes stomweg onmogelijk, zoals vaststellen of er wordt gehoverd over een element. Zonder pseudo-classes zou je dat alleen met iets als JavaScript kunnen vaststellen.

css 2.1 kent de volgende pseudo-classes: :active, first-child, :focus, :hover, :lang(), :link en :visited.

css3 voegt daar nog een aantal pseudo-classes aan toe. Die worden nog niet breed ondersteund, maar in de toekomst zul je ze steeds vaker gaan zien, dus voor de volledigheid zet ik ze er hier ook maar bij: :checked, :default, :disabled, :empty, :enabled, :first-of-type, :in-range, :indeterminate, :invalid, :last-child, :last-of-type, :nth-child(), :nth-last-child(), :nth-last-of-type(), :nth-of-type(), :not(), :only-child, :only-of-type, :optional, :out-of-range, :read-only, :read-write, :required, :root, :target en :valid.

Waar je pseudo-classes voor kunt gebruiken, gaat de bedoeling van dit artikel ver te buiten. Voor wat betreft de css is een pseudo-class volledig hetzelfde als een gewone class, zoals die in de html wordt toegekend.

Attribuut-selector

Een attribuut-selector (Engels: attribute selector) is simpel te herkennen aan het gebruik van [ en ]:

abbr[title] {border: 0;}

Haal de border weg bij <abbr>, maar alleen als de <abbr> een title heeft.

Een attribuut-selector wordt gebruikt om een stijlregel wel of niet te laten werken, afhankelijk van of een attribuut aanwezig is (of juist afwezig). Ook kunnen bepaalde voorwaarden aan het attribuut worden gesteld, zoals dat het moet beginnen met "http://", om maar iets te noemen. Wordt niet aan deze voorwaarden voldaan, dan wordt de css niet uitgevoerd.

Ook de attribuut-selector wordt niet aangegeven in de html. Je gebruikt gewoon normale html, en in de css wordt gekeken of die html aan bepaalde voorwaarden voldoet. Waardoor ook deze selector veel code uitspaart.

Waar je attribuut-selectors voor kunt gebruiken, gaat de bedoeling van dit artikel ver te buiten. Voor wat betreft de css is een attribuut-selector volledig hetzelfde als een gewone class, zoals die in de html worden aangegeven.

Elementen met (meer dan) één class én een id

Een element mag een id én een of meer classes hebben, daar is niets op tegen. Je geeft gewoon in de html een id én een class aan een element. Voor de css maakt dat verder niet zo heel veel uit.

Class én id

Vaak is een combinatie van een class en een id handig. Je zou bijvoorbeeld een serie tabellen dezelfde basis-opmaak kunnen geven. Alle tabellen krijgen dezelfde class, en via die class dezelfde css. En vervolgens zou elke tabel een eigen id kunnen geven, en via die id een eigen kleur. Dat wordt dan zoiets in de html:

<table class="algemeen" id="parken">(...) <table class="algemeen" id="huizen">(...)

En in de bijbehorende css:

table.algemeen {width: 300px; height: 500px; background: white; color: black; border: black solid 1px;} table#parken {border: solid green 1px;} table#huizen {border: solid red 1px;}

Dit kan heel veel css uitsparen, omdat je op deze manier de gemeenschappelijke kenmerken van elementen in één stijlregel kunt opgeven. Vervolgens geef je de afwijkende eigenschappen voor elke id apart op.

Hierboven krijgen alle tabellen met class="algemeen" een aantal eigenschappen, waaronder een zwarte border. Voor de tabellen die een andere kleur border moeten krijgen, geef je dat vervolgens via een id aan.

Meerdere classes

Een element kan ook probleemloos meerdere classes krijgen:

<div class="een twee drie vier">

of een id én meerdere classes:

<div id="header" class="een twee drie vier">

In de css wordt zo'n element op precies dezelfde manier benaderd, als wanneer het maar één class of id zou hebben. Voor bovenstaande div gelden al deze stijlregels:

div#header{ ...} div.een{ ...} div.twee{ ...} div.drie{ ...} div.vier{ ...}

Dit geeft de mogelijkheid om, door verschillende classes aan verschillende elementen te geven, met relatief weinig css veel combinaties te maken. <div class="een twee drie"> komt er anders uit te zien dan <div class="twee drie vier">, omdat ze niet dezelfde set stijlregels krijgen.

Alleen moet je hierbij wel oppassen voor de zogenaamde classitis: een overdaad aan classes. Het is niet de bedoeling dat je elke mogelijke eigenschap en elke mogelijke waarde een eigen class gaat geven, want dan krijg je iets als:

<div class="rode-border witte-achtergrond grote-letter links-boven">

Dan is het simpeler om gewoon de verouderde html-tags te blijven gebruiken voor de opmaak, want de css levert dan nog nauwelijks voordelen op en is uiterst onduidelijk. Voor de zekerheid: dit is geen advies om die verouderde troep te blijven gebruiken, maar een advies om niet een overdaad aan classes aan te maken.

(Terzijde: zijn er nog meer besmettelijke ziektes? Ja. Divitis. Een overdaad aan divs. Soms zie je dat élk element in een div wordt gezet, of dat nou nodig is of niet. Je kunt door de divs de body niet meer zien. Hoe ingewikkelder de code, hoe groter de kans op moeilijk te vinden fouten, en hoe moeilijker het voor derden is de code te begrijpen.)

Je kunt ook in de css classes combineren. De namen van de classes worden gewoon tegen elkaar gezet. De betreffende stijlregel geldt dan alleen voor een element, dat álle classes heeft:

div.een.twee.drie{...}

Deze regel geldt voor <div class="een twee drie">, maar niet voor <div class="een"> of <div class="twee drie">, want alleen de eerste div uit dit rijtje heeft alle drie de classes. De regel geldt ook voor <div class="een twee drie vier vijf">, want ook deze heeft alle drie de benodigde classes.

De volgorde van de classes is hierbij niet van belang. De volgorde mag in de html anders zijn dan in de css, als ze maar allemaal aanwezig zijn.

Zoiets kan soms handig zijn. Ik heb een pagina met huizen die een zwembad hebben, huizen die een garage hebben, en huizen die een zwembad én een garage hebben, zodat de auto makkelijk gewassen kan worden.

De zwembadhuizen staan in een div die een rode border moet krijgen. De huizen met een garage krijgen een groene border. En de huizen met zwembad én garage moeten een blauwe border krijgen. De html:

<div class="zwembad"><div> <div class="garage"><div> <div class="zwembad garage"><div>

Met deze twee classes kan ik toch drie verschillende borders geven:

div.zwembad {border: red solid 1px;} div.garage {border: groen solid 1px;} div.zwembad.garage {border: blue solid 1px;}

De huizen divs met class="zwembad garage" krijgen, op grond van de eerste regel, een rode border. Op grond van de tweede regel krijgen ze een groene border. En die regel wint van de eerste, omdat hij later komt in de css en evenveel specificiteit heeft. Dus de tweede regel overrulet de eerste.

De derde regel komt nog later in de css en overrulet dus de bovenste twee regels (en heeft meer specificiteit). Uiteindelijk krijgen de divs met een class="zwembad garage" dus een blauwe border, zonder dat ik daar een extra class voor nodig heb. En als de garage afbrandt, hoef ik alleen maar de class "garage" weg te halen om de border rood te maken.

Ook een id en een class kun je in de css combineren, maar dat is zelden zinvol. De volgende html is voor álle headers binnen een site:

div#header {background: red; color: orange; border: blue dashed 10px; outline: yellow dashed 5px;}

Een kleurencombinatie waarmee je zelfs de meest gemotiveerde kleuter wegjaagt, maar dat maakt voor de css niets uit.

Als ik nu één header heb met <div id="header" class="saai">, zal deze div gewoon de css van bovenstaande regel gebruiken. Maar de volgende regel wordt alleen door déze div gebruikt:

div#header.saai {background: white;}

Deze header ziet er dus hetzelfde uit als de andere headers, maar de achtergrond is wit. En omdat in de selector een id én een class staan, heeft die regel meer specificiteit dan de stijlregel met alleen de id. Het maakt dus niet uit of hij voor of na de regel met alleen de id komt: hij overrulet altijd de regel met alleen de id. Hij heeft meer specificiteit.

Dat *(x##*-ding gaat de gracht in. Het werkt niet!

Als css niet werkt, zoals het volgens jou zou horen te werken, is dat volstrekt normaal. Het is pas vreemd, als het gelijk werkt, zoals jij vindt dat het zou moeten werken. Er is een vrijwel oneindig aantal mogelijke oorzaken.

Het komt heel vaak voor dat de browser trouwhartig uitvoert, wat jij hebt opgegeven. Alleen bedoelde jij iets anders, dan wat er staat. Als je twee elementen over elkaar heen positioneert, laat de browser gehoorzaam de onderste verdwijnen achter de bovenste. Dat wilde je toch?

Tegen dat soort fouten helpt alleen maar het nalopen van de code. Het kan dan nuttig zijn er iemand anders naar te laten kijken, omdat het vaak voorkomt dat de schrijver van de code over iets heel simpels heen ziet, wat 'n ander gelijk ziet.

(Op de pagina met links vind je onder Gereedschap → Debuggen een serie links naar hulpmiddelen bij het nalopen van je code.)

De rest van dit artikel gaat niet over dit soort logische fouten, maar over dingen als de volgorde waar css in wordt uitgevoerd, fouten tegen de syntax (taalregels), en dergelijke. Het is geen handleiding om css en html volledig te debuggen (fouten eruit te halen). Daarvoor is een eigen uitgebreid artikel, eigenlijk een compleet boek, nodig. Het is ook geen handleiding over hoe je moet testen in verschillende browsers en met verschillende resoluties en zo. Ook dat is uitgebreid genoeg voor een apart artikel.

Syntactische fouten

Syntactische fouten zijn fouten tegen de regels van de taal, hier de html en/of de css. Zolang er nog syntactische fouten in je html of css zitten, is het echt volstrekt zinloos naar andere oorzaken van problemen te gaan zoeken. Een fout helemaal bovenin je pagina kan tot de wildste dingen onderin je pagina leiden.

Als op een forum een vraag binnenkomt, wordt vaak de code bekeken. Heel vaak blijken daar dan fouten in te zitten. Als je daar dan op wijst, krijg je met grote regelmaat als reactie dat die fout echt niets te maken heeft met het probleem. Ik vind dat heel knap. Ik durf te zeggen dat ik toch wel enige ervaring heb, en ik kan dat nooit zeggen. En ook anderen die veel ervaring hebben in het oplossen van problemen, beginnen altijd met syntactische fouten eruit te halen.

Het kan natuurlijk zijn dat er een of ander ondergronds, geheim circuit is van Toegepaste Voodoo Voor Websites Met Fouten of zoiets dat ik niet ken, maar zonder zo'n soort occult genootschap kun je nooit zeggen dat een fout niets met je probleem te maken heeft. Kortom: eerst die fouten eruit, anders heeft het echt geen enkele zin naar de dingen verderop in deze tekst te kijken.

De meeste fouten tegen de syntax van html en css kunnen door een validator worden gevonden. Dat zijn dus overtredingen van de regels. Als je zelf twee elementen over elkaar heen zet, maar je houdt je aan de regels, wordt dat niet gevonden, want dat is geen syntactische fout. Dat is gewoon de soort stommiteit waar mensen nou eenmaal in grossieren (rustig, ik heb het ook over mezelf, grinnik).

html kan worden gevalideerd op validator.w3.org.

css kan worden gevalideerd op jigsaw.w3.org/css-validator.

Als alle fouten eruit zijn en je css werkt nog niet, zoals het zou moeten, is er mogelijk een kans dat je hieronder eventueel de oorzaak zou kunnen vinden.

Standaardwaarden en default stylesheet

Veel elementen hebben standaardwaarden, die automatisch worden gebruikt. Maar zodra met behulp van css een andere waarde wordt opgegeven, wordt die andere waarde gebruikt. Een waarde opgegeven met behulp van css wint áltijd van een eventuele standaardwaarde.

Overigens zitten er nogal wat verschillen tussen de diverse browsers wat betreft sommige standaardwaarden. Bijvoorbeeld de standaard-padding en -marge in een lijst (<ul> en <ol>) verschilt enorm tussen de verschillende browsers. <abbr> heeft in sommige browsers standaard een border, in andere niet.

Die verschillen worden deels veroorzaakt, omdat elke browser een ingebouwd standaard (Engels: default) stijlbestand heeft. Zodra op een andere manier een stijl wordt opgegeven, wint die áltijd van het ingebouwde stijlbestand.

Een voorbeeld van zo'n default stylesheet is te vinden op de site van w3c.

Verschillen in weergave tussen verschillende browsers, terwijl je geen css voor dat deel van de weergave hebt opgegeven, hebben vaak met verschillende standaardwaarden te maken. Als je bijvoorbeeld bij een lijst zelf padding en marge opgeeft, zie je dat de weergave plotsklaps hetzelfde is in de diverse browsers. In vrijwel elke goede handleiding kun je bij de betreffende eigenschappen waarschuwingen vinden voor dit soort verschillen in standaardwaarden.

Stylesheet van gebruiker

Ook een gebruiker kan zelf een stijlbestand opgeven. Als zo'n stijlbestand aanwezig is en de bouwer van de site zelf geen css heeft opgegeven, wordt de css van de gebruiker gebruikt. Maar zodra de bouwer css heeft opgegeven, wint deze.

Omdat dit een stijlbestand is dat, als het al aanwezig is, van gebruiker tot gebruiker verschilt, zal het weinig problemen opleveren. De gebruiker kent het en weet dus wat het doet, en de bouwer weet niet eens dat het bestaat. Het is alleen handig om te weten dat het kán bestaan. Dit is bijvoorbeeld de reden dat het in de regel goed is, om te zorgen voor een voorgrondkleur én een achtergrondkleur, en niet alleen maar een van die twee.

Als de gebruiker, bijvoorbeeld omdat ze moeite heeft met contrast, zelf kleuren opgeeft, kan het misgaan als de tekstkleur van de bouwer en de achtergrondkleur van de gebruiker worden gebruikt. Het contrast kan dan te laag zijn, of zelfs volledig ontbreken, als beide kleuren toevallig hetzelfde zijn.

In principe wint de css van de bouwer dus. Daar is één uitzondering op: als de gebruiker !important heeft gebruikt, wint de bijbehorende stijlregel van de gebruiker altijd. Ook als de bouwer !important heeft gebruikt, wint het !important van de gebruiker.

Overigens heeft de gebruiker ook nog de mogelijkheid om JavaScript te injecteren. Dat kan bijvoorbeeld met behulp van de extensie greasemonkey bij Firefox. Ik heb dat verder niet uitgeprobeerd, maar ik neem aan dat de JavaScript dan ook wint. Kortom: nooit alleen op css vertrouwen voor het overbrengen van belangrijke dingen.

Overerving

In het Engels: inheritance.

Elementen erven vaak, maar niet altijd, de eigenschappen van één of meer voorouders. En net als bij echte families kan dat tot forse ruzies leiden. 'Die dochter van jou lijkt op je moeder'. 'Hoe kom je erbij. Ze heeft dat van jouw grootvader. En heb je al 'ns naar jezelf gekeken?'

Als een element binnen een ander element staat, is dat element een kind van het buitenste element. En dat buitenste element heet, je raadt het al, de ouder. Zo heb je ook kleinkinderen, grootouders, enz.

Als een element een afstammeling is van een ander element, kan het soms eigenschappen erven van dat andere element. Bijvoorbeeld de regelhoogte wordt van een <div> doorgegeven aan alle <p>'s, <span>'s, enz. binnen de <div>. Hetzelfde geldt voor de voorgrondkleur: de kleur van tekst en dergelijke.

Maar niet alles wordt doorgegeven. Een border bijvoorbeeld wordt niet doorgegeven aan de kinderen. Elke goede handleiding over css zal bij elke eigenschap opgeven, of die erfelijk is of niet.

Overerving verschilt dus van eigenschap tot eigenschap bij css. Ook zonder dat je dat verder in de css apart aangeeft, worden sommige eigenschappen doorgegeven aan kinderen.

Zodra je zelf css voor een bepaalde eigenschap van een element opgeeft, wint die altijd van overerving.

Je kunt wel met behulp van de gecombineerde selector een bepaalde eigenschap beperken tot de kinderen van één of meer specifieke elementen.

Volgorde van uitvoeren

Bij css worden de afzonderlijke stijlregels volgens een strikte volgorde uitgevoerd. Ook dit is niet heel erg ingewikkeld, maar je moet het wel even weten. Als je bijvoorbeeld een externe stylesheet gebruikt én een stijlblok in de <head> van de pagina, en er zitten tegenstrijdige stijlregels in, welke wint er dan?

Alle css op één plaats

Normaal genomen worden stijlregels die binnen hetzelfde bestand staan, volgens de volgorde binnen dat bestand uitgevoerd: van boven naar beneden. Daarbij 'wint' een latere regel van een eerdere. Hetzelfde geldt voor de stijlregels die bij elkaar in een stijlblok binnen de <head> van een pagina staan. Stel dat in de css de volgende regels staan:

div {color: black;} (...) andere css (...) div {color: red;} (...) andere css (...) div {color: blue;}

De kleur van de div wordt nu blauw, want dat is de laatste regel in de css. En die overrulet de twee eerdere regels. Het maakt niet uit als er andere regels tussen deze drie regels staan: de laatste regel met blauw wint van de twee eerdere met zwart en rood.

Hierbij is een heel belangrijke voorwaarde dat de selector - bij bovenstaande regels div - hetzelfde is, evenveel gewicht, evenveel specificiteit heeft. Als daar verschil in zit, gaan hele andere regels spelen.

(Er wordt ook van links naar rechts gelezen. Als er bijvoorbeeld twee kleuren in dezelfde stijlregel staan, wordt de laatste kleur gebruikt. Dat heeft echter alleen nut als je aan het testen bent. Normaal genomen is het volstrekt zinloos om twee keer dezelfde eigenschap te gebruiken in één stijlregel, dus daar ga ik verder niet op in. En o ja, het kan ook worden gebruikt om voor een oudere browser bijvoorbeeld een simpele achtergrondkleur op te geven, gevolgd door bijvoorbeeld een gradiënt voor nieuwere browsers.)

Meerdere externe stijlbestanden

Als er vanuit de pagina naar meerdere externe stylesheets wordt gelinkt, worden deze in volgorde van linken uitgevoerd. Ze worden als het ware achter elkaar gezet. Als je in de eerste stylesheet een achtergrond rood maakt bij div#floepie, en in de tweede stylesheet maak je bij div#floepie de achtergrond blauw, dan wordt de achtergrond blauw.

Meerdere stylesheets kunnen handig zijn, als de pagina bijvoorbeeld geprint moet worden. Door het toevoegen van media="print" aan de link naar de stylesheet, wordt deze css alleen uitgevoerd als er wordt geprint. De link voor het stijlbestand voor het printen moet ná de link naar de algemene css komen. Door die volgorde overrulen stijlregels uit het printbestand gelijke regels uit het algemene bestand.

En dat is precies wat je wilt. Nu kun je gewoon een stijlbestand voor algemeen gebruik maken. Voor het printen hoef je dan meestal maar weinig aan te passen en kun je meestal voor het grootste deel gewoon het algemene stijlbestand gebruiken.

Op deze manier kun je ook stijlbestanden voor alleen Internet Explorer toevoegen via een handigheidje van Microsoft: conditional comments. En ook hier geldt: stijlregels uit het laatste stijlbestand overrulen die uit eerdere bestanden.

Uit de <head> van de pagina die je nu leest (als het inmiddels niet is veranderd):

<link rel="stylesheet" type="text/css" href="../../css/main.css"> <!--[if IE 7]> <link rel="stylesheet" type="text/css" href="../../css/ie7.css"> <![endif]--> <link rel="stylesheet" type="text/css" href="../../css/printen.css" media="print">

In main.css staat de css die gewoon voor alle browsers geldt.

Tussen <!--[if IE 7]> en <[endif]--> staat voor alle browsers gewoon commentaar, dus ze negeren dit. Maar Microsoft heeft dit gebruikt om conditional comments te maken: alleen als aan de voorwaarde, de conditie, wordt voldaan, wordt de tussenliggende code uitgevoerd. Hier is de voorwaarde dat de browser Internet Explorer 7 moet zijn. En omdat ie7.cssmain.css staat, wordt main.css overruled door ie7.css.

(Eerlijk is eerlijk: ik mag graag mopperen op Microsoft, maar dit is echt ongelooflijk handig. Ik zou willen dat elke browser op deze manier kon worden aangesproken. Je kunt zelfs een bepaalde subversie selecteren, of een hele serie versies. En omdat het geen hack is, is het veilig te gebruiken. Een hack maakt vaak gebruik van fouten in een browser en kan dus worden gerepareerd. Conditional comments zijn opzettelijk aangebracht en blijven dus altijd werken.)

Als derde wordt er gelinkt naar printen.css met in de link de toevoeging media="print". Daardoor wordt deze stylesheet alleen gebruikt bij printen. Omdat dit de allerlaatste is, overrulet deze de twee eerdere.

Hierbij is een heel belangrijke voorwaarde dat de stijlregels uit de verschillende bestanden evenveel gewicht, evenveel specificiteit, hebben. Als daar verschil in zit, gaan hele andere regels spelen.

Geïmporteerde stylesheets

Met behulp van @import kan een stijlbestand in een ander stijlbestand of stijlblok (bovenin de <head> van de pagina) worden geïmporteerd.

De inhoud van een geïmporteerd stijlbestand wordt gewoon volledig tussengevoegd op de plaats waar @import staat. En omdat @import altijd helemaal bovenaan moet staan, is dat altijd helemaal bovenaan.

Het geïmporteerde bestand wordt volledig tussengevoegd, waardoor één stijlblok of stylesheet ontstaat. En daarbinnen gelden weer de gewone regels.

Ik ben overigens geen voorstander van het gebruik van @import. Internet Explorer 7 en eerder hebben er problemen mee, en ook sommige content management systemen en JavaScript-bibliotheken schijnen er niet goed mee uit de voeten te kunnen.

Stylesheet én stijlblok

Bovenin de <head> van een pagina mag ook css worden neergezet. Dat gebeurt vaak als css alleen maar voor één specifieke pagina nodig is. Als je het dan binnen die specifieke pagina zet, spaar je mogelijk 'n aanroep naar de server voor een externe stylesheet uit. En in ieder geval wordt de algemene stylesheet niet nodeloos groot door code die maar op één pagina nodig is.

Een stijlblok is te herkennen aan de eerste en laatste regel:

<style> (...) css (...) </style>

(In html-versies ouder dan versie 5 moet je <style type="text/css"> opgeven. In html5 en later kun je type="text/css" wegelaten.)

In principe gaat een stijlblok in de <head> boven een extern stijlbestand. Het maakt daarbij niet uit of de link naar de externe stylesheet boven of onder het stijlblok staat: het stijlblok wint gewoon altijd.

Maar ook hier geldt weer dat de specificiteit, het gewicht van de selector, gelijk moet zijn. Als de regel uit het externe stijlbestand meer specificiteit heeft, wint die regel toch.

Specificiteit

Ik zou ooit graag nog 'ns een spoedcursus vrij worstelen volgen en daarna 'n halfuurtje alleen willen doorbrengen met degene die deze naam heeft bedacht. In het Engels specificity, niet echt veel makkelijker te typen. En probeer 't 'ns uit te spreken op 'n feestje, als je geen geheelonthouder bent... Lijkt me een duidelijke kandidaat voor de met spoed op te richten Raad voor de Tongbescherming.

Specificiteit is het gewicht dat een bepaalde selector, en daardoor de bij de selector horende css, heeft. Selectors met meer specificiteit overrulen selectors met minder specificiteit. De specificiteit kun je berekenen met behulp van bepaalde regels. En nou ik toch al aan het mopperen was: die regels zijn naar mijn mening nodeloos ingewikkeld gemaakt. Bovendien is de uitleg in de specificatie ook nog 'ns onduidelijk en veel te beknopt.

Hierdoor is er heel veel verkeerde informatie over specificiteit in omloop. Het overgrote deel van de handleidingen over het berekenen van specificiteit, zelfs op bekende sites, is gewoon ronduit onjuist. Waarom dat zo is, kun je lezen bij Wat is er mis met de gebruikelijke manier van specificiteit berekenen?

Na deze vrolijke inleiding begrijp je misschien al, waarom ikzelf vrijwel nooit de specificiteit bereken. Gewoon even uitproberen is vrijwel altijd veel makkelijker, sneller en aangenamer.

In essentie is specificiteit heel simpel. Het aantal (pseudo-)elementen, (pseudo-)classes, attribuut-selectors en id's wordt geteld. Elk van die onderdelen heeft meer of minder gewicht. Door de totalen te vergelijken en rekening te houden met het gewicht van de afzonderlijke soorten, kun je de specificiteit bepalen. Hoe hoger de specificiteit, hoe meer gewicht de selector en dus de bijbehorende stijl heeft.

Als je denkt dat de selector te weinig specificiteit heeft, kun je gewoon even één of meer elementen toevoegen aan het begin van de selector. Meestal is er nog wel 'n <body> of <div#content> of zoiets vrij. Stel dat je denkt dat de specificiteit van de volgende selector te laag is, en dat die daarom niet werkt:

p span.waarschuwing {color: red;}

Als je voor de p wat ouders van de <p> neerzet (die zullen er vrijwel altijd zijn), krijgt de selector meer specificiteit:

body div#content div#footer p span.waarschuwing {color: red;}

Door het toevoegen van body, div#content en div#footer zijn er drie elementen en twee id's bijgekomen. Als de regel nu wel werkt, weet je vrijwel zeker dat het aan een te lage specificiteit lag dat hij eerst niet werkte.

Overigens moet je selectors altijd zo kort mogelijk houden. Het verwerken van selectors kost tijd, maar dat is met de tegenwoordige krachtige computers eigenlijk geen probleem meer. Wat nog wel een probleem is, is de leesbaarheid van de code. Als je specificiteit op wilt lossen door steeds langere selectors te gebruiken, kun je beter een spaghetti-restaurant beginnen. Spaghetti in een restaurant is prima (als de kok kan koken), spaghetti in code leidt altijd tot geestelijke verstopping en virtuele buikloop.

De volgende selector komt uit het horizontale uitklapmenu op deze site. In dit geval kon de selector eigenlijk niet korter vanwege alle :hover's:

#menu ul li:hover ul li:hover ul li:hover ul a:hover

Het ontrafelen van dit soort selectors levert geen bijdrage aan het Bruto Nationaal Geluk van Nederland. Als het dus enigszins kan: houdt selectors zo kort mogelijk.

Blind vertrouwen op specificiteit is niet goed. Zeker in het verleden hadden sommige browsers (ik ga hier niet zeggen dat dat Internet Explorer 6 was) problemen met het juist toepassen van de regels. En ook een toevoeging als !important kan de specificiteit beïnvloeden.

Daarnaast is de berekening van specificiteit in elke nieuwe versie van css steeds 'n heel klein beetje veranderd. Logisch dat makers van browsers daar problemen mee hadden. Als jij een berekening maakt volgens css 2.1, en de browsermaker heeft css2 gebruikt, kun je al 'n klein verschil hebben.

Het zal duidelijk zijn, waarom ik meestal gewoon even uitprobeer. Maar voor de echte doorzetters de rekenregels, zoals die er op dit moment uit schijnen te zien. Ik gebruik daarvoor css 2.1, want css3 is nog in ontwikkeling, en tijdens die ontwikkeling zijn daar al kleine veranderingen in de manier van berekenen in aangebracht. (Op het moment dat ik dit schrijf is de manier van berekenen weer hetzelfde als die in css 2.1. Zucht.)

html-opmaakcodes (<font> en dergelijke)

We beginnen met een hele simpele: opmaakcodes in de html, zoals de zwaar verouderde <font>-tag. De specificiteit daarvan is heel makkelijk te berekenen: ze hebben altijd een specificiteit van 0. Gewoon helemaal geen specificiteit. En omdat een css-regel altijd minimaal een heel klein beetje specificiteit móét hebben, wint de css áltijd van de opmaakcode in de html. Als je dus met behulp van <font> de tekstkleur rood maakt, en in de css geef je zwart op, dan wordt de tekstkleur áltijd zwart.

Althans: dat is zoals het hoort te werken. Maar ik zou er nog geen afgeknipte nagel om durven te verwedden, dat dit altijd bij elke mogelijke combinatie van html en css in elke browser foutloos werkt. Als een site niet meer wordt veranderd en goed werkt: die oude tags lekker zo laten staan. En als een site nog wel verandert en je css wilt gaan gebruiken, is het echt veel beter om die verouderde tags gewoon helemaal weg te halen.

Inline-stijl (<div style="...">)

Een inline-stijl is stijl die rechtstreeks bij het element staat, waar de stijl voor is bedoeld. Voorafgegaan door style=" en afgesloten door ".

<div style="background: blue;"> <span style="color: red;"> <p style="border: black dashed 1px; outline: blue solid 3px;">

De drie tags hierboven bevatten alle drie een inline-stijl.

Ook dit is een makkelijke. Een inline-style wint gewoon áltijd van elke andere stijlregel. Daardoor is een inline-stijl heel handig te gebruiken, als je even snel iets uit wilt proberen.

Hier is één uitzondering op. Als je !important gebruikt in een stijlregel in een extern stijlbestand of in een stijlblok in de <head>, dan wint de regel met !important.

(De truc om een inline-stijl bij het berekenen van de specificiteit een waarde van 1000 te geven, zoals je die veel op internet ziet, is onjuist. Waarom dat onjuist is, staat bij Wat is er mis met de gebruikelijke manier van specificiteit berekenen?)

Specificiteit van de selector berekenen

Bij een gewone stijlregel zonder !important, wordt de specificiteit vastgesteld aan de hand van de selector. De selector is het deel voor de {

div {background: red;}

Selector: div

body#ul p span.waarschuwing {color: red;}

Selector: body#ul p span.waarschuwing

Meerdere selectors

Vaak zijn er meerdere selectors. Dat is te herkennen aan de komma tussen de verschillende selectors. In dat geval moet voor elke selector apart de specificiteit worden berekend. De specificiteit van een selector geldt alleen maar voor de elementen, waar de selector betrekking op heeft.

div#een, p.twee {color: red;}

In bovenstaande regel heeft de selector div#een helemaal niets te maken met de selector p.twee. Ze hebben alleen toevallig dezelfde css, meer niet.

Als Pietje hetzelfde model broek draagt als Keesje, maakt ze dat nog niet tot broertjes van elkaar. In bovenstaande regel is de specificiteit van div#een ook nog 'ns anders dan die van p.twee, zoals verderop aan de orde komt.

id's tellen

Om de specificiteit van een selector te bepalen tel je allereerst het aantal id's in de selector. Een id is de naam die je in de html met id="..." aan een element hebt gegeven. In de css is een id te herkennen aan de # die eraan voorafgaat:

div#uitleg {background: red;}

1 id: #uitleg

body#tekst #uitleg p#waarschuwing {color: red;}

3 id's: #tekst, #uitleg en #waarschuwing

Classes, pseudo-classes en attribuut-selectors tellen

Vervolgens tel je hoeveel classes, pseudo-classes en attribuut-selectors in de selector zitten.

Een element wordt in de html ingedeeld in een class met class="...". In de css is een class te herkennen aan de . (punt) die eraan voorafgaat.

Een pseudo-class is iets als :hover of :first-child. Voor de bepaling van de specificiteit telt dit net zo mee als een gewone class. Een volledige lijst en wat het ongeveer is kun je vinden bij pseudo-class.

Een attribuut-selector is iets als abbr[attribuut]. Het is te herkennen aan de [ en ]. Voor de bepaling van de specificiteit telt dit net zo mee als een gewone class. Een iets langere uitleg vind je bij attribuut-selector.

(Voor de volledigheid: de attribuut-selector :not() telt niet zelfstandig mee, maar het attribuut dat erin zit wel.)

Voorbeelden:

div.afbeelding {background: blue;}

1 class: .afbeelding

div.hoofdstuk p.inleiding .kop {font-size: 1.2em;}

3 classes: .hoofdstuk, .inleiding en .kop

div.hoofdstuk p.inleiding a:hover {background: blue;}

2 classes: .hoofdstuk en .inleiding. 1 pseudo-class: :hover. Voor de bepaling van de specificiteit telt dit gewoon als 2 + 1 = 3 classes.

div.hoofdstuk p.inleiding:hover abbr[title] {display: block;}

2 classes: .hoofdstuk en .inleiding. 1 pseudo-class: :hover. 1 attribuut-selector: [title]. Voor de bepaling van de specificiteit telt dit gewoon als 2 + 1 + 1 = 4 classes.

Elementen en pseudo-elementen tellen

Ten slotte tel je hoeveel elementen en pseudo-elementen er in de selector zitten. Elementen en pseudo-elementen zijn even belangrijk voor het bepalen van de specificiteit. Ook dit is weer een kwestie van simpel tellen.

Een element is hetzelfde als een element in de html, dus dingen als body, div, h1, span, enz. De universele selector * telt niet mee, want dat is geen html-element. (Officieel zou ik een 'element' hier trouwens 'type-selector' moeten noemen: wat in de html een element heet, heet in de css type-selector.)

Een pseudo-element is iets als :first-letter. Voor de bepaling van de specificiteit telt dit net zo mee als een gewoon element. Een volledige lijst en wat het precies is, kun je vinden bij pseudo-elementen.

div {background: red;}

1 element: div

div p {background: blue;}

2 elementen: div en p

div p span:first-letter {color: #faa;}

3 elementen: div, p en span. 1 pseudo-element: first-letter. Voor de bepaling van de specificiteit telt dit gewoon als 3 + 1 = 4 elementen.

Wat niet meetelt

Naast de hierboven al genoemde universele selector *, tellen nog drie symbolen niet mee voor de bepaling van de specificiteit: >, + en ~.

Deze drie symbolen zeggen alleen iets over de elementen waarvoor bepaalde css geldt, maar ze veranderen de waarde van de specificiteit niet. Net zoals de * gewoon volledig negeren dus.

Alles samen tellen

Nu heb je dus drie getallen: eentje voor de id's, eentje voor de (pseudo)-classes en attribuut-selectors, en eentje voor de (pseudo-)elementen. Alleen: heel vaak heeft een selector natuurlijk id's én classes én elementen. Voor de telling maakt dat weinig uit:

div#content p span.waarschuwing {color: #faa;}

1 id: #content. 1 class: waarschuwing. 3 elementen: div, p en span.

div#content div[title] p a:hover span.waarschuwing:first-letter {color: #faa;}

1 id: #content. 1 class: waarschuwing. 1 pseudo-class: :hover. 1 attribuut-selector: [title]. 4 elementen: div, div, p en span. 1 pseudo-element: :first-letter.

pseudo-classes, attribuut-selectors en classes zijn hetzelfde voor de specificiteit, dus ik heb 1 + 1 + 1 = 3 classes.

elementen en pseudo-elementen zijn hetzelfde voor de specificiteit, dus ik heb 4 + 1 = 5 elementen.

In totaal heb ik dus 1 id, 3 classes en 5 elementen.

Elke stijlregel levert op deze manier drie getallen op: het aantal id's, het aantal (pseudo-)classes en attribuut-selectors, en het aantal (pseudo-)elementen.

Neem de volgende html

<div id="buitenste"> <div class="binnenste"> <p id="para">tekst</p> </div> </div>

Ik heb hier vier stijlregels, die alle vier gelden voor deze html: een <p> binnen twee <div>'s, al dan niet met id of class. De volgende stijlregels leveren allemaal blauwe tekst in de <p> op:

div div p#para {color: blue;}

1 id, 3 elementen.

div#buitenste div p#para {color: blue;}

2 id's, 3 elementen.

div#buitenste div.binnenste p#para {color: blue;}

2 id's, 1 class, 3 elementen

div div.binnenste p#para {color: blue;}

1 id, 1 class, 3 elementen

De specificiteit van deze vier regels is anders. En in dat geval bepaalt niet de volgorde van de stijlregels in het css-bestand, maar de specificiteit welke regel er 'wint'.

Nog 'n keer dezelfde css, maar nu met vier verschillende kleuren:

div div p#para {color: blue;}

1 id, 3 elementen.

div#buitenste div p#para {color: red;}

2 id's, 3 elementen.

div#buitenste div.binnenste p#para {color: black;}

2 id's, 1 class, 3 elementen

div div.binnenste p#para {color: white;}

1 id's, 1 class, 3 elementen

Welke kleur krijgt de <p> nu? De volgorde zegt niet alles meer, want die is alleen bepalend als de specificiteit hetzelfde is.

Om de specificiteit te bepalen, kijk je eerst naar het aantal id's. Een id wint altijd van classes, hoeveel classes er ook zijn, al staan er duizend. En hoe hoger het aantal id's, hoe hoger de specificiteit.

In bovenstaande css hebben de kleuren red en black beide 2 id's, en blue en white maar 1. Blue en white verliezen dus van red en black, ongeacht de volgorde van de regels, want ze hebben minder specificiteit.

Blijven red en black over als mogelijke winnaars. Beide hebben 2 id's, dus dat maakt geen verschil, dat levert geen winnaar op.

black heeft 1 class. En 'n class wint altijd van elementen, hoeveel elementen er ook staan. Red heeft helaas geen class. Dus black wint. Dat ze allebei drie elementen hebben speelt verder helemaal geen rol meer.

Nog eens, maar met andere aantallen id's, classes en elementen:

div#content div#hoofdstuk p#para {color: blue;}

3 id's, 3 elementen.

body div#buitenste div p#para {color: red;}

2 id's, 4 elementen.

div#buitenste div.binnenste p#para a:hover[title] {color: black;}

2 id's, 1 class, 1 pseudo-class, 1 attribuut-selector, 4 elementen.
1 class, 1 pseudo-class en 1 attribuut-selector tellen als 3 classes, dus voor de duidelijkheid:
2 id's, 3 classes, 4 elementen)

body#pagina div div.binnenste p#para.een.twee.drie.vier.vijf.zes {color: white;}

2 id's, 7 classes, 4 elementen

Wie wint? Heel simpel. Er is er maar één met 3 id's, dus die wint: blue Naar het aantal classes en elementen hoef ik niet eens te kijken.

Als laatste een typische instinker. Weer dezelfde html als eerst:

<div id="buitenste"> <div id="binnenste"> <p id="para">tekst</p> </div> </div>

Twee bijbehorende stijlregels:

div div p#para {color: blue;} div#buitenste div#binnenste p {color: red;}

Welke kleur krijgt de tekst? Je zou denken blauw, omdat de id van p#para wordt gebruikt.

Mooi niet dus. Beide regels hebben 3 elementen. Maar de tweede regel bevat 2 id's, en de eerste maar 1. Dat die twee id's bij de ouder-divs van de <p> horen, maakt niets uit. 2 id's winnen gewoon van 1. Altijd. Punt.

Laat ik aan de eerste regel een id toevoegen:

div#buitenste div p#para {color: blue;} div#buitenste div#binnenste p {color: red;}

Pech voor de eerste regel: nog steeds wint de tweede regel. Beide regels hebben nu 2 id's en 3 elementen: dezelfde specificiteit dus. En in dat geval wint de laatste regel.

Ik maak de eerste regel nóg wat langer, geef nog wat meer specificiteit:

body div#buitenste div p#para {color: blue;} div#buitenste div#binnenste p {color: red;}

Nu wint eindelijk de eerste regel. Beide regels hebben 2 id's, dus wat dat betreft hebben ze dezelfde specificiteit. Maar de eerste regel heeft nu 4 elementen, en de tweede regel maar 3. En dat geeft de eerste regel net genoeg specificiteit om te winnen van de tweede regel.

Vuistregel voor masochisten

Mogelijk ben jij wat masochistisch aangelegd en wil je dit oerwoud aan regels over volgorde en specificiteit precies volgens de letter volgen. Dan zijn dit stukje tekst en mijn diepe gevoel van deelneming voor deze zelfkwelling aan jou gericht.

Toch nog 'n laatste poging je van je rekenwerk af te houden...

Het is dus vrijwel altijd veel simpeler om gewoon even met het aantal elementen, classes, en dergelijke te spelen. Dat gaat veel sneller dan die berekeningen. Bovendien weet je gelijk zeker dat het werkt (dat wil zeggen: als je in alle bekende browsers test!). Specificiteit is namelijk gewoon niet helemaal foutloos, zeker niet in oudere browsers, en zeker niet zolang w3c als hobby heeft het steeds iets veranderen van de regels.

Het is belangrijk om de selectors niet krankzinnig lang te maken. Een selector met 37 id's wint ongetwijfeld, maar ik ben bang dat de verkoper van kalmerende middelen ook wint.

Als er een conflict is tussen twee stijlregels, probeer dan - als dat enigszins kan - de langere selector korter te maken. Of geef 'n element 'n id of class, zodat je het rechtstreekser kunt aanroepen.

Doe vooral niet wat ik op deze site heb gedaan. In de downloads valt het nog wel mee, maar op de site zelf staan nogal wat (hele) lange selectors. Anders had ik nóg meer classes en id's moeten gebruiken, of nóg meer stylesheets.

Maar deze site is, wat css betreft, geen 'normale' site. Deze site heeft verhoudingsgewijs een idiote hoeveelheid css, omdat hij nou eenmaal voorbeelden op dat gebied bevat. De gemiddelde site bevat veel en veel minder css en zal dus ook veel kortere selectors kunnen gebruiken, omdat er minder conflicten op het gebied van specificiteit zullen zijn.

Als je nou nóg aan het rekenen wilt, tja, dan houdt het op. Sterkte. Hierboven staan de precieze regels. En hieronder staat een vuistregel.

!important wint altijd (en zou je niet moeten gebruiken).

Als !important niet wordt gebruikt, wint een inline-stijl altijd.

Als !important niet wordt gebruikt en er is ook geen inline-stijl:

Het grootste aantal id's wint.

Bij een gelijk aantal id's wint het grootste aantal classes (inclusief pseudo-classes en attribuut-selectors).

Bij een gelijk aantal id's én een gelijk aantal classes, wint het grootste aantal elementen (inclusief pseudo-elementen).

Bij een gelijk aantal id's, een gelijk aantal classes én een gelijk aantal elementen, bepaalt de volgorde van uitvoeren van de stijlregels welke er wint.

Wat is er mis met de gebruikelijke manier van specificiteit berekenen?

Op heel veel sites wordt uitgelegd, hoe je specificiteit kunt berekenen. In het overgrote deel van de gevallen, zelfs op bekende sites, is de manier van berekenen die daarbij wordt gebruikt ronduit onjuist.

Ik denk dat dit komt, doordat de uitleg in de specificatie over het berekenen van specificiteit erg summier en onduidelijk is. Tegelijkertijd wordt de manier van berekenen erg ingewikkeld voorgesteld. Bovendien staan in de specificatie alleen voorbeelden met kleine aantallen id's, classes, enz. Die aantallen worden dan gewoon achter elkaar gezet, waardoor de indruk ontstaat dat er met het tientallig stelsel wordt gewerkt. Een bron van misverstanden.

De complete uitleg in de specificatie beperkt zich feitelijk tot:

Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity. (w3.org/TR/selectors-3/#specificity)

En daar moet je dan maar uit afleiden dat er niet opgeteld moet worden, maar achter elkaar gezet, en dat er geen tientallig stelsel moet worden gebruikt.

De meest gebruikelijke uitleg op internet:

Geef een inline-stijl 1000 punten.

Geef elke id 100 punten.

Geef elke class, pseudo-class (en attribuut-selector, als die tenminste niet gewoon wordt vergeten) 10 punten.

Geef elk element en pseudo-element 1 punt.

Deze getallen zet je nu onder elkaar en tel je op.

3 id's en 4 elementen geeft dan:

300
        4

Totaal 304

Zo'n selector wint dan van een selector die bijvoorbeeld na optelling 263 heeft.

Deze manier van berekenen gaat goed, zolang je niet meer dan 9 id's, 9 classes of 9 elementen hebt. Na 9 komt 10 in het tientallig stelsel. We maken nog eens een berekening op bovenstaande manier, maar nu met 2 id's, 10 classes en 5 elementen.

2 id's en 10 classes en 5 elementen geeft:

200
    100
        5

Totaal 305

305 is meer dan 304, dus een stijlregel met 2 id's, 10 classes en 5 elementen zou winnen van een regel met 3 id's en 4 elementen. En dat klopt dus niet. Een stijlregel met meer id's wint áltijd van een stijlregel met minder id's.

Zou je de id's, de classes en de elementen gewoon als losse kolommen zien en niet optellen, dan zou je gelijk zien dat de 3 id's winnen van de 2 id's. Ongeacht het aantal classes en elementen.

Een variant hierop die je ook vaak ziet, is het achter elkaar zetten van de totalen.

Bij 3 id's, 0 classes en 4 elementen werkt dat aldus:

3 - 0 - 4 maakt 304

Zo'n selector wint dan van een selector die bijvoorbeeld na achter elkaar zetten 263 heeft.

Ook deze manier van berekenen gaat goed, zolang je weer niet meer dan 9 id's, 9 classes of 9 elementen hebt. Na 9 komt 10 in het tientallig stelsel, en dan ontstaat er plotsklaps een waanzinnige sprong in grootte. We maken nog eens een berekening, maar nu met 2 id's, 10 classes en 5 elementen.

2 id's en 10 classes en 5 elementen geeft:

2 - 10 - 5 maakt 2105

Dat is groots, zo'n selector wint overal van! Als het zou kloppen. De fout is hier beter zichtbaar dan bij de eerste methode van optellen: na 9 komt 10 in het tientallig stelsel. Maar de '10' uit bovenstaande berekening is geen '10' zoals de '10' na '9'. De '10' uit bovenstaande berekening geeft alleen aan dat het 'meer dan 9' is, maar niet dat het een tiental is. Dat 'meer dan 9' bij ons wordt weergegeven als 10, is gewoon een menselijke afspraak, een naampje, een rekenhulp, geen natuurwet.

Alleen hebben we in het tientallig stelsel gewoon niet meer dan 10 symbolen om een cijfer weer te geven. Er zijn gewoon niet meer dan 10 namen voor een getal. Na de 9 zijn ze op.

In het hexadecimale (zestientallige, zijn computers ook dol op) stelsel gebruik je de cijfers 0 tot en met 9 en de letters A tot en met F. Dan kun je 16 eenheden weergeven: 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 enz. Daar zou de laatste samenvoeging geen 2 - 10 - 5 worden, maar:

2 - A - 5 maakt 2A5

Maar ook hier gaat het mis, alleen nu pas bij aantallen boven de 16.

Als je ervoor kiest om specificiteit te berekenen en de pech hebt meer dan 9 id's, elementen of classes (of dingen die daar gelijkwaardig aan zijn) te gebruiken, kom je er met deze manier van berekenen nooit achter, waarom het mis gaat. Bij het berekenen van specificiteit moet je niet het tientallig stelsel gebruiken. De gevonden totalen moeten in aparte kolommen, los van elkaar, worden vergeleken.

(Ik heb de inline-stijl hier nauwelijks genoemd, maar dat gaat op precies dezelfde manier mis. 10 id's zouden evenveel wegen als 1 inline-stijl, en dat is ook onzin.)

Een uitstekende (Engelstalige) uitleg over de foutieve manier van berekenen is te vinden op Juicy Studio.

!important, een css-steenpuist op je achterste

Duidelijk een geval van verdringing. Was ik !important haast vergeten. Alsnog.

!important is als een steenpuist op je achterste. Je bent je steeds hinderlijk bewust van zijn aanwezigheid, hij kan op de meest vreemde plaatsen in je lichaam voor problemen zorgen, hij belemmert je enorm in je flexibiliteit en het is niet echt een verrijking van je lichamelijke mogelijkheden. De steenpuist heeft ook nog met !important gemeen, dat ze beide voor vreselijke kleuren, enorme overflow, en andere ongewenste eigenschappen op de meest onverwachte plaatsen en de meest ongelegen momenten kunnen zorgen.

Een nettere vergelijking voor als je niet tegen een steenpuist op je zitvlak kunt: een stijlregel die zichzelf !important vindt, lijkt heel erg op mensen die zichzelf belangrijk vinden. Ze richten beide rampen aan en zijn beide onuitstaanbaar. !important is zeg maar wat mensen als Balkenende en Verhagen in de politiek zijn: je hebt er niets aan, maar als ze er zijn, veroorzaken ze problemen.

(Satanische grijns: dat is het leuke van eigen teksten schrijven. Kun je erin kwijt wat je wilt. En klagen heeft geen nut, want het is gratis en ik verplicht geen enkele CDA-aanhanger om mijn uitleg te lezen. En al helemaal niet om het met me eens te zijn.)

Het gebruik van !important maakt een stijlbestand uiterst moeilijk te volgen. Het wint overal van, zelfs van een inline-style. Als er 'n probleem is, zoek je je ongans naar de oorzaak. Kilometers verderop blijkt dan in de stylesheet een stijlregel te zitten, die zichzelf zo ontzettend !important vindt, dat hij als de Opstelten van de css over alles en iedereen heen walst.

Los daarvan wordt het niet in alle browsers goed afgehandeld. Ik ga niet zeggen dat oudere versies van Internet Explorer !important op weerzinwekkende wijze mishandelden. Na mijn fantastische cursus Holistisch Positivistisch Denken voor de Spiritueel Aangelegen Webmaster omschrijf ik het zo: Internet Explorer 7 en later handelen !important (vrijwel) goed af.

(Ja zeg, ik móét wel zeggen dat die cursus goed was. Ik kan toch moeilijk zeggen dat ik zo stom ben geweest me te laten tillen voor 2000 euro om op het weerzinwekkende tijdstip van vijf uur 's ochtends bij volle maan in m'n blootje in de vrieskou rond de Heilige Eik te mogen dansen. Kostte ook nog 'n bekeuring wegens schennis van de openbare eerbaarheid. Agent was 'n typisch geval van spiritualiteitsvernauwing.)

Lezers die gevoelig zijn voor subtiele, kleine aanwijzingen, hebben mogelijk al opgemerkt hoe ik over !important denk: niet gebruiken. Bagger. Weg ermee! En ik ben niet de enige die er zo over denkt.

important! is alleen zinvol in de stylesheet van een gebruiker. Het kan ervoor zorgen dat de stylesheet van de gebruiker altijd wint, wat belangrijk kan zijn voor mensen die bijvoorbeeld een grotere letter willen, of meer contrast. Of om snel te kijken of die stijlregel misschien wel werkt, als je er even !important bij zet, tijdens het testen.

Engelse vertaling van sleutelbegrippen

De meeste informatie op internet is Engelstalig. Omdat ikzelf heel veel van internet afhaal, gebruik ik Engelse en Nederlandse begrippen in dit artikel vrolijk door elkaar heen.

Bij zoeken op internet kun je vaak beter zoeken op de Engelse zoektermen, omdat je dan vrijwel altijd veel en veel meer informatie vindt. Daarom hieronder een lijstje met Nederlandse sleutelbegrippen en hun Engelse tegenhangers.

Overigens bestaan voor veel begrippen geen Nederlandse namen, dus af en toe is het een beetje een vreemde mengelmoes van Nederlands en Engels.

NederlandsEngels
afstammelingdescendant
attribuutattribute
attribuut selectorattribute selector
eigenschapproperty
gecombineerde selectordescendant selector
identiteitidentity
inline-stijlinline style
meerdere selectorsmultiple selector(s)
onderstreping ('_')underscore
opmaaklayout
overervinginheritance
schermlezerscreenreader
specificiteitspecificity
standaarddefault
standaard stijlbestand browserdefault style, browser style
stijlstyle
stijlbestandstylesheet
stijlbestand van gebruikeruser stylesheet
stijlblokinternal style
stijlregelstyle rule
universele selector ('*')universal selector
verbindingsstreepje ('-')hyphen

Bekende problemen en bugs

Echte bugs zijn er eigenlijk niet met deze simpele selectors. In oudere browsers, zoals Internet Explorer 6, ligt dat anders. Als je dat soort ongein nog wilt ondersteunen, is het dus belangrijk goed te testen. (Maar dat is natuurlijk altíjd belangrijk.)

Oudere browsers hebben vooral af en toe problemen met de specificiteit, met !important en met meerdere classes. Als je bijvoorbeeld meerdere classes gebruikt in je css (div.rood.groen), gebruikt Internet Explorer 6 alleen de eerste class.

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