Here are a couple of examples showing RPC using the XHR object (AJAX) to retrieve information from a database. When you click the button labeled "Find", it sends either zip code or city and state entered to the server-side script as search part of the URL passed to the XHR object.

Server-side code looks up the corresponding city and state or zip code in a database, returns the data, and the fields are filled in.

The Examples

Try it out. Enter a zip code (or city and state in second example) and click the appropriate button. The database is old and missing some codes so you may have to try a couple before getting a city and state.

Enter Zip find City and State
Enter City and State fine Zip Code

This would be a lot more difficult to do on the client. The database has 42K records. It would be possible to have the data in an array, but not practical. The initial page load would take a while even with a high speed connection and the search slow.

Only the function handleResponse presented in the JavaScript explanation is changed.

  City Look Up Code

There is a twist in handling the returned data. The data is received in JSON format. The text string returned looks like {city: 'city-name', state: 'IL'}. This is an object literal. Let's see how that is used in processing

function handleFormResponse(){
    if (HTTPObj.readyState == 4 || HTTPObj.readyState == "complete") {
        try {
            if (httpReq.status == 200) {
                var id = (HTTPObj.targetId) ? HTTPObj.targetId : HTTPTarget;
                expForm = document.getElementById(id);
                eval("var jsObj = " + HTTPObj.responseText);
                for (name in jsObj) {
                    expForm[name].value = jsObj[name];
                }
            }
            else (HTTPObj.readyState != "complete" {
                /* This branch is only valid when used with the XMLHttprequest
                * object. Then readyState will be a number. With iframes,
                * IE triggers the onreadystatechange event before
                * we're ready and would display this alert and then
                * finish the process.
                */
                alert(HTTPObj.status + ": " + HTTPObj.status.Text);
            }
        } catch (e) {
            alert("Network error!\nPlease try later.");
        }
    }
} //eof handleFormResponse

The highlighted lines of code are generic and can populate small or large forms thanks to JSON.

The line, eval("var jsObj = " + HTTPObj.responseText), converts the return data in httpObj.responseText into a JavaScript object named jsObj. Its properties, like properties of all objects, can be accessed with square bracket notation. All the named inputs of a form (the form's elements collection) can be accessed the same way. The syntax is formObj.elements['field_name'] or formObj['field_name].

By naming the properties in the return data with the same names as the form input field, it's possible to use a for-name-in loop to step through all properties in the data assigning their values to the fields. This short code will update any number of text or textarea fields. Select list, checkbox, and radio fields require a little extra handling.

An IE 5.0 and 6.0 bug, with the combination of ActiveX, for-name-in and framesets—like this site uses for the side menu—causes the menu links to load in a new window rather than the content frame specified in target. See the code for a work around; also see my Journal entry on 2/8/2006.

  Zip Code Look Up

There wasn't much to the first example. Doing the reverse requires more code, because there can be more than one return value.

function handleResponseCityState() {
    /* Call back for zip code lookup
     * based on city and state
     */
    if (httpReq.readyState == 4 || HTTPObj.readyState == "complete") {
        document.getElementById("dlist").style.display = "none";
        try {
            if (httpReq.status == 200) {
                var id = (HTTPObj.targetId) ? HTTPObj.targetId : HTTPTarget;
                var zipField = document.getElementById(id).elements['zip'];
                var data = HTTPObj.responseText;
                if (data.length < 6) { //only one zip code
                    zipField.value = data;
                } else {
                    processZipLookUp(HTTPObj.responseText, zipField);
                }
            } else (HTTPObj.readyState != "complete" {
                /* This branch is only valid when used with the XMLHttprequest
                * object. Then readyState will be a number. With iframes,
                * IE triggers the onreadystatechange event before
                * we're ready and would display this alert and then
                * finish the process.
                */
                alert(httpReq.status + ": " + httpReq.status.Text);
            }
        } catch (e) {
            alert("Network error!\nPlease try later.");
        }
    }
}//eof handleResponseCityState

The highlighted line is where the extra code for handling more than one zip code starts. We're simply passing the data on to another function. This allows us to use exactly the same code used in iframe example, which is copied below.

function processZipLookUp(data){
    /* Zip codes returned as comma delimited string */
    var links = '', alist = data.split(',');
    var dlist = document.getElementById('dlist');
    var opt;
    /* Add each zip entry */
    dlist.options.length = 1;
    for (var i = 0; i <alist.length; i++) {
        opt = new Option(alist[i], alist[i], false, false);
        dlist.add(opt, null);
    }
    if ( i > 9) dlist.size = "10";
    else dlist.size = "1";
    /* Position menu */
    var xy = getObjectUpperLeft(zipField);
    dlist.style.left = xy.x + 'px';
    dlist.style.top = (xy.y + 5) + 'px';
    dlist.style.display = "block";
    dlist.focus();
    toggleEscListener(true);
} //eof processZipLookUp

When multiple zip codes are returned (think New York or Chicago) the code must provide for user selection. (I don't have nor would my Host provider allow me to install a database that includes the street address.) To handle this, four additional functions are included.

getObjectUpperLeft
GetObjectUpperLeft corner calculates the position where the upper left corner of the zip code pick list will appear. dList is already in the html.
fillZip
This does what you would expect. It puts the users choice in the field.
listener4esc
As a special touch, the user can close the pick list without having made a choice by pressing the Esc key.
toggleEscListener
To avoid dealing with the Esc key when a pick list is visible, the listener is turned on or off as needed.

fillZip

function fillZip(e) {
    /* Put user zip code selection in field*/
    e = e || event;
    var elem = e.target || e.srcElement;
    var zip = document.getElementById("exp2").elements['zip'];
    zip.value = elem.innerHTML;
    zip.focus();
    document.getElementById("dlist").style.display = "none";
    toggleEscListener(false);
}//eof fillZip

getObjectUpperLeft

function getObjectUpperLeft(obj){
    /* For positioning in reference to zip code field
     * Fix due to IE 7 requires that the fieldset be in a
     * positioned block (position: relative; will do)
     * Works with all browsers
     */
    var x = obj.offsetLeft + (typeof obj.clientLeft != "undefined" ? obj.clientLeft : 0);
    var y = obj.offsetTop + obj.clientHeight + 2;
    return {x:x, y:y};
}//eof getObjectUpperLeft

listent4esc

function listen4esc(e) {
   /* Event handler to close zip code
    * menu on esc key with no selection
    */
    e = e || event;
    keyCode = e.keyCode || e.charCode;
    if (keyCode == 27) {
        document.getElementById("dlist").style.display = "none";
        toggleEscListener(false);
    }
}//eof listen4esc

toggelEscListener

function toggleEscListener(on) {
   /* Bind and release event handler so
    * it is only running when menu is up
    */
    if (browser.isMSIE) {
        if (on) document.body.onkeydown = listen4esc;
        else document.body.onkeydown = "";
    } else {
        if (on) window.onkeypress = listen4esc;
        else window.onkeypress = "";
    }
}//eof toggleEscListener

This additional code is outside of our purpose here, demonstrating remote procedure calls using AJAX, so I won't elaborate on it. The code was needed so the example could work in a reasonable fashion, and I show the code to be complete. It is included in the download.