Lazy loading of images via HTML and JavaScript


I previously wrote a tutorial explaining how to preload images on a web page using HTML, CSS or JavaScript. The reason we took the trouble to preload images is to provide a better browsing experience for users so they don’t have to wait for an image to load.

The same philosophy of improving user experience also applies to slow loading images. Images are one of the biggest contributors to page weight when we browse websites. Loading them optimally can improve performance and save bandwidth.

In this tutorial, we will learn different methods of lazy image loading.

The need for lazy loading images

We’ll start the tutorial by first learning why you should care about lazy image loading. Let’s say you’re building a portfolio website for a photographer and they showcase all of their best images on a single page.

Not everyone will scroll to the bottom of the page to see all the images. However, the images would still download from the user’s browser. This is evident in the CodePen demo below:

Even if you don’t scroll past the first image in the demo above, you’ll see that the browser has loaded all of the images. The following screenshot of the Net tab from my browser’s developer tools shows that 38 requests were made with about 2.5MB of data transferred. The browser downloaded a total of 19 images with redirects that double the number of requests.

Loading images normallyLoading images normally

We will now try to improve or optimize image loading to save resources.

Lazy loading of images via HTML

The easiest way to load images lazily involves the use of loading attribute. All modern browsers support the loading attribute on images that can be used to tell the browser to prevent loading an image if they are off-screen and only start loading them when the user has come close enough for them to be visible.

THE loading the attribute can accept two possible values:

The first value is eager which tells the browser to immediately load the image even if it’s not currently inside the viewport. This is the default behavior of browsers.

The second value is lazy which tells browsers to postpone loading an image until it has reached a specific distance from the viewport. This distance is defined by the browser. Setting the upload attribute value to lazy can potentially save bandwidth for clients.

It’s important to remember that browsers only load images that aren’t currently visible in the viewport. Usually, images on a web page are placed together with other text that pushes them out of the viewport. You don’t need to do anything special to make sure your images load lazily in this case.

However, consider our example in this tutorial where the web page contains only images. In such a scenario, it becomes important for you to mention the size of the images if you want them to load lazily. Otherwise, all images will initially have zero width and zero height. This will make the browser think that all images are actually visible in the viewport and load them all at once.

Specifying the image width and height explicitly in this case would push some images out of the viewport. You are free to specify the size of the image using the file width AND height HTML or CSS attributes.

Here is the markup that will lazily load images:

1
<img loading="lazy" src="https://picsum.photos/id/628/1080/1080" width="600" height="600">

As I said before, you can also specify the image size in CSS and remove the file width AND height attributes from markup:

1
<img loading="lazy" src="https://picsum.photos/id/628/1080/1080">

The corresponding CSS would be:

1
img {
2
  width: 600px;
3
  height: 600px;
4
}

The following CodePen demo shows lazy-loading in action:

THE Net tab in my browser’s developer tools shows that this time only four images were downloaded with about 450kb of data transfer. There are a total of 19 images on the page, which means another fifteen will be lazily downloaded. In terms of bandwidth, this translates into savings of around 80%.

Loading images lazilyLoading images lazilyLoading images lazily

One important thing to keep in mind here is that even though there are no scripts involved, lazy image loading only works when JavaScript is enabled. This is done to prevent strategically placed images from tracking a user’s scroll position.

How does the browser determine when it should download images that should load lazily? The exact conditions that trigger the download of slow-loading images vary between browsers. However, two main factors appear to be distance from the viewport and network speed.

If you want to control exactly when slow-loading images are downloaded, you’ll need to use JavaScript.

Lazy loading of images via JavaScript

Now we will learn how to use JavaScript to load images lazily. This will give us more control over the whole process. If you feel that the default lazy loading isn’t aggressive enough, you can create your own lazy loading script using the Intersection Observer API.

Before writing any JavaScript, we need to make a few changes to our markup:

1
<img class="lazy-load" data-src="https://picsum.photos/id/628/1080/1080">

Our img tags will now contain a class called lazy-load to help us identify which images need to load lazily. Instead of a src attribute, the img tags will use the data-src attribute to track image path. This prevents the image from starting download immediately.

The Intersection Observer API allows us to detect if our target element intersects with any of its ancestor elements or the document viewport. We will use the IntersectionObserver() constructor to create our own IntersectionObserver object. The constructor takes a callback function as the first parameter and an optional object to customize the behavior of the observer as the second parameter.

The callback function we pass to the constructor receives two parameters. The first is an array of intersecting elements and the second is the observer itself. The customization options allow you to specify the root element with which you want to check for intersection, the root margin that adds an additional offset value to the root element, and a threshold to determine when the browser should start reporting the intersection .

Here is the code for our intersection observer object:

1
function preload_image(img) {
2
  img.src = img.dataset.src;
3
  console.log(`Loading ${img.src}`);
4
}
5

6
const config_opts = {
7
  rootMargin: '200px 200px 200px 200px'
8
};
9

10
let observer = new IntersectionObserver(function(entries, self) {
11
  for(entry of entries) { 
12
    if(entry.isIntersecting) {
13
      let elem = entry.target;
14
      preload_image(elem);   
15
      self.unobserve(elem);
16
    }
17
  }
18
}, config_opts);

In this case I’ve provided a 200px margin on all sides of our root or viewport element. Our intersection observer will become active whenever an image is within 200px of the viewport. The threshold value is set to 0 by default. A value of zero means that the callback preload_image() function will be called as soon as a small part of the image falls within the specified bounds. THE unobserve() The method tells the browser to stop looking at this particular image for further matching.

THE preload_image() the function takes the value of data-src attribute for our image and apply it to the src attribute. This triggers a download of our image.

All we have to do now is query all the images in our document and then tell the viewer to look at them all for the intersection. Here is the code that accomplishes this for us.

1
let images = document.querySelectorAll('img.lazy-load');
2

3
for(image of images) {
4
  observer.observe(image);
5
}

Did you notice that we are using the img.lazy-load selector to query our images? This class helps us to easily identify all the images we want to load lazily. Images without this class will load normally.

Here is a demo of CodePen to see if our images are actually loading slowly or not.

This time the Net tab in my browser’s developer tools shows that only two images have been downloaded previously with a total data transfer of around 192kb. Our bandwidth savings have now increased to 92% compared to our original demo.

Loading Images Lazy IntersectionLoading Images Lazy IntersectionLoading Images Lazy Intersection

I must admit I made the intersection observer very aggressive to only load images very close to the viewport. However, that’s the beauty of implementing the feature yourself.

Final thoughts

Lazy loading of images is a win-win. It will reduce the load on your server while saving users bandwidth. Remember that data, especially mobile data, is quite expensive in some places in the world.

Now that browsers have native support for lazy image loading, it’s just a matter of making minor markup tweaks to take full advantage of the feature. Browsers are also smart enough to determine the ideal time to lazily load an image based on network speed and the image’s location. You can also implement the functionality yourself relatively easily with the use of the Intersection Observer API.

An important thing to keep in mind here is that none of these techniques will work if JavaScript is disabled by the user.



Source link

By LocalBizWebsiteDesign

Leave a Reply

Your email address will not be published. Required fields are marked *