Je wilt user engagement zo vroeg mogelijk plaats laten vinden, maar ook je website voorzien van widgets en interactie. Hoe ga je hier mee om?
Laten we beginnen met de stelling dat je qua performance reeds een stap in de goede richting zet, wanneer je aandacht uit gaat naar dergelijke maatregelen. Alle maatregelen hebben in de basis hetzelfde doel:
- Het render-proces in de browser niet af laten hangen van JavaScript;
- De gebruiker (voor hen gevoel) niet oneindig een wit scherm tonen.
Dat CSS, maar ook JavaScript render blocking is, is hiervan de reden. Doordat JavaScript bovendien CPU en UX technisch zwaarder is dan CSS, vormt dit een aanzienlijke performance en conversie bottleneck. Die wil je dus uit de weg ruimen, want ondertussen laten we de hoeveelheid JavaScript enkel groeien.
Asynchrone JavaScript
Dit kan met het async attribuut op je script-elementen. Async geniet een optimale browser ondersteuning. Wanneer het async
attribuut wordt gebruikt, wordt het JavaScript bestand alvast gedownload, maar zal de browser niet wachten tot de download klaar is. Echter, zodra de browser het signaal krijgt dat het JavaScript bestand is gedownload, stopt het met verdere parsen van de HTML code, en wordt de JavaScript code direct uitgevoerd.
Via het async attribuut commandeer je de browser dus om het script alvast uit te downloaden, terwijl de browser doorgang mag krijgen met het renderen van overige HTML. Wanneer de browser in dit proces overigens alsnog een script element zonder async of defer attribuut tegen komt, wordt het render proces alsnog onderbroken. Exact de reden waarom dit soort bestanden render blocking resources heet.
Terwijl de browser dus verder kan met renderen, heeft het async-attribuut twee kanttekeningen:
- De volgorde van je script-declaraties worden niet gehonoreerd. Je JavaScript bestanden kunnen in een heel andere volgorde uitgevoerd worden, dan jij gedeclareerd hebt. Dit komt doordat je met async aangeeft dat de browser de JavaScript code direct moet uitvoeren, zodra het gedownload is.
- Je weet niet wanneer het JavaScript bestand exact gedownload is. Dit hangt immers van veel externe factoren af, zoals iemand zijn type toestel, andere processen die op zijn of haar device draaien, internet snelheid. Hierdoor kan ook de UX verschillen, als ook het visuele laadproces.
JavaScript defer attribuut
Het JavaScript gedrag lijkt bij gebruik van het defer
attribuut op het gedrag van async, met als verschil dat de browser de HTML code ongestoord verder zal parsen. Pas als het parsen voltooid is, zal de browser de JavaScript file gaan uitvoeren, ongeacht of de download van het bestand al in een eerder stadium klaar was.
Afbeeldingen zijn afkomstig van bitsofco.de.
Een combinatie is ook mogelijk, zoals een script-tag in de body plaatsen in combinatie met asynchroon (async
) of uitgesteld (defer
) laden. Wat het verschil is en hoe de browser er mee omgaat, heeft Addy Osmani opgenomen op zijn website.
Combinatie van defer
en async
als attribuut is gezien de huidige brede browser ondersteuning voor beide individuele attributen overbodig.
Bootstrap, Fancybox en jQuery libraries
Ook de volgorde van je JavaScript bestanden blijft intact. Dit is vooral praktisch, wanneer je JavaScript libraries gebruikt, zoals een Bootstrap of Fancybox, die weer afhankelijkheid hebben van andere libraries, zoals een jQuery. Zou je jQuery wel een defer-attribuut meegeven, maar je Fancybox niet, dan zou je Fancybox niet werken, omdat op het moment dat Fancybox wordt uitgevoerd, jQuery nog niet beschikbaar is.
JavaScript onderaan of in de footer plaatsen
Een veel genoemde maatregel, zeker binnen Magento en Wordpress, is het verplaatsen van je script elementen, naar de footer danwel onderaan de HTML code. Dit sorteert nagenoeg hetzelfde effect als gebruik maken van de defer-attribuut:
- Je JavaScript bestanden worden laat in het render proces uitgevoerd;
- De volgorde van je script-declaraties blijven intact.
Verschil versus defer-attribuut
Een kanttekening, is dat de script-verwijzingen naar je JavaScript bestanden pas later in de broncode staan. Oftewel, de browser zou de script files later kunnen detecteren, dan wanneer je een script-element in je header plaats, voorzien met het defer-attribuut. Dit klopt in de basis; een browser rendert van boven naar beneden.
Browsers zijn slim: Preload scanner
Om die reden is het toevoegen van een defer-attribuut de meest waterdichte methode, ook dankzij de browser-ondersteuning van het defer-attribuut. Desondanks kan het zijn dat je geen verschil merkt in laadtijden, tussen defer, of je script elementen in de footer. Dit komt doordat browsers al sinds 2009 voorzien zijn van een preload scanner.
JavaScript prioriteit van laden en uitvoeren
Naast dat JavaScript gedownload moet worden, moet JavaScript dus ook uitgevoerd worden. Hier kun je op bovenstaande manieren invloed op uitoefenen. De prioriteiten (die tussen browsers enigszins kunnen afwijken) worden dan ook als volgt:
Oplossing | Laad-prioriteit | Uitvoer prioriteit |
Gemiddeld / hoog | Erg hoog, blokkeert de parser | |
Laag(st) | Onderbreekt de parser | |
Laag(st) | Erg laag, na andere scripts | |
Gemiddeld / hoog | Laag, wachtend op de parser |
Een completere lijst is te vinden op de website van Addy Osmani.
Wanneer je buiten JavaScript om andere render blocking resources hebt, kun je via resource hints een hogere prioriteit geven aan het laadproces van een JavaScript bestand.
Andersom kun je de prioriteit extra laag leggen door een script element onderaan de HTML te plaatsen en gelijktijdig te voorzien van het defer-attribuut. Wanneer bijvoorbeeld gebruik wordt gemaakt van externe a/b-testing, kan een a/b test hiermee sneller geladen worden, afhankelijk van andere condities binnen de webpagina.
Impact op Lighthouse metrics
Het effect van async of defer maatregelen, is dat de browser niet van zijn render-werk gehouden kan worden, en je als developer in staat bent gebruikers sneller van een waardevolle weergave te voorzien. Men hoeft minder lang naar een wit scherm te kijken en externe factoren zoals internetsnelheid, telefoontype en CPU beperkingen spelen een minder grote rol in de eerste zogenaamde user engagement.
Met andere woorden: wanneer je een audit zou doen in Lighthouse of PageSpeed Insights, zou de First Contentful Paint en First Meaningful Paint verbeteren.