» direct naar zoek en menu

Tijdschrift voor webwerkers » Artikel #131

CSS-modificatie - De presentatielaag aanpassen met JavaScript

JavaScript geeft je de mogelijkheid om de CSS-presentatielaag van je site te veranderen. Een verandering in hoe een element in de pagina er uit ziet is een uitstekende (en veelgebruikte) manier om de aandacht van je bezoekers te trekken naar het element waar je hun aandacht wilt hebben.

Zeven van de voorbeeldscripts in mijn boek gebruiken een of andere vorm van CSS-modificatie. Form Validation, bijvoorbeeld, verandert de stijl van een formulierveld dat niet correct is, en XMLHTTP Speed Meter gebruikt animaties (d.w.z. meerdere veranderingen in dezelfde stijl in korte tijd) om het oog van de bezoeker naar de gegevens over de snelheid te trekken (en, eerlijkheidshalve, ook om het er extra leuk uit te laten zien). Dropdown Menu gebruikt stijlveranderingen om menu-items te tonen en verbergen. Al deze veranderingen hebben hetzelfde doel: het oog van de bezoeker naar het element te trekken waar je hun aandacht op wilt vestigen.

JavaScript kan op vier manieren de CSS veranderen:

De meeste scripts die CSS modificeren gebruiken óf de style property óf een class/id change. De document.write methode wordt alleen in bepaalde gevallen gebruikt om de toegankelijkheid van een pagina te vergroten. Het veranderen van het hele stylesheet is maar zelden nuttig, omdat het niet door alle browsers ondersteund wordt en omdat je in de meeste gevallen alleen bepaalde elementen wilt selecteren waarvan je de stijl wilt veranderen.

Ik zal echter alle vier de methoden gebruiken in de voorbeeldscripts. In dit hoofdstuk zullen we ze allemaal bekijken, in de juiste context.

NOOT: De bespreking van de laatste twee methoden is niet opgenomen in deze voorpublicatie.

A: De style property

De eerste en bekendste manier om CSS aan te passen is met behulp van de style property die alle HTML elements hebben en die toegang biedt tot hun inline styles. style bevat één property voor iedere inline CSS declaration. Als je de CSS margin van een element wilt wijzigen, gebruik je element.style.margin. Als je de CSS color wilt wijzigen, gebruik je element.style.color. De JavaScript property heeft altijd een naam die lijkt op die van de CSS property.

Inline styles!

Onthoud: de style property van een HTML element geeft toegang tot de inline styles van dat element.

Laten we eerst eens naar wat CSS-theorie kijken. CSS geeft je vier manieren om de stijl van een element te definiëren. Je kunt inline styles gebruiken, waarbij je je CSS direct in het style attribuut van de HTML tag plaatst:

<p style="margin: 10%">Test</p>

Daarnaast kun je een stylesheet embedden, er naar linken, of hem importeren. Maar aangezien een inline style specifieker is dan alle andere soorten stijlen, overschrijft hij stijlen die gedefinieerd zijn in een gelinkte, embedded of geïmporteerde style. Omdat de style property toegang geeft tot deze inline styles, gaat het altijd voor alle andere stijlen. Dat is de grote kracht van deze methode.

Maar wanneer je de stijlen probeert in te lezen kan het zijn dat je problemen tegen komt. Neem dit voorbeeld:

<p id="test">Test</p>

p#test {
 margin: 10%;
}
alert(document.getElementById('test').style.margin);

De testparagraaf heeft geen inline styles. In plaats daarvan wordt de marge, margin: 10%, gedefinieerd in een embedded (of gelinkte of geïmporteerde) stylesheet en kan daardoor niet gelezen worden door middel van de style property. De alert blijft leeg.

In het volgende voorbeeld zal de alert de waarde "10%" laten zien, omdat de margin nu gedefinieerd is als een inline style:

<p style="margin: 10%" id="test">Text</p>
alert(document.getElementById('test').style.margin);

De style property is dan ook uitstekend geschikt om stijlen te definiëren, maar veel minder voor het inlezen. Verderop in dit artikel zullen we manieren bespreken waarop we de stijlen kunnen inlezen uit embedded, gelinkte of geïmporteerde stylesheets.

Verbindingsstreepjes

Veel CSS property-namen bevatten een verbindingsstreepje, zoals bijvoorbeeld font-size. In JavaScript betekent het verbindingstreepje echter 'min', dus kun je het niet gebruiken in je property namen. Dit veroorzaakt een foutmelding:

fout! element.style.font-size = '120%';

Moet de browser de (ongedefinieerde) variabele size aftrekken van element.style.font? En zo ja, wat betekent het stukje = '120%'? In plaats daarvan verwacht de browser een camelCase property naam:

element.style.fontSize = '120%';

De algemene regel is dat alle verbindingsstreepjes verwijderd worden uit de CSS property namen, en dat de letter na het verbindingsstreepje een hoofdletter wordt. Op die manier wordt margin-left marginLeft, text-decoration wordt textDecoration, en border-left-style wordt borderLeftStyle.

Eenheden

Veel numerieke waarden in JavaScript moeten een eenheid hebben, net zoals in CSS. Wat betekent fontSize=120? 120 pixels? 120 punts? 120 procent? De browser weet het niet en doet daarom ook niets. De eenheid is onmisbaar om te laten zien wat je bedoeling is.

Laten we eens kijken naar de setWidth() functie, die het hart vormt van de animaties in de XMLHTTP Speed Meter:

XMLHTTP Speed Meter, regel 70-73

function setWidth(width) {
 if (width < 0) width = 0;
 document.getElementById('meter').style.width = width + 'px';
}

De functie krijgt een waarde aangereikt en zou de breedte van de meter aan deze nieuwe waarde moeten aanpassen. Na een veiligheidstest die alleen waarden doorlaat die groter dan of gelijk zijn aan 0, krijgt de style.width van het element de nieuwe breedte. Daarna voegt de functie + 'px' toe, omdat de browser anders niet zou weten hoe hij het getal moest interpreteren en niets zou doen.

Vergeet je 'px' niet

Het toevoegen van de 'px' eenheid aan hoogte of breedte wordt regelmatig vergeten wanneer CSS gemodificeerd wordt. In de CSS Quirks Mode is het toevoegen van 'px' niet nodig, aangezien de browsers de oude regel volgen dat een getal zonder eenheid een pixelwaarde is. Dat is op zichzelf geen probleem, maar veel webontwikkelaars hebben de gewoonte aangeleerd om eenheden weg te laten wanneer ze hoogtes of breedtes veranderen, en komen dan problemen tegen wanneer ze in CSS Strict Mode werken.

Styles uitlezen

Pas op: u nadert browserproblemen

Zoals we gezien hebben, kan de style property geen stijlen uitlezen die zich bevinden in embedded, gelinkte of geïmporteerde stylesheets. Omdat webontwikkelaars af en toe tóch die stijlen moeten ophalen, hebben Microsoft en het W3C allebei een manier bedacht om non-inline styles te benaderen. De oplossing van Microsoft werkt alleen in Explorer, terwijl de W3C-standaard in Mozilla en Opera werkt.

De oplossing van Microsoft is de currentStyle property, die precies zo werkt als de style property, behalve dat:

Een voorbeeld:

var x = document.getElementById('test');
alert(x.currentStyle.color);

De alert toont de huidige color style van het element, onafhankelijk van waar hij bepaald wordt.

De oplossing van het W3C is de window.getComputedStyle() methode, die ruwweg op de zelfde manier werkt maar met een complexere syntax.

var x = document.getElementById('test');
alert(window.getComputedStyle(x,null).color);

getComputedStyle() geeft altijd een pixel-waarde terug, zelfs als de beginwaarde van de style bijvoorbeeld 50em of 11% was.

Zoals altijd wanneer we verschillen tussen de browsers tegenkomen, moeten we een beetje vorken in de code om alle browsers tevreden te stellen:

function getRealStyle(id,styleName) {
 var element = document.getElementById(id);
var realStyle = null;
 if (element.currentStyle)
  realStyle = element.currentStyle[styleName];
 else if (window.getComputedStyle)
  realStyle = window.getComputedStyle(element,null)[styleName];
 return realStyle;
}

Je gebruikt deze functie als volgt:

var textDecStyle = getRealStyle('test','textDecoration');

Onthoud dat getComputedStyle() altijd een pixelwaarde teruggeeft, terwijl currentStyle de eenheid handhaaft die in de CSS werd gespecificeerd.

Shorthand styles

Pas op: u nadert browserproblemen

Of je nu inline styles benadert via de style property of andere stijlen via de functie die we zojuist besproken hebben, je zult altijd problemen tegenkomen wanneer je shorthand styles wilt uitlezen.

Zoals bij deze border declaratie:

<p id="test" style="border: 1px solid #cc0000;">Text</p>

Aangezien dit een inline style is, zou je verwachten dat deze regel code werkt:

alert(document.getElementById('test').style.border);

Helaas, hij werkt niet. De browsers verschillen van mening over welke waarde ze zullen tonen in de alert.

rgb()

Let op de speciale color syntax die Mozilla gebruikt: rgb(204,0,0). Dit is een valide alternatief voor de traditionele #cc0000; en je kunt beiden manieren gebruiken in CSS en JavaScript.

Het problem is dat border een ‘shorthand’ declaratie is, een afgekorte declaratie. Hij bestaat ‘onder water’uit niet minder dan twaalf stijlen: breedte, stijl en kleur voor de boven-, linker-, onder- en rechterkant. Op dezelfde manier is de font declaratie shorthand voor font-size, font-family, font-weight, en line-height, dus krijg je met dezelfde problemen te maken .

Hoe moet de browser omgaan met dergelijke shorthand declaraties? Het voorbeeld hierboven lijkt duidelijk; je zou intuïtief verwachten dat de browser de waarde 1px solid #cc0000 aangeeft, precies zoals het inline style attribute. Helaas zijn shorthand properties ingewikkelder.

Kijk eens naar het volgende voorbeeld:

p {
 border: 1px solid #cc0000;
}
<p id="test" style="border-color: #00cc00;">Test</p>
alert(document.getElementById('test').style.borderRightColor);

Alle browsers rapporteren de juiste kleur, ondanks dat de inline style geen border-right-color bevat maar een border-color declaratie. Kennelijk gaan de browsers er van uit dat de kleur van de rechter rand bepaald is wanneer de kleur van de gehele rand is vastgesteld. Geen onredelijke manier van denken.

Zoals je ziet moeten browsers regels bedenken voor dit soort ongewone situaties, en ze hebben allemaal nét iets verschillende regels vastgesteld bij het behandelen van shorthand declaraties. Er is geen standaard-specificatie voor hoe de shorthandproperties exact behandeld moeten, dus het is ook onmogelijk om uit te maken welke browsers het goed doen en welke fout.

B: Classes en ids veranderen

JavaScript geeft je de mogelijkheid om de class of de id van een element te veranderen. Op het moment dat je dat doet, actualiseert de browser automatisch de stijlen van het element.

Na de baaierd aan ingewikkelde details en browserproblemen die we tegenkwamen bij de behandeling van de style property zijn veranderingen aan class en id een oase van begrijpelijkheid en pais en vree. Zoals in dit voorbeeld:

p {
 color: #000000; /* black */
}
p.emphasis {
 color: #cc0000; /* red */
}
<p id="test">Test</p>

Als we beginnen heeft de paragraaf geen class en is daarom de kleur van de paragraaf zwart. Eén regel JavaScript is echter genoeg om de styles te veranderen:

document.getElementById('test').className = 'emphasis';

De tekst wordt direct rood. Om het weer terug te veranderen doe je het volgende:

document.getElementById('test').className = '';

Je verwijdert de class en de paragraaf valt weer terug op de standaard p{} regel.

className, niet class!

Let er op dat JavaScript className gebruikt, omdat class in reserve wordt gehouden voor een mogelijke toekomst waarin JavaScript wellicht Java-achtige classes gaat ondersteunen.

Voor een praktijkvoorbeeld kijken we naar Textarea Maxlength. De teller heeft deze structuur en presentatie (de structuur wordt gegenereerd door JavaScript, maar dat doet er niet toe):

<div class="counter"><span>12</span>/1250</div>

div.counter {
 font-size: 80%;
 padding-left: 10px;
}
span.toomuch {
 font-weight: 600;
 color: #cc0000;
}

Wanneer het script ziet dat de bezoeker de maximumlengte heeft overschreden, verandert het de class van de counter span in toomuch:

Textarea Maxlength regel 20-23

if (currentLength > maxLength)
 this.relatedElement.className = 'toomuch';
else
 this.relatedElement.className = ''; 

Nu wordt de counter <span> vet en rood.

Een id-verandering werkt op precies dezelfde manier:

p {
 color: #000000; /* black */
}
p#emphasis {
 color: #cc0000; /* red */
}

<p>Test</p>

document.getElementsByTagName('p')[0].id = 'emphasis';

Weer wordt de paragraaf rood. Toch adviseer ik je om niet te veel te werken met veranderingen aan de ids. Naast dat ze werken als aanhaakpunten voor de CSS, werken ze vaak ook als aanhaakpunt voor de JavaScripts, en als je ze weghaalt kan dat vreemde bijverschijnselen opleveren. In de praktijk kun je elke CSS-modificatie die je nodig hebt bereiken met veranderingen in de class, en hoef je niet te werken met ids.

Classes toevoegen

In veel gevallen hoef je de class van een bepaald element niet te veranderen in een nieuwe waarde. In plaats daarvan zul je een nieuwe class-waarde toevoegen, omdat je geen stijlen wilt verwijderen die het element misschien al heeft. CSS geeft je de mogelijkheid om meerdere classes te gebruiken, en dus worden de stijlen van de nieuwe class toegevoegd aan het element zonder dat je de instructies die al bestaande classes geven weghaalt.

De writeError() en removeError() functies van Form Validation zijn een goed voorbeeld. Ik gebruik meestal een aantal classes voor formuliervelden, omdat het grafisch ontwerp in veel gevallen twee of zelfs drie verschillende breedtes gebruikt voor invulvelden. Op het moment dat een formulierveld een fout bevat wil ik een speciale waarschuwingsstijl gebruiken, maar ik wil de stijl die het element al heeft niet verstoren. Daarom kan ik de bestaande class-waarde niet simpelweg overschrijven: dan zou ik mijn specifieke breedtes kwijtraken.

Kijk bijvoorbeeld eens naar deze situatie:

<input class="smaller" name="name" />
input.smaller {
 width: 75px;
}
input.errorMessage {
 border-color: #cc0000;
}

Het uitgangspunt is een invulveld met een breedte van 75px. Wanneer het script de class verandert in 'errorMessage' en de oude waarde weggooit, krijgt de rand rond het formulierveld een rode kleur, maar hij verliest zijn breedte, en dat zou erg verwarrend zijn voor de bezoeker.

Daarom besluit ik de class errorMessage toe te voegen:

Form Validation, regel 105-106

function writeError(obj,message) {
 obj.className += ' errorMessage';

Deze code pakt de bestaande className en voegt er een nieuwe class aan toe, voorafgegaan door een spatie. Deze spatie zorgt er voor dat de nieuwe class-waarde gescheiden wordt van de class-waardes die het object al heeft. Nu krijgt het invulveld een rode rand mét een breedte van 75px—precies wat we willen. Het invulveld past nu beide classes toe, alsof de HTML het volgende was:

<input class="smaller errorMessage" name="name" />

Classes verwijderen

Op het moment dat de bezoeker haar vergissing hersteld heeft, moeten we de class errorMessage verwijderen. Maar classes die in de uitgangspositie aanwezig waren moeten ongemoeid blijven. De removeError() functie levert die functionaliteit:

Form Validation, regel 119-120

function removeError() {
 this.className = this.className.replace(/errorMessage/,'');

De functie pakt de class van het element en verandert de string 'errorMessage' in '' (een lege string). Het stukje “errorMessage” wordt verwijderd uit de class-waarde, maar de andere waarden worden in tact gelaten. Het invulveld raakt de rode kleur van zijn rand kwijt, maar de rand behoudt de breedte van 75px.

Class names en spaties in Mozilla

Misschien is het je opgevallen dat removeError() de class-waarde “errorMessage” verwijdert zonder de spatie die er aan voorafgaat. Dat heeft te maken met een browserbug. Als je “ errorMessage” toevoegt aan een class die nog geen waarde heft, gooit Mozilla de spatie aan het begin weg. Als we daarna replace(/ errorMessage/,'') doen, zal Mozilla de class niet verwijderen: hij kan namelijk de string “ errorMessage” niet vinden omdat de spatie aan het begin ervan er niet is.

NOOT: De secties C en D van het hoofdstuk maken geen deel uit van dit artikel. Ze gaan over het toevoegen van stijlen met document.write() (wat je alleen zou moeten doen uit toegankelijkheidsoverwegingen) en over het veranderen van hele stylesheets. Beide technieken zijn vrij specialistisch en zouden niet gebruikt moeten worden in de gemiddelde website.

Na deze paragrafen gaat het hoofdstuk verder met het vergelijken van de vier methoden.

E: Vergelijking

Nu we alle vier de methoden van CSS-modificatie bekeken hebben, kunnen we ons afvragen welke methode de beste is. De document.write-methode zou alleen gebruikt moeten worden in speciale situaties die te maken hebben met toegankelijkheid. Het veranderen van een heel stylesheet is maar zelden nuttig. Dan blijven er nog twee methoden over. Wat kun je het beste doen: de style property gebruiken of class- en id-waarden veranderen?

Om daar een antwoord op te formuleren zullen we eens kijken naar de voorbeeldscripts. Site Survey verandert geen CSS, Edit Style Sheet verandert de hele stylesheet, en Usable Forms gebruikt de document.write-methode. Alleen XMLHTTP Speed Meter verandert style properties, terwijl vier scripts — Textarea Maxlength, Sandwich Picker, Form Validation en Dropdown Menu — veranderingen in de class gebruiken. Zoals je misschien al raadt, vind ik het veranderen van de class in de meeste gevallen de beste CSS-modificatietechniek.

Ik ben van mening dat te veel leunen op de style property de scheiding van presentatie en gedrag aantast, en het is ook nog eens ingewikkelder om te schrijven. Stijlen horen in de CSS presentatielaag—hun natuurlijke omgeving—niet in de HTML structuurlaag of in de JavaScript gedraglaag.

Op het moment dat je een style verandert zeg je “geef dit element een nieuw uiterlijk”. Het geven van deze opdracht is duidelijk een taak voor het JavaScript. Het definiëren van dat nieuwe uiterlijk is een taak voor de CSS. De nieuwe styles van het element (en ook de oude) moeten daarom gedefinieerd worden in het CSS-bestand.

Op die manier blijven presentatie en gedrag gescheiden: JavaScript geeft de opdrachten om stijlen te veranderen, maar het is aan CSS om die stijlen te definiëren.

Voorbeeld

Laten we naar een praktijkvoorbeeld kijken. Stel je voor dat ik de style property had gebruikt in plaats van een class-verandering in Form Validation:

Form Validation, regel 105-6 en 119-120, aangepast

function writeError(obj,message) {
 obj.style.border = '1px solid #cc0000';

function removeError() {
 this.style.border = '';

Dit werkt natuurlijk prima. Maar stel je voor dat de grafisch ontwerper op een later moment besluit dat de tekst ook rood moet worden. Je zou dan twee regels moeten toevoegen aan je script:

function writeError(obj,message) {
 obj.style.border = '1px solid #cc0000';
 obj.style.color = '#cc0000';

function removeError() {
 this.style.border = '';
 this.style.color = '';

Enzovoorts. Iedere extra stijl voor invulvelden met fouten zou twee regels JavaScript vergen. Een script dat gebruikmaakt van class-veranderingen is daarentegen makkelijker te onderhouden, aangezien een extra stijl maar één regel CSS vergt.

Twee regels JavaScript toevoegen kan natuurlijk nauwelijks complex programmeren genoemd worden. Desalniettemin vraagt deze verandering wel de inzet van iemand die goed de weg weet in de JavaScript code. Een grafisch ontwerper/CSS wizard zonder JavaScript-ervaring zou het niet zelf kunnen doen.

CSS-ontwikkelaars die niets van JavaScript weten kunnen wel een stylesheet aanpassen, en dat is een aanzienlijk voordeel in een groter bedrijf, waar het regelmatig gebeurt dat mensen gespecialiseerde functies hebben. Als JavaScriptdeveloper zit je er niet op te wachten dat iedere minieme verandering altijd via jou moet verlopen. Het is daarom ook in je eigen voordeel dat je de complexiteit van de JavaScriptlaag beperkt houdt.

Er zijn dus een aantal redenen waarom ik geloof dat een class-verandering over het algemeen beter is dan een verandering aan de style property. Maar zoals altijd zijn er uitzonderingen die de regel bevestigen. De twee belangrijkste zijn eenvoudige show/hide scripts en animaties.

NOOT: Einde van deze voorpublicatie. Het hoofdstuk in het boek gaat verder met het bespreken van show/hide scripts, animaties en het zoeken van de afmetingen en positie van HTML-elementen.

Dit artikel is een gedeelte uit hoofdstuk 9 van 'ppk on JavaScript' door Peter-Paul Koch. Copyright 2007. Gepubliceerd met toestemming van Pearson Education, Inc. en New Riders.
Vertaling: Marrije Schaake

Auteur

Peter-Paul Koch

woont zijn hele leven al in Amsterdam. Peter-Paul (mét streepje) is een freelance web developer — je kunt hem dus inhuren — en specialiseert zich in client side programming - HTML, CSS en JavaScript, met de nadruk op dat laatste. Daarnaast schrijft hij veel, klust hij wat bij in werving & selectie en geeft hij zo regelmatig CSS- en JavaScript-cursussen. 'ppk on JavaScript' is zijn eerste boek.

Publicatiedatum: 31 oktober 2006

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