LazyLoad is a concept where we defer the loading of the assets until they are needed; generally Images.
In this article, we will see how to defer the loading of the Images and how to make Casper theme on Ghost blog do the lazyload.
In simple steps; we need to do the following to build lazyload for image:
- build/find a small size image which acts as placeholder and loading indicator
- show the placeholder for image and store the image info in another way like attribute or data-*
- Now the JS code will evaluate whether the picture is going to be in front of the user
- If yes; replace the image from attribute to main src or whichever way it is needed
- If No, let it be like as it was
So let's see things step by step for an img
tag which will be lazy loaded.
Step 1: Placeholder
let's consider following image for placeholder:
And the img tag we will be using is:
<img
alt=''
src="https://images.unsplash.com/photo-1517232875856-75e69324a4f9?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ&s=d5655cd81179d3df9288b7993f300358" />
Let pit the placeholder on it
<img src="http://via.placeholder.com/400x250?text=Loading..." class="lazyload" />
Step 2: Image Info
Let's add the correct image info with data-src
attribute
<img
class="lazy"
src="http://via.placeholder.com/400x250?text=Loading..."
data-src="https://images.unsplash.com/photo-1517232875856-75e69324a4f9?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ&s=d5655cd81179d3df9288b7993f300358" />
Step 3: Check if img is in viewport
To check if the image is in the viewport, we will need to watch the scroll movements.
It can be done by attaching the scroll
event listener which will evaluate the position.
function onScroll() {
// check the positions here
}
window.addEventListener('scroll', onScroll, {passive: true});
Now in our onScroll
function, we will collect all the lazyload elements i.e. img
tags with class lazy
document.querySelectorAll('img.lazy');
Anf now we will loop over the collection and see their position. The position of an element is obtained by the DOM function getBoundingClientRect()
in the Element. This function returns DOMRect object which has following structure:
The important property for our current use case is top
and bottom
And we need the window's height to know our visual anchor point. We will have our calculation point with window.innerHeight
Now we can determine if the element is entering from bottom by following condition:
var pos = el.getBoundingClientRect();
if (pos.top <= window.innerHeight) {
//
}
But this element can be way above and not actually be inside viewport; we can check that by adding following condition with top to be non-negative
if (pos.top <= window.innerHeight && pos.top > 0) { }
And similarly if the element enters from top:
var pos = el.getBoundingClientRect();
if (pos.bottom >= 0 && pos.bottom <= window.innerHeight) { }
And combining both of them; it looks like as follows:
function inViewport(pos) {
return (pos.top <= window.innerHeight && pos.top > 0)
|| (pos.bottom >= 0 && pos.bottom <= window.innerHeight);
}
And our onScroll
function can go like this:
function onScroll() {
var lazyEls = document.querySelectorAll('img.lazy');
for (var i = 0; i < laxyEls.length; i++) {
var el = lazyEls[i];
var pos = el.getBoundingClientRect();
if (inViewport(pos)) {
//
}
}
}
Step 4: Load image if needed
Loading image is quite simple part, we just need to update the src
and remove the lazy class from the element; which can be done as follows
function lazyLoad(el) {
el.src = el.getAttribute('data-src');
el.classList.remove('lazy');
}
And putting it inside the onScroll it will go like:
function onScroll() {
var lazyEls = document.querySelectorAll('img.lazy');
for (var i = 0; i < laxyEls.length; i++) {
var el = lazyEls[i];
var pos = el.getBoundingClientRect();
if (inViewport(pos)) {
lasyLoad(el);
}
}
}
LazyLoad in Casper for Ghost
In casper, the main concept stays same; thought the approach changes a little bit because the images are added as background-image
to div
rather than src to the img
In casper, for the homepage, the element we are looking for is having the class post-card-image
and on the single post page, the element is with class post-full-image
.
These elements will have the featured image set as the background-image. So we update the src in template as:
{{#if feature_image}}
<a class="post-card-image-link" href="{{url}}">
<div class="post-card-image lazy" data-src="{{feature_image}}"></div>
</a>
{{/if}}
And the lazy class will have out placeholder as the background image. And our lazy collection and the lazyLoad function will update as follows:
document.querySelector('.lazy');
function lazyLoad(el) {
el.style.backgroundImage = 'url(' + el.getAttribute('data-src') + ')';
el.classList.remove('lazy');
}
Rest remains same and this will enable the lazyLoad on the featured images of your casper theme.
If you are more curious about how it is done on this site Time to Hack; you can check it out here: github.com/time2hack/casper/blob/t2h/index...
Conclusion
LazyLoad will definitely improve the sitespeed. What do you think about it? Let me know through comments 💬 or on twitter at @patelpankaj and @time2hack.
If you agree, share this article with others 🗣