Translate this page

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


Translate this page

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.