Ajax is changing web applications, giving them a responsiveness that's unheard of beyond the desktop. But behind all the hype, there's not much to Ajax -- (X)HTML, JavaScript, and XML are nothing new, and in this tutorial, I'll show you how to simplify the process of adding Ajax to your application even further with the help of jQuery, a popular JavaScript library.
You've probably heard about Ajax before, or at least used an Ajax-based application -- Gmail, for instance. Quite simply, Ajax is a technique for handling external data through JavaScript asynchronously, without reloading the entire page. SitePoint offers a good introduction to Ajax . Jesse James Garrett is credited with coining the term in this article .
Unfortunately, in-depth tutorials on practical ways to enter the world of Ajax are few and far between. To add to the problem, the XMLHttpRequest
class used by Ajax isn't very easy for beginning web developers to use. Luckily, a number of JavaScript libraries offer an easier way. Today I'll show you how jQuery -- one of these libraries -- allows you to easily add Ajax to your application.
jQuery is another mature JavaScript library that offers some features that the others do not. Admittedly, it's not exactly as lightweight as some of the other offerings: jQuery comes in at 19kb, while moo.fx is only 3kb. You can read more about jQuery at The JavaScript Library World Cup for a comparison of a few other JavaScript libraries that offer similar functionality.
To complete this tutorial, you'll need some basic JavaScript knowledge. If you know any C-style languages, you'll get the hang of JavaScript in no time. Just think curly braces, function declarations, and optional semicolons at the end of each line (they're not optional with jQuery, though). If you're keen to get started with JavaScript, see this excellent, concise JavaScript tutorial designed for programmers. Also, since we're talking about web applications, a basic knowledge of HTML is required.
Let's walk through a quick introduction to jQuery. To be able to use it in your pages, you'll first need to download the library. You can download the latest version -- 1.1.2 at the time of writing. jQuery's methodology is simple: find things, do stuff. We select elements from the document (via the DOM) using the jQuery function, aliased as $()
. This handy function acts just like document.getElementById() , except that instead of only supporting IDs, it supports CSS selectors and some XPath selectors; and, instead of returning one element, it can return an array of elements. Okay, so maybe a better description of $()
is that it's like document.getElementById()
on steroids.
We then use functions to perform actions on our selections. For example, to append the text "Hello World!" to all divs with the class 'foo'
, then set the color to red, we'd use the following code:
$("div.foo").append("Hello World!").css("color","red");
Easy! Normally, this task would require two lines of code, like so:
$("div.foo").append("Hello World!");
$("div.foo").css("color","red");
<script src="http://adscluster.aws.sitepoint.com/openx/adjs.sp.php?region=6&did=adz" type="text/javascript"></script>
jQuery's chainable methods allow us to write much more compact code than other JavaScript libraries. There are functions in jQuery that don't need an object, as they work independently, and many of the Ajax functions fall into this group. For example, the post function, which we will soon make use of, is called by typing $.post(parameters)
. For more jQuery functions, check the online documentation or visualjquery.com .
As an example, we're going to make an interactive concept generator. Basically, this involves our selecting two terms at random from a list, then combining them to create a phrase. For this exercise, we'll use web 2.0 buzzwords ('Mashup', 'Folksonomy', 'Media' and so on), and normally we'd fetch these terms from a flat file. To save you from downloading every single combination (or at least every element) in JavaScript, we're going to generate it on the fly at the server end, and fetch it for the client with jQuery. jQuery integrates perfectly with normal JavaScript, so you'll find it an easy task to work it into your code.
Server-side Code (PHP)
To keep it simple, we'll use the basic code below to create our concept generator. Don't worry about how it works, just look at what it does: it outputs a randomised quote. Note that this code doesn't output XML -- it merely outputs raw text:
<?php
header("Cache-Control: no-cache");
// Ideally, you'd put these in a text file or a database.
// Put an entry on each line of 'a.txt' and use $prefixes = file("a.txt");
// You can do the same with a separate file for $suffixes.
$prefixes = array('Mashup','2.0','Tagging','Folksonomy');
$suffixes = array('Web','Push','Media','GUI');
// This selects a random element of each array on the fly
echo $prefixes[rand(0,count($prefixes)-1)] . " is the new "
. $suffixes[rand(0,count($prefixes)-1)];
// Example output: Tagging is the new Media
?>
Here, I've used the Cache-Control
header response because Internet Explorer has a habit of caching pages that have the same URL, even if the content between the pages differs. Obviously, that defeats the purpose of our script -- the production of a new quote on every load. We could have used jQuery to include a random number in the URL that would then be discarded, but it's easier to address this caching issue on the server side than the client side.
Client-side Code (HTML)
Let's start creating the HTML for the front end, then work our Ajax into it. All we need on the page is a button that users can click to request another quote, and a div into which we'll put the quote once we've received it from the server. We'll use jQuery to select this div and load the quote into it, and we'll reference the div by its id
. If we wanted to, we could use jQuery to load the quote into multiple elements, with the help of a class, but an id is all we need for now. Let's make this the content of our body element:
<input type="submit" id="generate" value="Generate!">
<div id="quote"></div>
We can put the quote itself inside the div. Normally, we'd have a lengthy onSubmit
event for the button (the input with the id 'generate'
). Sometimes, we'd have an onSubmit
event handler that called a JavaScript function. But with jQuery, we don't even need to touch the HTML -- we can separate behaviour (the event handler) from the structure (the page HTML) with ease.
Client-side Code (jQuery)
It's time to bring our back end together with our front end using jQuery. I mentioned earlier that we can select elements from the DOM with jQuery. First, we have to select the button and assign an onClick
event handler to it. Within the code for this event, we can select the div and load the content of our script into it. Here's the syntax for the click event handler:
$("element expression").click(function(){
// Code goes here
});
As you probably already know, if we were to select this element in CSS, the #
would identify that we were making our selection using the element's id
attribute. You can use exactly the same syntax with jQuery. Therefore, to select the button with the id 'generate'
(which we gave it above), we can use the element expression #generate
. Also, be aware that this syntax defines our event handler as an anonymous function within the event itself.
Mark Wubben's JavaScript Terminology page offers a great explanation of anonymous functions, if you'd like to know more.
We're going to use one of jQuery's higher level Ajax functions, load()
. Let's assume that our generator script is saved as script.php
. Let's integrate it with our client side with the help of the load()
function:
$("#generate").click(function(){
$("#quote").load("script.php");
});
That's it: three lines of code, and we have fully functioning Ajax random quote generator! Well, almost.
The problem with JavaScript is that code that's not within a function is executed as soon as the browser reaches it during rendering -- not once the page has finished rendering. As such, this code will try to attach to an element that has not yet loaded. Normally, we'd use window.onload
to deal with this issue. However, the limitation with that approach is that window.onload
is called once everything has finished loading -- images and all. We're not interested in waiting for those images -- it's just the DOM that we want access to.
Fortunately, jQuery has $(document).ready()
, which, as its name suggests, is executed when the DOM is ready to be manipulated.
The Complete Code
Here's the complete code, including the $(document).ready
wrapper and some basic HTML and CSS:
<html>
<head>
<title>Ajax with jQuery Example</title>
<script type="text/JavaScript" src="jquery.js"></script>
<script type="text/JavaScript">
$(document).ready(function(){
$("#generate").click(function(){
$("#quote p").load("script.php");
});
});
</script>
<style type="text/css">
#wrapper {
width: 240px;
height: 80px;
margin: auto;
padding: 10px;
margin-top: 10px;
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="quote"><p> </p></div>
<input type="submit" id="generate" value="Generate!">
</div>
</body>
</html>
This code is also included in this downloadable zip file . Remember, this code assumes the jQuery library has been saved as jquery.js
in the same folder as the PHP script and the HTML front end. Now that you're familiar with jQuery, let's move on to something more complicated: form elements and XML handling. This is true Ajax!
To demonstrate the true power of jQuery, we're going to make a fully featured Ajax chat application. This will allow users to post messages, and automatically update itself -- all without any page refreshes. As we're now dealing with a slightly more complex application, I'll also take you deeper into jQuery, demonstrating other handy functions.
First, we'll plan out the application. We won't need much for this app -- a front end, a back end, and the jQuery library. However, there will be a fair bit of code for both the back and front ends of the app, so our basic plan will let you know what to expect from the system.
Planning the Server Side
The back end of our application needs to process message submissions and output the messages. With this in mind, let's put together a rough outline of the back-end code:
As you can see, it's all very simple and straightforward. You can use this plan as a guide if you have to write a back end in another language.
<script src="http://adscluster.aws.sitepoint.com/openx/adjs.sp.php?region=6&did=adz" type="text/javascript"></script>
Planning the Client Side
The front end has to call the back end using Ajax, similarly to the approach we used in the first example. It has to handle the submission of the message-posting form, and update the chat window with newly submitted messages at regular intervals. However, we're going to add another feature here -- we'll use the current UNIX timestamp to determine which messages have already been downloaded, and only fetch the new messages, reducing bandwidth usage and server load. Here's a rough outline of the front-end code:
This plan may seem far more complex than the back end, but thanks to jQuery, the code involved is fairly short.
Planning the Database
We'll use a MySQL database to store the messages (although any SQL database will work, with a few tweaks to the code). We need a table with four columns: a column for the id of each message, two text columns to store the author of the message and the message itself, and a numerical timestamp column for our UNIX timestamps. Here's the query that creates the table:
CREATE TABLE `messages` (
`id` int(7) NOT NULL auto_increment,
`user` varchar(255) NOT NULL,
`msg` text NOT NULL,
`time` int(9) NOT NULL,
PRIMARY KEY (`id`)
);
Because we can't tell what length the message will be, we'll use a text field for now.
Server-side Code (XML)
In building the back end, let's first decide what the back end should output (to determine the interface between the back end and front end), and work backwards from there. Here's a simple XML structure:
<?xml version="1.0"?>
<response>
<status>1</status>
<time>1170323512</time>
<message>
<author>John Citizen</author>
<text>Hello world!</text>
</message>
<message>
<author>John Citizen</author>
<text>Hello world again!</text>
</message>
</response>
Notice that I've added the tag 'status'
, with the value of '1'
. As I mentioned above, a status code of 1
will represent a successful request with new messages, 2
as successful without new messages. Each instance of the message tag includes the author and his or her message.
Server-side Code (PHP)
Now, to the back end. I'll have to do this in PHP, but because the output is XML you can write the back end in any language -- Perl, ASP, whatever you like. Let's start it logically by defining some configuration values so that we can easily change them later. We need database connection details, the number of messages we want to store in the database (databases can handle thousands of rows, so this figure can be set fairly high), and the number of messages to display when the user enters the chat. Here's the code:
$dbhost = "localhost";
$dbuser = "root";
$dbpass = "";
$dbname = "chat";
$store_num = 10;
$display_num = 10;
Now we need to get on to the basics of the back end itself. A database connection is required, but we also need to make sure that Internet Explorer doesn't cache the request, and that the output is treated as XML. To make sure we're able to identify any errors in the code, we'll set error reporting to "all errors". And to easily work with the request data, we'll set a variable for every parameter in the request; each variable will take as its value the value of the request parameter. These few lines do the trick:
error_reporting(E_ALL);
header("Content-type: text/xml");
header("Cache-Control: no-cache");
$dbconn = mysql_connect($dbhost,$dbuser,$dbpass);
mysql_select_db($dbname,$dbconn);
foreach($_POST as $key => $value)
$$key = mysql_real_escape_string($value, $dbconn);
The foreach line looks through all the POST data, and creates a variable for every parameter and assigns it a corresponding value (e.g. path/to/file.php?variable=value
would set $variable
to "value"
). This simplifies the process of grabbing request data, as we don't have to specify it manually.
Next we get to the main functionality. It's at this point that we handle the insertion of messages into the database, and the retrieval of the latest few messages based on the number of messages to be displayed, as defined in $display_num
. I mentioned when we planned the front end that we would specify an action stating that a message was being submitted. We now need to check for this action -- let's assign the parameter 'action'
a value of 'postmsg'
to specify that we're performing this check and insert the data as a new row in the database; we'll insert the current UNIX timestamp in the database while we're at it.
However, we also need to clean out the database. Depending on your database space limits, you may want to restrict the number of posts stored. Generally, logging of messages is frowned upon, so I've decided to store ten messages by default. We'll use a function to grab the id of the last inserted row, and determine the rows to delete based on the value of that id
. For example, if we insert the eleventh message, we'll subtract the number of stored messages (10) from the id
of the latest one (11) which gives us the id
threshold (in this case, 1). We can then delete all messages that have an id
equal to or less than that threshold, which in this example would result in us deleting the first message. And, thanks to SQL, we can do this all in one query.
Here's the snippet that checks for the 'postmsg'
action, inserts the message into the database, and cleans it out on the fly as well:
if(@$action == "postmsg")
{
mysql_query("INSERT INTO messages (`user`,`msg`,`time`)
VALUES ('$name','$message',".time().")");
mysql_query("DELETE FROM messages WHERE id <= ".
(mysql_insert_id($dbconn)-$store_num),$dbconn);
}
Developers using other server side technologies should be able to write equivalent code easily enough. Notice that we call the time function to grab the current UNIX timestamp. We can safely assume that the value that time returns will probably not change during script execution (even on a slow server, this script executes in under one hundredth of a second). So when we return a timestamp to the front end later, we can just call the time function again and the value should still be reliable.
The code that's left handles the job of fetching the latest messages from the database and outputting them as XML. This is where the XML I outlined above comes into play. However, the bulk of the code lies in the MySQL query. We utilize the power of SQL to handle most of the processing tasks, so that script execution time isn't affected. Here are the requirements for our SQL query:
Anyone who's familiar with SQL will agree that this is all fairly simple stuff. For the rest of us, here's the code that does the trick. First, the query:
$messages = mysql_query("SELECT user,msg
FROM messages
WHERE time>$time
ORDER BY id ASC
LIMIT $display_num",$dbconn);
The rest of the code is elementary. If no results are returned, set the status code to 0; otherwise, set it to 1. Output the initial XML, the XML for each message, and the final XML. That's all! Here's the code:
if(mysql_num_rows($messages) == 0) $status_code = 2;
else $status_code = 1;
echo "<?xml version=\"1.0\"?>\n";
echo "<response>\n";
echo "\t<status>$status_code</status>\n";
echo "\t<time>".time()."</time>\n";
while($message = mysql_fetch_array($messages))
{
echo "\t<message>\n";
echo "\t\t<author>$message[user]</author>\n";
echo "\t\t<text>$message[msg]</text>\n";
echo "\t</message>\n";
}
echo "</response>";
The final code is all in the attached zip file, so don't worry about copying this into your text editor. Now that the back end is finished, we can move on to the fun work -- the HTML and jQuery!
Client-side Code (HTML)
Before we head into the jQuery, we need to prototype the HTML of the page. This way, when we have to work out which elements we are selecting to fetch or update using jQuery, we know what to do. We won't need much: a wrapper div, a paragraph for the messages, and a form with fields for the user's name and message, along with a Submit button. A briefly displayed loading message would add a final touch -- we can remove this at the appropriate time using jQuery. Here's the HTML:
<div id="wrapper">
<p id="messagewindow"><span id="loading">Loading...</span></p>
<form id="chatform">
Name: <input type="text" id="author" />
Message: <input type="text" id="msg" />
<input type="submit" value="ok" /><br />
</form>
</div>
Client-side Code (jQuery)
Now, on to the jQuery front end. First, we need to declare the timestamp of the current message as 0, and call the function that loads the messages from the server:
timestamp = 0;
updateMsg();
Next, we'll write the code for the form submission. jQuery allows us to add an event hook for the form's submit event, as though we were adding an onSubmit
event within the HTML itself, except that we don't have to touch the HTML. Here's the submit event:
$("form#chatform").submit(function(){ /* Code */ });
Here we're using CSS selector syntax to refer to the form element with an id
of 'chatform'
. As soon as we're into the form submission code, we can fire off a POST request to the server using jQuery's $.post
. Within the call to $.post
, we can select the values of the form elements on the page on the basis of their id
s, as we determined earlier. With this in mind, we can dive into our Ajax call:
$.post("backend.php",{ message: $("#msg").val(),
name: $("#author").val(), action: "postmsg", time: timestamp }, function(xml) {
<script src="http://adscluster.aws.sitepoint.com/openx/adjs.sp.php?region=6&did=adz" type="text/javascript"></script>
Notice that the array of parameters to be passed in the request is enclosed with curly braces. If you have more parameters, simply separate them with commas and use the format presented here, JSON-style . You can also use jQuery's Ajax functions to send a GET request that expects a JSON-style response, and have jQuery convert the response text into an easy-to-use format. Bear in mind, though, that this capability is only available for the GET request type, not the POST request type we use for our chat system. As such, we'll stick with plain XML for the moment.
Now, let's look at handling the XML response. Since we're all for code reuse, we'll create a function that handles the XML and call it now:
addMessages(xml);
We'll write this function later so that we can finish off the form submission event code. The code we've written so far is all we need for our $.post
callback function, so we can close it off and add a return false; line. This line fails using the standard browser form submission error code. The browser doesn't send the user to another page to submit the form -- we've already handled the form submission, so the browser doesn't need to. Here's the event code in its entirety:
$("form#chatform").submit(function(){
$.post("backend.php",{
message: $("#msg").val(),
name: $("#author").val(),
action: "postmsg",
time: timestamp
}, function(xml) {
addMessages(xml);
});
return false;
});
Now, let's get back to the addMessages()
function, which handles the response xml. It's quite simple, making use of jQuery's DOM manipulation and traversing functions. Remember that I mentioned the status code before? Now's the time to handle it:
if($("status",xml).text() == "2") return;
I haven't mentioned context in jQuery yet. The XML in this function call tells jQuery to look not inside the document HTML, but in the XML that the server sent us.
That line of code checks for a status code of 2, representing a successful request that resulted in us having no new messages to add to the window. The 'return'
keyword terminates the function call. Next, we set the timestamp to the timestamp in the XML:
timestamp = $("time",xml).text();
Again, this fetches the text value of the <time>
tag in the XML.
Now we can move on to jQuery's array iteration function, each()
. jQuery has an interesting way of handling array iteration. We use a standard selector statement, and the each()
function is passed one parameter -- a function to handle each instance of the matched elements. In this case, the elements are the instances of the <message>
tag in the server response, each instance representing a message to be displayed. One parameter -- the id
of the instance -- is passed to the function. We can use this, with jQuery's get()
function, to grab a new context -- the actual XML of the <message>
tag. This is how we select it:
$("message",xml).each(function(id) {
message = $("message",xml).get(id);
We can then select elements by passing the context 'message'
to the jQuery / $
function. Now that we have all the data we need, we must add it to the top of the message window on the page. The message window has the id 'messagewindow'
, so we select it using $("#messagewindow")
and use the prepend()
function to add our data:
$("#messagewindow").prepend("<b>"+$("author",message).text()+
"</b>: "+$("text",message).text()+
"<br />");
That's all there is to it! Putting it all together, here's the code for the function:
function addMessages(xml) {
if($("status",xml).text() == "2") return;
timestamp = $("time",xml).text();
$("message",xml).each(function(id) {
message = $("message",xml).get(id);
$("#messagewindow").prepend("<b>"+$("author",message).text()+
"</b>: "+$("text",message).text()+
"<br />");
});
}
Finally, we need the updateMsg
function we called at the very beginning of our code. This function has to query the server for new messages, and call the above addMessages
function with the response. It also has to set a timeout to call itself after a set period of time, which makes the chat window update automatically. To begin, we don't need to submit anything to the server besides a timestamp, so this is our $.post
call:
$.post("backend.php",{ time: timestamp }, function(xml) {
As I noted before, we also need to remove the loading message at this point, so we call jQuery's remove function on the span:
$("#loading").remove();
Then, we've received our xml response in the object 'xml'
, so we pass it to our addMessages
function:
addMessages(xml);
We round it off with a call to the JavaScript setTimeout()
function, which executes specified code after a specified interval. Here's the whole function put together:
function updateMsg() {
$.post("backend.php",{ time: timestamp }, function(xml) {
$("#loading").remove();
addMessages(xml);
});
setTimeout('updateMsg()', 4000);
}
Putting it All Together
Now we can put all of the pieces of the puzzle together. The code is, as I mentioned, available in this downloadable zip file . However, you can peruse it here, where I've added a bit of HTML and CSS for layout:
<html>
<head>
<title>Ajax with jQuery Example</title>
<script type="text/JavaScript" src="jquery.js"></script>
<script type="text/JavaScript">
$(document).ready(function(){
timestamp = 0;
updateMsg();
$("form#chatform").submit(function(){
$.post("backend.php",{
message: $("#msg").val(),
name: $("#author").val(),
action: "postmsg",
time: timestamp
}, function(xml) {
$("#msg").empty();
addMessages(xml);
});
return false;
});
});
function addMessages(xml) {
if($("status",xml).text() == "2") return;
timestamp = $("time",xml).text();
$("message",xml).each(function(id) {
message = $("message",xml).get(id);
$("#messagewindow").prepend("<b>"+$("author",message).text()+
"</b>: "+$("text",message).text()+
"<br />");
});
}
function updateMsg() {
$.post("backend.php",{ time: timestamp }, function(xml) {
$("#loading").remove();
addMessages(xml);
});
setTimeout('updateMsg()', 4000);
}
</script>
<style type="text/css">
#messagewindow {
height: 250px;
border: 1px solid;
padding: 5px;
overflow: auto;
}
#wrapper {
margin: auto;
width: 438px;
}
</style>
</head>
<body>
<div id="wrapper">
<p id="messagewindow"><span id="loading">Loading...</span></p>
<form id="chatform">
Name: <input type="text" id="author" />
Message: <input type="text" id="msg" />
<input type="submit" value="ok" /><br />
</form>
</div>
</body>
</html>
So, with 22 lines of JavaScript, eight lines of HTML, and around 50 lines of PHP, we now have a fully functional Ajax web application. Try it out, and integrate it into your own site. Build your own Ajax application, using these techniques and your own ideas. Take this code and modify it to build something new. If you aren't comfortable with generating and handling XML, stick with using your web application to generate HTML, and using load()
to bring it to the client. Then, once you get the hang of it, try out an application that utilises the full power of XML using tag attributes and jQuery's attr()
function -- you'll be amazed at just how powerful Ajax with jQuery can be.