» direct naar zoek en menu

Tijdschrift voor webwerkers » Artikel #73

De kracht van :hover - Weg met al die scriptjes!

Dankzij stylesheets en semantisch verantwoorde code worden websites al steeds toegankelijker en bruikbaarder. Maar hoe kun je nou wat complexere dingen in je site – die je eerst aan een scriptje of een flinke lading <div>’s overliet – ook op een toegankelijke en elegante manier oplossen? We gaan eens kijken naar een voorbeeld: een uitklapmenuutje.

Uitklapmenu’s zijn erg gewild, ook omdat ze veel informatie op een compacte manier toegankelijk maken. Maar veel van deze menu’s zijn technisch achterhaald. Zo bestaan de succesvolle coolmenu’s uit lange scripts die hun oorsprong nog in het Netscape 4 tijdperk vinden. De variant beschreven in het A List Apart-artikel Suckerfish Dropdowns toont echter overduidelijk aan dat een (on)geordende lijst met CSS tot hetzelfde in staat is. In moderne browsers kan dat zelfs zonder javascript door het gebruiken van de :hover pseudo-class.

:hover pseudo-class

Door een :hover pseudo-class in je stylesheets toe te voegen, kun je stijlen definiëren voor elementen die de gebruiker op dat moment met zijn of haar muis aanwijst. De meestgebruikte toepassing is waarschijnlijk wel het daarmee al dan niet onderstrepen en van kleur veranderen van links. Minder bekend, maar zeker zo praktisch, is het gebruiken van een hover op andere elementen. Zo kunnen bijvoorbeeld tabelrijen en inputelementen van een hover worden voorzien voor functionele en visuele ondersteuning.

Helaas is dat laatste in de huidige versies van Internet Explorer niet mogelijk. Vrijwel alle andere moderne browsers ondersteunen het wel, maar aangezien IE toch de meest gebruikte browser is, kun je belangrijke functionaliteiten niet aan een hover hangen zonder een alternatief te bieden. Kijk eens naar het volgende voorbeeld, dat zou kunnen dienen voor een op ongeordende lijst gebaseerd menu:

<ul>
   <li><a>item</a></li>
   <li>
      <a>item</a>

      <ul>
         <li><a>item</a></li>
         <li><a>item</a></li>

         <li><a>item</a></li>
      </ul>
   </li>
   <li><a>item</a></li>   

</ul>

Deze code toont zonder CSS een gewone lijst met daarin opgenomen (genest) nog een lijst:

Uitgaande van een volledige ondersteuning van :hover leggen de volgende CSS-regels dan de functionele basis voor een uitklapmenu, waarbij de geneste lijsten als subnavigatie dienen:

ul ul {
   display:none;
}

li:hover > ul {
   display:block;
}

Met deze code wordt door de eerste regel alle aanwezige subnavigatie verborgen, die hoeft immers niet meteen zichtbaar te zijn. De tweede regel toont als de gebruiker over een menu-item heengaat een submenu, maar door het gebruik van de > child selector alleen het direct gekoppelde submenu. Dieper gelegen menu’s blijven verborgen tot de gebruiker hun menu-item aanwijst. Alle voorgaande menu’s blijven bovendien gewoon openstaan als je de verschillende lagen van het menu bekijkt. Omdat de menu’s in elkaar genest zijn blijft de hover op alle voorgaande items namelijk automatisch behouden.

Met deze code zou javascript in het geheel niet nodig zijn, zodat de gebruiker ook als hij het gebruik van scripts geblokkeerd heeft de navigatie toch kan gebruiken. Maar Internet Explorer ondersteunt dit gebruik van zowel de > child selector als de :hover niet. En dat maakt het hele concept onbruikbaar. Een oplossing die gebruik maakt van javascript is dus alsnog nodig, maar in plaats van zelf per component een oplossing te schrijven kan een generieke oplossing ook zelf naar de CSS kijken, en de :hover automatisch toepassen. De voordelen hiervan zijn overduidelijk. CSS is niet alleen een geaccepteerde standaard, het bevat ook alle informatie die nodig is voor de oplossing: de te selecteren elementen, en het effect van de hover.

De :hover nabouwen

Om gebruik van de :hover mogelijk te maken in Internet Explorer moet er een drietal dingen gebeuren;

Een veel gebruikte aanpak om een rollover-effect te creëren met CSS is met gebruik van javascript de CSS class van een element aanpassen. Het script gaat dit automatiseren, om te beginnen door een kopie van de regel aan te maken en daarin de :hover door een class te vervangen, bijvoorbeeld van

li:hover ul {}

naar:

li.onHover ul {}

Daarna zoekt het script de juiste elementen op om de mouseover en mouseout-events te koppelen die uiteindelijk de class gaan wisselen. Is dat gedaan, dan is de gescripte hover compleet. Omdat deze aanpassing alleen in Internet Explorer mag werken is het de meest praktische oplossing om deze met een behavior aan het document te koppelen. Zo’n behavior werkt namelijk alleen in IE. De andere browsers gebruiken dan de standaard :hover-methode.

Behaviors maken het mogelijk om HTML elementen via CSS van specifieke functionaliteiten te voorzien. Voorbeelden van enkele standaard behaviors zijn de download behavior die het mogelijk maakt dynamisch bestanden te downloaden, of de userData behavior, die fungeert als een soort supercookie. Ter illustratie; zo kan van een element met de download behavior de startDownload() functie worden aangeroepen, zodat heel eenvoudig een bestand van de server kan worden gedownload:

CSS: 

#downloader {
   behavior:url("#default#download");
}

Script:

var element = document.getElementById('downloader');
element.startDownload('file.txt', handleFileSource);

function handleFileSource(src) {
   alert(src); // inhoud van het bestand
}

Naast een groot aantal voorgedefiniëerde behaviors is het ook mogelijk eigen behaviors te schrijven in de vorm van HTML Componenten (HTC’s), en deze op te slaan als .htc bestand. Ook deze zelfgescripte behaviors zijn via CSS aan individuele elementen te koppelen. Omdat het script dat het hoverprobleem gaat oplossen maar één keer uitgevoerd hoeft te worden, hoeft het ook maar één keer gekoppeld te worden. De body-tag is daarom het meest voor de hand liggende element om te kiezen:

body {
   behavior:url("csshover.htc");
}


De uiteindelijke behavior: csshover.htc

Tot zekere hoogte is dit wel een soort misbruik van het behavior-principe, aangezien behaviors normaal gesproken extra functionaliteiten aan elementen toevoegen in de vorm van script-functies, en dat doet deze behavior op zich niet. Voor het beoogde doel – het mogelijk maken van een techniek in IE en nergens anders in – is het echter zeer goed te verdedigen juist wél een behavior te gebruiken.

Deze aanpak gaat er natuurlijk wel vanuit dat javascript niet uitgeschakeld is. Met CSS onzichtbaar gemaakte elementen die een :hover nodig hebben om getoond te worden zouden in IE dan verborgen blijven. Je moet dus wel een alternatief blijven bieden die niet alleen van scripting of CSS afhankelijk zijn. Zonder CSS gebeurt er niets, aangezien er dan niets aangepast wordt, en er ook geen :hover is om op te kunnen reageren voor het script.

Het menu en de voorrangsregels

We gaan nog eens kijken naar dat waar het eigenlijk allemaal om draait: het menu. Als je maar één laag subnavigatie hebt, is de descendant selector (spatie) voldoende om een goed werkend CSS-menu te ontwikkelen. Ook voor overige toepassingen (voor zover IE het je toestaat met de huidige CSS-ondersteuning en de bugs daarin) is hiermee zonder beperkingen gebruik te maken van CSS-rollover effecten.

Voor menu’s met meerdere lagen subnavigatie voldoet die descendant selector echter niet. Als je over een list-item van het hoofdmenu zou gaan, dan zou je alle subnavigatielagen zichtbaar maken, en dat is niet de bedoeling. Hiervoor is eigenlijk de > child selector nodig, maar zoals je weet werkt die niet in IE, dus moeten we ook dit anders oplossen. Een voor de hand liggende mogelijkheid is het gebruik van unieke classes per menu-niveau. Er is echter nog een andere optie, namelijk het gebruik maken van de voorrangsregels van CSS.

Afhankelijk van het gebruik van id’s, (pseudo-)classes en elementnamen berekent CSS de voorrang van een bepaalde regel over een andere: de zogenaamde specificity. Ruwweg kan voor CSS-regels een optelsommetje gemaakt worden waarbij id’s een factor 10 voorrang over (pseudo-)classes hebben, en die weer een factor 10 over elementnamen. In het onderstaande voorbeeld:



ul ul {
   display:none;  
}
 
li:hover ul {
   display:block;
}

geldt de eerste regel als "2" door het gebruik van twee elementnamen. De tweede regel geldt in verhouding tot de eerste als "10 + 2" door het gebruik van de pseudo-class, en zal daarom voorrang krijgen over de eerste en alle geneste lijsten in dat item tonen. Om zonder de > selector alleen direct geneste lists van een list-item te tonen, en dieper geneste nog niet, moeten er dus één of meer regels gemaakt worden die van deze voorrangsregels gebruik maken.

De tweede regel van het laatste voorbeeld kan dus ondergeschikt gemaakt worden door een regel te maken die tenminste 13 waard is. Dan moet er voor de derde navigatielaag echter weer een regel komen die tenminste 14 waard is, enzovoort. Dit is helaas niet met één regel te doen, maar voor drie lagen navigatie is de CSS toch nog erg kort:


ul ul, li:hover ul ul { 
   /* waarde: 2 en 13 */
   display:none;
}

li:hover ul, li:hover li:hover ul { 
   /* waarde: 12 en 23 */
   display:block;
}

Voor de duidelijkheid zijn de opgetelde regelwaarden erbij gezet. Het linkerdeel van de eerste regel was zoals net bleek ondergeschikt aan het linker deel van de tweede regel, zodat alle subnavigatie getoond wordt als een menuitem wordt aangewezen. Die is nu echter ondergeschikt aan het rechterdeel van de eerste regel, zodat een eventueel derde submenu nog niet getoond wordt. Dat laatste menu wordt getoond door het rechterdeel van de tweede regel, dat door de twee pseudo-classes weer meer waard is.

Tenslotte

Om het hier omschreven idee in actie te laten zien volgt tot slot een voorbeeld dat alles combineert tot een werkend geheel.

Door het combineren van beschikbare technieken is het dus mogelijk om een niet beschikbare techniek alsnog na te bouwen op een manier die bovendien de standaard volgt. De hover hoeft daarin niet het einde te zijn, eerder een begin. Het is goed denkbaar dat er nog veel meer voorbeelden zijn van specifieke problemen die op een vergelijkbare manier opgelost kunnen worden. Met zo’n aanpassing en door een beetje creatief met stylesheets om te gaan, kan CSS die misschien op verre toekomstmuziek lijkt toch al gebruikt worden.

Auteur

Peter Nederlof

werkt als clientside webdeveloper bij Inpact en studeert momenteel af in Digitale Communicatie. Eerder studeerde hij Informatica.

Zijn persoonlijke site Peterned gaat (uiteraard) over scripten en dhtml.

Met dank aan Arnoud Berendsen en Wouter Demuynck

Publicatiedatum: 03 februari 2004

Let op

Naar Voren is op 18 juli 2010 gestopt met publiceren. De artikelen staan als een soort archief online. Het kan dus zijn dat de informatie verouderd is en dat er inmiddels veel betere of makkelijkere manieren zijn om je doel te bereiken.

Copyright © 2002-heden » NAAR VOREN en de auteurs