Introduction to Ajax

Tags: 

This page is for Kieran's students in MIS 422/622 at Oakland University, in the fall semester of 2013. Assumptions:

  • You've read the first six or so chapters of CoreDogs client core.
  • You've read the first two or so chapters of CoreDogs server core.
  • You know how to create simple jQueryMobile pages.

Fetching pages

So, there you are, sitting at your computer. Reading this, right now. In a browser. Or maybe you're looking at a tablet, or a phone. Whatever.

Here's a link. Click on it. Then come back to this page.

The Onion

When you clicked on the link, your browser asked onion.com for a file, like index.html. The server at onion.com sent a bunch o' HTML, that replaced this page, the one you're reading now.

That's how things normally work. Page 1 is showing in your browser. Page 1 has a link to page 2. You click on the link. Page 2 replaces page 1 in your browser.

You can see this in action. Open up Firebug, the development tools in Chrome, whatever (try F12). Click on the Net tab. It will show you all the network traffic a page fetch generates. Now refresh this page. Go on. I'll wait...

Lots o' stuff, huh? The entire page is reloaded. HTML, JavaScript, CSS, images...

Now click on the Onion link above, and watch the network activity. Then come back here.

Again, lots of GETs, for images, etc.

Fetching parts of pages

Not everything works that way. Add a post to your Facebook wall, and FB doesn't reload the entire wall. It just adds the new post to the top. Twitter is like that, too.

Let's see how it works. Try this page. Click on the button. You'll see a photo and a description. Click again. Another photo.

When you click, a new page is not loaded. No, no, no! Instead, some JavaScript runs. It replaces just the photo and description. That's all. The rest of the HTML on the page isn't affected.

When you're looking at the photo page, open up the Net console in Firebug or Chrome. Click on the button, and watch what happens. You'll see a few requests, not an entire page of requests. Less network traffic.

You'll also see a spinny thing. It tells the user that something is happening. Otherwise, the user might think that nothing happened when s/he clicked the button. The spinny thing doesn't appear automatically. You have to program it in.

Download the code for that app. Get it running on your server.

Let there be code

Let's see how this works, starting with the HTML. Here it is (I've omitted all the usual jQM template code):

  1. <p><a id="show-button" href="#" data-role="button">Show photo</a></p>
  2. <p id="description"></p>
  3. <p><img id="photo"></p>
  4. ...
  5. <div id="spinner" class="spinner" style="display:none;">
  6.     <img id="img-spinner" src="ajax-loader.gif" alt="Loading"/>
  7. </div>

Line 2 is where the description of the photo goes. It starts off MT (empty). Line 3 is where the HTML for the image goes. It's MT, too.

Lines 5 to 7 show the spinny thing. It's hidden when the page is first displayed (the style in line 5). Here's what ajax-loader.gif looks like:

Spinny thing

It keep cycling forever. Well, until you go to another page.

In line 5, the div has an id of spinner, and a class of spinner. That's OK. In CSS, the id has # in front of it, and the class has . in front. There won't be any confusion. Well, the browser won't be confused, anyway.

Here's the JavaScript executed when the user clicks the button:

  1. $("#show-button").click(function(){
  2.   //Erase what is showing.
  3.   $("#description").html("");
  4.   $("#photo").attr("src", "");
  5.   $.getJSON(
  6.       "../server/choose_random_photo.php",
  7.       function( photo ) {
  8.         var file_name = photo.file_name;
  9.         var description = photo.description;
  10.         $("#description").html(description);
  11.         $("#photo").attr("src", "../server/images/" + file_name);
  12.       }
  13.   )
  14. });

Lines 3 and 4 erase the current description and photo. Notice how the image is erased: by setting the img tag's src attribute to MT.

Line 5 is where the fun starts. Here's the general form:

$.getJSON(
  URL of page to run,
  function ( result ) {
    code to run when the page returns the result
  }
);

$.getJSON loads the page given by the URL. Instead of returning HTML, that page returns data in the JSON format. The returned data is passed into the function, which does something with it. 

If you're wondering about the ..s, have a look at the directory structure of the files you installed on your server. Trace the path from show_photo.html to the other files.

If you haven't installed the code on your server, slap yourself, and then install it.

JSON stands for JavaScript Object Notation. It's useful when a Web page wants to return just a little chunk of data, without any of the HTML, CSS, JavaScript and other things that usually come along with a page.

JSON results have properties, also called fields. For example:

$.getJSON(
  "dog.php",
  function ( dog ) {
    alert(dog.name);
    alert(dog.breed);
    alert(dog.weight);
  }
);

dog.php runs, and returns a data structure like this:

name: Oscar
breed: Black lab
weight: 85 lbs

Where does dog.php get its data from? The JS doesn't care. Could be from a database, a file, or tea leaves.

The JS puts the data structure into the variable dog. It can then refer to dog.name, etc. It can do what it likes with the data. Show it in alert boxes, insert it into the page's HTML, whatever.

Here's that code again:

  1. $("#show-button").click(function(){
  2.   //Erase what is showing.
  3.   $("#description").html("");
  4.   $("#photo").attr("src", "");
  5.   $.getJSON(
  6.       "../server/choose_random_photo.php",
  7.       function( photo ) {
  8.         var file_name = photo.file_name;
  9.         var description = photo.description;
  10.         $("#description").html(description);
  11.         $("#photo").attr("src", "../server/images/" + file_name);
  12.       }
  13.   )
  14. });

The JS calls the URL in line 6. The JS then waits, twiddling its metaphorical thumbs. Eventually, choose_random_photo.php returns some data. The data is in the variable photo. Lines 8 and 9 extract the two fields. Lines 10 and 11 insert the data into the page's HTML.

How does getJSON know that the returned data will have the fields file_name and description? It doesn't. Programmers have to make sure that the PHP gives the JS the data it needs.

Some terms

The Web page that contains the HTML and JS - the page that the user sees - is called the client. The client has the $.getJSON call in it. The Web page that the $.getJSON calls is the server. It gives data to the client.

Client/server

What matters is where code runs. In the code you installed on your server, the file show_photo.html is the client. You type http://something/show_photo.html into your browser. The file show_photo.html is downloaded to your computer, and sent to your browser. However, the JavaScript code in the show_photo.html doesn't run until it reaches your browser. The file show_photo.html is stored on your server for convenient access, but your server never runs the code in show_photo.html.

The server code is in the file choose_random_photo.php. PHP code runs on the server. When show_photo.html asks the server for choose_random_photo.php, the server runs choose_random_photo.php, and sends the output to the browser. Only the output it sent. The PHP code in choose_random_photo.php is not sent directly to the browser.

Notice that users never interact directly with servers. Only with clients. Clients interact with servers.

Not our server

Another example. Here's code for a server. Not the one our program uses, but a simplified server:

  1. <?php
  2. echo json_encode(
  3.     array(
  4.       'name' => 'Oscar',
  5.       'description' => 'Played Kieran in CoreDogs',
  6.     )
  7. );

Lines 3 to 6 create a data structure, called an array in PHP. Two fields are set: name and description. JavaScript can't understand PHP arrays. json_encode in line 2 converts the PHP array into JSON, that JavaScript can understand. echo outputs the data structure, so the browser can get it.

There's no closing PHP tag after line 7. That's OK. It's not needed.

Here's a client. Not our client:

  1. ...
  2. <p><a id="click-me" href="#" data-role="button">Show something</a></p>
  3. <p id="dog-name"></p>
  4. <p id="dog-description"></p>
  5. ...
  6. <script>
  7.   $(document).ready(function(){
  8.     $("#click-me").click(function(){
  9.       $.getJSON(
  10.           "simple_server.php",
  11.           function( data ) {
  12.             $("#dog-name").html(data.name);
  13.             $("#dog-description").html(data.description);
  14.           }
  15.       )
  16.     });
  17.   });
  18. </script>

When the client first shows, you get:

Start

Click the button, and see:

End

The people who wrote the client and server had to agree on what data would be exchanged.

You can try it.

Before you go further, download the files, and get them running on your server.

Exercise

Modify the code you just downloaded. Clicking the button should show the title, author, and publication date of a book you like. Use meaningful property names.

Not our server, either

Here's another server:

  1. <?php
  2. $best_dog = array(
  3.   'dog_name' => 'Oscar',
  4.   'dog_notes' => 'Played Kieran in CoreDogs',
  5.   'dog_weight' => '85 lbs',
  6.   'dog_color' => 'black',
  7. );
  8. echo json_encode(
  9.     array(
  10.       'name' => $best_dog['dog_name'],
  11.       'description' => $best_dog['dog_notes'],
  12.     )
  13. );

It's almost the same. Lines 2 to 7 create an array with some dog data. Lines 8 to 13 send some of the data to the server.

Lines 8 to 13 don't care where the data in $best_dog comes from. As long as the data has the elements dog_name and dog_notes, the code is happy.

Notice the syntax. dog_name is the index or key of one of the elements in the array $best_dog. dog_notes is the index of another element. So $best_dog['dog_name'] is one piece of data about a dog.

Arrays let us take a bunch of data, and store it together in $best_dog. Another way to say it: $best_dog wraps all of the data about a dog.

(Not our server, either), either

What if we want to store data about more than one dog? Here's another server:

  1. <?php
  2. $best_dogs =
  3.   array(
  4.     array(
  5.       'dog_name' => 'Oscar',
  6.       'dog_notes' => 'Played Kieran in CoreDogs',
  7.       'dog_weight' => '85 lbs',
  8.       'dog_color' => 'black',
  9.     ),
  10.     array(
  11.       'dog_name' => 'Renata',
  12.       'dog_notes' => 'Played herself in CoreDogs',
  13.       'dog_weight' => '25 lbs',
  14.       'dog_color' => 'black and brown',
  15.     ),
  16. );
  17. echo json_encode(
  18.     array(
  19.       'name' => $best_dogs[0]['dog_name'],
  20.       'description' => $best_dogs[0]['dog_notes'],
  21.     )
  22. );

Lines 4 to 9 create an array about Oscar. Lines 10 to 15 create an array about Renata. Lines 2, 3, and 16 wrap those two arrays in another array, called $best_dogs.

$best_dogs is called a nested or multidimensional array. It's an array of arrays. It looks like this:

Nested array

The outer array has two elements in this case. Each element is itself an array. This lets us collect data elements about a dog (name, weight, etc.), and treat the elements as one group. We can add data about as many dogs as we like.

Here's that array code again:

  1. <?php
  2. $best_dogs =
  3.   array(
  4.     array(
  5.       'dog_name' => 'Oscar',
  6.       'dog_notes' => 'Played Kieran in CoreDogs',
  7.       'dog_weight' => '85 lbs',
  8.       'dog_color' => 'black',
  9.     ),
  10.     array(
  11.       'dog_name' => 'Renata',
  12.       'dog_notes' => 'Played herself in CoreDogs',
  13.       'dog_weight' => '25 lbs',
  14.       'dog_color' => 'black and brown',
  15.     ),
  16. );

Recall that array elements have indexes, like dog_name. The code gives indexes for the inner arrays, but not for the outer array. When you don't give indexes, PHP adds them itself. The first element gets an index of 0, the next element an index of 1, and so on.

Why does the first element have an index of 0? Just because. There is a reason, buried in the distant past, having to do with the way arrays were first implemented in C compilers nearly a half a century ago. You don't need to know. Just accept it, and move on.

So the data about Oscar is in $best_dogs[0]. The data about Renata is in $best_dogs[1]. Notice that the highest index is one less than the number of elements in the array. Got 16 elements? The first element will be [0], and the last one [15]. Got 984 elements? The first element will be [0], and the last one [983].

$best_dogs[0] is all of the data about Oscar. $best_dogs[0] is an array itself. If we want a particular piece of data about Oscar, we need to give the index of that piece of data.

Oscar's name: $best_dogs[0]['dog_name']

Oscar's color: $best_dogs[0]['dog_color']

Renata's weight: $best_dogs[1]['dog_weight']

Renata's color: $best_dogs[1]['dog_color']

Now look at this code:

  1. echo json_encode(
  2.     array(
  3.       'name' => $best_dogs[0]['dog_name'],
  4.       'description' => $best_dogs[0]['dog_notes'],
  5.     )
  6. );

The code extracts a little data from $best_dogs. It sends the data to the browser, in the format the browser is expecting, that is, a JSON data structure with the elements name and description. This is a common task in programming: converting data from one format to another.

The real server

Try this page again. Click on the button. You'll see a photo and a description. Click again. Another photo.

The server has data about several photos. It chooses a photo randomly, and returns data about it.

The server code is broken into two files. The file load_image_data.php has the data:

  1. $image_data = array(
  2.   array(
  3.     'file_name' => 'photo1.jpg',
  4.     'description' => 'Dudes in boaters',
  5.   ),
  6.   array(
  7.     'file_name' => 'photo2.jpg',
  8.     'description' => 'Lectures haven\'t changed',
  9.   ),
  10.   array(
  11.     'file_name' => 'photo3.jpg',
  12.     'description' => 'Halloween',
  13.   ),
  14.   array(
  15.     'file_name' => 'photo4.jpg',
  16.     'description' => 'Hot chick',
  17.   ),
  18.   array(
  19.     'file_name' => 'photo5.jpg',
  20.     'description' => 'Woodrow Wilson, vampire',
  21.   ),
  22.   array(
  23.     'file_name' => 'photo6.jpg',
  24.     'description' => 'Giants examine a ship',
  25.   ),
  26. );

$image_data is a nested array. It has six elements. Their indexes range from [0] to [5].

Here's the other server code file:

  1. <?php
  2. /**
  3.  * Return the file name and description of a random photo.
  4.  */
  5. //Load the data array.
  6. require( 'load_image_data.php' );
  7. //How many photos are there?
  8. $number_photos = sizeof($image_data);
  9. //Choose a random index value.
  10. $photo_index = rand(0, $number_photos - 1);
  11. //Wait for a second. Simulates processing delay.
  12. sleep(1);
  13. //Return data about the random photo.
  14. echo json_encode(
  15.     array(
  16.       'file_name' => $image_data[ $photo_index ][ 'file_name' ],
  17.       'description' => $image_data[ $photo_index ][ 'description' ],
  18.     )
  19. );

Line 6 loads the data. Keeping the data in a separate file makes it easier to swap in different data sets. As long as the included code defines an array called $image_data, everything will work. It doesn't matter whether the data is from a file, a database, whatever.

Line 8 computes the number of photos, that is, the number of elements in the array $image_data. (We'll need that in a moment.) The built-in PHP function sizeof returns the number of elements in an array. You can read about it.

Recall that we want the code to pick a random photo. How? We pick a random number from 0 to (number of elements - 1). That's what line 10 does. The rand function is built-in to PHP. You can read about it.

Line 12 makes the PHP script go to sleep for one second. I just put this in so we would know whether the JavaScript code for the spinny thing (coming up) is working. Normally you wouldn't add artificial delays. You can read about the sleep function.

Lines 15 to 18 grab some data from $image_data, and package it for the JavaScript. Line 14 converts the data into JSON format, and sends it to the browser. You can read about json_encode and echo.

The real client

Here's the HTML code, with standard jQM template stuff omitted.

  1. <p><a id="show-button" href="#" data-role="button">Show photo</a></p>
  2. <p id="description"></p>
  3. <p><img id="photo"></p>
  4. ...
  5. <div id="spinner" class="spinner" style="display:none;">
  6.     <img id="img-spinner" src="ajax-loader.gif" alt="Loading"/>
  7. </div>

Here's the JavaScript:

  1. $(document).ready(function(){
  2.   //Triggered when Ajax starts. Show the processing indicator.
  3.   $(document).ajaxStart(function() {
  4.     $("#spinner").show();
  5.   });
  6.   //Triggered when Ajax ends. Hide the processing indicator.
  7.   $(document).ajaxStop(function() {
  8.     $("#spinner").hide();
  9.   });    
  10.   $("#show-button").click(function(){
  11.     //Erase what is showing.
  12.     $("#description").html("");
  13.     $("#photo").attr("src", "");
  14.     $.getJSON(
  15.         "../server/choose_random_photo.php",
  16.         function( photo ) {
  17.           var file_name = photo.file_name;
  18.           var description = photo.description;
  19.           $("#description").html(description);
  20.           $("#photo").attr("src", "../server/images/" + file_name);
  21.         }
  22.     )
  23.   });
  24. });

You should understand lines 10 to 23 now. Remember, if you don't understand the ..s, have a look at the directory structure of the files you installed on your server. Trace the path from show_photo.html to the other files.

Have a look at this:

  1.   $(document).ajaxStart(function() {
  2.     $("#spinner").show();
  3.   });

ajaxStart is an event, just as click is an event. As you know, you associate code with click like this:

$(something).click(function() {

You associate code with ajaxStart like this:

$(document).ajaxStart(function() {

The event is triggered when $.getJSON starts.

$.getJSON sends a request to a server, and waits for a response. When $.getJSON starts, we want to inform the user that something is happening, so s/he doesn't keep clicking the button. That's what this does:

$("#spinner").show();

ajax-loader.gif is an endlessly spinning animation. It has some CSS that positions it on the page.

You should be able to work out what ajaxStop does.

That's all folks

You've seen how Ajax works. Web page clients ask servers for data. Not a bunch of HTML, necessarily. Just the data that is needed to update the page. The result is faster and smoother applications.

Ajax is all over the Web these days, and is spreading fast. It lets programmers create Web pages that are as fluid as native desktop applications. GMail is an example. One reason that it is so fast is that the GMail client running in your browser only asks the GMail server for the minimum data it needs. Ajax is behind it all.

Next up, we'll see how clients send data to servers.

This discussion is tailored for students in MIS 422/622. I've only covered one way to do Ajax. There are others.