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.

Backbutton
This is for future enhancements and currently serves no function.
Type
This is a flag to let server side code know how to format the return data. This way one server side script can handle remote scripting for AJAX or iframe.
TargetId
This stores the id of the target for the return data.

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
targetId
This is the ID of the element that will display the data. We extended the object with this property for convenience and the ability to write more generalized handlers.
open
Open takes three arguments: the method GET in these examples, the URL, and a boolean flag to use an asynchronous process.
onreadystatechange
This is assigned the handler that will process the return data.
send
The actual HTTP request. There are no arguments when using the GET method. (null is required for non-Microsoft browsers, and while not required will work with Microsoft)

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.