In een vorig artikel behandelden we de verschillende CSS selectoren. Je zal op een punt komen in je werk waar je verschillende CSS regels zal hebben die hetzelfde element selecteren.  Welke regel "wint" er in zo'n gevallen?  Welke zal er dus worden toegepast op het element?  Dit wordt bepaald door een mechanisme dat de Waterval (Cascade) genoemt wordt.  Het is ook verwant met overerving (elementen nemen soms eigenschapswaarden over van hun ouders, maar andere niet).  In dit artikel definiëren we wat de waterval is, wat specificiteit is, wat belang is en hoe eigenschappen overerven van verschillende regels.

Vereisten: Algemene computervaardigheden, de basissoftware geïnstalleerd hebben, algemene kennis van hoe met files werken, en een basiskennis van HTML (bekijk Introductie tot HTML) en een idee van hoe CSS werkt (bestudeer de vorige artikelen in deze module).
Doelstelling: Leren over de waterval en specificiteit, en hoe overerving werkt in CSS.

De laatste stijl voor een element kan gedefinieerd worden op een pak verschillende plaatsen, die op complexe manieren interageren.  Deze complexe interactie maakt CSS enorm krachtig, maar kan haar ook verwarrend maken en moeilijk om te debuggen.  Dit artikel wil wat van die complexiteit ophelderen.  Als je het artikel niet onmiddelijk begrijpt, geen zorgen.  Dit is één van de moeilijkste onderdelen van CSS.  Probeer het nu uit, maar keer terug wanneer je met vragen over de waterval en overerving zit.

De waterval

CSS is een acroniem voor Cascading Style Sheets, wat al direct duidelijk maakt dat het idee van de waterval (cascade) belangrijk is.  Simpel gesteld betekent dit dat de volgorde van de CSS regels ertoe doet, maar het is ingewikkelder dan dat.  Welke selector wint in de waterval hangt af van drie factoren (deze zijn opgelijst in rangorde — diegene die hoger op de lijst staan zullen zwaarder wegen dan de lagere):

  1. Belang
  2. Specificiteit
  3. Bron volgorde

Belang

In CSS is er een speciale syntaxregel die je kan gebruiken om er voor te zorgen dat een regel altijd zwaarder zal wegen dan de andere: !important.

Laat ons kijken naar een voorbeeld:

<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>
#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

Dit geeft het volgende resultaat:

Laat ons dit voorbeeld overlopen om te zien wat er gebeurt.

  1. Je zal zien dat de color en padding waarden van de derde regel werden toegepast, maar de background-color waarde niet.  Waarom?  Ze zouden toch alledrie moeten toegepast worden, omdat regels die later in de broncode comen de eerdere regels overschrijven.
  2. De regel erboven wint echter, omdat IDs en klasse selectoren een hogere specificiteit hebben dan element selectoren (je leert hier meer over in de volgende sectie).
  3. Beide elementen hebben een better classe, maar de tweede heeft ook de winning id. Aangezien IDs een hogere specificiteit hebben dan klasses (je kan maar één element hebben met een bepaald ID op een pagina, maar meerdere elementen met dezelfde klasse ... IDs zijn dus zéér specifiek in welk element ze selecteren) zou de rode achtergrondkleur en de rand van 1 zwarte pixel allebei moeten toegepast worden op het tweede element.  Het eerste element krijgt dan een grijze achtergrond en geen rand, gezien de klasse.
  4. Het tweede element krijgt de rode achtergrondkleur wel, maar geen rand.  Waarom?  Omwille van de !important declaratie in de tweede regel, waaronder die achter border: none wat betekent dat deze declaratie de rand-waarde in de vorige regel zal overschrijven, ook al heeft de ID een hogere specificiteit.

Opmerking: De enige manier om de !important declaratie te overschrijven zou zijn om een andere !important declaratie van dezelfde specificiteit later in de broncode te zetten, of één met een hogere specificiteit eender waar in de broncode.

Het is belangrijk te weten dat !important bestaat, zodat je weet wat het is wanneer je het tegenkomt in de code van anderen.  MAAR. We adviseren ten strengste om dit nooit te gebruik tenzij als laatste redmiddel.  Een situatie waarin je het mogelijks zou moeten gebruiken is wanneer je werkt met een CMS, en je kan de CSS-modules niet aanpassen.  Wil je dan toch een stijl overschrijven kan het soms enkel met gebruik van!important.   Maar gebruik het niet als je het kan vermijden.  Omdat !important de waterval zo ingrijpend verandert, kan het zeer moeilijk zijn om CSS te debuggen, zeker in een groter stylesheet.

Het is ook nuttig om te weten dat het belang van een CSS declaratie afhangt van waar die gedefinieerd is.  Het is mogelijk voor gebruikers om hun eigen stylesheets te definiëren die de stijlen van de developper overschrijven.  Een voorbeeld hiervan kan zijn indien iemand slechtziend is en de lettergrootte van alle webbapgina's die hij bezoekt wil verdubbelen zodat die gemakkelijker te lezen zijn.

Conflicterende declaraties zullen toegepast worden in de volgende orde, waarbij latere declaraties eerdere overschrijven:

  1. Declaraties in de user agent stylesheets (bv. de standaard instellingen van browsers, die gebruikt worden wanneer er geen andere stijlen meegegeven worden met een webpagina).
  2. Normale declaraties in stylesheets van de gebruiker (de eigen stijlen die ingesteld worden door een gebruiker, zie het voorbeeld hierboven).
  3. Normale declaraties in de stylesheet van een auteur (dit zijn de stijlen die wij als developpers definiëren).
  4. Belangrijke declaraties in de stylesheets van auteurs (!important).
  5. Belangrijke declaraties in de stylesheets van gebruikers (!important).

Het is logisch dat de stylesheets van webdeveloppers die van gebruikers kunnen overschrijven, zodat het design getoond wordt zoals de bedoeling is.  Maar soms zijn er goede redenen om gebruikers het laatste woord te geven, en dat kan met behulp van de !important declaratie in hun regels.

Specificiteit

Specificiteit is een maatstaf voor hoe specifiek een selector is, me andere woorden hoeveel elementen hij zou kunnen selecteren.  Zoals gezien in het voorbeeld hierboven, hebben elementselectoren een lage specificiteit.  Klasseselectoren geven een hogere specificiteit en overschrijven dus elementselectoren.   ID selectoren hebben een nog hogere specificiteit en winnen dus tegen zowel klasse- als elementselectoren.  De enige manier om te winnen tegen een ID selector is om !important te gebruiken.

De hoeveelheid specificiteit die een selector heeft wordt gemeten met behulp van vier verschillende waarden (of componenten).  Je kan die beschouwen als duizendtallen, hondertallen, tientallen en eenheden, vier nummers in vier rijen:

  1. Duizendtallen: Indien de declaratie in een style attribuut staat, dan zet je hier een 1.  Zulke declaraties hebben geen selectoren, dus hun specificiteit is altijd een ronde 1000.  Staat de declaratie ergens anders dan is dit 0.
  2. Honderdtallen: Voor elke ID selector die deel uit maakt van de totale selector verhoog je dit met 1.
  3. Tientallen: Verhoog deze kolom met 1 voor elke klasseselector, attribuutselector of pseudo-klasseselector die in de totale selector staat.
  4. Eenheden: Verhoog deze kolom met 1 voor elke elementselector of pseudo-elementselector die deel uitmaakt van de totale selector.

Opmerking: De universele selector (*), combinatoren (+, >, ~, ' ') en negatieve pseudo-klasse (:not) hebben geen effect op de specificiteit.

De volgende tabel toont een paar voorbeelden om een idee te geven.  Probeer deze te overlopen en te begrijpen waarom ze een bepaalde specificiteit gekregen hebben.

Selector Duizendtallen Honderdtallen Eenheden Ones Totale specificiteit
h1 0 0 0 1 0001
#important 0 1 0 0 0100
h1 + p::first-letter 0 0 0 3 0003
li > a[href=*"en-US"] > .inline-warning 0 0 2 2 0022
#important div > div > a:hover, binnen het style attribuut van een element 1 1 1 3 1113

Opmerking: Indien meerdere selectoren dezelfde specificiteit hebben en hetzelfde belang, zal de selector die later in de Source order komt de andere overschrijven.

Voor we verder gaan, kijken we eerst naar een live voorbeeld.  Hier is de HTML die we gebruiken:

<div id="outer" class="container">
  <div id="inner" class="container">
    <ul>
      <li class="nav"><a href="#">One</a></li>
      <li class="nav"><a href="#">Two</a></li>
    </ul>
  </div>
</div>

En hier is de CSS voor het voorbeeld:

/* specificity: 0101 */
#outer a {
  background-color: red;
}

/* specificity: 0201 */
#outer #inner a {
  background-color: blue;
}

/* specificity: 0104 */
#outer div ul li a {
  color: yellow;
}

/* specificity: 0113 */
#outer div ul .nav a {
  color: white;
}

/* specificity: 0024 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* specificity: 0023 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* specificity: 0033 */
div div .nav:nth-child(2) a:hover {
  border: 10px double black;
}

a {
  display: inline-block;
  line-height: 40px;
  font-size: 20px;
  text-decoration: none;
  text-align: center;
  width: 200px;
  margin-bottom: 10px;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
}

Het resultaat dat we krijgen van deze code is als volgt:

Laten we dit eens overlopen.  We zijn enkel geïnteresseerd in de eerste zeven regels van dit voorbeeld, en we hebben voor elke regel hun specificiteit in commentaar gezet.

  • De eerste twee selectoren concurreren om de stijl van de achtergrondkleur voor een link te definiëren.  De tweede selector wint en zorgt ervoor dat de achtergrondkleur blauw wordt omdat er een extra ID selector in de keten zit: specificiteit 201 is hoger dan 101.
  • De derde en vierde selectoren concurreren om de tekstkleur van de link te definiëren.  De tweede wint en maakt de tekst wit, ook al heeft het één elementselector minder.  De missende selector is in de plaats van een elementselector een klasseselector geworden, die 10 waard is ipv. 1.  De winnende specificiteit is dus 113 tov. 104.
  • Selectoren 5–7 concurreren om de randkleur van de link in te stellen indien de muis erboven zweeft.  Selector zes verliest overduidelijk van selector vijf, met een specificiteit van 23 tegenover 24, omdat er een elementselector minder in de keten zit.  Selector zeven daarentegen verslaat zowel vijf als zes.  Hoewel het een gelijk aantal subselectoren heeft als de vijfde selector, is er ook hier een elementselector verwisselt met een klasseselector.  De winnende specificiteit is dus 33 tov. 23 en 24.

Opmerking: Als je dit nog niet gedaan hebt, herbekijk dan nog eens alle selectoren, zodat je zeker begrijpt waarom de specifiteitswaarden toegekend werden zoals hierboven weergegeven.

Bron volgorde

zoals hierboven vermeld, is er een derde factor die in het spel komt wanneer verschillende selectoren zowel hetzelfde belang als dezelfde specificiteit hebben.  Bron volgorde betekent dat latere regels zullen winnen van eerdere regels.  Een voorbeeld:

p {
  color: blue;
}

/* This rule will win over the first one */
p {
  color: red;
}

Contrasteer dat met onderstaand voorbeeld waar de eerste regel wint omdat de specificiteit ervan hoger is.

/* This rule will win */
.footnote {
  color: blue;
}

p {
  color: red;
}

Een bedenking over het mengen van regels

Eén iets wat je in gedachten moet houden wanneer je de watervaltheorie overloopt, is dat dit allemaal plaatsvindt op het niveau van een eigenschap.  Eigenschappen overschrijven andere eigenschappen, maar wat niet gebeurd is dat een regel een andere regel overschrijft.  Wanneer verschillende CSS regels hetzelfde element selecteren, dan zullen ze allemaal worden toegepast op dat element.  Enkel indien er voor een eigenschap in meerdere regels een declaratie is, zal de waterval in werking treden om conflicten op te lossen.

Kijken we naar een voorbeeld, met eerst de HTML:

<p>I'm <strong>important</strong></p>

En vervolgens de CSS:

/* specificity: 0002 */
p strong {
  background-color: khaki;
  color: green;
}

/* specificity: 0001 */
strong {
  text-decoration: underline;
  color: red;
}

Resultaat:

In dit voorbeeld, wordt de color eigenschap van de eerste regel overschreven door de kleureigenschap van de tweede regel.  Maar, zowel de background-color van de eerste regel en de text-decoration van de tweede regel worden toegepast op het <strong> element. Je zal zien dat de tekst van dat element vet staat: dit is het gevolg van de standaard stylesheets van de browser.

Overerving

CSS overerving is het laatste stukje van de puzzel dat we moeten bestuderen om volledig te begrijpen welke stijl zal toegepast worden op een element.  Het idee is dat sommige eigenschappen waarden zullen krijgen die overgeërfd worden door de kinderen ervan, en sommige niet.

  • Zo is het logisch dat de font-family en de color overgeërft wordt, aangezien dat het eenvoudig maakt om eenzelfde lettertype in te stellen voor de volledige site door aan het <html> element een declaratie voor het lettertype mee te geven.  Je kan die lettertypes overschrijven voor individuele elementen indien nodig, maar het zou enorm vervelend zijn om voor elk element het standaard lettertype te moeten meegeven.
  • Op eenzelfde manier is het logisch dat de margin, padding, border, en background-image eigenschappen NIET overgeërft worden.  Stel je voor wat een boeltje het zou worden indien je deze eigenschappen instelt voor een containerelement en alle kinderen ervan die overerven.  Je zou voor elk kindelement manueel die overerving moeten overschrijven!

Welke eigenschappen standaard overgeërft worden en welke niet is grotendeels gezond verstand.  Indien je zeker wil zijn kan je de CSS Referentie consulteren — elke afzonderlijke eigenschapspagina begint met een overzichtstabel die verschillende details over die eigenschap opsomt, waaronder of het overgeërfd wordt of niet.

Overerving controleren

CSS voorziet drie speciale waarden om overerving in te stellen:

  • inherit : Met deze waarde zal de eigenschap van het geselecteerde element dezelfde waarde krijgen als die van het ouderelement.
  • initial : Met deze waarde zal de eigenschap van het geselecteerde element dezelfde waarde krijgen als het element in de standaard stylesheet van de browser.  Indien geen waarde werd ingesteld door het stylesheet van de browser, en de eigenschap wordt normaal overgeërfd, dan zal de waarde op inherit ingesteld worden.
  • unset : Deze waarde stelt de eigenschap in op zijn natuurlijke waarde.  Dit betekent dat als de eigenschap standaard wordt overgeërfd, da die zal overgeërfd worden (inherit), anders zal die zich gedragen als initial.

De inherit waarde is de interessantste: ze laat ons toe om expliciet in te stellen dat een element een eigenschap overerft van zijn ouder.

Laten we kijken naar een voorbeeld.  Eerst de HTML:

<ul>
  <li>Default <a href="#">link</a> color</li>
  <li class="inherit">Inherit the <a href="#">link</a> color</li>
  <li class="initial">Reset the <a href="#">link</a> color</li>
  <li class="unset">Unset the <a href="#">link</a> color</li>
</ul>

Dan de CSS:

body {
  color: green;
}

.inherit a {
  color: inherit;
}

.initial a {
  color: initial
}

.unset a {
  color: unset;
}

Resultaat:

Wat gebeurt hier nu?:

  • We stellen eerst de color eigenschap van het <body> in op groen.
  • Aangezien de color eigenschap standaard overgeërft wordt, zullen alle kind-elementen van body dezelfde groene kleur hebben.  Merk wel op dat browsers standaard de kleur van hyperlinks blauw instellen en de natuurlijke overerving blokkeren voor dat element.  Daarom is de eerste link in onze lijst blauw.
  • De tweede regel stelt in dat hyperlinks met de klasse inherit de kleur overerven van hun ouder.  In dit geval betekent dat dat de link zijn kleur overerft van de <li> (zijn ouder), die op zijn beurt de kleur overerft van de <ul>, die - ten slotte - de kleur overerft van de <body>.  Voor die laatste stelden we de kleur in met de eerste regel.
  • De derde regel selecteert alle hyperlinks binnen een element met de klasse initial en stelt de kleur in op initial. Doorgaans is de initiële waarde die door browsers voor de tekstkleur wordt ingesteld zwart, en bijgevolg is deze link dus zwart.
  • De laatste regel selecteert alle hyperlinks binnen een element met de klasse unset en stelt de kleur in op unset — we "verwijderen" de waarde.  Omdat kleur een waarde is die normaal overgeërft wordt, gedraagt dit zich op dezelfde manier als wanneer de waarde zou ingezet zijn op inherit. Bijgevolg is deze link dezelfde kleur als het body-element: groen.

Actief leren: spelen met de waterval

In deze actief leren zouden we graag hebben dat je experimenteert met het schrijven van één enkele nieuwe regel die de tekstkleur en achtergrondkleur overschrijft die standaard wordt toegepast op hyperlinks.  Kan je één van de speciale waarden gebruiken die we bekeken in de Controlling_inheritance sectie, om een declaratie te schrijven in een nieuwe regel die de achtergrondkleur op wit zal instellen, zonder een kleurwaarde te gebruiken?

Indien je een fout maakt, kan je altijd herbeginnen door op de Reset knop te drukken.  Indien je echt vast zit, druk dan op Show solution om een mogelijk antwoord te zien.

Verdere literatuur

Als je de meerderheid van dit artikel verstond, dan ben je goed bezig. Je zou moeten vertrouwd zijn met de fundamentele werking van CSS.  Het laatste deel van die centrale theorie is het doosmodel, dat hierna aan bod komt.

Geen zorgen als je de waterval, specificiteit en overerving nog niet volledig verstond!  Dit is zonder twijfel het meest ingewikkelde wat we tot nu toe zagen in deze cursus, en het is iets wat zelfs professionele webdeveloppers moeilijk vinden.  We raden je aan om naar dit artikel een paar keer terug te keren in de loop van de cursus.  Blijf er over denken, en gebruik dit als een referentie indien je rare problemen tegenkomt met stijlen die niet worden toegepast zoals je verwacht.  Het zou wel eens om een specificiteitsprobleem kunnen gaan.

Documentlabels en -medewerkers

 Aan deze pagina hebben bijgedragen: Badlapje
 Laatst bijgewerkt door: Badlapje,