This tutorial will walk you through the step in
order to create an AJAX driven web chat program. This will be a very
simple program, but will be expanded upon in future tutorials.
Introduction
This tutorial will walk you through the step in order to create an
AJAX driven web chat program. This will be a very simple program, but
will be expanded upon in future tutorials.
Creating the Chat Tables
We are going to create two tables for the chat program, but we will
only use one for the first part of this tutorial. The two tables are
"chat" where we will in later tutorials store all of the different chat
rooms, and "message" which contains the list of all messages sent to
the chat system.
--Chat Table
DROP TABLE IF EXISTS `chat`;
CREATE TABLE `chat` (
`chat_id` INT(11) NOT NULL AUTO_INCREMENT,
`chat_name` VARCHAR(64) DEFAULT NULL,
`start_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`chat_id`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;
--Message Table
DROP TABLE IF EXISTS `message`;
CREATE TABLE `message` (
`message_id` INT(11) NOT NULL AUTO_INCREMENT,
`chat_id` INT(11) NOT NULL DEFAULT '0',
`user_id` INT(11) NOT NULL DEFAULT '0',
`user_name` VARCHAR(64) DEFAULT NULL,
`message` TEXT,
`post_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`message_id`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;
These tables are just a preleminary setup and will probably change
quite a bit through then upcoming parts of these tutorials. The HTML
Skeleton The HTML is going to be pretty straight forward to start out
with. Lets start with our header. We know that we will want to add some
style information, and instead of placing it in a seperate file, we
will leave it in a script tag for now Later we will want to move this
to an external file for caching benifits, but we can leave it here for
development purposes. We will also need some JavaScript. Once again we
could move this to an external file, but we'll leave it here for now.
Now lets add the rest of the HTML:
AJAX Driven Web Chat.
Status: Normal
Current Chitter-Chatter:
We have a header, a paragraph where we can display status, a main
div to be used for displaying the chat. As for HTML controls, we have a
refresh button for testing and incase the program freezes for some
reason, a reset button to clear out our database, an area for the user
to enter a message, and a send button. Notice that we have one CSS
class that we haven't defined yet, and one inline CSS style. We'll
define our chat_main next. Our one CSS class that we have for now will
be.
overflow: auto;
height: 300px;
width: 500px;
background-color: #CCCCCC;
border: 1px solid #555555;
The only real item of interest here is the overflow. This will allow
our div tag to behave like an IFrame. As we will see, this will work
very well for FireFox, but will require some extra code for IE to
behave correctly. It is better to use div's with overflows rather than
IFrames because search engines can index them easier.
The Javascript
Let's start with my favorite piece of AJAX code.
//Gets the browser specific XmlHttpRequest Object
function getXmlHttpRequestObject() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if(window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP");
} else {
document.getElementById('p_status').innerHTML 'Status: Cound not create XmlHttpRequest Object.' +
'Consider upgrading your browser.';
}
}
This code creates the XMLHttpRequest object for the current uses
values. You will see this code written in many different ways in all
the different AJAX sites out there. A lot of people like to use
Try...Catch blocks to check browser compatibility, but I personally
think this way looks more professional. I'm not sure what the best way
is, but I've been taught that using Try..Catch blocks leads to sloppy
programming (Damn CS purists.). As you can see, IE suports an ActiveX
XMLHttpRequest object, while FireFox, Opera, and the rest use a native
object. If the browser doesn't suport either of these items, we alert
the user with our status paragraph. Now we can use this block of code
anywhere we need to create a browser specific XMLHttpRequest object.
Lets create 4 global variables. We'll add these to the top of the
script.
var sendReq = getXmlHttpRequestObject();
var receiveReq = getXmlHttpRequestObject();
var lastMessage = 0;
var mTimer;
We create two XMLHttpRequest objects, one integer that holds the
most recent message that we have recieved (so we don't have to send the
whole message list on each request), and a variable that will store our
auto refresh time (so we can clear out our setTimout in any function).
Lets add the function to make the call to get the most recent messages.
You can recieve your messages in plain text or XML. Both have their
advantages and disadvantages. For this AJAX app we will be using XML.
//Gets the current messages from the server
function getChatText() {
if (receiveReq.readyState == 4 || receiveReq.readyState == 0) {
receiveReq.open("GET", 'getChat.php?chat=1&last=' + lastMessage, true);
receiveReq.onreadystatechange = handleReceiveChat;
receiveReq.send(null);
}
}
First we check to make sure that our XMLHttpRequest object is ready
to send a request. We should probably add some code here to handle
other states, but that is too involved for this section. The first
line: receiveReq.open("GET", 'getChat.php?chat=1&last=' +
lastMessage, true);Starts the connection to our chat service which will
be located at getChat.php along with some data passed as a querystring.
The request type will be a GET. We could also make the request with a
POST as you will see in a minute. The second like:
receiveReq.onreadystatechange = handleReceiveChat; sets what JavaScript
function will handle the response from the chat service. We will create
this function in a minute. Inside this function we will be able to
access the data sent back from the server. The third line sends the
request. Here we pass null because we are doing a GET requets type. If
we were using a POST request, we could pass our parameters here. Now
let's create the function tha will handle the servers response:
//Function for handling the return of chat text
function handleReceiveChat() {
if (receiveReq.readyState == 4) {
var chat_div = document.getElementById('div_chat');
var xmldoc = receiveReq.responseXML;
var message_nodes = xmldoc.getElementsByTagName("message");
var n_messages = message_nodes.length
for (i = 0; i < n_messages; i++) {
var user_node = message_nodes[i].getElementsByTagName("user");
var text_node = message_nodes[i].getElementsByTagName("text");
var time_node = message_nodes[i].getElementsByTagName("time");
chat_div.innerHTML += user_node[0].firstChild.nodeValue + ' ';
chat_div.innerHTML += ''
chat_div.innerHTML += time_node[0].firstChild.nodeValue + '
';
chat_div.innerHTML += text_node[0].firstChild.nodeValue + '
';
lastMessage = (message_nodes[i].getAttribute('id'));
}
mTimer = setTimeout('getChatText();',2000);
}
There is a lot of stuff going on in this function so let's just take it one line at a time. The first line:
if (receiveReq.readyState == 4) {
Check to see if the response from the server was good. We could do
error handling here if we would like. The next line is just setting up
a short-cut variable for later use. The third line creates the XMLDoc
object that contains the servers response information.
var xmldoc = receiveReq.responseXML;
We could also get this data as plain text with
receiveReq.response.Text. Plain text is better when you are returning a
single value, but XML is easier for multiple values. Since we will be
returning multiple messages with 4 values each on every request, XML is
the way to go. In order to work with XML responses, you need to
understand the XMLDOM. There can be some tricky little differences
between the browsers when working with the XMLDOM. The next line:
var message_nodes = xmldoc.getElementsByTagName("message");
Get's all the messages for this request. Each message node contains
multiple pieces of information about the message. Below is an example
of how our response XML will look:
Ryan Smith
Here is the Message Text
07:53
We can have multiple message elements in each response. We pass
along the Users Name who posted the message, the message text, and the
current time that the message was posted. The next two lines start the
loop through the messages from the response.
var n_messages = message_nodes.length
for (i = 0; i < n_messages; i++) {For every message we will all the new message to our chat screen.
var user_node = message_nodes[i].getElementsByTagName("user");
var text_node = message_nodes[i].getElementsByTagName("text");
var time_node = message_nodes[i].getElementsByTagName("time");
chat_div.innerHTML += user_node[0].firstChild.nodeValue + ' ';
chat_div.innerHTML += ''
chat_div.innerHTML += time_node[0].firstChild.nodeValue + '
';
chat_div.innerHTML += text_node[0].firstChild.nodeValue + '
';
The next six lines just update our chat display from the results of
the server. Finally we store the last ID that we recieved from the
server so that we can avoid receiving duplicate data.
lastMessage = (message_nodes[i].getAttribute('id'));
After we have updated our display, we will set a timeout to refresh our display.
mTimer = setTimeout('getChatText();',2000);
We will send another request to the server in 2 seconds to see if
any new messages have been posted. We also store it in our global
variable so that we can clear it out later. Sending the Request We need
to write the code to send a message to the server. When sending our
message, we will use a POST request rather than a GET. This allows us
to send larger messages to the server because the overall size of a
querystring is limited. Creating a POST request is a lot like creating
a GET request.
//Add a message to the chat server.
function sendChatText() {
if (sendReq.readyState == 4 || sendReq.readyState == 0) {
sendReq.open("POST", 'getChat.php?chat=1&last=' + lastMessage, true);
sendReq.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
sendReq.onreadystatechange = handleSendChat;
var param = 'message=' + document.getElementById('txt_message').value;
param += '&name=Ryan Smith';
param += '&chat=1';
sendReq.send(param);
document.getElementById('txt_message').value = '';
}
}As you can see, it's pretty much the same code, but when we call the send() method, we pass in an additional parameter.
var param = 'message=' + document.getElementById('txt_message').value;
param += '&name=Ryan Smith';
param += '&chat=1';
sendReq.send(param);
We are passing three values in our POST data; the message text, the
user's name(HardCoded to my name right now, and the current chat room
(HardCoded to 1 right now). We'll change these later to allow for
different chat rooms and different user names. On this request, instead
of executing handleReceiveChat on the callback, we will execute a new
function called handleSendChat. This function is going to be pretty
simple.
//When our message has been sent, update our page.
function handleSendChat() {
//Clear out the existing timer so we don't have
//multiple timer instances running.
clearInterval(mTimer);
getChatText();
}
All we are doing here is clearing out any refresh timer that may be
executing, and then making the call to refresh the chat. This sets up
our client side functionality for sending and receiving chat messages.
The one last piece of functionality that we want to add right now is
the ability to reset our chat application.
//This cleans out the database so we can start a new chat session.
function resetChat() {
if (sendReq.readyState == 4 || sendReq.readyState == 0) {
sendReq.open("POST", 'chat/getChat.php?chat=1&last=' + lastMessage, true);
sendReq.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
sendReq.onreadystatechange = handleResetChat;
var param = 'action=reset';
sendReq.send(param);
document.getElementById('txt_message').value = '';
}
}
We create a function that is pretty much the same as our
sendChatText function, but instead of passing a message in the POST
parameters, we will pass an "action" parameter with the value of reset.
Our callback for the resetChat request simply clears out any messages
that are currently in the chat display and resets our refresh timer.
//This function handles the response after the page has been refreshed.
function handleResetChat() {
document.getElementById('div_chat').innerHTML = '';
clearInterval(mTimer);
getChatText();
}
We'll finish off our JavaScript by adding our onclick handlers to our buttons.
For each button click, we call it's corresponding JavaScript function
The Backend
Now it's time to create the back-end to the chat application. The
way we have setup our client-side, all requests go to the same URL. The
backend decides how to handle this based on the parameters passed with
the request. The first thing that our back-end file does is create some
HTTP headers to keep the clients brower from caching the respose.
//Send some headers to keep the user's browser from caching the response.
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
header("Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . "GMT" );
header("Cache-Control: no-cache, must-revalidate" );
header("Pragma: no-cache" );
header("Content-Type: text/xml; charset=utf-8");
You can see that we set the "Expires" header to a date that has
already passed. Without this line, IE tends to cache the response
regardless of the other headers. The one other header we send is the
Content-Type. We set our content type to text/xml so the clients
browser knows that we are sending an XML file rather then a text file.
This eases the creation of the XMLDoc object for the different browser
types. The next line includes a file that contains our database
functions. I like to abstract my database functions in a seperate file
in case I need to change by database connection type (MSSQL, Oracle,
etc.). require('database.php');I'll explain this file in a little bit.
The first action that we perform is to check our POST variables to see
if a new message was sent in the request.
//Check to see if a message was sent.
if(isset($_POST['message']) && $_POST['message'] != '') {
$sql = "INSERT INTO message(chat_id, user_id, user_name, message, post_time) VALUES (" .
db_input($_GET['chat']) . ", 1, '" . db_input($_POST['name']) .
"', '" . db_input($_POST['message']) . "', NOW())";
db_query($sql);
}
If a new message was sent, then we add the message to our database.
$sql is a string containing the query text. We use to the function
db_input to escape any quote characters that the user may have entered.
We then execute the query text with our db_query function. Next we'll
check to see if a request to reset the chat was sent:
//Check to see if a reset request was sent.
if(isset($_POST['action']) && $_POST['action'] == 'reset') {
$sql = "DELETE FROM message WHERE chat_id = " . db_input($_GET['chat']);
db_query($sql);
}
Once again we create our query text and execute it with our db_query
function. This query simply deletes all messages from the database that
are from the current chat session. Now it's time to create the response
that will get sent back to the use. After we have finished updating the
database with any new messages sent by the user, we will get the list
of any new messages that the user hasn't recieved yet. We start by
creating our XML header and document element opening tag.
//Create the XML response.
$xml = '';
It is important to note that there cannot be any whitespace or other
characters before our XML declaration, otherwise we will run into
processing errors when trying to parse the XMLDom on the client-side.
Next, we will check to see if the user is in a chat room. If not we
will send the user a message informing them that they need to enter a
chat room before they can get any chat messages.
//Check to ensure the user is in a chat room.
if(!isset($_GET['chat'])) {
$xml .='Your are not currently in a chat session. Enter a chat session here';
$xml .= '';
$xml .= 'Admin';
$xml .= 'Your are not currently in a chat session. ';
$xml .= 'Enter a chat session here';
$xml .= '' . date('h:i') . '';
$xml .= '';
We have an empty link in this message that we can later replace with
a URL to our chat room selection page, but that is beyond the scope of
this tutorial. If the user is in a chat room, then we will get every
message that the user hasn't received yet and add them to our XML
response.
} else {
$last = (isset($_GET['last']) && $_GET['last'] != '') ? $_GET['last'] : 0;
$sql = "SELECT message_id, user_name, message, date_format(post_time, '%h:%i') as post_time" .
" FROM message WHERE chat_id = " . db_input($_GET['chat']) . " AND message_id > " . $last;
$message_query = db_query($sql);
while($message_array = db_fetch_array($message_query)) {
$xml .= '';
$xml .= '' . htmlspecialchars($message_array['user_name']) . '';
$xml .= '' . htmlspecialchars($message_array['message']) . '';
$xml .= '' . $message_array['post_time'] . '';
$xml .= '';
}
}
The first step here is to check and see if the user has recieved any
messages yet. If the request didn't specify the last request recieved,
then we set the $last variable to zero in order to get every previous
message for this chat session Next, we create our SQL statement to get
all the message for the current chat session that were posted since our
last update. By limiting our request to just the messages that we
haven't recieved yet, we reduce the amount of network traffic we need
to send as well as limit the client site work involved. After we have
performed our query, we loop through each message row and add a message
node to our XML. Each message node contains the message id, the user
who posted the message, the text of the message, and the time the
message was posted. Finally, we close out our XML document element and
add our XML to the response.
$xml .= '';
echo $xml;
We are now ready to test out our Chat Function. Be sure that you
have created the database tables and set the correct database
connection value in database.php.
www.Ajaxprojects.com For more Ajax tutorials
Usability Additions
We can see that we have a semi-functioning AJAX driven chat
application. To finish off part one of this tutorial, we will make a
few small additions to the HTML in order to make it a bit more user
friendly. One of the first things you'll probably notice is that when
you type a message and press enter, the page refreshes and your message
isn't added. You have to actually click the "Send" button to add your
message to the chat. To fix this, we will add a simple JavaScript
function that is called on the form's submit event.
This function will call our sendChatText function and return false
to keep the form from submitting. //This functions handles when the
user presses enter. Instead of submitting the form, we //send a new
message to the server and return false.
function blockSubmit() {
sendChatText();
return false;
}
This will add our message to the chat every time we enter a message
and press enter. You will also notice that when you first open the
page, the chat is not refreshing itself. We will create a JavaScript
function that is called when the page loads to handle initializing our
application.
When the document loads, we start refreshing the chat messages by calling the getChatText() function.
//Function for initializating the page.
function startChat() {
//Set the focus to the Message Box.
document.getElementById('txt_message').focus();
//Start Recieving Messages.
getChatText();
}
Additionaly we set the focus to the message textbox so the user can
immediately start typing a message. This concludes the first part of
the AJAX driven web chat tutorial. In the next installment, we will see
how to create multiple chat rooms with different user names. We will
also see other ways to extend our chat application to make it more
robust and user friendly. Hopefully this tutorial gave you a good
introduction to creating application using AJAX technologies.
|