Lazy Loading for images Implementation

I m sure many of you aspiring web devs must have come across performance improvement techniques to improve loading time of web page. It is necessary to have non-blocking resources in your web page so that the content is not blocked. By default HTML and CSS are blocking resources. Apart from that JS can be made non-blocking by loading it asynchronously or loading it in the end, when all resources are loaded and rendered. If your website is really interactive, it will be full of images. And these images eat up most of ur loading time. So lazy loading is needed for this purpose, such that only those images have network call that are present in viewport, while others are not. You will notice a fair amount of improvement after implementing this.

I have come up with a lazy load implementation with jquery and some Javascript.

function lazyLoadOnScroll() {

// image array
this.images = [];
this.lazyCount = 0; // keeps the count of elements legible for lazy load

// fetches the elements with lazy class and filters the one that are not to be displayed on mobile
var query = document.querySelectorAll(‘.lazy’);
for( var i=0; i< query.length; i++) {
if (query[i].getAttribute(‘data-background’)) {
images.push(query[i]);
lazyCount++;
}
}

// checks if the image exists inside the viewport and is visible
this.isElementInViewport = function(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
$(el).is(‘:visible’)
)
}

// sets background images of div or src of image tag
this.loadImage = function(el, callback) {
var imgSrc = el.getAttribute(‘data-background’);
if (imgSrc) {
if ($(el).hasClass(‘lazy_parent’)) {
var imageTag = el.querySelector(‘img’);
if (imageTag.getAttribute(‘src’) !== imgSrc) {
imageTag.setAttribute(‘src’, imgSrc);
}
}
else {
el.style.backgroundImage = ‘url(‘ + imgSrc + ‘)’;
}
}
$(el).removeAttr(‘data-background’);
callback ? callback() : null
}

// initializes the lazy load
this.lazyInit = function() {
for (var i = images.length-1; i>=0 ; i–) {
if (isElementInViewport(images[i])) {
loadImage(images[i], function() {
images.splice(i,1);
lazyCount–;
});
}
}
}

if (lazyCount > 0) {
lazyInit();
}

// touchmove for mobile devices and executes lazy load on scroll for other devices
scrollEvent = $(window).on(‘scroll touchmove’, function(event) {
lazyInit();
if (lazyCount == 0) {
$(this).unbind(event);
}
});
}

Now lets go part by part of this function lazyLoadOnScroll().

Here is the working demo for this code..

To load the image using lazy load, add ‘lazy‘ class on the element. If you are using image tag then on the parent div add ‘lazy’ class and along with that add ‘lazy_parent‘ class. Its that simple. Now the key here is we dont specify the background-image property or add image src attribute for images. Coz on adding this property the images network call will be done. The key is to store the image url in data-background tag and load the images after the JS is loaded and load the images using JS. This will help you in avoiding unnecessary network call at the time of page rendering.

First of all I have defined an image array (images) that will contain all the elements containing lazy class. Next variable is lazyCount which stores the count of all elements having data-background set to some url. In the next section I m just fetching the elements having lazy class and pushing those elements in image array while increasing the count for same.

Next up is a function that checks whether the image is present in viewport or not. This function is the key to decide whether to load the image or not to. It uses the bounding rectangle property of Javascript element, to get the height, width, top, bottom, left and right. Along with that it also confirms if the element is visible or not. Coz there are some scenarios in which you would need to show some images dynamically and would not show images in the beginning. Then you would have to trigger the lazyLoadOnScroll function on the show event of image.

Next function loads the image or you could say makes the network call for that image, by adding that image’s src or setting the background-image property if its not an image element. Like I said earlier just add the ‘lazy_parent‘ class on the parent div of image tag. It reads that and changes the src of child image tag. In the end it deletes the data-background property and pops that element from ‘images‘ array, which is provided in the callback function.

Now we just have to iterate through all array elements and check if it exists in the viewport and load images accordingly. On page load the function should be triggered. So call the lazyLoadOnScoll function() on page load event. LazyInit is called to initialize the lazy load. Now to idea was to load images only if they are present in viewport. So on window scroll event i have attached the function to load images. And on completion of loading of images the event is removed.

Now for CSS i would like you add transition property on your element with lazy class just to give it a bit of fade effect.

.lazy {
-webkit-transition: background-image 0.3s ease-in-out;
-moz-transition: background-image 0.3s ease-in-out;
-o-transition: background-image 0.3s ease-in-out;
transition: background-image 0.3s ease-in-out;
background-position: center top;
background-repeat: no-repeat;
}

Thanks for reading this post. If you find it useful do comment.

 

 

Iteration with deletion in Array JS

We often face a basic problem of iteration through an array in Javascript, but recently when i was implementing lazy load in my app, I came across a problem in which i had to delete the element parallely as i was iterating through the array, to avoid running two separate loops for both. So there are 3 approaches that i tried which i will explain below, and only one of them is a correct way to do it.

1.  Running a basic for loop from i=0 to i= array.length

var dummyArr = [1,2,3,4,5,6]

for (var i=0; i< dummyArr.length; i++) {

if ( dummyArr[i] == 2 || dummyArr[i] == 3) {

dummyArr.splice(i,1);

}

}

Now this will give output as dummyArr = [1,3,4,5,6]. It skips the ‘3’ coz after splice is called the indexes in array are modified, just for starters splice is used for deleting element in array (Array Splice). So that element was skipped

2.  Next approach that i tried was using forEach method ( forEach ) in JS. It also does the same thing as basic for loop, but in my conquest towards becoming a better JS dev. I try to use some new and cool things for my code to look better.

var dummyArr = [1,2,3,4,5,6]

dummyArr.forEach(function( elem, index) {

if (elem == 2 || elem == 3) {

dummyArr.splice(index,1);

}

});

So like I said it does the same thing as a basic for loop, nothing fancy. It ignores the elements that are newly appended and skips the indexes that have been deleted.

Output — dummyArr = [1,3,4,5,6]

3.   Last and apt approach was running a reverse for loop through the array.

var dummyArr = [1,2,3,4,5,6]

for (var i=dummyArr.length -1; i>=0;  i–) {

if ( dummyArr[i] == 2 || dummyArr[i] == 3) {

dummyArr.splice(i,1);

}

}

Output — dummyArr = [1,4,5,6]

Now in this case on deletion of element and modification of index the upcoming element’s position and value are not compromised which was happening in previous cases. So the element can be deleted as u desire it to be.

 

Thanks for going through the post.. Will come back with a new post soon.

Adios

Event Bubbling and Event Capturing in Javascript

Most of us use event handlers in javascripts be it onclick, onmousedown or any other random event handlers.

Behind the scene, how does javascript deal with them. Lets see.

I will go through some examples which will make it easier for you to understand the concept behind event bubbling and event capturing.

Suppose we have a nested div structure.

Event Bubbling: In javascript when an event is triggered on an element inside DOM, the click gets triggered to its next parent until it reaches the topmost element i.e. html.  This is event bubbling. The event bubbles up the DOM. So when we click on the div with id “three” in above code. The onclick event is fired on “three“, “two” and at last “one“. So we will get inside console the output

three

two

one

Now the target element stays the same i.e. div with id “three“. But event pops up to next parent which can be accessed using ‘this‘.

Event Capturing:  Event capturing is just the opposite of event bubbling, the event starts from topmost parent till it reaches the target element. So on clicking the inside most div i.e div#three. We will get the following response.

one

two

three

Now in javascript if you want to specify whether to use event capturing or event bubbling for event handler. We will concentrate on the third parameter inside addEventListener.

addEventListener(“eventName”, EventHandlerfunction(), boolean);

If you specify it as true you will get event capturing for your event handling while if you specify it as false you will get event bubbling.

Thanks for reading this post.