Learning AJAX From the Ground Up

Posted June 22, 2006 at 8:49 AM

Tags: Javascript / DHTML, AJAX

I am just now getting into AJAX, and no, I have not been living in cave in Mars with my fingers in my ears. But yes, it's time to get on the horse. For those of you who don't know, AJAX is a way to send and receive data from the server without having to reload the current web page. This is done through the XmlHttpRequest object and some sweet Javascript.

AJAX in and of itself is very simple. It's just a matter of creating the connection object, sending the data, and handling the response. This is where I am starting. Then, I will tackle the ins and outs of actually writing an effective AJAX module or application with DHTML and ColdFusion.

I am a hands-on learner. So, I must create to learn. As such, I have created my own brand of Http Connection object wrapper. I am sure this has been done a few million times before by other people, but now I know how it all works and I can tweak it suite my needs. In the code below, you will see that I do not use any serialization or variables or any WDDX or things of that nature. I leverage the fact that Javascript is an interpreted language and its code can be generated and evaluated on the fly. That's how this AJAX system handles data returns.

Data is expected to be returned as regular Javascript. So, for instance, on the server if you wanted to return a struct, you would return it in this fashion:

 Launch code in new window » Download code as text file »

  • new Object({ ben:"is cool", molly:"is sexy", arnold:"is the man!" });

Then, on the data handler, the result is evaluated and returned:

 Launch code in new window » Download code as text file »

  • this.OnLoad(
  • eval( responseText )
  • );

This way, you can mostly mirror the functionality of the server-side objects but with real Javascript objects. This is not required though. When you send the request, you can send a boolean that determines whether or not the responseText should be evaluated. As far as creating the Javascript strings on the server side, that's for a later post.

 Launch code in new window » Download code as text file »

  • // This is the base class for the ajax HttpConnection object.
  • function HttpConnectionClass( strUrl ){
  • // The url for the connection.
  • this.Url = strUrl;
  •  
  • // The parameters to send (the data).
  • this.Parameters = new Object();
  •  
  • // The connection object.
  • this.Connection = this.CreateConnectionObject();
  •  
  • // This handles the data load (completion) function.
  • this.OnLoad = null;
  •  
  • // Return This pointer.
  • return( this );
  • }
  •  
  •  
  • // This builds the connection object.
  • HttpConnectionClass.prototype.CreateConnectionObject = function(){
  •  
  • // Try to create the generic connection.
  • try {
  •  
  • this.Connection = new XMLHttpRequest();
  •  
  • } catch ( errTryMicrosoft ){
  •  
  • // Try the MS xml object.
  • try {
  •  
  • this.Connection = new ActiveXObject( "Msxml2.XMLHTTP" );
  •  
  • } catch ( errTryOtherMicrosoft ){
  •  
  • // Try secondary Microsoft method.
  • try {
  •  
  • this.Connection = new ActiveXObject( "Microsoft.XMLHTTP" );
  •  
  • } catch ( errFailed ){
  •  
  • // None of the connection objects were created. Be sure
  • // to se the connection object back to null for later
  • // reference.
  • this.Connection = null;
  •  
  • }
  • }
  • }
  •  
  •  
  • // Check to see if the object is null. If so, alert the user that
  • // it will not work.
  • if (!this.Connection){
  • alert( "Your browser cannot create XmlHttpRequests.\nPlease use a browser that was developed in the 21st century." );
  • }
  •  
  • // Return the connection object.
  • return( this.Connection );
  • }
  •  
  •  
  • // This handles changes in the state of communication. Javascript is an
  • // interpreted language and therefore can be created and evaluated on
  • // the fly. Here, you can pass in a flag to tell the load handler to try
  • // and return an evaluation of the result or to just return the raw data.
  • // This system default to evaluation, however if the evaluation ever
  • // fails, the raw data is returned.
  • HttpConnectionClass.prototype.ReadyStateChangeHandler = function( blnEvaluateResponse ){
  • var strResponseText = null;
  •  
  • // Check to see if we are finished.
  • if (this.Connection.readyState == 4){
  •  
  • // Trim the response text.
  • strResponseText = this.Connection.responseText.replace( new RegExp( "^[\\s]+|[\\s]+$", "g"), "" );
  •  
  • // Check to see if we need to evaluate the response. If not,
  • // then just send the the raw data.
  • if ( blnEvaluateResponse ){
  •  
  • // We are going to try and evaluate the returned data since
  • // we are expecting some sort of javascript object. No matter
  • // what happens, we will be sending the connection object as
  • // a second argument so that the handler can check status
  • // and raw data.
  • try {
  •  
  • // We are going to try to evaluate the returned data to see if
  • // it is a javascript data object.
  • this.OnLoad(
  • eval( strResponseText ),
  • this.Connection
  • );
  •  
  • } catch ( errBadDataEvaluation ){
  •  
  • // The data was not meant to be evaluated, so try to
  • // return the raw string.
  • this.OnLoad(
  • strResponseText,
  • this.Connection
  • );
  •  
  • }
  •  
  • } else {
  •  
  • // The data was not meant to be evaluated, so return the
  • // raw string.
  • this.OnLoad(
  • strResponseText,
  • this.Connection
  • );
  •  
  • }
  •  
  • }
  •  
  • // Return out.
  • return;
  • }
  •  
  •  
  • // Send the data through the connection. When you send the data, you
  • // have the option to tell the load handler to try and evaluate the
  • // return data. By default the load handler will try to evaluate the
  • // result. You can also send a flag as to whether you want to clear
  • // the parameters after the data send. By default the parameters will
  • // be cleared post-send.
  • HttpConnectionClass.prototype.Send = function( blnEvaluateResponse, blnClearParameters ){
  • var objSelf = this;
  • var strData = "";
  • var strKey = null;
  • var blnEvaluateResponse = (blnEvaluateResponse == null) ? true : false ;
  • var blnClearParameters = (blnClearParameters == null) ? true : false ;
  •  
  • // NOTE: objSelf
  • // We set of a seemingly add variable (objSelf) here to point to
  • // (this). We do this for variable-binding. We are going to create
  • // a function that will be called after this function is called.
  • // In that function, we will need to refer to variables in the THIS
  • // scope. However, "this" is a scope that will mean something
  • // completely different in the function to be called at a later date.
  • // To get around this, we create the variable objSelf, which we can
  • // then use to create local variable bindings to this function even
  • // when the variables are referenced in the future function.
  •  
  • // Check to make sure we have a connection.
  • if (this.Connection){
  •  
  • // Open the connection.
  • this.Connection.open(
  • "POST",      // Method of data delivery.
  • this.Url,      // The Url we are posting to.
  • true      // Perform this async.
  • );
  •  
  • // Set the ready state handler only if we have a connection
  • // and Load Handler. If we do not have a load handler, then
  • // no need to check the ready state.
  • if (this.OnLoad){
  • this.Connection.onreadystatechange = function(){ objSelf.ReadyStateChangeHandler( blnEvaluateResponse ); };
  • }
  •  
  • // Set the content type to be a form post. This is needed since
  • // we are sending the data via a form post.
  • this.Connection.setRequestHeader(
  • "content-type",
  • "application/x-www-form-urlencoded"
  • );
  •  
  • // Create the data string. Loop over all the value parameters
  • // that were set.
  • for (strKey in this.Parameters){
  •  
  • // Add the name/value pair. Escape values.
  • strData = (strData + strKey + "=" + escape( this.Parameters[ strKey ] ) + "&");
  •  
  • }
  •  
  • // Strip any extra "&" that were appended to the string and
  • // send the data.
  • this.Connection.send(
  • strData.replace( new RegExp( "&$", "g" ), "" )
  • );
  •  
  • }
  •  
  • // Check to see if we should clear the parameters post-send.
  • if ( blnClearParameters ){
  • this.Parameters = new Object();
  • }
  •  
  • // Return out.
  • return;
  • }
  •  
  •  
  • // Sets the url that the data is sent to.
  • HttpConnectionClass.prototype.SetUrl = function( strUrl ){
  • this.Url = strUrl;
  •  
  • // Return out.
  • return;
  • }
  •  
  •  
  • // This sets a data in key-value pairs.
  • HttpConnectionClass.prototype.SetValue = function( strName, strValue ){
  • this.Parameters[ strName ] = strValue;
  •  
  • // Return out.
  • return;
  • }

And to use it, you would include the Javascript file and write:

 Launch code in new window » Download code as text file »

  • // Create the connection object.
  • var objRequest = new HttpConnectionClass( "ajax.cfm" );
  •  
  • // Set a variable.
  • objRequest.SetValue( "ben_nadel", "is wicked cool" );
  •  
  • // Set the data handler.
  • objRequest.OnLoad = fnDataHandler;
  •  
  • // Send the data.
  • objRequest.Send();

Download Code Snippet ZIP File

Comments (1)  |  Post Comment  |  Ask Ben  |  Permalink  |  Print Page



Adobe ColdFusion 8.0.1 Update - Helping Programmers To Be Signifanctly Less Girlie - Download ColdFusion 8 Update 8.0.1 Now.

Reader Comments

"// Check to see if the object is null. If so, alert the user that
// it will not work.
if (!this.Connection){
alert( "Your browser cannot create XmlHttpRequests.\nPlease use a browser that was developed in the 21st century." );
}"

This had me laughing for quite a few minutes. Im thinking the world shold implement it...

Posted by Jay Morris on Jul 25, 2007 at 9:10 AM


Post Comment  |  Ask Ben


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting