Lazy Loading Basics

Chapter 2 13 mins

Learning outcomes:

  1. How lazy loading works
  2. Laying out the HTML and CSS


In the previous Lazy Loading Introduction chapter we looked at the background of lazy loading in great detail and saw its history coupled with its significance in modern day web and app designing. If you somehow missed it then consider reading it first before proceeding any further.

In this chapter we will specifically be looking over some fundamental concepts involved in lazy loading together with exploring how it operates at the basic level.

Let's begin!

How lazy loading works?

Recall from the last chapter, that a lazily loaded image gets loaded only once it enters the viewport. For a moment if we think on this, we see that in order to implement lazy loading we'll need to know just one thing - when does a given image enter the viewport.

And to know this, we will need the distance of the image from the top of the document, also known as the offset of the image.

Once we have this distance in hand, we can then handle the document's scroll event and load the image when we scroll to a distance greater than the offset of the image.

Since both element offsets and scroll events are a concern of Javascript, it turns out that the heart of lazy loading lies in this scripting language.

Let's take the example of a single image, and see how would it be lazily loaded, step by step.

  1. We start by calculating the offset of the image, from the bottom edge of the viewport.
  2. Then, we assign a scroll listener to window, to track the scroll bar's position against the image's offset calculated in step 1).
  3. When the image comes into view i.e it enters the viewport, its corresponding listener is removed from the listener stack of window and the image is ultimately loaded into view.

In step 1, the offset of the image, calculated from the bottom edge of the viewport, tells us exactly how much pixels would the document need to be scrolled in order to bring the image into view.

For example, let's suppose that the viewport's height is 300px and an image is 450px from the top of the document (not the bottom edge of the viewport).

Subtracting the viewport's height from the image's offset yields 150px. This is the distance of the image from the bottom edge of the viewport. When we scroll the document more than 150px, the image comes into view and consequently we load it.

This is lazy loading at the very basic level.

There are obviously a lot of things which we can, and definitely will, add to this basic algorithm to make our lazy loader efficient, performant and stylish, but this is the foundation for all those variants.

We can also couple it with modern interfaces such as the IntersectionObserver API to get the most out of supporting devices as we will see in the Lazy Loading Intersection Observer chapter.

In short, there is a lot to cover as of yet!

For now at least, you shall be comfortable in understanding how lazy loading works at this basic level, since, this understanding will form the foundational basis for you to be able to comprehend some awaiting concepts.

With this done let's move over to laying out the structure for the loader.

HTML - building blocks

Before beginning to build anything on the web, we first need to think for a moment on how its HTML will be layed out.

Brainstorming must be the first process in any program development.

After all, HTML sets the foundational structure of any component on the web, without which we can't go even a step further.

Likewise, let's start to think on what would we need for our lazy loader.

Container for a lazy image

Firstly, we need to settle on the nesting structure of a lazy image. That is, we need to ask ourselves the following questions:

  1. Will the lazy image have a container element?
  2. How many containers will the image have — 1, 2, 3, how many?
Does a lazy image need to have a container?

Our conclusion is that yes, a lazy image indeed requires a container. We'll use the simple and typical <div> element for this.

But why is a container element required?

The reason to give a container is to limit all the actions, messages and stuff related to a given lazy image within the bounds of that image.

We don't want to create a mess over all our other HTML code, if ever in future we need to give some other features to our lazy loader.

A perfect example is of giving a background color to denote an area where a lazy image will be shown with a fade effect. The fade effect requires that the image be at opacity: 0, due to which the background can't be seen on it!

The container is what takes the background color, and thus makes is possible for us to give a fade feature to our lazy loader.

Below shown is the structure we'll be using for our lazy loader:

<div><img src="someImage.jpg"></div>

One thing done - over to the next one..

The class attribute

After settling on the markup, now we need something to indicate that the underlying element shall be lazy loaded. In this way we can easily fetch all the to-be-lazy-loaded images using DOM methods.

To explore DOM methods in detail, please refer to JavaScript HTML DOM - Accessing Elements.

Any thoughts on how can we get this done?

Well this can be very easily done using an HTML class.

You can come up with any concise, yet meaningful, class name for your lazy images as it's upto the taste of the developer. We'll go with the class .lazy for the <div> container and .lazy_img for the <img> element.

The idea is that we fetch all the elements with the class .lazy in JavaScript, and then put them in the process to be lazy loaded.

As you know, by using a class we get the advantage of being able to fetch all the concerned elements very conveniently using class-based selection methods like getElementsByClassName() and querySelectorAll().

There definitely exist other ways to indicate that a given element shall be lazy loaded, for example setting a data-lazy attribute, but all those aren't as simple as using a class.

Consider the following extension to the code above:

<div class="lazy"><img class="lazy_img" src="someImage.png" /></div>

The .lazy class is given to the <div> element, while the .lazy_img class is given to the underlying <img> element.

Now we know right away that this image ought to be lazy loaded. Perfect!

data-src attribute

The next thing we need to do in our lazy loader's HTML is to remove the src attribute from the <img> element.

Can you think why we need to do this?

Why do we need to remove the src attribute from a lazy image?

Here's why:

If the src attribute is present on an <img> element, is gets loaded right away, regardless of whether it's in the viewport or not.

In short, the whole point of lazy loading becomes useless with src, and hence we need to remove it.

But there's a problem if we do so...

If we remove the src attribute from an <img> element, we lose the source of the image i.e the location from where it needs to be fetched. The browser loads an image only when the src attribute is present on it.

How do we solve this issue?

Well, the solution is pretty simple.

Firstly, the source of the image is saved in another attribute. The most suitable and sensible one is data-src.

The attribute serves to temporarily store the source URL of a given image, assigning it to the src attribute of the image once it appears into the viewport.

Below we set up the data-src attribute on our lazy image:

<div class="lazy"><img class="lazy_img" data-src="image.png" /></div>

image.png is the source URL of our image, and therefore we put it in the data-src attribute.

Why we use the data-src attribute?

Many people tend to think that data-src is some dedicated attribute to temporarily store the source of an image, but in reality it's not! We can use any other attribute we like - the idea is just to store the source URL of the image, that's it.

But here are some benefits of using data-src instead:

  1. Firstly the name sounds pretty familiar to src i.e it gives us the feel of a source URL.
  2. Secondly, Javascript has a dedicated dataset property to fetch data- attributes.

Therefore although we can use any other attribute we like, data-src sounds similar to src and has good JavaScript support.

And this is all we need to do in the HTML phase of developing our lazy loader.

A talk on CSS

Talking about the CSS in lazy loading, there isn't really much to discover in it for now.

It's only when we discover responsive lazy loaders that CSS coding will show up, where we'll give a background-color to .lazy elements to indicate that the vacant space is for an image about to be loaded. Without a background-color users might take the white space as white space only.

Then, in the Lazy Loading Fade Effect chapter, we shall use CSS transitions to give a smooth fading effect to images as they load into view.

Moving on

So by this point we have successfully laid out the stucture we will need for our lazy loader, before moving over to its scripting. In the next chapter we will discover the actual logic behind the operation of this loader and construct an extremely simple example with just a single image to lazy load.

We consider a single image to let things remain manageable at this novice stage; things can get quite complex when multiple images are to be dealt with.

Anyways it's always good, and in fact the best way, to start from simpler and smaller problems and build up from them to solve a more complicated one. We can't really approach a tangled task right away!

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage