CSS Positionering

Het navolgende artikel is een gebaseerd op diverse (web)bronnen. Speciale dank gaat uit naar de andere auteurs voor het delen van hun kennis. Middels dit artikel hoop ik een steentje bij te dragen aan het verschaffen van inzicht in de wondere wereld van CSS Positionering.

Om CSS effectief in te kunnen zetten voor de layout van je site, is het handig te weten hoe dit gebruikt wordt voor het positioneren van page content. Dit artikel verschaft een overzicht van de methodes en regels voor het visueel presenteren en positioneren van page content volgens CSS2 specificatie. Daarnaast worden enkele aandachtspunten vermeld.

Dit artikel richt zich op de (uit)werking in browsers. Veel details zijn bewust weggelaten.

Het is belangrijk om te weten dat sommige browsers bepaalde CSS eigenschappen in z’n geheel niet ondersteunen, of wel, maar dan net iets anders dan omschreven in de CSS standaard. Daar waar nodig wordt hierop gewezen in dit artikel.

Het CSS Box Model

Om positionering met behulp van CSS te kunnen begrijpen, dien je allereerst meer te weten over het ‘CSS box model’.

Voor een browser is alles een vierhoek. Als je dus een layout voor een web-pagina ontwerpt, denk dan in termen van rechthoeken en vierkanten.

Deze vierhoeken kunnen op verschillende manieren geplaatst (positionering) worden. Ze kunnen naast elkaar, onder elkaar, in elkaar (nested) en zelfs overlappend gepositioneerd worden. Elk element dien je te beschouwen als een vierhoek (box) met een ruimte voor inhoud (zgn. content vlak) welke omgeven wordt door respectievelijk padding, een border en margin. De illustratie hieronder toont de verschillende delen:

content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

Margins zijn altijd transparent (deze kunnen dus geen kleur hebben). Borders kennen diverse stijl-types zoals dotted, solid. De achtergrond (Background) settings van een element gelden voor de ruimte binnen de borders (en dus incl. de padding en content vlak).

Wanneer gesproken wordt over een box in dit artikel, dan betekenen de termen "margin edge," "border edge", etc. de buitenste grens van een box zoals deze hierboven is weergegeven.

Margins, borders en padding zijn optioneel, maar voor het bepalen van een positie en grootte hebben ze een default breedte (width) van nul als er niets is aangegeven. Indien gewenst kunnen er verschillende breedtes (widths) aangegeven worden voor elke zijde (top, right, bottom en left) van de box. Margins kunnen zelfs een negatieve waarde hebben.

De breedte en hoogte van elke box is gelijk aan de breedte en hoogte van de outer margin box. Merk op dat dit niet automatisch overeenkomt met de breedte en hoogte van het content-vlak.

Een box kan een willekeurig aantal andere boxes bevatten. Hiermee wordt een hierarchie van boxes gemaakt welke overeenkomt het ‘nesten’ van pagina elementen. De browser window dient als root element voor deze hierarchie.

Box Types

Er worden twee type boxen ondescheiden:

1. Block
2. Inline

Block boxes worden gemaakt door elementen als P, DIV of TABLE. Inline boxes worden gemaakt door tags als B, I of SPAN en de feitelijke content zoals text en images.

Het box type kan bepaald worden met behulp van de eigenschap ‘display’. Als je aan een inline element een display waarde ‘block’ toekent, zal deze als een block element behandeld worden. Als de display gelijk is aan ‘none’, dan wordt er geen box aangemaakt. De browser negeert dit element dan. Dit geldt dan ook voor de geneste elementen hiervan, zelfs als hier wel een echte display waarde voor gedefinieerd is.

Container Block

Block boxes functioneren als een container block voor elke box die er in valt. In de volgende code bijvoorbeeld:

<div>
Dit is de eerste zin.
<p>Dit is de tweede zin.</p>
</div>

is het DIV element het container block voor zowel de eerste tekststring als het P element. Het P element op zijn beurt creeert een container block voor de tweede tekststring.

Het container block is van belang voor de positionering, en soms de dimensies, van de boxen die hierin vallen. Als bijvoorbeeld een element een breedte heeft van 50%, dan betekent dit 50% van de breedte van het container block.

Voor elk element dat niet absoluut gepositioneerd is, geldt dat het container block bepalend is voor de rand van de content van de dichtbijzijnde block-level erfgenaam. Als er geen container block is, dan dient het browser window als het container block. Absoluut gepositioneerd elementen worden hieronder apart besproken.

Positioningsvormen

Zoals hierboven aangegeven kunnen boxen (vierhoeken) op verschillende manieren geplaatst (positionering) worden. Ze kunnen naast elkaar, onder elkaar, in elkaar (nested) en zelfs overlappend gepositioneerd worden.

CSS biedt de mogelijkheid het plaatsen van een element te bepalen middels de "position" eigenschap. Er zijn vier mogelijke waarden:

position: static;
position: relative;
position: fixed;
position: absolute;

Elke box wordt gepositioneerd middels een van deze vormen, en elke vorm heeft zijn eigen set van regels. Een fout die veel gemaakt wordt is dat men denkt te weten wat dit betekent. Echter, deze waarden zijn nu niet bepaald vanzelfsprekend. Zo is "absolute" nu niet bepaald “absoluut", en bij "relative" vraag je je gelijk af “relatief ten opzichte van wat? Hieronder volgt een uiteenzetting van deze waarden. Merk op dat ‘position’ niet 1-op-1 vertaald kan worden met positie. Het gaat om de wijze waarop het plaatsen van de boxen gebeurt (maw de flow).

Statische Positionering

Statische Positionering is de standaard vorm binenn CSS positionering. Deze vorm is van toepassing op elk element waarvan de eigenschap ‘position’ niet expliciet is aangegeven (dus geen position:absolute of fixed etc).

In deze vorm worden block boxen verticaal, van boven naar onder, gestapeld beginnend in de top van hun container block. Inline boxes worden horizontaal, van links naar rechts, geplaatst. In de meeste gevallen betekent "static" dat het block geplaatst wordt waar je het ook zou verwachten.

Merk op dat verticale margins onderdrukt (collapsed) worden in de normale statische flow. Dat wil zeggen dat in plaats van de bottom margin van een box en de top margin van de box hier recht onder weer te gegeven, wordt alleen de grootste van deze 2 waarden getoond (zie hieronder).

content content content content content content content content content
content content content content content content content content content content content content content content content content content content

Horizontale margins worden echter nooit onderdrukt.

Inline boxes (ook content) schuiven op naar de volgende nieuwe regel als de beschikvare breedte overschreden.

content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

Relatieve Positionering

Wanneer de definitie van een element ‘position:relative’ bevat, dan zal het positioneren in eerste instantie plaatsvinden volgens de normale (statische) regels. Dit geldt ook voor de aangrenzende boxen. Daarna zal de box verschoven worden overeenkomstig de offset eigenschappen.

content content content content content content content content content
content content content content content content content content content
content content content content content content content content content

Merk op dat de aangrenzende boxen ook normaal gepositioneerd worden, inclusief het onderdrukken (collapsing) van de verticale margins, en dat de verschoven box kan overlappen met een andere box.

Browser Compatibility

Browsers gaan verschillend om met het weergeven van relatief gepositioneerde elementen wanneer deze overlappen met content. Helaas is de CSS standaard niet geheel helder op dit vlak. Sommigen, zoals Internet Explorer 5.5 en Netscape 6.0, zullen de gepositioneerde elementen altijd op de voorgrond plaatsen. Anderen, zoals Opera 5.0, hanteren de default stacking order, welke als onderdeel van absolute positionering wordt besproken.

Merk op dat de z-index eigenschap gebruikt kan worden voor het expliciet bepalen van de stapel-volgorde (voorgrond/achtergrond) voor relatief gepositioneerde elementen.

De offset waarden wordt bepaald middels de combinatie van de top, right, left en bottom stijl eigenschappen. De waarde hiervan wordt genomen om de afstand te bepalen waarmee de buitenste rand van de box verschoven zou moeten worden ten opzichte van de originele positie in de normale flow.

Merk op dat tegengestelde offset waarden een beperking kunnen vormen. Als je bijvoorbeeld zowel ‘left’ als ‘right’ specificeert maar de waarde van de een is niet precies de negatieve waarde van de ander, dan zal de waarde van ‘right’ genegeerd worden. Een specifieke ‘width’ waarde kan er ook voor zorgen dat een offset waarde genegeerd wordt. Dit geldt ook voor de ‘top’, ‘bottom’ en ‘height’ eigenschappen..
In de praktijk wordt  daarom vaak alleen ‘left’ (of juist right) en ‘top’ (of juist bottom) gedefinieerd, maar nooit allemaal.

Descendant Positioning

Relatief gepositioneerde elementen kunnen (maar dit hoeft niet) een nieuw container block vormen voor gepositioneerde (relatief of absoluut) afstammende elementen (zgn. childs). Deze volgen dezelfde regels als voor niet gepositioneerde elementen

Als het relatief gepositioneerde element een block element is, dan vormt het een nieuw container block. De hierin geposiotioneerde elementen zullen de "offset" positie van dat element gebruiken als de basispositie voor het positioneren. Anders geformuleerd, de offset waarde van child elementen zijn samengesteld.

Als het relatief gepositioneerde element een inline element is, dan zal de offset waarde hiervan niet bepalend zijn voor de offset waarde van de gepositioneerde child elementen. In plaats hiervan wordt de offset waarde gebaseerd op dezelfde waarde als het containerer block.

Browser Compatibility

De originele standaard verklaart dat relativief gepositioneerde elementen altijd in een nieuw Container Block geplaatst dienen te worden. Echter, in een recente update van deze standard is dit gewijzigd zodat dergelijke boxes dezelfde regels gelden als voor niet-gepositioneerde elementen. In sommige browsers is deze correctie doorgevoerd, maar in anderen nog niet. Door dit verschil is het raadzaam dergelijke situaties te vermijden of om altijd block elementen te gebruiken voor relatieve positionering.

Floats

Floating wordt bereikt door de ‘float ‘eigenschap van een element te definieren. De mogelijke waarden zijn: ‘left’ of ‘right’. Er gelden speciale regels voor floated elements.

Wanneer men een float gebruikt, zal de betreffende box verticaal gepositioneerd worden, op dezelfde manier als bij statische positionering. Dat wil zeggen, de bovenkant zal overeenkomen met de bovenkant van de huidige box, maar horizontaal zal deze verschuiven naar de meeste rechter of linker kant van z’n Container Block (rekeninghoudend met de padding van dat block.

De illustratie hieronder laat het resultaat zien van de volgende code, waarin een floated element gedefinieerd is voor inline tekst (margin, border en padding styles zijn weggelaten voor de duidelijkheid).

<p>
  <span style="float:right;width:40%;">content...</span>
  content content content content content content content content...
</p>
content content content content
content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

De float eigenschap kan de waarde 'left', 'right', 'none' of 'inherit' bevatten.

Er zijn enkele belangrijke dingen die je in je achterhoofd moeten houden als het gaat om floated boxes.

Ten eerste dient voor een floated box altijd de breedte (width) gedefinieerd te worden, ofwel expliciet ofwel impliciet. Anders zullen de blocken hierin horizontaal opgevuld worden, zoals bij non-floated content, en zal er hieromheen geen ruimte meer zijn voor andere content. Hierdoor dienen floated boxes hetzelfde behandeld te worden als block boxes, zelfs als deze gedefinieerd zijn als inline elementen.

Ten tweede, in tegenstelling tot boxes in een normale flow, worden de verticale margins van een floated box niet onderdrukt (collapsed) en vervangen met de margins van de boxes er boven of onder.

Ten slotte, een floated box kan overlappen met block-level boxes die hier naast liggen in de normale flow. Hieronder volgt een voorbeeld waarbij het floated element meer tekst bevat dan zijn containier block. Een andere paragraaf is toegevoegd naar de vorige code om dit te demonstreren.

<p>
  <span style="float:right;width:40%;">
    content content content content content content content content
    content content content content content content content content...
  </span>
  content content content content...
</p>

<p>
content content content content
content content content content...
</p>

content content content content content content content content content content content content content content content content content content content content content content content content
content content content content content content content content content
content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

Merk op dat de floated box overlappend is met de randen (borders) van zowel de parent box en de hieropvolgende (al hoewel de tekst in het tweede block om de floated heengaat). Dit kan vermeden worden door gebruik te maken van de ‘clear’ eigenschap.

Mogelijke waarden van Clear

left         floating elementen aan de linker kant zijn niet toegestaan
right      floating elementen aan de rechter kant zijn niet toegestaan
both      floating elementen aan de linker en rechter kant zijn niet toegestaan
none     Default. elementen aan beide kanten zijn toegestaan

Het definieren van clear:right; voor de tweede box verplaatst deze net onder de floated box. Hieronder volgt een voorbeeld dat gebruikt maakt van dezelfde code als hiervoor.

<p>
  <span style="float:right;width:40%;">
    content content content content content content content content
    content content content content content content content content...
  </span>
  content content content content...
</p>

<p style="clear:right;">
content content content content
content content content content...
</p>

content content content content content content content content content content content content content content content content content content content content content content content content
content content content content content content content content content
content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

Dit zorgt ervoor dat de top margin van de tweede box zodanig toeneemt dat de rand van de tweede paragraaf en content net onder de floated box komt.

De float box overlapt nog wel met de bovenste box (de paragraaf hiervan) maar dat is omdat dit de bedoeling is van dit voorbeeld: veel content plaatsen in een floated element dat omringd wordt door content.

Normaal gesproken kan dit worden voorkomen door de ‘floated’ buiten de paragraaf te plaatsen, en beide paragrafen hieromheen te laten lopen.

<span style="float:right;width:30%;">
  content content content content content content content content...
</span>

<p>content content content content...</p>

<p>content content content content...</p>

De clear eigenschap kan een van de volgende waarden hebben:left, right, both, none of inherit. Het heeft geen betekenis voor Block-level elementen.

Aangrenzende Floats

Als twee of meer naast elkaar liggende elementen floated zijn, dan zal de bovenkant van beide elementen op dezelfde hoogte liggen (als er hiervoor voldoende horizontale ruimte is). Als dit niet het geval is, dan zal het laatste element naar onderen doorschuiven totdat er voldoende ruimte is, altijd in overeenstemming met een line box (bijv. paragraph met tekst).

Een dergelijke ‘downward shift’ kan ook plaatsvinden voor een enkel floated element als deze te breed is voor de initiele positie met aangrenzende content.

De illustratie hieronder laat zien wat het effect is van twee aangrenzende right-floated elementen. Merk op dat het eerste element het verste naar rechts getoond wordt.

first float
second float
content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

De clear eigenschap kan worden gebruikt bij floated elementen om ervoor te zorgen dat deze onder aangrenzende floated elementen wordt geplaatst (herinner dat floated elementen voor de positionering hetzelfde worden behandeld als Block elementen). De verticale margin zal niet vervallen bij floated boxes. Hieronder is een illustratie weergegeven van dezelfde code maar met een clear:right voor het tweede floated element.

first float
second float
content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content content

Absolute Positionering

Deze vorm van positionering is van toepassing op elke element waarvan de ‘position’-eigenschap gelijk is aan absolute of fixed.

Dergelijke boxen worden uit de normale flow gehaald en hebben geen effect op de boxen in die flow. Evenals bij floated elementen worden absoluut gepositioneerde elementen altijd behandeld als Block-level elementen. Hierdoor wordt een nieuw container Block gecreëerd voor de child elementen dwz voor elk element dat onder het absoluut gepositioneerde element hangt.

De positie van een absoluut gepositioneerde element wordt bepaald door de offset waarden: top, right, bottom en left. Deze waarden functioneren grotendeels op dezelfde manier als bij relatief gepositioneerde elementen.

Het enige verschil met relatief gepositioneerde elementen zit in de berekening van de offset waarden. Bij absoluut gepositioneerde elementen wordt de offset waarde berekend vanaf zijn Container Block, en bij relatief gepositioneerde elementen wordt de offset waarde ‘berekend’ vanaf de positie van het element in de normale flow.

Container Block voor Absoluut Gepositioneerde Elementen

Het container block van een absoluut gepositioneerde element wordt iets anders vastgesteld dan voor andere elementen. Het container block van een absoluut gepositioneerde element wordt namelijk bepaald door zijn dichtstbijzijnde ‘parent’ element. Dit is het dichtstbijzijnde element waarvan de positioneigenschap gelijk is aan absolute, relative of fixed. Als een dergelijk element niet voorkomt, dan wordt het initiele Container Block (lees de browser window) gebruikt.

Zoals al vermeld, niet-absoluut gepositioneerde elementen gebruiken het Container Block van het dichtstbijzijnde Block-level parent element. Maar voor absoluut gepositioneerde elementen kan dit ook een inline element zijn.

Bovendien zal, als dat container element een Block level element is, de padding rand van dat element het container Block vormen, en dus niet de content. Met andere woorden, de offset waarde wordt berekend vanaf de binnenkant van de rand van het container element.

Als het container element een inline element is, dan wordt het iets gecompliceerder. Aangezien een inline element verscheidene line boxen kan aanmaken, wordt de container box gedefinieerd als de ruimte tussen de linker bovenkant van de eerste box in dat inline element, en de rechter onderkant van de laatste box.

Voorbeeld

Stel je hebt een relatief gepositioneerde SPAN welke 2 lijnen met tekst bevat, waarvan een deel dikgedrukt (bold) is.

content content content content content content
content content

In het bovenstaande voorbeeld wordt het container Block gevormd door het grijs getinte gebied dat de eerste en de laatste inline box omkadert.

Vanwege de reeds aangegeven browser inconsistentie inzake het weergeven van relatief gepositioneerde elementen, kunnen absolute elementen, welke deel uitmaken van relatief gepositioneerde elementen, voor onaangename verrassingen zorgen. Dit kan dus het beste vermeden worden.

Merk op dat hoewel margins gerespecteerd worden bij absoluut gepositioneerde elementen (de positie wordt immers berekend vanaf de rand van de margin van de box van dat element) ze voor de rest geen betekenis hebben, omdat absoluut gepositioneerde elementen uitgesloten worden bij de normale flow.

Een absoluut gepositioneerd element vormt een container block voor elk element daarbinnen. Deze child elementen volgen dezelfde positioneringregels als normaal, alleen met een offset waarde die bepaald wordt door het container element.

Ze kunnen zelfs andere absoluut gepositioneerde elementen bevatten. Ook deze child elementen worden onttrokken aan de normale flow binnen dat container Block, waardoor ze weergegeven kunnen worden buiten de grenzen van het container element. Dit wordt in het volgende voorbeeld nader toegelicht.

Voorbeeld Absolute Positionering

Beide DIVs zijn rechts gepositioneerd dmv Absolute Positionering. DIV "A1" maakt deel uit van DIV "A." Merk op dat "A1" zich uitstrekt buiten grenzen van "A."

De code hiervan is:

Vaste positionering (Fixed Positioning)

Vaste positionering is een bijzondere variant van absolute positionering. Voor elementen, die fixed zijn, komt het Container Block altijd overeen met de browser window. Een fixed element verplaatst zich niet bij het scrollen van de webpagina.

In het geval van het printen van een webpagina, zal elk fixed element op elke pagina afgedrukt.

Internet Explorer en Netscape 6.0 ondersteunen Vaste Positionering niet. Ze ondersteunen wel vaste achtergronden (via de background-attachmenteigenschap) waardoor eenzelfde effect wordt bereikt, maar alleen voor achtergrondplaatjes.

Netscape 6.1 ondersteunt, evenals Opera 5, Vaste Positionering wel.

Stacking Volgorde

Absoluut gepositioneerde elementen kunnen overlappen met niet-gepositioneerde elementen, en met elkaar. Er zijn twee items die bepalen wat er op de voorgrond en wat er op de achtergond wordt getoond. Dit zijn de stacking contexten de z-indexeigenschap.

Elk absoluut gepositioneerde element behoort tot de stacking context. Het eerste container Block genereert de eerste stacking context. Absoluut gepositioneerde elementen in dezelfde stacking context worden weergegeven conform hun z-indexwaarde. Een element met een hoge z-indexwaarde wordt meer op de voorgrond getoond dan een element met een lage z-index waarde.

Wanneer twee elementen dezelfde waarde hebben (of juist geen) dan is de volgorde in de source code bepalend. Het element, dat later gedefinieerd is, zal getoond worden achter het eerste element.

Een z-indexwaarde kan ook negatief zijn. Een element met een negatieve waarde ligt achter ongedefinieerde of positieve waarden (binnen dezelfde stacking context).

Internet Explorer 5.5 en Opera 5 gaan niet correct om met negatieve z-index waarden, en Netscape 6 kan foutmeldingen geven.

Een absoluut gepositioneerde element kan een lokale stacking context aanmaken welke alleen van toepassing is op de hierin voorkomende absoluut gepositioneerde elementen. De stacking volgorde van deze child elementen komt to stand overeenkomstig de hierboven beschreven regels.

Klik hier om een stacking volgorde demo te bekijken.

Wanneer er overlap is met een element in een andere stacking context, dan worden deze elementen er of voor of achter getoond, afhankelijk van van de stack level van hun container element. Anders geformuleerd, hun z-index waarde is alleen van toepassing vop elementen binnen dezelfde lokale stacking context. Anders wordt de z-index waarde van het container element gebruikt. Met een eenvoudig voorbeeld wordt dit duidelijker.

Een lokale stacking context wordt aangemaakt zodra een absoluut gepositioneerd element een z-index waarde krijgt, anders dan auto. Elk absoluut gepositioneerd element dat hieronder valt participeert in deze lokale stacking context.

In Internet Explorer 5.5 en Netscape 6.0, creert een absoluut gepositioneerd element altijd een lokale stacking context voor de hieronder hangende elementen, zelfs als de z-index de waarde auto krijgt. Opera 5.0 gaat wel correct om met de stacking order, maar positioneert de onderliggende elementen verkeerd.

Overlappende Form Controls en Plug-ins

Sommige elementen veroorzaken problemen qua weergave wanneer deze overlappen met gepositioneerde elementen. Het voorbeeld hiernaast, wat gebaseerd op IE versie 5.5, illustreert dit. Een dergelijk effect krijg je als elementen overlappen met form controls (bijv. DropDownList), applets of plug-in (bijv. Flash).IE 5.5 Screenshot

Het gebeurt omdat browsers de weergave van deze elementen overlaten aan andere programm's, zoals een plug-in of het operating system. Zelf het definieren van de z-index helpt dan niet. Deze andere programma's zijn bepalend, en zullen bepaalde elementen simpelweg weergeven over hetgeen de browser rendert.

Welke elementen "doorzichtig" worden, hangt af van de browser. Netscape 6 en IE 5.5 bepalen zelf alle form controls, behalve voor SELECT elementen. Met IE zullen deze bovenop enige andere content worden getoond. Met Netscape, De Select box zelf werkt zoals verwacht, maar de scroll bar zal doorzichtig worden als de SIZE eigenschap groter is dan 1. Opera 5.0 heeft dit probleem met alle formulier elementen.

Dit kan niet opgelost worden. Het enige wat je kunt doen, is proberen te vermijden dat dergelijke elementen elkaar overlappen. In sommige gevallen weet je (of kun je achterhalen) dat er overlap is, en kun je voorkomen dat het ongewenste element wordt getoond door de style settings aan te passen met visibility:hidden of display:none.

Conclusie

Ondanks het feit dat dit artikel slechts een samenvatting is, geeft het al aan dat de manier waarop browsers de inhoud van een pagina weergeven veelomvattend is, en dit deel beschrijft alleen het positioneren hiervan. Zelfs als browsers voldoen aan de standaard is er nog voldoende ruimte over voor fouten. Bij het ontwerpen van een layout kunnen ‘code validators’ behulpzaam zijn bij het localiseren van eenvoudige fouten zoals unmatched HTML tags of syntax errors in style sheets. Vaak is het noodzakelijk om een pagina te bekijken met verschillende browsers om te achterhalen of de weergave voldoet.