vrijdag 4 september 2009

CSS uitdagingen bij DotCMS


Ik heb al een aantal keren wat verteld over de mogelijkheden van DotCMS, maar ik heb nog niets verteld over de interessante uitdagingen die erbij komen kijken om het gebruiksvriendelijk te houden. Het is niet zo moeilijk om een pagina (template) te definiëren waar je content in kan plaatsen, maar hoe zorg je ervoor dat de klant in zijn WYSIWYG editor ook meteen krijgt te zien wat er op de website komt te staan? En hoe ver moet je gaan om dat zo echt mogelijk te laten lijken? Daar ga ik het over hebben.



DotCMS maakt gebruik van TinyMCE, een gratis open-source WYSIWYG editor. In deze editor komt in standaard HTML te staan hoe de opmaak eruit zou kunnen komen te zien, maar in de praktijk is dat altijd anders. Nu kun je aan de WYSIWYG editor wel een stylesheet koppelen (per website), maar dan ben je er nog niet. Want de WYSIWYG editor weet niet in welke div je bezig bent, dus klopt het nog niet allemaal. Hoe kun je dit nu netjes oplossen?

Het antwoord is: "Dat ligt eraan...". Als je maar 1 soort content hebt op 1 plaats in een pagina (bijvoorbeeld in de div 'container'), dan kun je overal waar iets staat zoals '#container p {}' vervangen door 'p {}'. Dit werkt! Of je maakt gebruik van de class 'mceContentBody' die de WYSIWYG editor gebruikt (maar dan moet je vanalles 2x definieren, daar word je ook niet vrolijk van). Maar, stel je hebt 2 verschillend gekleurde templates met dezelfde content, of je hebt 1 template met daarin witte content en een grijze banner content, dan heb je een probleem.


Dus de vraag is eigenlijk: hoe ver wil je gaan om alles in de WYSIWYG editor precies zo te krijgen als in het echt? Ik zou zeggen: niet heel ver. Je kan niet rekening houden met alles. Wat je wel kan doen is standaard fonts definieren die in 90% van de gevallen worden gebruikt (h1, h2, p, a). Maar zelfs dat zou ik niet zo snel doen. Wat ik wel zou doen is speciale classes definieren. Bijvoorbeeld speciale classes voor tabellen, speciale a classes en speciale inputs. Dat een H1 net een andere kleur heeft is niet zo erg, maar je wil wel zien dat je link met class='fancybutton' een fancy knop weergeeft ipv een blauw tekstje.

Dit soort afwegingen en het spelen met de CSS maken het werken met DotCMS wel leuk en interessant. Bij veel css zaken moet je je afvragen hoe je het definieert en wat de gevolgen zijn voor de pagina's, de edit mode (daar heb ik het nog nieteens over gehad!) en de WYSIWYG editor. En hoe maak je het zo makkelijk mogelijk voor de klant om daarna zelf zijn pagina's op te maken in de, toch beperkte, WYSIWYG editor. Ik kan er nog wel aardig wat bladzijden over voltypen maar ik ga weer even verder met mijn CSS issues bij Lasercards in DotCMS :)

donderdag 3 september 2009

JavaScript laden zonder de browser te blokkeren


Er zijn heel veel factoren die invloed hebben op de snelheid waarmee een webpagina wordt afgebeeld. Zaken zoals het aantal requests per pagina, het wel of niet gebruik maken van een Content Delivery Network, het instellen van gzip voor bepaalde type content en een juiste plaatsing van scripts en stylesheets in de pagina, dragen allemaal bij aan de ervaring die de gebruiker heeft als hij de pagina opvraagt. Als de ontwikkelaar te veel van deze zaken fout aanpakt, dan zal hij de woede van de gebruiker op zijn hals halen en wellicht hevig bloedend ergens in een greppel treurig aan zijn einde komen.

Er zijn twee lekker pragmatische en niet te dikke boeken over deze onderwerpen geschreven die ik even wil aanstippen: “High Performance Web Sites” en “Even Faster Web Sites”, beide van Steve Souders (http://stevesouders.com/hpws/rules.php). Beide boeken zijn binnen ISAAC in ieder geval verplichte kost voor alle web develeopers.

In deze blogpost wil ik een van de technieken uit het tweede boek kort introduceren: “JavaScript laden zonder de browser te blokkeren”. Het probleem dat hier speelt is dat scripts die worden geladen m.b.v. een SCRIPT tag tot gevolg hebben dat de browser helemaal niets anders zal downloaden tijdens het laden en interpreteren van deze code. Dit alles kan resulteren in een significante verlenging van de laadtijd en dus tot een tragere pagina. Zonde, want browsers kunnen in theorie meerdere bestanden parallel downloaden.

De reden dat de browser niets anders downloadt is dat de JavaScript die op dat moment wordt gedownload de rest van de pagina kan wijzigen na executie. Het aantal en de volgorde van de andere resources kan afwijken van wat er in de oorspronkelijke HTML is gespecificeerd (bv met de document.write(…) constructie). Dit wijzigen van de HTML is in het overgrote deel van de gevallen helemaal niet aan de orde en resulteert dus in een onnodig lange laadtijd. Daarnaast is de volgorde waarin externe JavaScript bestanden zijn gedefinieerd in de HTML tevens de volgorde waarin ze moeten worden geëxecuteerd door de browser. Deze executievolgorde is eigenlijk niet van belang voor de volgorde waarin de scripts worden gedownload, iets dat tot nu toe alleen de ontwikkelaars van IE8, Safari 4 en Chrome 2 hebben begrepen: deze browsers ondersteunen het parallel downloaden van scripts wel. Andere resources worden echter wel nog steeds geblokkeerd.

In “Even Faster Web Sites” worden een aantal technieken geïntroduceerd die dit probleem (ten dele) oplossen. Ik zal deze hier kort introduceren. Voor de details verwijs ik echter door naar het boek zelf.

XHR Eval.
In deze techniek worden de scripts met een XMLHttpRequest gedownload en vervolgens met een aanroep van eval() geïnterpreteerd. Dit werkt: de browsers blokkeren het downloaden van de andere resultaten niet. Het grote probleem is echter dat je op deze manier alleen scripts kan laden die van hetzelfde domein komen als de pagina zelf.

XHR Injection
Deze methode lijkt enigszins op de voorgaande: het script wordt geladen mb.v. de XMLHttpRequest functie, maar wordt geëxecuteerd door een SCRIPT tag in de DOM te creëren en de gedownloade code daarin te injecteren. Deze methode is soms iets sneller dan het gebruik van eval(), maar lijdt onder dezelfde beperkingen.

Script in Iframe
Iframes worden parallel geladen met andere componenten op een pagina. Door in iframe’s HTML pagina’s te laden die verwijzen naar de gewenste scripts, worden deze scripts dus parallel gedownload. Ook deze techniek is beperkt tot scripts die op hetzelfde domein staan als de hoofdpagina zelf. Bovendien moet de code worden aangepast om een verbinding tussen de hoofd- en de iframepagina te leggen.

Script DOM Element
Als alternatief voor het initieel opnemen van een SCRIPT tag in de HTML, kan er ook een on-the-fly worden gecreëerd m.b.v. document.createElement(…). Deze tag wordt dan dynamisch voorzien van een src attribuut en vervolgens in de HTML geïnjecteerd. Deze methode is geschikt voor scripts die komen van een andere domein dan het domein van de pagina zelf.

Script defer
IE ondersteunt het DEFER attribuut bij de SCRIPT tag. Defer is een indicatie aan IE dat het gerefereerde script geen DOM wijzigingen zal uitvoeren en dat het niet meteen hoeft te worden gedownload. Dit heeft tot gevolg dat IE andere bestanden parallel met dit script zal downloaden. Het werkt echter alleen in IE en enkele nieuwe browsers.

Document.write Script tag
In deze techniek wordt net als bij de “Script DOM Element” methode een SCRIPT tag dynamisch aan de HTML toegevoegd, dit keer echter met een document.write() constructie. Het verschil is dat deze techniek alleen resulteert in het parallel downloaden van andere scripts, alle andere bestanden worden toch geblokkeerd.

Conclusie
Na het lezen van de technieken lijkt het redelijk simpel: kies altijd voor de “Script DOM Element” methode. De realiteit is echter niet zo eenvoudig. De keuze hangt af van het gewenste gedrag van de “busy indicators” in de browser (de visuele hints die aangeven dat er iets wordt gedownload) en van de wens om de download- en executievolgorde af te dwingen. Voor meer details over wanneer je nou precies wat moet kiezen verwijs ik je graag door naar hoofdstuk 4 van “Even Faster Web Sites”.