Stel scripts uit tot ze nodig zijn

Laad JavaScript on-demand via IntersectionObserver en interactietriggers

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-05

Stel scripts uit tot ze nodig zijn

De mediane mobiele pagina levert 251 KB aan ongebruikte JavaScript volgens de 2025 Web Almanac. Dat is JavaScript die de browser downloadt, parset en compileert voordat een bezoeker deze ooit nodig heeft. Formulieren waar niemand op heeft geklikt. Chatwidgets die niemand heeft geopend. Kaartintegraties die zich onder de vouw bevinden. Dit alles concurreert om bandbreedte en CPU-tijd tijdens de meest kritieke fase van het laden van de pagina.

De meest effectieve manier om hiermee om te gaan, is door scripts pas te laden wanneer ze daadwerkelijk nodig zijn. Dit is anders dan het gebruik van het async of defer attribuut op een script-tag. Die attributen downloaden het script nog steeds tijdens het laden van de pagina; ze veranderen alleen wanneer het wordt uitgevoerd. On-demand laden downloadt het script helemaal niet totdat er een trigger afgaat.

Laatst beoordeeld door Arjen Karel in maart 2026

Met afbeeldingen doen we dit al heel lang. Dit wordt lazy loading genoemd. Bij lazy loading wordt een afbeelding onder de vouw geladen vlak voordat deze in beeld scrolt. De browser kan zijn resources besteden aan het downloaden, parsen en tekenen van dingen die daadwerkelijk nodig zijn. Hetzelfde principe geldt voor JavaScript, en het lost de Lighthouse waarschuwing "reduce unused JavaScript" op en verbetert responsiviteitsstatistieken zoals Interaction to Next Paint (INP).

Helaas is het niet zo simpel als het toevoegen van loading="lazy" aan een afbeelding, maar met een kleine helperfunctie en een trigger kunnen we het laten werken.

De script-injectie helper

Om na het laden van de pagina scripts toe te voegen, hebben we een kleine functie nodig die een script-element aanmaakt en toevoegt aan de document head.

function injectScript(scriptUrl, callback) {
    const script = document.createElement('script');
    script.src = scriptUrl;
    if (typeof callback === 'function') {
        script.onload = callback;
    }
    document.head.appendChild(script);
}

De scriptUrl parameter is de URL van het script dat geladen moet worden. De optionele callback functie wordt uitgevoerd nadat het script klaar is met laden. Dit is belangrijk voor scripts die initialisatie vereisen, zoals het aanroepen van initMap() na het laden van de Google Maps API.

Het on-demand laden triggeren

Met de injectie-helper op zijn plaats, hebben we een trigger nodig. Er zijn twee betrouwbare methoden: laden wanneer een element in beeld scrolt, en laden wanneer de gebruiker interactie heeft met een element.

IntersectionObserver: laden wanneer zichtbaar

De IntersectionObserver vuurt af wanneer een element de viewport binnenkomt. Dit is de juiste trigger voor scripts die gekoppeld zijn aan een specifiek gedeelte van de pagina: een map container, een reactiesectie of een embedded widget onder de vouw.

function injectScriptOnIntersection(scriptUrl, elementSelector, callback) {
    const element = document.querySelector(elementSelector);
    const observer = new IntersectionObserver((entries, obs) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                injectScript(scriptUrl, callback);
                obs.unobserve(entry.target);
            }
        });
    });
    observer.observe(element);
}

De functie accepteert de script-URL, een CSS-selector voor het element dat het laden moet triggeren, en een optionele callback voor initialisatie. Wanneer het element in beeld scrolt, wordt het script geïnjecteerd en verbreekt de observer de verbinding.

// Laad de Google Maps API wanneer de map container in beeld scrolt
injectScriptOnIntersection(
    'https://maps.googleapis.com/maps/api/js?key=YOUR_KEY',
    '#map-container',
    () => initMap()
);

IntersectionObserver wordt ondersteund in alle moderne browsers (95,76% wereldwijde dekking volgens Can I Use). Er is geen polyfill nodig.

Bij interactie: laden wanneer de gebruiker actie onderneemt

De meest effectieve methode is om een script pas te laden wanneer de bezoeker daadwerkelijk interactie heeft met het element dat het nodig heeft. Een chatwidget hoeft pas te laden wanneer iemand op de chatknop klikt. Een bibliotheek voor formuliervalidatie hoeft pas te laden wanneer de gebruiker de focus op een formulierveld legt.

function injectScriptOnInteraction(scriptUrl, elementSelector, eventTypes, callback) {
    const element = document.querySelector(elementSelector);
    const handler = () => {
        eventTypes.forEach(type => element.removeEventListener(type, handler));
        injectScript(scriptUrl, callback);
    };
    eventTypes.forEach(type => {
        element.addEventListener(type, handler);
    });
}

Deze functie luistert naar de opgegeven events op het doelelement. Bij het eerste event verwijdert het alle listeners en injecteert het het script. Het voordeel: als de bezoeker nooit interactie heeft met het element, laadt het script helemaal nooit.

// Laad chatwidget script wanneer op de chatknop wordt geklikt of gehoverd
injectScriptOnInteraction(
    'chat-widget.js',
    '#chat-button',
    ['click', 'mouseover', 'touchstart'],
    () => initChat()
);

Impact in de praktijk

Dit patroon werkt voor elk script dat niet nodig is tijdens het initiële laden van de pagina. Enkele veelvoorkomende use cases:

  • Chatwidgets: Een typische chatwidget laadt 200 tot 400 KB aan JavaScript. Toen Postmark hun Intercom widget uitstelde om op klik te laden in plaats van direct, daalde hun Time to Interactive van 7,7 seconden naar 3,7 seconden.
  • Video embeds: Een YouTube embed laadt meer dan 1 MB aan data. Toon een thumbnail met een afspeelknop en laad de embed bij een klik.
  • Kaartintegraties: Google Maps laadt honderden kilobytes aan JavaScript. Gebruik IntersectionObserver om het te laden wanneer de map container in beeld scrolt.
  • Analytics en tracking: Analytics scripts kunnen wachten tot na de eerste gebruikersinteractie. Niemand is ooit teleurgesteld geweest dat hun heatmap-tool 3 seconden na het laden van de pagina begon met opnemen.
  • Formulierbibliotheken: Validatiebibliotheken, datumkiezers en rich text editors kunnen laden wanneer de gebruiker de focus op het formulier legt.

Wanneer je niet moet uitstellen

Niet elk script moet worden uitgesteld. Als een script verantwoordelijk is voor het renderen van content boven de vouw, zal het uitstellen ervan je Largest Contentful Paint verslechteren, niet verbeteren. Scripts die je header-navigatie initialiseren, je hero-sectie renderen of kritieke A/B-testvarianten opzetten, moeten vroeg worden uitgevoerd.

De regel is simpel: als de bezoeker binnen de eerste viewport zal zien wat het script produceert of er interactie mee zal hebben, laad het dan normaal. Als het script iets onder de vouw of achter een gebruikersactie aanstuurt, stel het dan uit met behulp van een van de bovenstaande patronen.

Tip: Voor een compleet overzicht van alle JavaScript laadstrategieën, zie 16 methoden om JavaScript uit te stellen of in te plannen.

De verbetering meten

De 2025 Web Almanac rapporteert een mediane mobiele Total Blocking Time van 1.916 ms, een stijging van 58% ten opzichte van 2024. Veel van die blokkade komt van JavaScript die niet tijdens het laden van de pagina hoefde te draaien. Door niet-kritieke scripts uit te stellen, verwijder je ze volledig van het kritieke pad.

Verifieer na het implementeren van on-demand laden de verbetering met Real User Monitoring. Controleer je INP-scores en Total Blocking Time in praktijkdata, niet alleen in Lighthouse. Labtests draaien op snelle machines met lege caches. Jouw bezoekers zitten op mobiele netwerken met 15 browsertabbladen open. Dat is waar het verschil zichtbaar wordt.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

Find out what is actually slow.

I map your critical rendering path using real field data. You get a clear answer on what blocks LCP, what causes INP spikes, and where layout shifts originate.

Book a Deep Dive
Stel scripts uit tot ze nodig zijnCore Web Vitals Stel scripts uit tot ze nodig zijn