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.
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.
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.
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.
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.
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.