Don’t Load it till it’s Needed

April 30, 2015 - Tim Evko

The average web page has nearly doubled in size since 2010. While this might not come as a surprise to any of us, there is certainly plenty that we can do to scale back the weight of our web pages.

One of my favourite ways to prevent page bloat is to treat everything as a resource that doesn’t need to be on the page until the user has to interact with it. The technique is called lazy-loading, and can be performed on almost any asset. It’s especially good for responsive websites, when the same content needs to be loaded across multiple devices, while still loading as quickly as possible. Let’s take a look at a few ways to make this possible.


Images, Video, and Audio

The key to lazy-loading these assets is to leave the html structure in tact, while deferring their src attributes until absolutely necessary.

Images

Let’s take a regular image that hasn’t been adjusted for lazy-loading.

<img src="myimg.png" alt="a picture of bacon or something">

In order to lazy-load this image, the first thing we need to do is address the src attribute, so that the DOM won’t grab the asset immediately. To do that, we’ll change the src attribute into a data attribute. <img data-src="myimg.png" alt="a picture of bacon or something">. If we need to lazy-load a responsive image, our snippet will end up looking something like this: <img data-src="myimg.png" dta-srcset="img1.png 320w, img2.png 640w, img3.png 1280w" data-sizes="(min-width: 33em) 75vw, 100vw" alt="a picture of bacon or something">. One thing to note is that while the sizes attribute is not responsible for loading images, it still needs to be lazy-loaded with the src and srcset attributes. This is because if the sizes attribute is present before any of the image assets, the browser will not load the correct image. A side effect of using this technique is that images won’t initially have a width or height, unless they are set beforehand using css or width and height attributes in the HTML.

Videos

Lazy-loading video assets can be a little bit tricky. While my initial approach was to do something like this – <video data-src="videofile.mp4"</video> – I found that when the src attribute is added, the video won’t function correctly in all browsers. In order to fix this, a slightly more difficult solution is needed. What does end up working is adding <source> tags in between the opening <video> tags as soon as the video is needed.

Audio

Lazy-loading an audio resource is much more like an image, in that all that is needed is to change a data attribute into a source attribute when the resource is needed. Don’t forget to ensure that the controls attribute is present in the audio tag, or else nothing will show up when the tag is present on the page. Here is an example.


Third Party Resources

Every now and then a third party widget needs to be present on a page. These can be things like photo galleries, social networking feeds, comment systems, and ads. Resources like these can often add to initial page load time, with extra requests and heavy files needed to render the widget. In order to prevent widgets from slowing down the site, we’ll want to lazy-load them in whenever possible. This usually means wrapping the entire widget inside of a script tag, and dropping its contents inside of an empty container just before the asset is needed.


Tying it all together

Now that we’ve gone over a few use cases for lazy-loading, let’s take a look at how we can use javascript to tell us when an element needs to be ready and visible to the user.

To do this we’ll be using the getBoundingClientRect() method, which will return an elements size and position relative to the viewport. We’ll attach this to a scroll event listener so that when the user is scrolling we can fire an event to tell us that the element is visible in the viewport. It’s important to remember not to run too many calculations inside of a scroll event listener, since this will lead to performance bottlenecks.

For this demonstration, we’ll be loading an image when it is visible in the users viewport.

See the Pen Lazy-loading an image by Tim (@tevko) on CodePen.

The PolyFill is being included in order to provide support for custom events in IE. We’re also using requestAnimationFrame() which will help to keep a high frame rate while we scroll, allowing for a smooth scrolling experience.

Once the custom event has fired, the notCalled variable is set to false, preventing the checkViewport function from being called needlessly.


Conclusion

While you now have a few techniques aimed at making your websites lighter, faster, and all around easier to use, remember that performance is a constant battle, and there are several other ways to make your site perform better for all devices.


Resources

CustomEvent API

requestAnimationFrame API