Instant Page Load By Lazy Loading Using Just JavaScript

I made all pages on this site load immediately, and then do all the extra work: load images, videos, JavaScript, CSS.

My benchmark was PageSpeed Insights, also available inside Chrome Dev Tools.

Run JavaScript After Page Load

The only way I’ve found to make the lazy loading not count as part of the page load time is to use a callback that runs after ~1.5 seconds:

function init() {
  // ...
}

setTimeout(init, 1500);

This is a little counter intuitive: I’m slowing down the extra work by 1.5 seconds, for the benefit of making the page immediately interactive.

Load Images And Iframes

I’ve changed all the img and iframe tags to use data-src instead of src. This means that nothing loads by default.

function update(tag) {
  var nodes = d.getElementsByTagName(tag);
  for (var i = 0; i < nodes.length; i++) {
    var data = nodes[i].getAttribute(
        "data-src");
    if (data) {
      nodes[i].setAttribute("src", data);
    }
  };
};

function init() {
  // ...
  update("img");
  update("iframe");
  // ...
}

The update function sets src for those nodes, which them triggers the loading.

Load JavaScript

Some libraries need to set extra attributes on the script node, and some need to run extra JavaScript code after the script has loaded.

var d = document;

function loadJS(src, attr, val, cb) {
  var s = d.createElement("script");
  s.src = src;
  s.type = "text/javascript";
  if (attr) {
    s.setAttribute(attr, val);
  }
  if (cb) {
    s.onload = cb;
  }
  d.body.appendChild(s);
};

function init() {
  // ...
  loadJS("https://cdnjs.cloudflare.com/....js");
  loadJS(
      "https://kaue-me.disqus.com/embed.js",
      "data-timestamp", +new Date());
  loadJS(
      "https://www.googletagmanager.com/...",
      undefined, undefined, function() {
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'UA-99999999-9', {});
  });
  // ...
}

One potential downside of this approach is that the analytics event will not trigger if people leave the page too early.

Load CSS

function loadCSS(href) {
  var n = d.createElement("link");
  n.href = href;
  n.rel  = "stylesheet";
  n.type = "text/css";
  n.media = "all";
  d.body.appendChild(n);
};

function init() {
  // ...
  loadCSS(
      "https://cdn-images.mailchimp.com/...");
  // ...
}

One potential downside here is that it causes a flash of unstyled content if the corresponding content is visible before the CSS is loaded.

Bonus: Use Hugo To Include Only What Is Needed

My Hugo layout includes only what is needed for each page:

{{- $hasVideo := .HasShortcode "video" -}}
{{- $hasImage := or
    (.HasShortcode "image")
    .Page.Params.has_image -}}
{{- $hasComments :=
    .Page.Params.show_comments -}}
{{- $hasSubscribe :=
    .Page.Params.ask_subscribe -}}
// ...

var d = document;

function loadJS(src, attr, val, cb) {
  // ...
};

{{- if or $hasVideo $hasImage -}}
  function update(tag) {
    // ...
  };
{{- end -}}

{{- if $hasSubscribe -}}
  function loadCSS(href) {
    // ...
  };
{{- end -}}

function init() {
  {{- if $hasImage -}}
    update("img");
  {{- end -}}

  loadJS(
      "https://www.googletagmanager.com/...",
      undefined, undefined, function() {
    // ...
  });

  {{- if $hasVideo -}}
    update("iframe");
  {{- end -}}

  {{- if $hasSubscribe -}}
    loadCSS(
        "https://cdn-images.mailchimp.com/...");
    // ...
  {{- end -}}

  {{- if $hasComments -}}
    loadJS(
        "https://kaue-me.disqus.com/embed.js",
        "data-timestamp", +new Date());
  {{- end -}}

  // ...
};

setTimeout(init, 1500);

You might also want to see how I use Hugo for this site.

Subscribe to my mailing list and get a free email course

* indicates required

Interests



Updated on 2020 Jun 1.

DISCLAIMER: This is not professional advice. The ideas and opinions presented here are my own, not necessarily those of my employer.