Lazy loading social widgets
I added social widgets (Facebook like, Google +1, and Twitter tweet buttons) to this site about three months ago. I had no idea if anyone would use them or not. Turns out, they do.
I have one big problem with them, though: they take forever to load! An entire page of mine takes about 750 milliseconds to load. Adding social widgets drags that down to almost 3 seconds — more than three times longer!
I like my pages to load fast. I have just over 6 kB of assets (CSS and JS, no images). Each post has about 3 kB of Markdown. So how can I keep my site performant while providing these social widgets for people that want them?
Lazy Loading
The answer is lazy loading: “defer[ing] initialization of an object until the point at which it is needed”. So now I need to know when the social widgets are needed. Better yet, when are they not needed?
If they’re not visible, they aren’t needed. Simple, right? Turns out, determining if an element is in the viewport is difficult if you don’t want to use a framework. (I don’t want to start using a framework, since that adds a big request and kind of defeats the whole point.)
So, what can I do instead? The scroll event is well-supported and doesn’t require a framework. Loading the social widgets when the user scrolls is nearly as good as loading when they should be visible. You need to scroll to see them, anyway.
window.onscroll = function () {
window.onscroll = null;
// Facebook JS SDK
script = document.createElement('script');
script.async = true;
script.id = 'facebook-jssdk';
script.src = '//connect.facebook.net/en_US/all.js';
document.body.appendChild(script);
// Google +1 button
script = document.createElement('script');
script.async = true;
script.src = '//apis.google.com/js/plusone.js';
document.body.appendChild(script);
// Twitter widgets
script = document.createElement('script');
script.async = true;
script.id = 'twitter-wjs';
script.src = '//platform.twitter.com/widgets.js';
document.body.appendChild(script);
};
Note that the first part of the onscroll
function is to unassign
it. That’s because the scripts for these social widgets only need
to be loaded once per page. Originally I used a boolean flag to
know if the scripts had been loaded, but that’s unnecessary since
the function can be reassigned from within the function body.
Special Cases
That’s all fine and dandy, but what happens if the viewport is taller than the document? Then scrolling is impossible and the social widgets never get loaded.
My initial goal was to reduce page load time by avoiding unnecessary
elements. In this case, I’m okay with the old behavior of just
loading the widgets anyway. So I can just call the onscroll
function if the viewport is tall enough.
if (window.innerHeight >= document.body.clientHeight) {
window.onscroll();
}
This method is reasonably well-supported, but it won’t work in Internet Explorer before version 9. That’s not a problem for my site, since those versions of IE account for less than 1% of all visits.
Improvements
As I mentioned above, a JavaScript framework would make this better by delaying the load until the element is visible. Another potential improvement in my case is delaying the load until the viewport has been scrolled all the way to the bottom, where the widgets are.
Ancillary Benefits
Although it wasn’t the motivation for exploring this technique,
this improves my site’s performance in the eyes of Googlebot.
It loads the page but doesn’t scroll around so the social widgets
aren’t loaded. I don’t know what it’s viewport is, so it could end
up calling onscroll
due to the workaround for tall viewports.