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