Ajax control

Generic Activity Indicator for Ajax Requests

Over the weekend I was fiddling was some code to see how to make a generic activity indicator for Ajax requests. I’m sure most of you have seen these but if not, here’s one similar to what Facebook uses:

While it’s may not look like much, that animated .gif is pretty important because it tells a user that something is happening and that results will appear shortly. The last thing you want is for a user to think the page has locked up or broken and using this simple method advises them that their request is being processed.

Targeted Indicators

In my case, I was looking for a way to set an indicator within the specific DOM element that would be affected by the XHR call. So if my XHR call was to set the contents of a specific DIV, I wanted the indicator to render within that specific DIV only (similar to what Facebook does).
First, I created three DIVs and 3 anchor tags:
<a href="#">Box 1a>|<a href="#">Box 2a>|<a href="#">Box 3a>
 
<br /><br />
 
<div id="box1"div>
<div id="box2"div>
<div id="box3"div>
and I set the DIVs to a specific width and floated them left:
#box1, #box2, #box3 { background-color:#E5EECC; border:1px solid #D4D4D4; width: 200px; float:left; };
Next, I created a new method called fillBox() that would take the target element’s ID and use that to dynamically create a new IMG tag and insert the activity indicator. What I decided to do was leverage jQuery’s global Ajax events to create the generic handlers for this. Using ajaxStart(), which fires off when an Ajax request is initiated, I dynamically create the tag for my activity indicator and inset it using the prepend() method:
$(ele).ajaxStart( function() {
 
    $(this).prepend( "" );
 
})
Then using the Ajax global event handler ajaxStop(), I would easily delete the indicator once the Ajax request was done by using the remove() method to delete the IMG tag from the DOM:
ajaxStop( function() {
 
    $("#throbber").remove();
 
});
Lastly, using some event delegation, I created an event handler for the clicks on the anchor tags:
$( "a" ).click( function(e) {
 
    var el = '';
    switch(e.target.text) {
        case "Box 1":
            el = '#box1';
            break;
        case "Box 2":
            el = '#box2';
            break;
        case "Box 3":
            el = '#box3';
            break;
    }
 
    fillBox( el );
 
});
The handler simply looked for the anchor tag’s text and based on that filled a local var with the ID that would be targeted for the indicator. It then passed that ID to the fillBox() method.

A Little Roadblock

I thought I was done and the code look pretty straightforward. When I ran this, though, I experienced some odd behavior:

When I would click on the “Box 1″ link, the indicator would correctly appear in the first DIV followed by the data populating the box. When I would next click on the “Box 3″ link, though, both the first box AND the third box would have the indicators displayed! Not good.
I looked into it and got feedback from Adam Sontag who provided the answer I needed. I had incorrectly assumed that ajaxStop() would unbind the jQuery global Ajax events from the attached element. Unfortunately it doesn’t so by using the unbind() method, I was able to resolve the issue.
    $.ajax({
      url: 'test.php',
      cache: false,
      success: function(data) {
 
        $(ele).html( data ).unbind( 'ajaxStart' ).unbind( 'ajaxStop' );
 
      }
    });
 
};

The Solution Gets Easier

 

Then I got more feedback from love heda which really streamlined the whole thing. He recommended forgoing the Ajax global events altogether and simply inserting the indicator via my fillBox() method directly and since I was replacing the contents of the DIVs with the html() method, there was no need to use remove() since the IMG tag was already being removed.
var fillBox = function( ele ) {
 
    $(ele).prepend( "" );
 
    $.ajax({
        url: 'test.php',
        cache: false,
        success: function(data) {
 
            $(ele).html( data );
 
        }
    });
 
};
So while I initially was looking to use jQuery’s built-in global Ajax event handlers for making this generic, I’m finding Karl’s solution much cleaner. It requires less event handlers and method calls to do the same task. Love it!
Here’s the end result and you can see the demo here:
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="robots" content="noindex" />
  <title>Throbber Testtitle>
  <style>
    #box1, #box2, #box3 { background-color:#E5EECC; border:1px solid #D4D4D4; width: 200px; float:left; };
  style>
  <script src="http://code.jquery.com/jquery.min.js" type="text/javascript">script>
  <script type="text/javascript">
 
    fillBox = function( ele ) {
 
        $(ele).prepend( "<img id='throbber' src='facebook.gif' />" );
 
        $.ajax({
            url: 'test.php',
            cache: false,
            success: function(data) {
 
                $(ele).html( data );
 
            }
        });
 
    };
 
    $(document).ready( function() {
 
        $( "a" ).click( function(e) {
 
            var el = '';
            switch(e.target.text) {
                case "Box 1":
                    el = '#box1';
                    break;
                case "Box 2":
                    el = '#box2';
                    break;
                case "Box 3":
                    el = '#box3';
                    break;
            }
 
            fillBox( el );
 
        });
 
    });
  script>
 
head>
 
<body>
 
<a href="#">Box 1a>|<a href="#">Box 2a>|<a href="#">Box 3a>
 
<br /><br />
 
<div id="box1"div>
<div id="box2"div>
<div id="box3"div>
 
body>
html>