Defer scripts until they are needed
Learn how to fix the Core Web Vitals by deferring scripts until they are needed
Defer scripts until they are needed
In this article I will show and explain a cool pattern to load scripts that are not needed during the start of the pageload at a later time, just before they are needed.
The absolute most effective thing you can do with JavaScript when it comes to the Core Web Vitals is to delay loading of a resource until it is needed. This will remove unused and unneeded JavaScript from the page and only load it when it is needed. This will fix the lighthouse warning 'reduce unused JavaScript' and also improve responsivity metrics like the interaction to Next Paint (INP).
We have been doing this with images for a long time. It is called lazy loading. With lazy loading a below-the-fold image is loaded right before it scrolls into view. This way we do not need to load the image immediately during page load and the browser can spend its precious resources on downloading, parsing and painting things that are actually needed.
Table of Contents!
- Defer scripts until they are needed
- Step 1: Load scripts on demand
- This function injects a script into the current web page by creating a new script element and appending it to the head of the document. The scriptUrl parameter specifies the URL of the script to be injected. The callback parameter is an optional function that will be called when the script has finished loading. When the script has finished loading, the onload event of the script element is triggered. If a callback function was provided, it will be called at this point.
- Step 2: Load scripts on demand
- Conclusion
Now imagine we could do the same thing with Scripts instead of images. Well it turns out that we can! Unfortunately it is not as simple as adding loading="lazy"
to an image but with a bit of effort we can make it work
Step 1: Load scripts on demand
To add scripts to the page after page load we will need a small script that does this for us.
function injectScript(scriptUrl, callback) { var script = document.createElement("script"); script.src = scriptUrl; if (typeof callback === "function") { script.onload = function () { callback(); }; } document.head.appendChild(script); }
This function injects a script into the current web page by creating a new script element and appending it to the head of the document. The scriptUrl parameter specifies the URL of the script to be injected. The callback parameter is an optional function that will be called when the script has finished loading. When the script has finished loading, the onload event of the script element is triggered. If a callback function was provided, it will be called at this point.
Step 2: Load scripts on demand
2a: Intersection observer
function injectScriptOnIntersection(scriptUrl, elementSelector) { var observer = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { injectScript(scriptUrl); observer.unobserve(entry.target); } }); }); var element = document.querySelector(elementSelector); observer.observe(element); }
This function takes two parameters: scriptUrl is the URL of the script to be injected, and elementSelector is a CSS selector for the element that should trigger the injection.
The function creates a new IntersectionObserver object and passes it a callback function that will be called whenever an observed element intersects with the viewport. The callback function checks if the element is intersecting and, if so, injects the script and stops observing the element.
Note that the Intersection Observer API is not supported in all browsers, so you may need to use a polyfill if you need to support older browsers.
injectScriptOnIntersection('script.js', '#my-element');
This will inject the script when the element with ID my-element becomes visible in the viewport.
2b: On interaction
function injectScriptOnInteraction(scriptUrl, elementSelector, eventTypes) { var element = document.querySelector(elementSelector); var eventHandler = function() { injectScript(scriptUrl); eventTypes.forEach(function(eventType) { element.removeEventListener(eventType, eventHandler); }); }; eventTypes.forEach(function(eventType) { element.addEventListener(eventType, eventHandler); }); }
This function takes three parameters: scriptUrl is the URL of the script to be injected, elementSelector is a CSS selector for the element that should trigger the injection, and eventTypes is an array of event types that should trigger the injection (e.g. ["click", "mouseover"]).
The function finds the element using document.querySelector and adds event listeners to it for each of the specified event types. When any of the specified events occur, the injectScript function is called with the specified URL, and the event listeners are removed using element.removeEventListener.
injectScriptOnInteraction( 'script.js', '#my-element', ['click', 'mouseover'] );
This will inject the script when the element with ID my-element is clicked or hovered over, and then remove the event listeners.
Conclusion
Stop Guessing, Start Measuring
I use Core/Dash to optimize my clients sites. So Should You.
- Easy set up.
- Over 5000 happy clients
- Free trial, No strings attached