1. Webdesign 101 forside
  2.  » Navigation
  3.  » CSS menuer

CSS dropdown menu og menubar

Artiklen demonstrerer hvorledes du kan skabe et dynamisk dropdown menupanel eller en dropdown navigationsbjælke ud af en liste med indlejrede lister.

Mit udgangspunkt er en almindelig uordnet liste med indlejrede lister. (Eksempel 1). Denne liste er indsat på en side, der er layoutet med følgende stylesheet:

div#page {
    width:45em;position:relative;
    margin:1em auto; 
    background:#efefff;
    padding:1em;}
div#leftcol {
    width:12em;
    float:left;}
div#rightcol {
    margin-left:12em;}

Det vil sige, jeg har en spalte med en bredde på 12 em til rådighed for mit menupanel. Som du kan se af eksemplet, er det ikke nok til at indeholde en uordnet liste med flere indlejrede lister.

Det kan vi imidlertid hurtigt ordne med noget stylesheet formatering af listerne:

  div#leftcol ul {
  margin: 0; padding: 0; 
  list-style:none;
  width: 11em; 
  background: white;
  border-width: 1px 1px 0 1px;
  border-style: solid;
  border-color:#257;}

Resultatet af denne tilføjelse til vort stylesheet ser du i eksempel 2.

Bemærk i dette eksempel følgende:

  1. Margin og padding for listerne nulstilles. Alle browsere har nogle standardværdier for hhv. margin og padding, men de er ikke ens. Derfor er vi nødt til eksplicit at sætte disse egenskaber i det omfang vi har brug for dem.
  2. list-style:none fjerner listepunkt-markørerne (bullets) og sætter listepunktets tekst ud i venstre margin.

Undermenuernes position

Når alle ul'erne formateres ens kommer de naturligvis til at stå oven i hinanden.

Det laver jeg om på ved

  1. at give alle li'er en position:relative. Så længe jeg ikke sætter nogen positionsangivelser, bevarer li'erne deres “naturlige” position.
  2. Men en li er nu en referenceramme for den absolutte positionering af en ul, der er indeholdt i en li:
div#leftcol li {
  position: relative; list-style: none; 
  margin: 0; padding:0;
  border-bottom: 1px solid #257;}
div#leftcol ul ul {
  position: absolute; top: -1px; left: 11em;}

Bemærk her, at en ul i en ul har fået en position left på 11 em, svarende til menuernes bredde. Det vil rykke alle indlejrede lister til højre for deres forældrelement med 11 em. Eksempel 3.

Nu står undermenuerne oven i hinanden, men det problem forsvinder, når vi ændrer værdien for left-positionen i formdeklarationen. Ideen er jo, at alle undermenuer skal være skjulte, indtil musen er over det menupunkt, der har en undermenu:

div#leftcol ul ul {position: absolute; top: -1px; left: -1000em;}

I mine tidligere eksempler om dette emne har jeg brugt display-værdierne block og none til at ændre på visningen af undermennuerne: display:none skjuler undermenuerne, mens display:block viser undermenuerne.

Skærmlæsere og søgerobotter har imidlertid et ambivalent forhold til blandt andet display-værdierne. Derimod spiller position ingen rolle og de fleste forfattere anbefaler nu anvendelsen af position til dette formål. left:-1000em flytter effektivt undermenuerne ud af browservinduet på en visuel browser medmindre man har en skærmbredde på over 16.000 pixel...

Før vi kigger nærmere på, hvordan det ordnes således at undermenuerne vises, når musen er over menupunkterne, skal der pyntes lidt på menuerne.

div#leftcol li.sub {
  background-image:url(rarrow.gif);
  background-position:right center;
  background-repeat:no-repeat;}
div#leftcol li a {
  display: block; padding: 0.25em 0 0.25em 0.5em;
  text-decoration: none; width:10.5em;}
div#leftcol>ul a {width: auto;}

Jeg starter med at give alle de menupunkter, der har en undermenu, en class="sub". Det gør det muligt for mig at forsyne disse, og kun disse, med en lille pil tilhøjre, der skal tilkendegive, at menupunktet skjuler en undermenu. Dernæst formateres li-markørernes indhold af links, med en padding, der giver alle menupunkter en vis fylde. a-elementet, der er et inline element, skal have sin display-egenskab ændret til block. Af hensyn til ældre versioner af Internet Explorer er vi nødt til at give a-markøren en eksplicit bredde på 11 em (10.5 em + 2 gange padding 0.25 em). Af hensyn til alle andre browsere ophæver jeg denne i den sidste formregel i koden lige herover.

Nu er vi nået til eksempel 4.

:hover på et vilkårligt element

Nu kommer to svære opgaver:

  1. At lave en :hover-effekt på listepunkterne, så menupunktet skifter bagrundsfarve, når musen er over det, og
  2. At folde undermenuerne ud, når musen er over et menupunkt med en undermenu, ved at ændre display-egenskaben for undermenuen fra none til block.
div#leftcol ul ul { position: absolute; 
  top: -1px; left: 11em;  display:none; }
div#leftcol li:hover {background-color: #aaf;}
div#leftcol ul.niveau1 li.sub:hover ul.niveau2,
div#leftcol ul.niveau2 li.sub:hover ul.niveau3 {
  display:block;}

Ovenstående tilføjelser til stylesheet'et klarer sagerne. Bemærk at den første formdeklaration er en gentagelse, hvor display-egenskaben for de indlejrede lister er ændret til none, så de ikke vises.

Dernæst følger to konstekstuelle selektorer for mine undermenuer. Bemærk først at jeg har givet mine undermenuer et class name, niveau1 for topmenuen, niveau2 for undermenuer og niveau3 for undermenuer til undermenuer.

Nu skal den første kontekstuelle selektor læses således: Under en given omstændighed skal en ul, der har klassenavnet niveau2, og som er barn af en li med klassenavnet sub og barnebarn af en ul med klassenavnet niveau1, have display-egenskaben block.

Den givne omstændighed er, at forældreelementet li.sub er i :hover tilstand. Og det er dette element når musen er over det.

Nu er vi nået til eksempel 5. Hvis du kun har Internet Explorer 6 som browser, behøver du ikke kigge på det eksempel. Den forventede funktionalitet glimrer ved sit fravær i denne browser. I enhver anden rimeligt moderne – inklusive Internet Explorer 7 og følgende – browser virker det fint.

Eksempel 6 virker derimod fint i alle moderne browsere, inklusive Internet Explorer 6. Hvordan jeg har opnået det forklarer jeg i det følgende afsnit.

Problemet med Internet Explorer

Problemet med ældre versioner af Internet Explorer består i, at disse browsere ikke kan fortolke pseudoklassen :hover på andre elementer end links. Og for ikke at gøre denne konstruktion unødigt kompliceret er det afgørende, at vi kan benytte os af :hover-effekten på li-elementer.

Den afgørende forskel på eksempel 5 og eksempel 6 er følgende kodestump i head-delen af eksempel 6:

 <!--[if lt IE 7]>
  <style type="text/css" media="screen">
  body {behavior: url(csshover.htc);}
  </style>
  <![endif]-->

Det, der sker her er, at jeg indsætter endnu et stylesheet for websiden. Jeg sørger for, at kun Internet Explorer indlæser dette stylesheet ved at bruge Microsofts betingede kommentar til at skjule HTML-kode for alle andre browsere end Microsofts egne. Derved undgår jeg at indlæse dette ekstra stylesheet i alle andre browsere. (Microsofts betingede kommentarer kan du læse noget mere om i min artikel om disse, Microsofts betingede kommentarer).

Microsoft behaviors

Inde i kommentarblokken finder du så det stylesheet, som kun Internet Explorer benytter. Stylesheet'et indeholder kun én formdeklaration, der knytter egenskaben behavior til body-elementet.

Microsofts behaviors er DHTML-scripts, der tilfører HTML-elementer dynamiske egenskaber ved hjælp af JavaScript eller Microsofts version af JavaScript, JScript. Et sådant Javascript skal have filtypen .htc og indlæses på siden ved hjælp af behavior-egenskaben på den her viste måde.

Enhver kan konstruere sådanne behaviors, hvis man sætter sig ind i hvordan de programmeres. Det ligger dog i yderkanten af mine evner. Heldigvis har andre taget handsken op på netop det felt, jeg her beskæftiger mig med, og som er:
Hvordan giver vi HTML-elementer de samme CSS-egenskaber i IE<7 som de har i mere standardkompatible browsere.

Det mest udbredte af disse scripts er lavet af Peter Nederhof, og kan downloades fra whatever:hover eller fra dette websted csshover.htc. Dette script gør det muligt at benytte :hover på et vilkårligt HTML-element, når det knyttes til body-elementet på den måde, der er beskrevet ovenfor.

.htc-filen føjer ca. 3 kilobyte til den datamængde, der skal overføres for at vise siden. Det er derfor ikke afgørende, at du skjuler det ekstra stylesheet for andre browsere, som jeg har gjort i eksempel 6. Det kan uden videre tilføjes det almindelige stylesheet for skærmvisning, som jeg har gjort i eksempel 7. Browsere, der ikke kender behavior-egenskaben vil uden videre springe denne formdeklaration over.

Bemærk, at brugen af denne teknologi vil medføre, at dit stylesheet ikke kan validere som korrekt CSS af W3S's CSS-validator.

.htc filer & IIS

Bruger du Windows XP Professional og har servicepack 2 installeret, vil du muligvis opleve problemer med Internet Explorers indlæsning af .htc-filer.

I egenskaber for IIS skal du oprette eller ændre mime-type (under http-headers) til text/x-application. Jeg har indtil jeg oprindeligt skrev denne artikel brugt mime-typen text/Jscript, hvad voldte mig store problemer indtil jeg fandt ud af det.

Læs evt. mere hos Big John.

jfj, 22/9-2005

Menupanelet i layout'et

En menu skal jo på en eller anden måde integreres i en websides layout, hvadenten det drejer sig om et menupanel eller en navigationsbjælke eller måske begge dele.

Jeg er sluppet lidt billigt fra det, fordi layout'et på mine eksempelsider er temmelig minimalistisk:

div#page {
width:45em;
margin:1em auto; 
background:#efefff;
border:1px solid #257;}

div#leftcol {
width:13em;
float:left;
padding-top:30px;}

div#rightcol {
margin-left:13em;
border-left:1px solid #257;
padding-left:1em;}

– plus foroven et sidehoved med et baggrundsbillede og en sidehovedtekst.

I min artikel Layout med float-egenskaben og denne artikels eksempler kan du læse en hel del om netop dette emne, så det vil jeg ikke gå i dybden med her.

Jeg har dog lavet yderligere et par kommenterede eksempler, der bygger på ovennævnte eksempel 7, hvor jeg viser, hvorledes du kan indpasse menupanelet i dels

  1. et elastisk layout hvor både menuspalten og artikelspalten har procentvise bredder (eksempel 7a),
  2. et elastisk layout, hvor menuspalten har en fikseret bredde (eksempel 7b), og
  3. Et elastisk layout, hvor menuens bredde indretter sig efter menuspaltens bredde (eksempel 7c).

Højrestillet menu

Det er ret uproblematisk at gøre menuen højrestillet, jf. eksempel 8. Nøglen til denne løsning knytter sig til positioneringen af undermenuerne:

div#rightcol ul ul {position: absolute; top: -1px; 
right: 10em; display:none; }

Navigationsbjælke

Ganske som vi kan lave en "flad" navigationsbjælke med en vandretstillet liste, kan vi bruge de samme principper til at lave en navigationsbjælke med dropdown undermenuer.

Eksempel 9 viser navigationsbjælken.

Alternativer til behavior-metoden

Der findes andre .htc-filer, der der gør det samme, ligesom der findes JavaScripts til indsætning i head-delen af dokumentet, der på forskellig vis tilfører en uordnet liste den samme funktionalitet som andre browsere med hensyn til at få :hover-pseudoklassen til at virke på li-elementer.

De fleste af disse alternativer kræver dog, at du mingelerer mere med din HTML-kode og/eller din styleshet-kode end det synes strengt nødvendigt. Det er en af fordelene ved Nederhofs .htc-fil, at du kan bruge nøjagtigt den samme HTML-kode og nøjagtigt den samme CSS-kode som du ville bruge, hvis Internet Explorer < version 7 var lige så kompatibel med CSS-standarden som stort set alle andre nyere browsere.

Internet Explorer 7

Hvis og hvis og hvis... Hvis grise havde vinger ville de flyve.

I denne sag kan grise nu flyve. Der findes et JavaScript modulbibliotek (udviklet af Dean Edwards), der forandrer Internet Explorer fra en version 6 til en version 7, med de fleste af de forbedringer, Microsoft for længst burde have indført.

Eksempel 9a viser fuldstændig den samme webside som eksempel 9, men her er behavior-egenskaben fjernet og i stedet refererer siden til et eksternt JavaScript.

Det eksterne JavaScript, der er tale om, hedder i daglig tale IE 7, og kan findes i Googles kodebibliotek på webadressen http://code.google.com/p/ie7-js/. På websiden IE 7 demo kan du se hvilke selektorer og egenskaber, der efter indlæsning af dette modulbibliotek liplusli bliver tilgængelige også i Internet Explorer.

Pillerier

Når jeg selv skal bruge en sådan menu ‘for alvor”, vil jeg som regel gerne have den til at fylde det overordnede element helt ud, hvadenten det nu er body-elementet eller som her en CSS-boks, der rummer sidens indhold.

Eksempel 10 viser, hvorledes det tager sig ud.

Hvis det er absolut og meget meget nødvendigt kan det godt lade sig gøre at ordne det således, at menupunkterne lige præcis fylder menublokken ud, især i et tilfælde som dette, hvor bredden af menublokken er fikseret. Eksempel 11 viser én lettere sjusket måde at gøre det på.

Sandt at sige foretrækker jeg at bruge kulør for at skabe samling på menublokken. Eksempel 12 viser, hvorledes jeg har farvesat menuen, og brugt en baggrundsgrafik ved :hover-tilstanden.

Det kræver lidt større indgreb end de tidligere eksempler: Da vi ikke kan have to baggrundsgrafikker på det samme element, og da vi fortsat ønsker både en pil-ned og en baggrundsgrafik i hvert fald ved :hover-tilstanden, er jeg nødt til at knytte baggrundsgrafik til både li- og a-elementerne. I eksemplet har jeg fremhævet disse ændringer.

Undermenuen forsvinder

I eksempel 12 vil du bemærke, at jeg har sat nogle undermenupunkter ind under menupunktet yderst til højre. Du vil også hurtigt konstatere, at de undermenupunkter ikke er til megen nytte – de forsvinder, så snart jeg fører musen ned over dem.

Årsagen til forsvindingsnummeret er, at jeg på websiden har to lag, der overlapper hinanden – dels det lag, det udgøres af menuen og dens underpunkter, og dels det lag der udgøres af den højre kolonne (#rightcol-elementet).

Sådan som reglerne er for stakkeordenen af to (positionerede) lag der overlapper hinanden, vil #rightcol-elementet ligge øverst, fordi det er indlæst senere i kildekoden.

Heldigvis kan vi manipulere med denne stakkeorden ved hjælp af z-index-egenskaben. Så løsningen på problemet med den forsvindende undermenu er ganske enkelt at give menuen en højere z-index-værdi end #rightcol-elementet. Dette element har ingen z-index-værdi, så enhver z-index-værdi for menuen større end 0 (Nul) vil ordne problemet, jf. eksempel 12a.

Grafisk baggrund

Det er i princippet ret ligetil at forsyne hvert menupunkt med en grafisk baggrund. Se for eksempel Grafisk rollover kun med CSS.

I eksempel 13har jeg taget skridtet videre og udført det sådan, at udsnit af sidehovedets billede vises som baggrund for hvert enkelt menupunkt. I :hover tilstande udskiftes hvert udsnit med et udsnit, hvor billedet er dæmpet.

CSS-menuens ulemper

Som jeg ser det, er der mange fordele ved denne meode at konstruere dynamiske menuer på: JavaScript-lamme browsere – og det så godt som alle browsere, der ikke er de visuelle browsere, vi bruger på vores laptops og desktop computere – viser menuen; er browseren desuden CSS-lam, vises hele menuen i udfoldet stand, hvad du let ser ved at indlæse en af mine eksempelsider og bruge dit eget, tomme stylesheet (jeg bruger jævnligt til IE et sådant stylesheet, som jeg har kaldt blank.css, og som ikke indeholder en eneste formdeklaration. I Firefox er det nemt, vælg No style i menuen Vis/Page Style).

Men der er også et par ulemper: For det første er det ikke muligt at indbygge nogen form for tidsforsinkelse i en sådan menu, som man jævnligt gør i en JavaScript DHTML menu. Tingene sker øjeblikkeligt, og det kan kræve en tilvænning hos brugere, der måske ikke er vant til at tingene sker hurtigt.

Den mest skadevoldende effekt af denne hastige respons på brugerinput er, at en undermenu forsvinder øjeblikkeligt, hvis brugeren flytter musen blot en pixel uden for det aktive område. Det er faktisk derfor man ofte indbygger et par hundrede millisekunders forsinkelse i DHTML menuerne.

For nylig (september 2005) fandt jeg imidlertid hos Holly'n'John ( www.positioniseverything.net) et ganske probat middel til at bekæmpe denne uhensigtsmæssige virkning. Det går ganske vist ikke ud på at forsinke menuens reaktion, men det går ud på at sørge for, at musen skal virkelig langt uden for menuen, før den lukkes. ( http://www.positioniseverything.net/css-dropdowns.html.)

Oprettet: August 2005. Opdateret: April 2009