Have you heard of first contentful paint? No? You should! The first contentful paint (or first meaningful paint) is a very important parameter of your website. The longer a page needs to load the more likely as user is to lose interest and leave your website. So there is a very close connection between pagespeed and bounce rate. Pagespeed means the time your website needs to load. A very nice ressource to test this is PageSpeed Insights. This is a tool by google which meassures the First Contentful Paint (FCP) and the DOM Content Loaded (DCL).
So what we want to achieve is to give the user a visible result as early as possible. There are many things you can do for speeding up the first contentful paint. People talk about critical CSS, tree-shaking JavaScript and brotli compression because gzip is not compressing it enough. They talk about avoiding the use of libraries and help you to optimize your code to make it as small as possible. And they are right. All those things are great improvements to pagespeed, and a good pagespeed means more users will enjoy you website. But before we dive deeper into these topics, we start with optimizing images. There is nothing else which will improve your pagespeed more with that little effort.
Optimizing images
Images should only be loaded in the size the users really need, meaning that you don’t need to load an image with the resolution of 3000x2000 when you only need to show it in a teaser size of 500x333. You can do the math how many data ressources you will save by just resizing the image and download it in only the size you really need it.
After resizing the image the next important step is to minify the image with an image compressor. We use OptiImage or ImageOptim for that. Take care that you do not minify too much. There is a chance that you will make the image look not nice anymore (blurry/unsharp edges or awful color gradients). By optimizing the images with those tools instead of photoshop or something similar, you can save a lot of bytes. Well done! But now we download and render all images on our page even though we do not see them.
Lazy loading images
Lazy loading means that you only load the images which are actually visible in your viewport. So if the users land on your website they don’t need to load the images which are at the very bottom of your page. What we want to do is only show the user the image which is in the viewport.
First we need some images in the DOM, so let’s create some dummy content.
Make sure you apply some styling so that you need to scroll on the page and you do not show all images above the fold (in the current viewport).
Let’s get to JavaScript part. There is a very nice little helper called Intersection Observer which was announced on the 14 September’17 by Google. W3C describes it as follows:
This specification describes an API that can be used to understand the visibility and position of DOM elements (“targets”) relative to a containing element or to the top-level viewport (“root”. The position is delivered asynchronously and is useful for understanding the visibility of elements and implementing pre-loading and deferred loading of DOM content.
This is just perfect for lazy loading images!
So we first have to gather all the images we marked above as “lazy-images”. Those are the images that have a data attribute containing our image source. Make sure that the image has no src attribute set, otherwise, the browser will load an image before the IntersectionObserver can do its magic. The logic result we are now trying to achieve is that no lazy load image has an image source, meaning that no image will be loaded initially. The imageObserver will now check on each image, if it is in the viewport. If it is, then we will replace the content of the src attribute with the content of the data-src attribute (the actual image source). If this is done, we will download and display the image.
Polyfill
As you can see here, the browser support is quite good, but not yet good enough for us. If you also want to improve the browser support, then follow the instructions on this npm module. If you are still unsure, you can wrap the start of our observer in an if-condition to check if the IntersectionObserver is supported:
No JavaScript fallback
If for any reason the user has not enabled JavaScript or the JavaScript throws an error and can not be executed, ALL images in your website will not be shown, because all of them rely on the JavaScript which manipulates our image source.
Here is a solution with a noscript tag. The noscript tag will only render when scripting is not active. So, if JavaScript is not active, the browser will show our images with no lazy loading. Our other images (the lazy loading images) with no valid source will have display: none.