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.
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.
Let's take the example of a single image, and see how would it be lazily loaded, step by step.
- We start by calculating the offset of the image, from the bottom edge of the viewport.
- Then, we assign a scroll listener to
window, to track the scroll bar's position against the image's offset calculated in step 1).
- When the image comes into view i.e it enters the viewport, its corresponding listener is removed from the listener stack of
windowand 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.
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:
- Will the lazy image have a container element?
- How many containers will the image have — 1, 2, 3, how many?
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:
One thing done - over to the next one..
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.
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
The idea is that we fetch all the elements with the class
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
data-lazyattribute, 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>
.lazy class is given to the
<div> element, while the
.lazy_img class is given to the underlying
Now we know rightaway that this image ought to be lazy loaded. Perfect!
The next thing we need to do in our lazy loader's HTML is to remove the
src attribute from the
Can you think why we need to do this?
srcattribute from a lazy image?
srcattribute is present on an
<img>element, is gets loaded rightaway, 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.
Well, the solution is pretty simple.
Firstly, the source of the image is saved in another attribute. The most suitable and sensible one is
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
Why we use the
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
- Firstly the name sounds pretty familiar to
srci.e it gives us the feel of a source URL.
datasetproperty to fetch
Therefore although we can use any other attribute we like,
data-src sounds similar to
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.
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 rightaway!