Usability Improvements
At this point we have the core functionality of our chat application
completed. However, we can add a few additional features to polish up
our implementation. To start we need to make the chat messages
automatically refresh at fixed intervals without requiring user input.
We will also add a small message to let the user know when a background
request is active. To finish up we remove the necessity of using the
"Say It!" button to submit new chat messages.
Refreshing at Intervals
Adding periodical refreshes is quite a simple addition. We already
know that the user can refresh the chat messages by clicking the "Say
It!" button without entering a message to send. This button implements
an onclick event which calls the javascript sendMessage() function.
To create our periodical refresh, we'll use Prototype's
PeriodicalUpdater object. You can find the documentation for this
object at: http://www.sergiopereira.com/articles/prototype.js.html#Reference.PeriodicalExecuter.
The javascript is relatively simple:
var periodicalExecuter = new PeriodicalExecuter(sendMessage, 30);
I'm afraid that's as complicated as it gets ;). The new instance of
the PeriodicalExecuter class will call the sendMessage() function every
30 seconds. Our sendMessage() function will then perform its assigned
tasks as described earlier - which includes updating the current list
of chat messages via the handleRefresh() handler function.
Improved interval refreshing: Although not documented here, the
downloadable source code in chat.js contains additional code to ensure
interval refreshes do not also attempt message submissions (i.e. while
user is typing).
User Feedback
One other touch is adding some visible feedback whenever the chat
application is performing an AJAX request. The simplest feedback is to
display a "Loading..." message between the time a new request is
started and the time it successfully completes.
A span element is already present in the HTML for the chat
application for this purpose. To display the "Loading..." message, we
will use two additional classes. The first is the Prototype
Ajax.Responders class documented at: http://www.sergiopereira.com/articles/prototype.js.html#Ajax.Responders
The second is the Scriptaculous Effects class documented at: http://wiki.script.aculo.us/scriptaculous/tags/effects.
Ajax.Responders is a class which allows developers call object
methods and functions based on the occurance of Ajax events. For
example, most of our current javascript functions utilizing the
Prototype Ajax.Request class have an onComplete event to bind callback
functions to. In a similar fashion, the Ajax.Responders class allows us
to bind functions to similar events but this time for all such Ajax
events regardless of how or when they are called.
Since our user feedback message should be displayed for all Ajax
requests in progress, we can prevent adding it to all our individual
Ajax.Request calls and just use a single Ajax.Responders register()
call. This avoids a lot of potential javascript code duplication on our
parts.
The javascript...
Ajax.Responders.register(
{
onCreate: function()
{
if($('loading') && Ajax.activeRequestCount>0)
{
Effect.Appear('loading',{duration: .2, queue: 'end'});
}
},
onComplete: function()
{
if($('loading') && Ajax.activeRequestCount==0)
{
Effect.Fade('loading',{duration: .2, queue: 'end'});
}
}
}
);
In the javascript above we've defined two function prototypes, one
for each event we're interested in: onCreate and onComplete. On
creation of a new request, the first function prototype will check that
an element with id "loading" exists and that an Ajax request is active.
Once this has been confirmed, it uses the Scriptaculous Effects class
to make the "Loading..." message appear.
Because Effect.Appear only alters an element's style attribute, we
need to make sure the "loading" span element is not initially displayed
upon loading the page by setting its style attribute to
"display:none;". We cannot put this style in our CSS external file - it
must be added to a style attribute for the Effects class to manipulate.
On completion of the request, the second function prototype is
called. This causes the message to fade into invisibility once more. If
you add the new javascript into the chat.js file in our ./javascript
directory you can reload the chat application and see the effect this
has. Every time the user submits a new chat message, changes their
screen name, or does nothing and waits for the periodical refresh we
set up using the Prototype PeriodicalExecuter class, the "Loading..."
message will appear and then fade once the current request successfully
completes.
Adding a Keypress Event
We have one final usability improvement to add. In time, the user
will inevitably get tired of typing a message and then using the mouse
to click the "Say It!" button. An acceptable convention in most chat
applications and instant messengers is to submit new messages when the
user presses the Return key.
We can easily emulate this behavior using a Prototype Event.
window.onload = function()
{
Event.observe(
'textmessage',
'keypress',
function(event)
{
if(event.keyCode == Event.KEY_RETURN)
{
sendMessage();
}
}
)
};
Because we can't add a new Event like onclick, onkeypress to an
element until the entire HTML for the page has loaded, we put our
Prototype Event.observe() call into a function prototype which is only
called when the page has loaded.
The Event observer itself is simple enough. We are observing the
element with id "textmessage" (i.e. our chat message input field) for
all keypress events. The function handler continually check each
keypress until a Return press is detected. Once the user has pressed
Return, the Event calls sendMessage() which submits the new chat
message, and eventually updates the chat message list.
Conclusion
This concludes the tutorial :).
Hopefully it has proven to be of some benefit to you, the reader.
Over the course of building our new chat application we have
encountered the Zend Framework and delved into several of its classes.
We have met Prototype and how it simplifies using Ajax, as well as
helps us with non-AJAX specific tasks like interval function calls and
event handlers. We introduced the PHP5 SimpleXML extension when using
XML as a storage medium for our lightweight application. Although we
only touched it very briefly we also used Scriptaculous' Effect class.
In meeting these many libraries, we've bound them all into a
relatively simple application demonstrating how each can be used in
combination with our own self sourced code. The final code, which you
can download from chat.tar.gz or chat.zip, should be easy to follow -
most of its size comes from the comments :).
A final note. There are any number of improvements that could be
made to the current source code. The XML storage solution could be
replaced with a database if necessary. Screen names could be replaced
with proper user accounts. Private messaging could be implemented and
icons could be added to spruce up the UI. These were all out of the
scope of the tutorial but are easily added with a little work.