Intersection Observer in action

Intersection Observer? Whaaat!? That sounds scary!

In this post I want to explain my journey through and use case for using Intersection Observer. I will also give you a brief overview of how you can get started.

As the specification says…

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

What this brand new Web API allows you to do, is checking whether you can visually see the element you are observing within your viewport or any other scrollable parent element.

My use case

What about lazy loading images on the page?

Recently, I have been working on improving the performance and payload size we provide to our users at Live Nation.

There are plenty of images on the Live Nation homepage, and sometimes these images are uploaded at such high quality, that we end up delivering images with unfortunately large file sizes.

I’m pretty sure users don’t really care about having every image, from top to bottom downloaded and ready to be seen. I would say they would like to see the page loading faster.

For that reason, if you are a user on a mobile device, you will only care about 1, 2, 3… images? Pretty much nothing. There is basically no point in serving users content which probably they may not even scroll to see.

So let’s get into it!

We are using React, which helps us create beautiful and reusable components that we can share within our website. So, I took my Image component and added some code to it.

The first thing I wanted to do was make sure it was going to work and behave as a normal image component after the modification, so I added it an new prop observeOnScroll.

With that in place, I could encapsulate my code and execute it by only passing observeOnScroll. It looked like this in my componentDidMount:

  1. I checked whetherobserveOnScroll was passed to the component, otherwise I won’t let the code continue.
  2. Create a new IntersectionObserver, which receives a callback function and a set of options. The callback function executes when your element become visible or invisible, so when the element intercepts the viewport and there are some options you can pass, like for example you can add a margin to the element so you can start taking some actions before the element becomes visible. If you would like to, you can investigate Intersection Observer options further.
  3. I tell my observer to observe the element, in this case, the image.

And now? What are we waiting for? Let’s start scrolling the page!

Once you start scrolling and your elements start intersecting with the viewport, you can see that the callbacks are being fired.

You will receive some intersection entries, each entry describes an intersection change for one observable. That’s why I am looping through them, so if any of them are intersecting I can take some action and finally create my image.

But I want my images to load nicely and preload them it without any visual changes, so they can be ready when I rerender my component with the real image. So, I will listen for the onload event and there I will set loaded state to true and I can finally show my Image. All that, together with a nice blurry animation to distract the user while the load is happening.

And we are done! 🎉

Browser Support

Intersection Observer current browser support

At the time of writing, as you can see, most of the browsers have support for it and it’s strong enough for you to start implementing it into your website. See current support.

But what about those browser that doesn’t have support? Okay, what I did was lazy load the polyfill in those cases.

We check the support for Intersection Observer by checking if it is on the window object. If it is, then there is no need for the polyfill, if not, we load our polyfill file into the page.

But as always, there are some difficulties and differences in terms of browser implementation:

Like for example, when I was testing with Samsung Internet browser, it didn’t work! There were no images… But why? I looked at the browser support and everything was green, so why!?

Next step, the debug journey.

I used a real device to be able to reproduce the problem (If you want to know how to debug in real devices, take a look at this awesome guide for remote debugging by Samsung Internet) and after some digging, I found out there was no isIntersecting property on the IntersectionObserverEntry available, so I decided to google it! and found this:

Turns out, Edge didn’t have isIntercepting either, so I needed to check intersectionRatio > 0 as well. According to the specification, it’s supposed to be the same as isIntersecting.

Why use IntersectionObserver

  • In the past, to perform the same action, we could use a document level scroll and resize event. But listening for those can be quite expensive if you end up running large numbers of calculations on them. I recommend you should avoid using them.
  • IntersectionObserver is becoming an standard alternative for those, it requires no libraries and have a good and declarative API.
  • When you are not observing for any changes, just disconnect them. No need to be listening all the time (In our use case, there was no need to continue listening for an image intersection once loaded, so we disconnect them right after).
  • Most modern browsers support Intersection Observer, which is a good sign.

Final Metrics

We were loading so many images off-screen, which were not needed for a first load in our page. It reduced the payload by 793 KB (4,450ms). Pretty impressive right? 😱

Content request payload before optimisation

Before, there were 66 request for images from the homepage, which is an excessively large amount, more than anything else. Now there are 21, not that many if we compare.

Content request payload after optimisation

Content request payload is the big boost here, as you can see, but there are many other important take aways here:

  • Time to load dropped from 4.104s to 3.543s .
  • Start render dropped from 1.579s to 1.080s .
  • Speed index marked 4018 and now it’s 2801 .
  • And loading was completed at 4.104s and now it’s 3.743s .

What can we do next?

At the moment, we have a lot of performance work to do, but we are sure that removing images from it, will be a big boost to improve in this terms.

Let’s look at the page load

As you can see in the figure above, our time to load is pretty big in mobile. If we think about it, we are working with around 30 countries, some of them, don’t even have a proper bandwidth connection. This number can be even more scary when loading the page on those countries! and there is no way we are giving them apart.

Conclusion

Using Intersection Observer is amazing, browser support is decent to start using it and can bring a big boost in terms on improving payload time in your page.

Keep an eye on updates! And remember, go and discover your favourite artists and events worldwide at www.livenation.co.uk.