Hoppa till navigeringen.

I vår förra artikel tittade vi lite närmare på en av metoderna för att skapa layouter med CSS: absolut positionering. Idag tänkte vi försöka belysa ett annat alternativ, nämligen flytande element (floats). Minst lika missförstått som absolut positionering, men oftast mycket mer användbart.

Allting flyter inte

Panta rhei, sade Herakleitos, allting flyter. Även om den bakomliggande filosofin, att allting befinner sig i oavbruten förändring, i allra högsta grad gäller för webben, så gäller det inte för dagens ämne. För att få ett flytande element måste vi uttryckligen ange det, exempelvis så här: float:left

När vi gör ett element flytande ber vi webbläsaren att knuffa det i sidled, antingen åt höger eller åt vänster, så långt det går.

Den som verkligen förstår vad föregående mening innebär vet redan hälften av vad man behöver veta för att kunna använda flytande element. Det finns tre viktiga bitar information i den där meningen, så låt oss undersöka dem innan vi ger oss på den andra, lite svårare halvan.

Ta ett steg åt sidan

Det första vi behöver lägga på minnet är att ett flytande element förskjuts antingen till höger eller till vänster. Det är alltså inte möjligt att få ett element att flyta i mitten, något som ofta frustrerar nybörjare. Om man tänker efter en stund så är det dock ganska självklart. Varför? För att svara på det måste vi gå händelserna en liten aning i förväg och titta på vad som händer när vi skapar ett flytande element i vårt dokument.

Ett vanligt användningsområde för float är bilder. Vi vill kunna lägga in en bild och få texten att flyta runt den. Med CSS är det dock inte texten vi gör flytande, utan bilden. Om bilden har float:left kommer texten att flyta förbi på dess högra sida. Har bilden float:right flyter texten förbi på dess vänstra sida.

Om det skulle vara möjligt att få bilden att flyta i mitten skulle webbläsaren alltså behöva få texten att flyta på två sidor om den. Till skillnad från de två enklare fallen är det långt ifrån självklart hur det skulle gå till. Ska textraderna delas för att ge plats för bilden? Eller ska texten plötsligt delas upp i två spalter? Om inte bilden är väldigt liten är det också en klar risk för att utrymmet på sidan om inte räcker till för långa ord.

Den andra viktiga informationsbiten i grundregeln ovan är att ett flytande element bara förskjuts i sidled. Vi har sett optimistiska försök att sätta float:top för att flytta något längst upp på sidan.

För att verkligen förstå teorin bakom flytande element måste man först förstå vad en line box i CSS innebär. Tyvärr kräver det i sin tur att man begriper vad som menas med en inline box, vilket är alldeles för komplicerat för att gå in på i detalj. Den som är intresserad (och har en viss masochistisk böjelse) kan försöka dechiffrera det som sägs i avsnitt 9.2.2 av specifikationen för CSS2.1.

En inline box genereras av de element som inte är blockelement, exempelvis EM. Dessutom genereras en anonym inline box av textnoder inuti blockelement. Det är inte så svårt att tänka sig att varje blockelement består av en rektangulär ruta. En inline box kan också ses som en sådan ruta, även om den lyder under lite andra lagar än kusinen block box.

En line box är en tänkt rektangel som omsluter alla inline boxes som bildar en rad i det omgivande blockelementet. Den är (minst) lika hög som sin högsta ingående line box.

Vi gräver inte djupare i boxteorin än så här, utan nöjer oss med föreställningen om att en line box är en tänkt ruta som omsluter en hel rad av text och (ickeflytande) bilder. Orsaken till den här utvikningen är att vi behöver känna till att överkanten på ett flytande element justeras mot överkanten på aktuell line box (eller mot botten av föregående block box om det inte finns någon line box).

Det är också bra att känna till att alla flytande element automatiskt blir en block box. Det är alltså inte nödvändigt att ange display:block för ett flytande element. Mer om det senare.

Jaha, och vad betyder det på ren svenska då? Jo, att ett flytande element aldrig kan hamna högre än överkanten på den rad där det förekommer. Eller med andra ord, att ett flytande element bara kan skiftas i sidled, men det visste vi ju redan.

Hit men inte längre

Den tredje viktiga detaljen i grundregeln ovan är orden så långt det går. När vi gör ett element flytande förskjuts det till höger eller vänster tills dess kant når kanten på det omgivande blockelementet. Om vi sedan gör ett annat, närbeläget element flytande åt samma håll förskjuts det tills dess kant når kanten på det första flytande elementet. Med andra ord, det lägger sig vid sidan av det första elementet. Gör vi fler element flytande åt samma håll fylls raden på, men förr eller senare tar utrymmet slut. Vad som händer då ska vi strax titta på, men först är det dags för en dos teori igen.

Vad är det egentligen som händer?

Precis som med absolut positionerade element tas ett flytande element bort ur dokumentflödet. Det påverkar inte efterföljande blockelement. Däremot förkortas de line boxes som skapas bredvid det flytande elementet. Vi kan föreställa oss att de helt enkelt lämnar plats i sidled.

Säg att vi har ett antal stycken med text. I det första stycket vill vi infoga en hög, men smal bild och vi vill att texten ska flyta på höger sida om bilden. Vi sätter alltså float:left på bilden.

De line boxes som genereras av texten i första stycket förkortas så att de börjar vid bildens högerkant och sträcker sig till styckets högra kant. Men bilden är så hög att texten i första stycket tar slut innan den nått bildens nederkant.

Nästa stycke, som ju är ett blockelement, tar ingen hänsyn till den flytande bilden. Den har ju tagits bort ur dokumentflödet. Dess block box skapas alltså på vanligt avstånd från det första styckets block box, med hänsyn taget till marginaler och sådant. Här kommer det fina i kråksången: det andra styckets line boxes förkortas också, precis som i det första stycket.

Så fortsätter det tills textraderna når bildens nederkant. Då behöver de inte längre förkortas, utan texten fortsätter nedanför bilden.

En, två, tre, många

Låt oss se tillbaka på exemplet där vi lät flera element flyta åt samma håll. För varje flytande element förkortas våra line boxes och till sist finns det inte rum för nästa flytande element.

Nu ser vi nästa viktiga egenskap hos ett flytande element: när det inte finns plats på raden skiftas det nedåt tills det får plats. Det här kan vara oerhört värdefullt i layouter som ska fungera med olika fönsterstorlekar. Om spalterna är flytande läggs de sida vid sida i ett brett fönster, eller i vertikal sekvens i ett smalt fönster.

Öh du, den hänger utanför…

Som vi nyss såg kan ett flytande element fortsätta utanför det blockelement där det skapades. Ett blockelement förlängs inte så att det omsluter sina flytande barnelement, eftersom det skulle omöjliggöra den tjusiga effekt vi ville åstadkomma i exemplet ovan med den höga smala bilden. (Ett flytande element förlängs dock vid behov så att det alltid omsluter sina flytande barn.)

Ibland är den här effekten önskvärd, ibland inte. Lyckligtvis finns det ett sätt att upphäva den. Vi kan tvinga ett element att lägga sig nedanför alla flytande element via egenskapen clear. Om vi sätter clear:left hamnar elementet nedanför alla flytande element på dess vänstra sida. Med clear:both kan vi se till att ett element hamnar nedanför alla flytande element som annars skulle ha påverkat det.

För ett blockelement åstadkoms förflyttningen genom att dess toppmarginal vid behov ökas tills dess att elementets överkant hamnar nedanför den nedre kanten på alla flytande element som skapats tidigare i dokumentet. (Känn dig inte skamsen om du måste läsa föregående mening ett antal gånger innan du förstår den, det här är klurigt.)

För inline-element är det ännu mer komplicerat, men resultatet blir detsamma. Sen kan ett flytande element ha egenskapen clear också…

Varför behöver vi veta det här? Därför att det får konsekvenser som annars är rätt obegripliga. Låt säga att vi vill vara säkra på att en H2-rubrik inte hamnar vid sidan av ett flytande element. Vi sätter alltså clear:both. Anta också att vi vill ha en viss marginal mellan det flytande elementet och själva rubriken. Så vi sätter margin-top:1em. Risken är stor att vi hamnar i ett läge där rubriken är alldeles för närgången mot det flytande elementet. Varför?

Kom ihåg att flytande element inte påverkar efterföljande block boxes. Om vi inte satt clear för rubriken skulle den ha skapats 1em nedanför närmast föregående blockelement (och rubriken skulle ha hamnat till höger om ett vänsterflytande element som sticker utanför sitt blockelement). I och med att vi sätter clear ökas toppmarginalen tills rubrikens överkant hamnar nedanför det flytande elementets nederkant. Men inte mer!

Om vi vill ha luft runt ett flytande element måste vi sätta marginaler på det flytande elementet.

Flytande spalter

Hur kan vi då utnyttja float för att skapa en layout med spalter, och varför är det ibland bättre än absolut positionering?

Om vi kapslar in varje spalt i ett DIV-element med float:left kommer de att placeras sida vid sida, precis som vi förväntar oss att spalter ska göra. Vill vi sedan ha en sidfot som spänner över hela sidbredden, och som ska visas längst ned oavsett vilken av spalterna som råkar vara längst, då kan vi bara sätta clear:both på den.

I en flytande layout vill vi ange bredden för de olika spalterna i procent. Ett bra tips är att inte ange procentsiffrorna så att summan blir exakt 100 % (exempelvis 60/40 eller 25/50/25). Minsta lilla avrundningsfel gör nämligen att den sista spalten halkar nedanför de andra. Använd hellre 60/39 eller 25/49/25, för att vara på den säkra sidan.

En helt flytande layout medför dock problem i sig. I moderna webbläsare (läs: allt utom IE) kan vi ange en min-width (förslagsvis i em) för varje spalt. I ett smalt fönster kan då en eller flera spalter flyttas ned under de andra, vilket kanske inte är så estetiskt tilltalande, men det är många gånger mer användarvänligt än att behöva skrolla i sidled.

Vi löser alltså sidfotsproblemet med flytande spalter, något som inte kan hanteras med absolut positionering. Däremot får vi ett annat problem som är lättare att lösa med absolut positionering: ordningsföljden. Det är ofta önskvärt att ha sidans huvudinnehåll så tidigt som möjligt i källkoden. Mindre väsentliga saker som navigering och annonser kan komma längre ned. Men med flytande element måste vi ange dem i rätt ordning. Vill vi ha en meny till vänster, annonser till höger och innehållet i mitten kan inte innehållet komma först. Eller…?

Det går, men det är inte helt trivialt. Säg att vi vill ha innehållet först, därefter vänstermenyn och så annonserna på slutet. Låt oss säga att vi vill använda proportionerna 25/50/25. Vi vill också ha en kantlinje mellan spalterna. För att åstadkomma detta behöver vi ta till ett par extra DIV-element för att gruppera saker. Strukturen blir följande:

<div id="outer">
  <div id="inner">
    <div id="content">Innehåll</div>
    <div id="menu">Meny</div>
  </div>
  <div id="sidebar">Annonser</div>
</div>

Den yttre behållaren innehåller alltså en inre behållare (till vänster) samt annonsspalten (till höger). CSS-reglerna ser ut så här:

#outer {
  margin:0 25%;
  border:1px solid #000;
  border-width:0 1px;
}

Vänster- och högermarginalerna sätts till 25 % för att lämna utrymme för sidospalterna, och så sätter vi en tunn svart kantlinje på vardera sidan.

Den inre behållaren består av innehållsspalten (till höger) och menyn (till vänster). CSS-regler:

#inner {
  float:left;
  width:99%;
}

#content {
  float:right;
  width:99%;
}

#menu {
  float:left;
  width:50%;
  margin-left:-49.9%;
}

Själva den inre behållaren flyter alltså till vänster och upptar i stort sett hela den yttre behållarens bredd – bortsett från marginalerna förstås. Innehållsspalten är flytande inuti den flytande inre behållaren, och utnyttjar i stort sett hela dess bredd.

Menyn flyter till vänster, så den hamnar jämsides med den flytande innehållsspalten. Dess bredd ska motsvara den yttre behållarens vänstermarginal, som är satt till 25 %. Men om vi anger bredden för menyn i procent så är det i procent av den inre behållarens bredd, inte av sidans bredd. Den inre behållarens bredd är (knappt) halva fönsterbredden (99 % av 100 % minus två marginaler om 25 %), så menyns bredd blir ungefär 25 % dividerat med 0,5, vilket blir 50 %. Puh!

Som om inte detta vore nog har menyn en negativ vänstermarginal som är nästan lika stor som dess bredd. Med betoning på nästan. Den negativa marginalen gör att menyn, som ju bara har 1 % av den inre behållarens bredd att existera i, flyttas utanför den inre behållaren och hamnar i den yttre behållarens vänstermarginal som vi ju skapat för just detta ändamål.

Varför sätter vi då inte vänstermarginalen till -50%? Därför att vi inte får flytta ut hela menyn utanför den inre behållaren. Gör vi det kommer den inte längre att ingå i den inre behållaren, och om menyspalten då råkar bli längst kommer den att skriva över en eventuell sidfot. Eftersom menyn med denna teknik faktiskt kommer att överlappa den inre behållaren en liten aning bör vi se till att menyns underelement har en viss högermarginal för att ge lite luft.

Annonsspaltens CSS-regler liknar menyns:

#sidebar {
  float:right;
  width:50%;
  margin-right:-49.9%;
}

Annonsspalten ligger innehållsmässigt efter den inre behållaren, som flyter till vänster och upptar 99 % av mitten. I och med att vi gör annonsspalten flytande till höger och ger den en negativ högermarginal flyttar vi ut den i marginalen precis som vi gjorde med menyn. Bredden beräknas på samma sätt och på motsvarande sätt som för menyn bör vi se till att underelementen har en viss vänstermarginal för att inte hamna ovanpå kantlinjen.

Om vi nu provar det här så fungerar det inte alls. Varför? I och med att alla beståndsdelarna är flytande och därmed tas bort ur dokumentflödet har den yttre och den inre behållaren inget innehåll alls! För att råda bot på detta lägger vi till ett par utjämnande element som ser till att behållarna omsluter sina flytande barn:

<div id="outer">
  <div id="inner">
    <div id="content">Innehåll</div>
    <div id="menu">Meny</div>
    <div class="cleared">&nbsp;</div>
  </div>
  <div id="sidebar">Annonser</div>
  <div class="cleared">&nbsp;</div>
</div>

CSS-reglerna för dessa element ser ut som följer:

.cleared {
  clear:both;
  line-height:0;
}

Vad clear:both betyder är väl inget större mysterium vid det här laget, men vad har line-height med saken att göra? Vi vill inte att dessa utjämnare ska ta någon plats, men om vi lämnar dem helt tomma kommer de inte att tas med alls i vissa webbläsare. Därför får de innehålla ett &nbsp; och så sätter vi radhöjden till noll.

Buggar, buggar och ännu fler buggar

Om du orkat läsa ända hit inser du förmodligen vid det här laget att flytande element inte är helt triviala. Det är därför inte alltför förvånande att även webbläsarna har vissa problem med dem. I alla fall vissa webbläsare. Vi talar naturligtvis om Internet Explorer för Windows.

IE/Win har så många och komplicerade buggar när det gäller flytande element att vi inte kan gå in på dem här. Exemplet i den här artikeln fungerar exempelvis inte alls i IE/Win, men artikeln är tillräckligt lång utan att vi ger oss in i buggrättningsdjungeln.

Lyckligtvis finns det en enkel lösning som rättar till många av dessa buggar. Vi såg tidigare att alla flytande element blir en block box. Standarden säger dessutom att egenskapen display ska ignoreras för flytande element, såvida den inte anges som none. Om vi anger display:inline för ett flytande element försvinner en del av buggarna i IE/Win som genom magi. Ingen vet riktigt varför. IE/Win gör inte om elementet till en inline box, men många av buggarna rättas till.

I övrigt överlåter vi med varm hand buggdetaljerna till den oumbärliga webbplatsen Position Is Everything.


Kommentarer är avstängda, på grund av för mycket spam.

Skyll inte på mig. Skyll på idioterna som förstör webben genom spam.


Avdelningar

Innehåll

Senaste

Sajtsökning