Unlike my previous code, which created the xmlHttpRequest object at the first RPC, this code creates the object on page load. Doing it up front facilitates fitting this and the iframe technique of remote scripting together.
The function createXMLHttp is the only code that is different than what is shown for remote script with an iframe . It creates the object needed for the process. Let's take a look at it.
var HTTPObj = null; addEvent(window, "load", createXMLHttp); function createXMLHttp() { /* Creates an XMLHttp object */ var httpReq = HTTPObj; if (!httpReq) { if (typeof window.XMLHttpRequest !='undefined' ) { // branch for native XMLHttpRequest object - Mozilla try { httpReq = new XMLHttpRequest(); } catch (e) { httpReq = null; } } else if (typeof window.createRequest) { /* For ICEbrowser -- untested. * per their site http://support.icesoft.com/jive/entry.jspa?entryID=471&categoryID=21 * There are a number of restrictions on the implementation and it only does get */ try { httpReq = window.createRequest(); } catch (e) { httpReq = null; } } else if (typeof window.ActiveXObject !='undefined') { // branch for IE/Windows ActiveX version try { httpReq = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try{ httpReq = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { httpReq = null; } }//catch } } if (httpReq && typeof httpReq.readyState == "number") { try { /* This properties create compatibility with * the iframe technique presented on this site */ httpReq.backbutton = false; httpReq.RPCType = "ajaxg"; httpReq.targetId = ""; } catch (e) { /* IEs active X XMLHttprequest object * cannot be extended like this so we'll * work around it. IE 7 changed to * a JavaScript object like Mozilla's */ } else { httpReq = createExtIframe(); //See Iframe RPC: JavaScript } HTTPObj = httpReq; }//eof createHTTPObj
The addEvent is a user function that binds createXMLHttp to the window onload event and is discussed in the article Adding an Event Handler. The window onload event is used to be compatible with the iframe technique. If the code fails to create an XHR object, it calls createExtIframe to create a iframe for remote scripting.
When the page has loaded, attempts are made to create the HTTPObj object. Because Firefox, IE 7, et al use a native JavaScript object and IE prior to version 7 use different activeX objects, there are test for the type of object available and a cascade of try-catch structures to find the right object class.
If an xmlHttpRequest object is successfully created, it is extended with three properties to be compatible with the iframe technique.
From here on, the functions are the same ones used for RPCs with an Iframe.
The following function is the event handler—for onclick or onsubmit—that starts the RPC.
function do_rpc(url, target_elem, data, handler) { /* 1. Test if with created an HTTPObj. * 2. Do any preprocessing for the URL * e.g. assembling a query string from form fields * 3. Make call to server * */ var doDefault = true; if (HTTPObj) { /* Assign reference to block receiving results.*/ try { HTTPObj.targetId = target_elem; } catch (e) { HTTPtarget = targer_elem; //global for IE activeX object } var formattedURL = formatURL(url, data); HTTPObj.open("GET", formattedURL, true); HTTPObj.onreadystatechange = handler; HTTPObj.send(null); doDefault = false; } else { doDefault = true; } return doDefault; }//eof
The next three functions format the URL. They aren't too difficult so I'll just show the code.
function getRPCType() { /* This is so the server side * code can support different RPC * methods from different platforms * We tell it what type of RPC is * being made. */ var RPCType = null; if (HTTPObj.RPCType) { RPCType = HTTPObj.RPCType; } else { /* For IE Active X */ RPCType = "ajaxg"; } return "type=" + RPCType; }//eof getRPCType function formatURL(url, data) { /* Assemble query string and append to url*/ var type = getRPCType(); if (data) { switch (typeof data) { case "object": //Get string from form like method get if (data.tagName.toLowerCase() == "form") { url += form2query(data); } url += "&" + type; break; case "string": //formated string supplied if (!/^[?]/.test(data) ) { data = '?' + data; } url += data + "&" + type; break; default: url += "?" + type; //use what is passed with url } } else { url += "?" + type; } return url; }//eof formatURL function form2query(frm) { /* To string together fieldname * value pairs from form elements * with name property set. * * Format ?name=value&name=value ... */ var qry = ""; //final query string var pair = "?"; //format one name/value pair var field; //form field being processed for (var i = 0; i < frm.elements.length; i++) { field = frm.elements[i]; if (typeof field.name != "undefined" && field.name != "") { switch (field.type) { case "select-one": pair += field.name + "=" + field.options[field.selectedIndex].value; break; case "radio": case "checkbox": if (field.checked) { pair += field.name + "=" + field.value; } break; default: pair += field.name + "=" + field.value; } if (pair.length > 1) { /* Test in case first element * is unchecked radio or checkbox */ qry += pair; pair = "&"; } } } return qry; }//eof form2query
Finally, here is a basic onreadystatechange event handler to process the returned data.
function handleResponse() { if (HTTPObj.readyState == 4 || HTTPObj.readyState == "complete" ) { try { if (HTTPObj.status == 200) { var targ = document.getElementById(HTTPObj.targetId); targ.innerHTML = HTTPObj.responseText; } else if (HTTPObj.readyState != "complete") { /* the test condition is for use with the iframe technique * presented elsewhere on this site. A readyState * value of 'complete' is returned by IE's iframe. */ alert(HTTPObj.status + ": " + HTTPObj.status.Text); } } catch (e) { alert("Network error!\nPlease try later."); } } } //eof handleResponse
This function's code is simple: everytime readyState value changes the onreadystate event fires. A readyState value of 4 indicates the process is complete and an HTTP status of 200 indicates the process was successful. Once the readyState is 4, either the data is processed or the error is handled.
The test for "complete" is for compatibility with the iframe technique and IE.
The returned data will be found in the XHR object's property responseText for all types of return data or in responseXML if the server returns the data with an XML header (see Ajax in a Nutshell: Retrieving XML). The code to process the data can be as simple or complex as needed.
The example above copies a block of HTML from the responseText property to a visible element's innerHTML property. We'll use this in one of our examples later.
The try-catch block is needed for FireFox/Netscape etc. to handle situations where the server doesn't send a response.
If, in Mozilia's JavaScript console, you see the error message
Component returned failure code:
0x80040111(NS_ERROR_NOT_AVAILBALE) [nsIXMLHttprequest.status]
look to one of three common causes.
function your_do_rpc(e) { e = e || window.event; [snip code, your code similar to do_rpc] /* at end stop form submit */ if (e.preventDefault) e.preventDefault(); else if (e.returnValue) e.returnValue = false; else return false; } where the function is bound to an event.