Lazy Load Image & WordPress Gravatar using JavaScript / JQuery

page-load-speed-viralpatel
A webpage load time plays a very crucial role in SEO and impacts your website ranking significantly. Google has already incorporated site speed as one of the several signals that it uses to determine search ranking. The faster your website loads the better it will perform in google ranking.

There are several things one can do to improve overall site speed. Like:

  1. Reduce number of http queries by combining JS and CSS files
  2. GZip all the content before serving the request.
  3. Compress images on web page with tools like http://tinypng.com
  4. And many more…

Reference: Compress PHP, JS, CSS for Website Optimization

You may already have implemented most of these steps in your website. But there is always a room for optimization :)

Just take one of your site page URL and use Google Pagespeed tool to determine overall PageSpeed score. This tool and its plugin for firefox and chrome can be used to analyze several performance related aspects of a webpage.

Among several suggestions that PageSpeed offers, one particular is of our interest which is serving multiple HTTP requests. If your webpage has too many external JavaScripts / CSS files or too many image files, the browser will send a unique http request for each of those content during page load. This reduces the performance significantly there by impacting your seo.

Although you can combine and compress all JS and CSS files to reduce number of http requests, but that would not help much if your webpage has several images.

The first step in reducing number of Image requests is to combine all the small image icons in one big image sheet using a simple technique called CSS Sprites. I strongly suggest you to use CSS Sprite if you are not already doing this on your website.

Lazy Image Loading

After doing all these changes still if you have lots of images on your website, you might want to lazy load these images. Meaning load the image only when it is being viewed there by deferring http call until user is watching the image.

This technique can be very much useful in websites which display list of products with images. User can scroll through list and instead of loading all the product images while loading webpage, each image is loaded asynchronously when it comes in users viewport.

Now to achieve this we use simple JQuery snippet:

Step 1: Change <IMG> tag

First change the <img> tag for which you want to lazy load image to:

<!-- Before: -->
<img src="http://foo/product.png" width="300px" height="200px" />

<!-- After: -->
<img src="blank.gif" class="lazy" data-src="http://foo/product.png" 
			width="300px" height="200px" />

Notice how we moved our image url from src to data-src attribute. Also we added blank.gif which is 1 px transparent gif file in src attribute. We also added a CSS class lazy to the image. This is to mark the image for lazy loading in jquery code which we will see shortly. Also do not forget to mention height and width attribute with <img> tag. This make sure browser leave enough space around blank.gif to load original image.

Now on pageload, browser will load blank.gif file instead of product.png thus making http request more faster. If you have several images with src=blank.gif, browser will load it just once and use it from cache.

Step 2: The JQuery Code Snippet

We want to load original image once that image is in browsers visible area. Following JQuery snippet do this task.

(function($) {
	var lazyloadImage = function(){

		$('img.lazy').each(function(){
			var distanceToTop = $(this).offset().top;
			var scroll = $(window).scrollTop();
			var windowHeight = $(window).height();
			var isVisible = distanceToTop - scroll < windowHeight;
			if (isVisible) {
				$(this).attr('src', $(this).attr('data-src')).removeClass('lazy');
			}
		});

	};

	$(function(){
		$(window).scroll(function() {
			lazyloadImage();
		});
	});

	lazyloadImage();

})(jQuery);

In this snippet we hook a function to browsers scroll event. For each image that has lazy css class on it, we iterate and see if the image is in visible area. If it is then we just copy value from image’s data-src to its src attribute. Once the image’s src attribute changes, browser will automatically load its content. Note how we remove lazy class once the image is loaded to avoid further changes in DOM.

Online Demo

Check the online demo: Online Demo

Further Optimization using data URI

The above image lazy loading technique reduce the page size significantly during the load time, but it still performs an http request on blank.gif. If somehow browser is not caching blank.gif image, then it will unnecessarily perform multiple http requests for it for each lazy image.

We can further optimize this by using Data URIs. It is a technique by which image data is directly embedded into document using some URI instead of linking external images within element or background-image in CSS.

The biggest reason why you want to do this is to save HTTP Requests.

Just change the <img> tag we saw previously to:

<!-- Before: -->
<img src="blank.gif" class="lazy" data-src="http://foo/product.png" 
			width="300px" height="200px" />

<!-- After: -->
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" 
		class="lazy" data-src="http://foo/product.png" 
			width="300px" height="200px" />

So intead of linking to external image blank.gif, we embedded its content in src attribute of <img> element using data URI. This will significantly reduces number of http requests in your webpage.

SEO Consideration

Lazy loading images would improve site speed significantly. But it also makes images less crawlable. Search engines like Google indexes the images from a website and display those in Image search. So if your website’s main source of traffic is image search than you may not want to implement this optimization technique.

But as I said earlier, there is always room for optimization. You can keep main images on your webpage as it is and make other less significant images to load dynamically. For example the Avatar images that you display in each comment. These images are not significant for indexing. A blog article can be showing 50 comments each with an Avatar image. Instead of loading 50 images during pageload, you can make them lazy by using our lazy code snippet.

Gravtar Images on WordPress

WordPress uses something called Gravtar to show user avatar image in each comment. If a user does not have an avatar setup in gravtar.com then a default gravtar will be displayed.

Lazy loading these hundreds of gravtar images in a wordpress blog should definitely increase site speed by reducing all those http requests. Another advantage of doing this is to reduce HTTP redirect requests at pageload time. See below screen shot of Google PageSpeed.

google-pagespeed-redirect

Notice how each http request generates an redirect request that browser must follow to load that image!! This is due to the fact that if a user has not setup gravtar image, then a default image will be displayed. You don’t want to load those 50 avatar images during load time and follow the redirects in case user doesnt have a gravtar.

So why not change our wordpress comment template and load these avatar images lazily.

If you check your wordpress theme’s comment template, you’ll notice a function get_avatar() which is used to display user avatar image.

//comment template
..
	<?php get_avatar( $comment, 50 ); ?>
..

WordPress’s get_avatar function will create an <img> tag with users avatar URL. We need to change the src attribute to data-src and use data-uri as mentioned above in src attribute.

But as wordpress does not provide users gravtar URL directly, we need to parse this information from get_avatar and use it to create our own <img> tag with lazy attribute.

Add below function in your wordpress theme’s functions.php file.

theme/…./functions.php

function get_avatar_url($get_avatar){
    preg_match("/src='(.*?)'/i", $get_avatar, $matches);
    return $matches[1];
}

And then use get_avatar_url function in comment template like:

//comment template
/* Before **
	<?php get_avatar( $comment, 50 ); ?>
*/

/* After */
..
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="
	width="50" height="50" class="lazy"
	data-src="<?php echo get_avatar_url(get_avatar( $comment, 50 )); ?>" />

This should render an avatar image with lazy attributes. Now combining this with our jquery code should load all the gravtar images in comment section asynchronously.

I Don’t Want To Use JQuery

If you are not keen on using JQuery snippet as you might not using JQuery alltogether in your website! No worries. Following JavaScript code from Lorenzo Giuliani does exactly same thing.

/* lazyload.js (c) Lorenzo Giuliani
 * MIT License (http://www.opensource.org/licenses/mit-license.html)
 *
 * expects a list of:  
 * `<img src="blank.gif" data-src="my_image.png" width="600" height="400" class="lazy">`
 */
 
!function(window){
  var $q = function(q, res){
        if (document.querySelectorAll) {
          res = document.querySelectorAll(q);
        } else {
          var d=document
            , a=d.styleSheets[0] || d.createStyleSheet();
          a.addRule(q,'f:b');
          for(var l=d.all,b=0,c=[],f=l.length;b<f;b++)
            l[b].currentStyle.f && c.push(l[b]);
 
          a.removeRule(0);
          res = c;
        }
        return res;
      }
    , addEventListener = function(evt, fn){
        window.addEventListener
          ? this.addEventListener(evt, fn, false)
          : (window.attachEvent)
            ? this.attachEvent('on' + evt, fn)
            : this['on' + evt] = fn;
      }
    , _has = function(obj, key) {
        return Object.prototype.hasOwnProperty.call(obj, key);
      }
    ;
 
  function loadImage (el, fn) {
    var img = new Image()
      , src = el.getAttribute('data-src');
    img.onload = function() {
      if (!! el.parent)
        el.parent.replaceChild(img, el)
      else
        el.src = src;
 
      fn? fn() : null;
    }
    img.src = src;
  }
 
  function elementInViewport(el) {
    var rect = el.getBoundingClientRect()
 
    return (
       rect.top    >= 0
    && rect.left   >= 0
    && rect.top <= (window.innerHeight || document.documentElement.clientHeight)
    )
  }
 
    var images = new Array()
      , query = $q('img.lazy')
      , processScroll = function(){
          for (var i = 0; i < images.length; i++) {
            if (elementInViewport(images[i])) {
              loadImage(images[i], function () {
                images.splice(i, i);
              });
            }
          };
        }
      ;
    // Array.prototype.slice.call is not callable under our lovely IE8 
    for (var i = 0; i < query.length; i++) {
      images.push(query[i]);
    };
 
    processScroll();
    addEventListener('scroll',processScroll);
 
}(this);

Just add above JavaScript code in your site and you are good to go.

Try this in your site today and see how much score you gain on Google PageSpeed :)

Check the online demo: Online Demo



One Comment

  • rakesh kumar 5 July, 2014, 17:41

    Dear Viral Patel, I am facing a very weired problem while implementing this on WordPress based blog at http://www.binarynote.com, on single page. sometimes it display gravatar as it was intended but some times it display a only a blank. I am not able to solve this issue can you see my code and if possible guide me to resolve this issue.

    ~rakesh kumar

Leave a Reply

Your email address will not be published. Required fields are marked *

Note

To post source code in comment, use [code language] [/code] tag, for example:

  • [code java] Java source code here [/code]
  • [code html] HTML here [/code]

Current day month ye@r *