How to delay the download of images with Flojo.js, a very lightweight jQuery script for loading images lazily

Throughout this article, we'll cover how to create a very lightweight jQuery script for delaying the download of images. Let's name the script 'Flojo.js', which is a spanish word that means the same as lazy.

See a Demo and Download the Code

If you want to see now how the script works, please click on see a demo. Likewise, if you want to download the source code, please click on download the source code to go to my Github page.

Why should I use this script?

Sometimes (mainly when a page is very long and has many images) there's no need to download all of them at once. That's the case of, for example, newspapers.

Often, newspapers have a very long frontpage with hundreds of heavyweight images. Since the latest news are located at the top and considering that very few visitors use the wheel on the mouse to scroll down the screen to the very bottom of the page, it makes little or no sense to request all the images to the server.

Instead, Flojo.js allows us to instruct the browser to download the images as visitors scroll down/up the page. Thus, we get the strictly necessary images and prevent the server from sending a lot of unnecessary information.

Please look closely at the following HTML5 document:

<!DOCTYPE html>
<lang="en">
<head>
<-- saved from url=(0014)about:internet -->
  <meta charset="utf-8">
  <title>Demo page for Flojo.js</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
  <style type="text/css">
    .img-center {
      margin-bottom: 20px;
      margin-left: auto;
      margin-right: auto;
    }

    .jumbotron {
      margin-top: 20px;
    }

    .col-lg-12 p {
      margin-bottom: 20px;
    }

    .col-lg-12 h4 {
      text-transform: uppercase; 
    }
  </style>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
  <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js" type="text/javascript"></script>
  <!-- Support for IE8 to understand HTML5 semantic elements and media queries -->
  <!--[if lt IE 9]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/respond.js/1.3.0/respond.min.js" type="text/javascript"></script>
  <![endif]-->
  <script src="js/flojo.min.js" type="text/javascript"></script>
</head>

<body>
  <div class="container">
    <div class="header">
      <ul class="nav nav-pills pull-right">
        <li class="active"><a href="#">Home</a></li>
      </ul>
      <h3 class="text-muted">FLOJO.js</h3>
    </div>
    <div class="jumbotron">
      <h1>Flojo.js, a very lightweight jQuery script for loading images lazily.</h1>
      <p class="lead">This script instructs the browser to delay the download of images. Thus, you get the strictly necessary images and prevent the server from sending a lot of unnecessary information.</p>
    </div>
    <div class="row">
      <div class="col-lg-12">
        <h4>This is how it works!</h4>
        <p>This document contains a string of images placed one under another. If you attach Flojo.js to your HTML documents, the browser will be instructed to download the images as visitors scroll down/up the page. In order to fully appreciate how it works, you can use your browser's development tools.</>
       <img src="img/lightgray.jpg" data-src="img/brown.jpg" class="img-responsive img-center" alt="brown box" />
       <img src="img/lightgray.jpg" data-src="img/darkgray.jpg" class="img-responsive img-center" alt="dark gray box" />
       <img src="img/lightgray.jpg" data-src="img/darkgreen.jpg" class="img-responsive img-center" alt="dark green box" />
       <img src="img/lightgray.jpg" data-src="img/darkorange.jpg" class="img-responsive img-center" alt="dark orange box" />
       <img src="img/lightgray.jpg" data-src="img/darkred.jpg" class="img-responsive img-center" alt="dark red box" />
       <img src="img/lightgray.jpg" data-src="img/gold.jpg" class="img-responsive img-center" alt="gold box" />
       <img src="img/lightgray.jpg" data-src="img/indigo.jpg" class="img-responsive img-center" alt="indigo box" />
       <img src="img/lightgray.jpg" data-src="img/yellow.jpg" class="img-responsive img-center" alt="light yellow box" />
       <img src="img/lightgray.jpg" data-src="img/lime.jpg" class="img-responsive img-center" alt="lime box" />
       <img src="img/lightgray.jpg" data-src="img/lt_turquoise.jpg" class="img-responsive img-center" alt="light turquoise box" />
      </div>
    <div><!-- end of .container -->
  </body>
</html>

As you can see, there is a string of images in the document body and each has a data-src attribute.

The 'data-*' attribute is new in HTML5 and allows us to embed custom data on all HTML.

What we are going to do is use this new attribute for storing a relative path to the image we want to load lazily. In addition, the value of the src attribute of every <img> tag must be a relative path to the image we want to display by default. This is everything a user needs to know to run the script properly.

Flojo.js

Now it's time to explain a little more in depth how the script works. This is what Flojo looks like under the hood:

The minified version of this script is less than 0.5 KB

/**
 * Title: Flojo.js
 * Created by: Jesús Heredia Reboira
 * Contact: http://www.jesusheredia.info
 *
 * Flojo.js is a very lightweight jQuery script for loading images lazily.
 * https://github.com/jesus-heredia/flojo
 *
 * Licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
 * http://creativecommons.org/licenses/by-sa/3.0/
 */

$(document).ready(function() {
  // Window object.
  var $win = $(window),

  // Threshold (in pixels). The number of pixels as a minimum between our
  // current position and the following image(s) to be loaded lazily.
  // 200 is the value by default, but you can use the one you like the most.
  th = 200,

  // Is this the first time that flojo() is called by another piece of code?
  // True is the value by default.
  first_time = true;

  function flojo() {

    if (first_time == false) {

      var $images = $('img:not([data-src="swapped"])');

    } else if (first_time == true) {

      var $images = $('img[data-src]');

      first_time = false;

    }

    // The height of the window.
    var  win_height = $win.height(),

    // Scrolling from the top of the page.
         win_scroll_top = $win.scrollTop();

    $images.each(function() {

      // The height of this image.
      var img_height = $(this).height(),

      // The position of this image from the top of the document.
          img_offset_top = $(this).offset().top;

      if (img_offset_top + img_height >= win_scroll_top - th &&
          img_offset_top <= win_height + win_scroll_top + th) {

        var new_image = new Image();

        new_image.src = $(this).attr('data-src');

        $(this).attr({

          src: new_image.src,

          'data-src': 'swapped'

        });

      }
            
    });

  }

  flojo();

  $win.scroll(flojo);

  $win.resize(flojo);

});

As you can see, flojo() is called not only when the document is ready, but also every time the browser window is scrolled or resized.

First, it carries out a search of images according to the value of the variable first_time. If it is equal to TRUE (the first time that the function is called), then every image with the data-src attribute must be stored; if it is equal to FALSE, then only the images whose 'data-src' attribute is not equal to 'swapped' must be stored. 'Swapped' is the word used by the script to flag the images that have already been swapped.

Once the search of images is over, the script iterates through each image to check if the image should be loaded lazily.

The following piece of code is the condition to be evaluated in order to run the code for swapping the images. If the condition is TRUE, then the code swaps the images; if it is FALSE, then the code does nothing with that particular image.

if (img_offset_top + img_height >= win_scroll_top - th &&
    img_offset_top <= win_height + win_scroll_top + th)

The previous two conditions must be TRUE in order for the code to swap the images. In addition, this is, without a doubt, the most complicated part about this script. Let's explain the meaning of each variable:

  • win_height: This variable stores how high the browser window is.
  • win_scroll_top: This variable stores how many pixels the browser window has been scrolled down from the top of the page.
  • th: This variable stores the threshold to be used to load the images lazily. In other words, the number of pixels between our current position and the following image(s) to be loaded lazily.
  • img_height: This variable stores the height of a particular image.
  • img_offset_top: This variable stores the position of a particular image from the top of the page.

If the previous conditions are TRUE, a new variable is created ('new_image') to store a new image object; the value of the data-src attribute of the <img> tag that's being processed is assigned to the 'src' attribute of the new image; lastly, the value of the src attribute of the <img> tag that's being processed is swapped for the new one and the value of its data-src attribute is changed to 'swapped', which is a mark for the script to work properly on the other images that have not been swapped yet.