Here are two examples of an RPC using an iframe to retrieve information from a database on the server. Clicking the button labeled "Find" sends the data (either zip code or city and state respectively) entered to the server-side script as the query or search part of the URL of the iframe.

On the server a remote procedure looks up the corresponding data in a database and returns it in a page to the iframe. That page and the main page contain code to fill in the respective fields. This is a better example of remote scripting than the arithmetic example since it accesses a large database—well, at least too large to download.

The Examples

Try it out. Enter a zip code (or city and state) and then click the appropriate button. If at first you don't get the desired result, try again. The database is old and missing some codes so you may have to try a couple.

Enter Zip find City-State
Enter City-State find Zip

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 would be slow.

Only two pieces of code presented in the explanation are modified.

  City Look Up Code

Examine the code that has been changed starting with the example looking up city and state based on zip code.

Start with the page returned to the iframe.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
    <script type="text/javascript">
        function returnCall() {
            var handle = null;
            if (parent!=self) {
                if (this.frameElement) {
                    handle = this.frameElement;
                } else if (parent.document.getElementById("iframe")) {
                    handle = parent.document.getElementById("iframe");
                } else {
                    alert("Out of luck");
                }
                if (handle) {
                    handle.callBack("{city: 'Kokamo', state: 'MI'}");
                }
            }
        }
        window.onload = returnCall;
    </script>
</head>
<body>
    <div id="data-wrapper">
        <div id='Kokamo'>&nbsp;</div>";
        <div id='MI'>&nbsp;</div>";
    </div>
</body>
</html>

A PHP script looks up the city and state based on zip code and generates the return html. Ignore the contents of the body section. That supports using DOM methods to extract the return data, but the example has been changed to use the JSON data format. The result is passed as a string argument to callBack.

JSON is a string, "{city:'Kokamo', state:'MI'}", that looks like a Javascript literal for an object. We'll see how this is converted into a JavaScript object in a moment. Now checkout the handler.

function handleResponseZip(){
    /* This clears a listbox if the user hasn't */
    document.getElementById("dlist").style.display = "none";
    if (HTTPObj.readyState == 4 || HTTPObj.readyState == "complete") {
        if (HTTPObj.status == 200) {
            var id = (HTTPObj.targetId) ? HTTPObj.targetId : HTTPTarget;
            var targ = document.getElementById(id);
            eval("var data = " + HTTPObj.responseText);
            targ.elements['city'].value = data['city'];
            targ.elements['state'].value = data['state'];
        } else if (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 so the test prevents showing the alert 
             * when using iframes and IE.
             */
             alert(HTTPObj.status + ": " + HTTPObj.status.Text);
        }
    }
} //eof handleResponseZip

This line, eval("var data = " + HTTPObj.responseText), converts the returned string into a JavaScript object. The for-name-in construct can be utilized to process the data; although, in IE there is an incompatibility with frames so I didn't use that.

See the Journal entry 2/8/2006 for more details on incompatibilities with IE.

  Zip Code Look Up

The page returned when finding the zip code for a specific city is similar to the page returned for finding a city based on zip code.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
    <script type="text/javascript">
        function returnCall() {
            var handle = null;
            if (parent!=self) {
                if (this.frameElement) {
                    handle = this.frameElement;
                } else if (parent.document.getElementById("iframe")) {
                    handle = parent.document.getElementById("iframe");
                } else {
                    alert("Out of luck");
                }
                if (handle) {
                    handle.callBack("60012,60013,60014");
                }
            }
        }
        window.onload = returnCall;
    </script>
</head>
<body>
    <div id="data-wrapper">  </div>
</body>
</html>

The data is returned as a comma delimited string or a single zip code.

The response handler code is much more complicated than previous examples because it potentially has to respond to more complex issues.

function handleResponseCityState(){
    /* Example 2b: Lookup zip code based on city and state.
     * 1. Handle 1 or no zip codes
     * 2. Make popup for multiple zip codes
     *    with zip code menu (dl-dt-dd)
     */
    if (HTTPObj.readyState == 4 || HTTPObj.readyState == "complete") {
        if (HTTPObj.status == 200) {
            var data = HTTPObj.responseText;
            var zipField = document.getElementById(HTTPObj.targetId).elements['zip'];
            var dlist = document.getElementById("dlist");
            if (data.length < 6) {
                zipField.value = data;
                dlist.style.display = "none";
            } else {
                processZipLookUp(data, zipField);
            }
        } else {
            //handler error
        }
    }
} //eof handleResponseCityState

As you can see, the handleResponseCityState is no different than the previous example when a single zip code or error is returned. However, 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.

processZipLookUp
Handles multiple possible zipcodes. Creates and displays the select list.
getObjectUpperLeft
GetObjectUpperLeft corner calculates the position where the upper left corner of the zip code pick list will appear. The select object, 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. This function handles that feature.
toggleEscListener
To avoid dealing with the Esc key when a pick list is not visible, the listener is turned on or off as needed.

Code to fill zipcode field allowing for the possible need to let the user select from a list.

function processZipLookUp(data, zipField) {
    /* Zip codes returned as comma delimited string */
    var links = '', alist = data.split(',');
    var dlist = document.getElementById('dlist');
    var opt;
    /* remove previous enteries */
    dlist.options.length = 1;
    /* Add each zip entry */
    for (var i = 0; i <alist.length; i++) {
        opt = new Option(alist[i], alist[i], false, false);
        if (browser.MSIE)
            dlist.add(opt, i + 1);
        else
            dlist.add(opt, null);
    }
    if ( i > 9) dlist.size = "10";
    else dlist.size = ++i;
    /* 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
function fillZip(opt) {
    /* Put user zip code selection in field*/
    var zip = document.getElementById("exp2").elements['zip'];
    if (opt.nodeName.toLowerCase() == "select")
        zip.value = opt.options[opt.selecctedIndex].text;
    else
        zip.value = opt.text;
    zip.focus();
    document.getElementById("dlist").style.display = "none";
    toggleEscListener(false);
}//eof fillZip

Code to position Zip code pick list.

function getObjectUpperLeft(obj){
    /* For postioning 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

Code to listent for ESC key to close pick list not selecting a zip code.

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

Code to toggel ESC kye Listener on and off.

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 scope, demonstrating remote procedure calls using iframes. The code was needed so the example could work in a reasonable fashion, and is shown to be complete. It is included in the rpc-handlers.js.