LazyLoad Images in Simple Steps for Casper Theme on Ghost
LazyLoad is a concept to defer the loading of assets until they are needed. Let's implement simple LazyLoad for Images & make Casper for Ghost do lazyload.
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 following to build lazyload for image:
- build/find a small size image which act as placeholder and loading indicator
- show placeholder for image and store the image info in other way like attribute or data-*
- Now the JS code will evaluate whether the picture is going to be in front of 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 lets see things step by step for a 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="https://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="https://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 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: https://github.com/time2hack/casper/blob/t2h/index.hbs
Conclusion
LazyLoad will definitely improve the sitespeed. What do you think about it? Let me know through comments ? or on twitter at @heypankaj_ and @time2hack.
If you agree, share this article with others ?