Skip to main content

Command Palette

Search for a command to run...

Lazy Loading Your HTML pages using JavaScript

Updated
โ€ข5 min read

Hi!๐Ÿ‘‹ Modern websites today use a lot of different components. And loading them all together in one go, where a lot of unused or unnecessary components are loaded completely before the browser can display something, will decrease your FCP (First contentful pain) score and FMP(first meaningful paint) score. This impacts the user experience negatively as there is now a need for higher power, memory, and network bandwidth that increases resource utilization. To improve this experience, we lazy load certain parts of the webpage meaning some content takes priority over others and is displayed immediately in the first go while other components are loaded as when required. This decreases the resource strain required and makes the website more user-friendly. Today we'll discuss one such technique to lazy load HTML components using JavaScript.

๐Ÿšง Before starting...

Let's first discuss the advantages and disadvantages of lazy loading your HTML pages into components to understand if it is really necessary to implement this in your web projects.

The main advantages go as follow:

  1. Better FCP and FMP scores
  2. Easy development of native HTML webpages by getting an import-like functionality to import in smaller components like headers, footers, navbars, menus, images, and many more can be easily lazy-loaded
  3. Lighter on low-power machines as content will be loaded on demand

The disadvantages go as follows:

  1. If your web page is not too large then this will just add overhead.
  2. It will be technically challenging to divide the page into smaller components
  3. There can be security challenges when you insert HTML content that is not parsed into your web page, for eg. XSS Attacks. (Refer here)

With this information in mind, let's dive right into the code...

๐Ÿš€ Fetch API

JavaScript exposes this nice fetch method in browsers which can be used to make network requests. We'll be using this fetch method to load our content over the network.

  1. Sample request code:

    fetch(URL,options).then(response).catch(error)
    

    Note: Please look at fetch MDN docs to understand if your browser version is supported or not.

  2. Let's apply this in our sample HTML file by creating a new file header.html with content:

    <h2>Great lookin pagesss!!</h2>
    

    and modify our index file by adding the following script:

    <div>
     <h2>Great lookin pagesss!!</h2>
    </div>
    <script>
     document.body.onload = function(){
         fetch("./header.html")
             .then(response => response.text())
             .then(header => document.body.append(header))
             .catch(document.write)
     }
    </script>
    
  3. and the page should look like init_lazy.png

๐Ÿช› Redefining with placeholders

Now that we have gotten a simple lazy loading up, let's rewrite the HTML page to add a placeholder element on the main page. Why a placeholder element? Well, it is ideal to show a loader or some dummy data to the client while the original element is loading.

  1. Rewriting the body of the index to match below

    <body>
     <div id="attach-here">Loading.....</div>
    </body>
    
  2. and the core loading logic method into a separate function to make it easier to use

    function lazy(url, elementSelector){
     let element = document.querySelector(elementSelector);
     fetch(url)
         .then(response => response.text())
         .then(component => {
             element.innerHTML = component;
         })
         .catch(document.write);
    }
    
  3. With this, all that we have to do is call the lazy method like lazy("./header.html","#attach-here") and this will load the header component into the "attach-here" div. The loading will look like new_init_loading.gif

๐Ÿ’ค Lazy loading images

Images are a major resource-consuming asset that a web page uses. Lazy loading these can greatly improve your FCP and FMP scores.

  1. Modify the lazy javascript function to now load images

    function lazy(url,placeholderSelector){
         let placeholder = document.querySelector(placeholderSelector);
         return fetch(url)
             .then(function (res) {
                 return res.blob();
             })
             .then(function (body) {
                 if (body.type.match("image")) {
                     placeholder.innerHTML = `<img src="${URL.createObjectURL(body)}">`
                 } else {
                     body.text().then((html) => {
                         placeholder.innerHTML = html;
                     });
                 }
             })
             .catch(document.write);
    }
    
  2. The modified HTML looks like (adding a placeholder SVG this time)

    <body>
     <div id="attach-here">
         <svg width="480" height="360">
             <rect width="480" height="360" style="fill: lightgrey;"></rect>
             <text x="0" y="180" style="letter-spacing: 1px;" fill="black" >Loading.....</text>
         </svg>
     </div>
    </body>
    
  3. And the final output looks like..... image_loading_final_final.gif

๐Ÿงน Wrapping it up with Sanitization

As discussed in the "Before Starting" section, there can be security concerns in adding unparsed HTML to your websites. As a security measure, we can use DOM purifiers to sanitize strings which can be then used as HTML components. I'll be using the DOMPurify library by cure53 for sanitization.

  1. We'll modify our function such that you can use any purifier with it.

    function lazy(url,placeholderSelector, sanitizer, ...sanitizerArgumentsOrConfig){
         let placeholder = document.querySelector(placeholderSelector);
         return fetch(url)
             .then(function (res) {
                 return res.blob();
             })
             .then(function (body) {
                 if (body.type.match("image")) {
                     placeholder.innerHTML = `<img src="${URL.createObjectURL(body)}">`
                 } else {
                     body.text().then((html) => {
                         placeholder.innerHTML = sanitizer
                             ? sanitizer(html, sanitizerArgumentsOrConfig) : html;
                     });
                 }
             })
             .catch(document.write);
    }
    

    Remember to add the following the DOMPurify script to your HTML page:

    <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
    
  2. and modify the lazy function like

    lazy("./header.html","#attach-here",DOMPurify.sanitize);
    // or with configuration options
    lazy("./header.html","#attach-here",DOMPurify.sanitize,{ USE_PROFILES: { html: true, ALLOWED_ATTR: ["style"] } });
    

๐Ÿช Hooks and Triggers

Until now, we saw how to load content using the fetch API. Now we will define events as triggers to lazy load the content when needed. You can trigger the lazy loading on any DOM event on any element. Some examples are:

  • onclick
  • ondblclick
  • onload
  • scrollIntoView

and many more

๐Ÿค˜ End

That's all for lazy loading to get you started implementing it Thank you for reading. Share and like if you find it useful. See you soon in the next blog โœŒ๏ธ

P

Kindly share lazy loading github repo

D

The code is very small to make a GitHub repository for it. I have created a GitHub gist which you can find at this