Skrevet af Tobias Hinnerup den 15. september 2006. Sidst redigeret den 10. maj 2002. © Alle rettigheder forbeholdt.
Indledningsvist skal det klargøres, at der bag realiseringen af faneblads-systemet ligger en relativt avanceret anvendelse af tre forskellige, web-relaterede teknologier. Konsekvent gælder, at hvis du er ny inden for programmering og www's virkemåde i almindelighed, bør du foreberede dig på at ikke alt i denne artikel umiddelbart giver mening.
Kildekode og eksempler kan findes på websitet for "Faneblade i XHTML".
Faneblade kan opfattes som bestående af to (af hinanden afhængige) dele: Overskrifter og indholds-sider.
Et givent faneblad kan enten være aktivt, altså ligge oven på alle andre, eller inaktivt og være delvist dækket. Når et faneblad er aktivt, vil hele indholdssiden være synlig, og det samme gælder overskriften. Når et faneblad er inaktivt, er indholdssiden fuldstændig skjult, og overskriften er nedtonet i forhold til overskriften på et aktivt faneblad.
Traditionelt er fanebladene arrangeret således indbyrdes, at alle overskrifterne ligger ved siden af hinanden (fra venstre mod højre), oven over indholdssiderne, således at alle overskriftes er synlige, mens kun den aktive indholdsside er synlig.
Kendetegnende for overskrifter er, at de har varierende længde, men for overskuelighedens skyld altid samme højde. En sådan struktur repræsenteres naturligt ved hjælp af tabeller i XHTML. Altså, første skridt mod at overføre fanebladene til XHTMl er at opfatte mængden af overskrifter som indeholdt i en tabel.
Så langt, så godt: Men hvad med indholdssiderne ? Disse skal ligge "over" hinanden - men det er der ikke umiddelbart defineret et koncept i XHTML der omfatter.
Løsningen er her igen, er at opfatte hver indholdsside som en celle i en tabel.
Disse indskrives i XHTML efter hinanden - og for en ordens skyld skyld i den rækkefølge de er knyttet til overskrifterne. På den måde sikres, at siden vil være i hvert fald nogenlunde forståelig, skulle den blive besøgt af en browser der ikke understøtter de Cascading StyleSheets der senere anvendes til at formattere tabellerne.
Slutteligt, for at holde styr på alle disse elementer i forhold til hinanden, indlejres disse i yderligere en tabel.
Bortset fra nogle enkelte detaljer, er dette faktisk den endelige repræsentation af fanebladenes struktur i XHTML.
Startes med at kigge på overskrifterne, er det gældende at på et givet tidspunkt vil "de fleste" af disse være inaktive. Almindelig brugt til at signalere dette, er at bruge en mørk baggrund, og eventuelt lægge en kant rundt om der signalerer at det indrammede en sænket i forhold til omgivelserne. Til formålet er det hensigtsmæssigt at definere en CSS klasse, og tildele denne til alle cellerne i tabellen med overskrifter.
Dernæst er det naturligt at definere klassen for den aktive overskrift med en lyst baggrund og en hævet kant, så den klart drager opmærksomheden.
| Overskrift 1 | Overskrift 2 | Overskrift 3 | ... |
Samtidig ønskes det at gøre opmærksom på, at de inaktive overskrifter kan gøres aktive ved at klikke på dem. Her kan muse-markøren bruges til at give et visuelt prik på skulderen, ved at den forvandles til en hånd når musen er over de overskrifter der kan udvirke en ændring i sidens udseende.
/* CSS klasser for overskrifter */
.PanelLabel
{
color: black;
background-color: #AAAAAA;
border-style: inset;
border-width: 2px;
cursor: hand;
padding: 0.3em;
}
.PanelActiveLabel
{
color: black;
background-color: #EEEEEE;
border-style: outset;
border-width: 2px;
cursor: default;
padding: 0.3em;
}
|
Ovenstående effekter er opnået ved at anvende farven #AAAAAA som baggrundsfarve på de inaktive overskrifter, og farven #EEEEEE på den aktive.
Tilsvarende har de inaktive fået en kant af typen inset og den aktive typen outset. Ændringen af muse-markøren er opnået ved at sætte cursor-typen til hand i de inaktive, og default i de aktive (det giver ikke nogen mening at klikke dér igen).
Bemærk, at jævnfør World Wide Web Consortium (W3C) burde bruges cursor-typen pointer i stedet for hand. Hand understøttes af Internet Explorer, og typen pointer af Netscape.
Altså har vi nu to CSS klasser defineret - navngivet logisk til henholdsvis PanelLabel og PanelActiveLabel, der præcist beskriver hvordan de to forskellige typer overskrifter ønskes repræsenteret.
| Overskrift 1 | Overskrift 2 | Overskrift 3 | ... |
|
Side 1 . . . |
/* CSS klasse for indholdssider */
.Panel
{
display: none;
text-align: left;
vertical-align: top;
color: black;
background-color: #EEEEEE;
border-style: outset;
border-width: 2px;
margin: 0px;
padding: 0.5cm;
}
|
Vendes opmærksomheden nu mod indholdssiderne, kan samme overvejelser vedrørende kanter og baggrundsfarver anvendes i forbindelse med den aktive side. Med de inaktive stiller det sig anderledes, idet de helt skal skjules.
Heldigvis er det netop muligt at forhindre elementer i at blive vist, ved at sætte deres display-egenskab til none.
Analogt til overskrifterne defineres en CSS klasse til de inaktive indholdssider med navnet Panel.
Nu begynder tingene at tage så meget form, at det kan genkendes som det det skal ende med at være. Her er det tiden at stoppe op, og tage et skridt tilbage og kigge på hvordan tingene hænger sammen - og om de overhovedet gør det.
For faneblade kan jo arrangeres anderledes end med overskrifterne i toppen ? For mange vil det være naturligt at have dem til venstre - lige som de fleste websites har en menu-bjælke til venstre. For andre vil det være naturligt at sætte overskrifterne ud til højre, som om de var indeks-blade i en mappe.
|
|||||||
| eller | |||||||
|
De forskellige layout af fanebladene skal der nemlig tages højde for, inden de sidste, finpudsende deltajler bliver lagt på. Når overskrifter og indholdssider rykkes op ad hinanden, skal den kanter der støder op til hinanden nemlig fjernes.
Præcist hvilke kanter der skal fjernes, vil være afhængigt at overskrifternes placering i forhold til indholdssiderne.
For at komme omkring dette problem, kan det svare sig at definere CSS klasser for de tabel-celler der indeholder henholdsvist overskrifter og indholdssider. Faktisk viser det sig hensigtmæssigt, at definere en klasse for både overskrifter og indholdssider for hver placering overskrifterne kan få. Altså defineres klasserne LabelContainerTop, LabelContainerLeft, LabelContainerTop og PanelContainerTop, PanelContainerLeft samt PanelContainerTop.
Ved således at angive en sigende klasse for den omgivende celle, er det muligt ved hjælp af selektorer i CSS at angive at når et element af typen Label forekommer inden i et af typen LabelContainerTop skal den nederste kant udelades - og tilsvarende når et element af typen Panel forekommer inden i et af typen PanelContainerTop skal den øverste kant udelades. Samme procedure anvendes på de to andre placeringer.
/* CSS selektor for overskrifter */
.LabelContainerTop .PanelLabel
{
border-bottom: none;
margin-bottom: 0px;
}
.LabelContainerLeft .PanelLabel
{
border-right: none;
margin-right: 0px;
}
.LabelContainerRight .PanelLabel
{
border-left: none;
margin-left: 0px;
}
|
Dette kan måske umiddelbart virke som unødvendigt besværligt - men resultatet er, at det kun er beskrevet et sted hvordan inaktive og aktive henholdvis indholdssider og overskrifter ser ud, i stedet for 6.
Erfaringsvist er det besværet værd, allerede første gang der skal vedligholdes på koden.
For at få overskrifter og indholdssider passet helt tæt op af hinanden, skal celle-afstand, margener og padding i alle de definerede CSS container-klasser sættes til 0.
Definitionener af container-klasserne er udeladt, idet de er relativt trivielle. De kan narturligvis findes i kilde-koden på websitet for "Faneblade i XHTML".
Tilbage er så blot at observere, at bredden af både panel- og label-containere skal sættes til 100% (af bredden af den tabel de er indlejret i).
Ellers vil det være muligt, at den ene pludselig er bredere end den anden. For de vertikale udgaver, er det naturligvis i stedet højden der skal justeres til 100% af den omgivende container.
|
||||
|
Bemærk:Den omgivende "panel"-tabel er ikke fjernet - dens kanter er blot ikke vist.
Nu træder det frem, at den valgte struktur-repræsentation i XHTML ikke er tilstrækkelig: Fordi celleafstanden kun kan sættes til nul på alle sider af cellerne samtidig, kommer overskrifterne til at ligge uadskilleligt tæt på hinanden. Derfor er det nødvendigt at indføre en celle mellem hver overskrift, med outset bund-kant og gennemsigtig baggrund. Tilsvarende tilføjes en celle i slutningen af overskrifts-rækken med disse egenskaber, for at lægge "låg" på den sidste, øverste del af indholdssiden.
|
||||||
|
Igen viser det sig hensigt-mæssigt at erklære en CSS-klasse for en mellemrums-celle uden at tage højde for om den er placeret over, til højre eller til venstre for indholdssiderne, og derefter anvende selektorer til at tilføje den relevante kant.
Så ser fanebladene ud som det kan forventes - det eneste der mangler er muligheden for at kunne komme til de faneblade der nu er skjulte/dækkede bag det forreste.
For at bringe liv i den endnu livløse XHTML gribes til JavaScript. Der er behov for at kunne reagere på at der bliver klikket på de inaktive overskrifter.
Når der klikkes på en sådan, skal klassen for den overskrift der blev klikket på sættes til PanelActiveLabel og alle andre skal sættes til PanelLabel. Samtidig skal alle andre indholdssider end den der hører til den aktive overskrift skjules.
Ved at indføre en fast navngivning gennem brug af id-attributten i XHTML, kan det lade sige gøre at lave en funktion, der givet antallet af sider og nummeret på den der skal være aktiv, kan først deaktivere dem alle, og derefter aktivere den ønskede.
Navngivningen får i denne sammenhæng også betydning for hvordan flere faneblade på samme side skal håndteres: Det er ikke ønskeligt, at der ved klik på et faneblad i et panel skiftes faner i samlige paneler på siden. Derfor skal navnet på elementerne ikke blot indeholde oplysninger om type (overskrift henholdsvis indholdsside), men også en del der er unik for alle paneler på siden.
Konsekvent kommer kaldet til JavaScript-funktionen til at se ud i stil med function ClickPanel(ContainerID, PanelNo, TotalNoOfPanels). I god programmerings-stil kan denne funktion splittes op i to: En der håndterer aktivering og en der håndterer deaktivering. Men idet der er defineret klasser bliver kodemængden i de to funktioner så lille, at det i praksis ikke er nødvendigt.
JavaScriptet kobles sammen med XHTML ved hjælp af onclick hændelsen på de tabel-celler der indeholder overskrifterne.
For ikke at ende med en alt for uoverskuelig kilde-fil, anbefales det at separere koden i tre filer - en til henholdsvis XHTML, CSS og JS. Disse kan så inkluderes i XHTML'en ved brug af de relevante tags.
<link rel="stylesheet" type="text/css" href="/CSS/panels.css" />
<script type="text/javascript" src="/js/panels.js">
</script>
Når det er gjort, er systemet færdigt og klar til at blive taget i anvendelse.
|
||||||
|
Ovenstående fungerende fanebladessystem (klik bare løs!) er generet ved den kode der er listet herunder. Hvis du har hentet CSS og JS filerne, og lagt dem i de korrekte mapper, skulle det være muligt at lave copy & paste, og se det virke på egen maskine.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Faneblade i XHTML</title>
<link rel="stylesheet" type="text/css" href="/CSS/panels.css" />
<script type="text/javascript" src="/js/panels.js"> </script>
</head>
<body>
<table cellspacing="0" style="width: 400px;">
<tr>
<td class="PanelContainer" >
<table class="LabelContainerTop" cellspacing="0">
<tr>
<td class="PanelActiveLabel"
id="LabelDemo1"
onclick="javascript:ClickPanel('Demo', 1, 3);" >
Overskrift 1
</td>
<td class="PanelLabelSpace">
</td>
<td class="PanelLabel"
id="LabelDemo2"
onclick="javascript:ClickPanel('Demo', 2, 3);" >
Overskrift 2
</td>
<td class="PanelLabelSpace">
</td>
<td class="PanelLabel"
id="LabelDemo3"
onclick="javascript:ClickPanel('Demo', 3, 3);" >
Overskrift 3
</td>
<td class="PanelLabelSpace" style="width: 100%;">
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="PanelContainer" >
<table class="PanelContainerTop" cellspacing="0">
<tr>
<td class="Panel" id="PanelDemo1">
Indholdsside 1<br />.<br />.<br />.<br />
</td>
<td class="Panel" id="PanelDemo2">
Indholdsside 2<br />.<br />.<br />.<br />
</td>
<td class="Panel" id="PanelDemo3">
Indholdsside 3<br />.<br />.<br />.<br />
</td>
</tr>
</table>
</td>
</tr>
</table>
<script type="text/javascript">
ClickPanel('Demo', 1, 3);
</script>
</body>
</html>
Den færdige implementation, demonstration og kilde-kode (både eXstensible HyperText Markup Language, Cascading StyleSheet og JavaScript) til det her beskrevne faneblads system kan findes på websitet for "Faneblade i XHTML".
Dér findes også en mængde baggrundsinformationer om systemet, som det er fuldstændig gratis at både hente og anvende. Undertegnede beder blot om at blive nævnt i sitets "Credits". Blandt andet kan også findes en ASP implementation af systemet, samt en artikel om "Faneblade i ASP".
Tilføjelse af mulighed for at lægge overskrifterne under indholdssiderne er det overladt som en øvelse til læseren at implementere. Hint: Det er tilstrækkeligt at ændre/tilføje i CSS-filen.
God fornøjelse.
Tobias Hinnerup
10. maj 2002