Use AJAX and jQuery to Dynamically Load Images Once In View

Sooner or later you'll need to create a page or document that holds far more images than you think is wise to display. A standalone image loads itself fine - but too many of them can wreak havoc on the amount of time it takes your page to load. In this tutorial I'll show you a quick way to delay loading these images until the end user scrolls them into view.

Just need to take a look at the code? Check out this JSFiddle below (click on "Result" to see the completed effect):

Want to load images dynamically in your project? You can grab a copy of the source on GitHub and include the javascript in your page (It's all ready to go; don't forget jQuery): https://github.com/zachalam/ajax-load-images

Let's take a look at what's going on here! To give our script some kind of "alert" that we want to take an image tag and load an image dynamically we simply add an additional attribute. In this case it's "data-async-load" and it holds the image resource we'd like to load. As a placeholder, we load a loading icon by default.

<img src="loading-icon.gif" data-async-load="image-to-load.jpg">  

Next we create the skeleton of our jQuery add-on.
1) Complete a DOM check - $(document).ready().
2) Create a empty function that will hold our work - load_images_in_view.
3) Determine when the function will be invoked - when document loads, and when user scrolls.

// verify the DOM is completely loaded.
$(document).ready(function() {

// check for images in view when page loads.
load_images_in_view();  
// check for images in view when page scrolls.
$(window).scroll(load_images_in_view); 


function load_images_in_view()  
{


} // end function

}); // end $(document).ready();

Within our load_images_in_view function we'll first need to do a simple calculation to determine the bottom position of the window. This can be done easily with by adding the distance to the top of the document with the height of the window.

function load_images_in_view()  
{
  // calculate the bottom of the window.
  var window_bottom = $(window).scrollTop() + $(window).height();

} // end function

Once we have this useful piece of information we can go ahead and loop through each image on the page and calculate where the "bottom" of each image appears. I've divided $(this).outerHeight() by 2, so that the image will start loading once it is halfway in view.

function load_images_in_view()  
{
  // calculate the bottom of the window.
  var window_bottom = $(window).scrollTop() + $(window).height();

  // a scroll was detected - check all images in DOM
  $("img").each(function() {
    // calculate the halfway to the bottom of the object.
    var object_bottom = $(this).offset().top + ($(this).outerHeight()/2);

  }); // end $("img").each()  

} // end function

Now that we have the position of the image AND the position of the view. We can do a simple comparison to see if this particular image is within view.

function load_images_in_view()  
{
  // a scroll was detected - check all images in DOM
  $("img").each(function() {
    // calculate the halfway to the bottom of the object.
    var object_bottom = $(this).offset().top + ($(this).outerHeight()/2);
    // calculate the bottom of the window.
    var window_bottom = $(window).scrollTop() + $(window).height();

    if(window_bottom > object_bottom)
    {      
      // image is now in view    
    }

  }); // end $("img").each()  
} // end function

Within our conditional statement we set the image source the value stored in data-async-load. We also do a little bit of extra work and create a new attribute called data-image-loaded on the element. This prevents the image from being loaded over and over again each time the page is scrolled.

if($(this).data("image-loaded") != true)  
{
  // obtain the image to replace
  var image_source = $(this).data("async-load");

  // set image loaded complete flag, and change image src.
  $(this).data("image-loaded",true);
  $(this).attr("src",image_source);
} 

If you'd like to grab a copy and play around with the code I've made it super easy and ported a copy to JSFiddle. You can access it publicly here: https://jsfiddle.net/zachalam/89wxmzeo/