AJAX

A few practical observations

 

While doing some custom AJAX development I ran across a few things that did not work and were not covered in the books I read. Below are some of the things I learned myself or found on the Internet. The most frustrating thing about some of the things I found on the Net is that they did not work as stated. Sometimes the writer had found ‘the fix’ but did not know they were only describing part of what was required. Something else was also required; they must have been doing it but did not document it. Therefore, ‘the solution’, as documented, did not work.

 

Standalone Page

In development, I have sometimes needed a page that would communicate with a server using AJAX but the page itself could not be put on the server. Using IE for this was easy, when the security message comes up, you just allow it. But IE is not my browser of choice for development. Firefox with Firebug makes a great development environment. But Firefox always gives the following error when you try to use the stand-alone page.

 

uncaught exception: Permission denied to call method XMLHttpRequest.open

 

This can be fixed by editing the prefs.js file for Firefox. The file can be found here:

C:\Documents and Settings\<USERNAME>\Application Data\Mozilla\Firefox\Profiles\<RANDOMCHARS>.default

 

Add the following to the file:

 

user_pref("capability.policy.default.XMLHttpRequest.open", "allAccess");

 

Getting the XMLHttpRequest Object

The different ways to get the XMLHttpRequest object is well document on the Web. I include my version to save you from having to do the search. The code below can be put into a function to return the request object, declared as the transport in the code below:

 

function getTransport(){

var transport;

// Try for Mozilla and Safari

if ( window.XMLHttpRequest ){

transport = new XMLHttpRequest();

// Try for IE

}else if ( window.ActiveXObject ) {

// Prefer the Msxm12 version

try {

transport = new ActiveXObject('Msxml2.XMLHTTP');

}

catch(err) {

transport = new ActiveXObject('Microsoft.XMLHTTP');

}

// Unknown browser

}else{

alert("This browser is not supported.");

}

return transport;

}

You will want to check for the response. Here is a simple function. The request object in the code below is the object returned from the getTransport function above.

 

Function checkAjaxResponse (request) {

if ( request.readyState == server.READY_STATE_COMPLETE ) {

if ( isSuccess(request) ){

handleAjaxResponse(request);

}else

handleAjaxError(request);

}

}

 

The server object in the above code is where I created constants for each of the request states. See the code below:

 

var server = new Object();

server.READY_STATE_UNINITIALIZED = 0;

server.READY_STATE_LOADING = 1;

server.READY_STATE_LOADED = 2;

server.READY_STATE_INTERACTIVE = 3;

server.READY_STATE_COMPLETE = 4;

 

In the checkAjaxResponse method there is an isSuccess function. You will need to test whether the response is successful or not. The test is placed inside a try/catch block because Mozilla will throw an error when trying to access the status if there was a network error.

 

Function isSuccess (request) {

try{ //Mozilla fix

return request.status == 0 || (request.status >= 200 && request.status < 300);

}catch(e){

return false;

}

}

InnerHTML

Using innerHTML is well covered in books like “Ajax in Action”. The code below illustrates a simple example of this. In the code below the dataDiv element ‘suddenly’ displays the response. In this case, the text is HTML text. Everything has trade offs and this is no exception. AFAIK, the elements in the innerHTML are not part of the DOM. At least for the things I needed to do I could not access any of these elements. This is why I include the next section, Import Node.

 

var dataDiv = document.getElementById('dataDiv');

dataDiv.innerHTML = request.responseText;

 

ImportNode

This technique adds the response from the server to the DOM. As part of the DOM, the elements can be accessed and used like any other DOM element. Below is sample code of doing this. The ‘if’ is there to determine if this is IE or any other browser. Mozilla and Safari and others have a document importNode function. This function makes the element argument a part of the DOM; IE does not have this capability. I don’t know who figured out this hack but you can create a DOM element and set it’s innerHTML to the response and then set the element to its first child.

 

if(document.importNode){

var el = document.importNode( request.responseXML.documentElement,true);

}else{ //ie hack

var el = document.createElement('div');

el.innerHTML = request.responseXML.xml;

el = el.firstChild;

}

 

I did have a few cases where the above IE hack did not work. However, I did find that substituting the code before works in cases where the above code does not. Sorry, I don’t know why or which is the better method. I use the above method and switch to the code below when it does not work.

 

el.innerHTML = request.responseText;

el = el.firstChild;

 

Now for the special features you need to make sure the DOM import works. First, make sure that whatever technology you use to create the response, sets the response header attribute to: text/xml. Below is a JSP scriptlet illustrating setting the response header.

 

<%response.setContentType("text/xml"); %>

 

Here is the requirement that took me a long time to figure out. The first element must specify the xhtml namespace. In this example, the response starts with a table element and declares it is part of the xhtml name space.

 

<table id='ConfigAssocData' border="1" xmlns="http://www.w3.org/1999/xhtml" >

 

Now keep in mind that the response you generate must be xhtml. Do not use standard html tags. Tags must contain closing tags or be self closing. For example, <br>, wrong, <br/> correct. One more thing to keep in mind, xhtml attributes are lowercase only. Using the html standard, onClick will not work. You must use onclick.

 

Return to the Java Help Page