Laadtijd als SEO-factor 2: javascript deferral

In dit tweede deel van de serie “laadtijd als SEO-factor” gaan we verder in op het terugdringen van de invloed van javascript op de laadtijd van een site. Want hoewel in deel 1 al duidelijk werd hoe we het blokkeren van een browser op scripts kunnen minimaliseren, kunnen we middels deferred execution en deferred loading de optimalisatie nog een stapje verder voeren.

Recap: script blocking

Eerst even een korte recap: zodra een browser een resource tegenkomt (CSS, favicon, afbeelding), opent deze een extra kanaaltje om deze resource tegelijk met de rest van de pagina te downloaden. Dit zorgt proces zorgt ervoor dat de browser de content zo snel mogelijk accuraat weer kan geven op het scherm. Dit geldt voor alle resources, behalve scripts. Omdat de uitvoer van een script gevolgen kan hebben voor andere pagina-onderdelen, blijft de browser net zo lang ‘hangen’ op het script tot dit gedownload en uitgevoerd is. In het geval van grote scripts kan dit ervoor zorgen dat de laadtijd van de pagina negatief beïnvloed wordt.

Deferred execution

Een groot deel van de javascripts op het web bestaat uit functionaliteit die geen directe invloed heeft op de weergave van de content op een site. Dat wil zeggen: terwijl ze ingeladen worden, verandert er eigenlijk niets op de pagina zelf. Denk hierbij aan analytics en allerlei event handlers (onclick bijvoorbeeld) die eigenlijk pas na een gebruikersactie in actie treden. Middels het HTML-attribuut defer is het mogelijk de browser de uitvoer (en dus niet het inladen!) van dergelijke scripts uit te laten stellen tot deze ‘tijd heeft’. Een voorbeeld:

<script type="text/javascript" defer="defer">
alert("Eerste script");
</script>
<script type="text/javascript">
alert("Tweede script");
</script>
<p>Content</p>

Als je deze code uitvoert (klik hier voor een live versie), zul je twee dingen opmerken:

Deze methode kan dus mooi gebruikt worden om zaken als google analytics pas op te starten nadat de pagina is ingeladen. Echter, er is wel een grote valkuil: je kunt deferred execution niet gebruiken op scripts die later inline aangeroepen moeten worden! Onderstaand voorbeeld werkt bijvoorbeeld niet (zie live versie):

<script type="text/javascript" defer="defer">
<!--
function halloWereld() {
alert("Hallo wereld!");
}
//-->
</script>
<script type="text/javascript">
<!--
halloWereld();
//-->
</script>

Er verschijnt in dit script geen dialogbox. Dit heeft te maken met het feit, dat het aanmaken van de functie halloWereld() middels deferral is uitgesteld tot na het inladen van de pagina. De functie-aanroep halloWereld() die in het tweede script wordt gedaan, komt dus niet aan! Er zijn hier twee oplossingen: óf je stelt het tweede script óók uit, of je stelt het eerste script niet uit. Conclusie: deferred execution kan handig zijn om de content van de site sneller op beeld te krijgen, maar kijk uit met afhankelijkheden!

Deferred loading

Let op: in deze bespreking wordt uitgegaan van een begrip van het Document Object Model.

Een betere, zij het complexere, manier om javascript uit te stellen is deferred loading. Hierbij wordt niet alleen de uitvoer, maar ook het eigenlijke inladen van een script uitgesteld tot na het inladen van de rest van de pagina. In het geval van grote externe scripts kan dit ervoor zorgen dat de site een heel stuk sneller in beeld verschijnt. Door gebruik te maken van het window.onload-event is het mogelijk javascripts pas na het daadwerkelijke laden van een pagina op iedere gewenste plaats in het document te haken. Voorwaarde hiervoor is wel, dat de plaats waar je het javascript hebben wilt, voorzien is van een marker; een element met een uniek id-attrbuut. Een voorbeeld van een script:

<script type="text/javascript">
<!--
window.onload = function() {
myScript = document.createElement("script");
myScript.type="text/javascript";
myScript.src="http://www.domein.nl/externjavascript.js";
marker = document.getElementById('marker');
marker.appendChild(myScript);
}
//-->
</script>

Dit script kun je het beste plaatsen vlak voor de . Op de plaats waar je het script uiteindelijk ingeladen wilt hebben, plaats je de volgende code:

<div id="marker"></div>

Ook inline-scripts kunnen op deze manier worden uitgesteld, maar hiervoor is het wel nodig dat het script regel voor regel aan het myScript.text attribuut wordt toegevoegd. Omdat het aanroepen van een extern script een extra HTTP-request genereert, is het beter deze techniek alleen toe te passen op de grotere scripts. Kleine inline-scripts (denk aan het zetten van een paar variabelen of het maken van een simpele berekening) hebben sowieso nauwelijks invloed op de laadtijd van een pagina.

Deze techniek is mooi, maar heeft zijn limieten. In de eerste plaats moet je ook hier goed op de afhankelijkheden letten: een functie inline aanroepen die pas na het inladen van de pagina wordt aangemaakt, gaat uiteraard niet werken. Om dit op te lossen, kun je ofwel alle scripts deferred laden (niet altijd wenselijk), ofwel na het deferred laden van de benodigde functies d.m.v. het DOM en de functie eval() alle javascript nog eens opnieuw uitvoeren. Tot slot is het vanwege diverse bugs niet mogelijk om zaken als bijvoorbeeld het http://pagead2.googlesyndication.com/pagead/show_ads.js-script van adsense uit te stellen. Dit heeft te maken met het feit, dat dergelijke scripts intensief gebruik maken van de functie document.write.

Wie daar een oplossing voor heeft, mag zich melden in de comments!

Zie ook: Laadtijd als SEO-factor #1: de head.


Stem / voeg toe / tweet : Stem voor dit bericht op op nujij.nl Stem voor dit bericht op op ekudos.nl Tweet dit artikel

Reageren

Niels schreef op 28 november 2009, 20:51

Defer werkt alleen in IE en FF 3.5+ volgens mij, en de code in je blog staat tussen commentaartags.
Daarnaast kun je javascript beter naar het einde van je body verplaatsen.

John den Haan schreef op 28 november 2009, 21:05

Niels,

Bedankt voor je reactie. De commentaartags staan er voor browsers die geen JS ondersteunen. Legacy support heet dat ;) Code naar het eind verplaatsen kan, maar is niet altijd wenselijk. Denk aan adsense of ieder ander script dat op een bepaalde plek in je pagina hoort.

Niels schreef op 28 november 2009, 21:14

Tegenwoordig met JQuery etc, doe je eigenlijk heel weinig met document.write en kun je prima alle javascripts naar beneden verplaatsen.

John den Haan schreef op 29 november 2009, 00:44

Tja, als Google nou eens met DOM-methodes zou werken in plaats van dat achterhaalde document.write… Maar je hebt wel gelijk: het is simpeler, hoewel het netter is als je content van functionaliteit gescheiden houdt.

De linkbuilder schreef op 20 maart 2012, 19:14

En het scheelt een hoop code dus de site wordt sneller. win – win situatie lijkt mij! Goede post.