JavaScript-based RPC

This article is intended to demonstrate a very nice way to communicate with a server script from an HTML page, without forcing a full page reload. Communication could be needed, say, for automatic filling of some forms' fields, based on user input. But I am sure that other usages could be found...

Beware that there are more (and probably better) ways to do this, but this is, in my opinion, the simplest, because it requires less knowledge and no auxiliary tools, neither on client nor server side.

Note 1: this article is a very light presentation of this technique. I included only what I considered simple and beautiful. You can find more information in [1], including some tricks to avoid the "back/forward problem".

Note 2: the examples in this page were reduced to minimum coding necessary, so they might not work with any browser in existence. My tests show that Mozilla (thus any Gecko-based browser) or IE6 work. Opera, however, does not work correctly.

Note 3: trying the examples might screw up the history of your browser (specifically the "Back"/"Forward" buttons), making it behave strange until you go to a different page by directly clicking the link. We will explain why a bit later in this article.

First, the demo

The most simple demonstration I can think about, let's say that we want to find out the server's current time, without making a page reload, and without loading a different page. That is, we will keep the current page on screen, but if you click here, in the following rectangle you will receive the response from the server with the current time.

-- placeholder --

Note: the server deliberately sleeps one second before answering the client's request. This is so that you can see the nice red text that says "waiting response..." :) Of course, for more serious communication that could possibly involve transmission of large chunks of data over possibly slow networks, the "sleep" call is not necessary. Anyway, in these cases it is even more important to provide visual feedback to the surfer, like I did in this simple example.

Distribution

In this scene we have the following actors:

The server
Is the web server that serves you these pages. It is the backend process which responds to your request.
The client
This is the web browser. By itself, however, it doesn't know how to make a request to retrieve the time from server, therefore this is handled by the JavaScripts on this page.

How do they act?

A browser can only make HTTP requests. In order for we to get this answer from server, we actually have to make an HTTP request, and the server replies as usual with an HTML page containing the bit of information that we required.

For this to happen without replacing the page on screen, we request this page in another frame -- a hidden one. The tag that helps much here is IFRAME. IFRAME ("inline frame") -- it behaves like a frame, but the nice thing about it is that, regarding the layout, it can exist inside an HTML document. To make it hidden is therefore very simple -- we just set width, height and border thickness to "0px", using CSS, and we don't care where this frame is.

The link above asks the IFRAME to open a new document from server, inside the frame. We do this from JavaScript, by changing the frame's "src" attribute. The code looks like this:

// Retrieve the IFRAME element from the document tree
var iframe = document.getElementById("COMM");

// Retrieve the answer rectangle; used for providing visual feedback.
var answ = document.getElementById("ANSW");

// Announce the end user that we are going to make a call to the server
answ.style.color = "#f40"; answ.innerHTML = "waiting for answer...";

// By setting the "src" attribute of the hidden frame, we actually ask the
// browser to request a file from server, which in our case is generated by a
// simple CGI script.
iframe.src = "cgi/gettime.cgi";

The iframe variable will point to the IFRAME element, which is included in the document with the following HTML code:

<iframe id="COMM" style="border: none; width: 0px; height: 0px;"></iframe>

The most important fact about the HTML page returned by the script is that it triggers a JavaScript function which resides in the client side scripts.

So, you can notice that the BODY tag of the returned page has as handler for the "onLoad" event (which is called immediately after the page is loaded into the IFRAME) the following code:

window.parent.handleServerResponse('$today_time');

Because it's Perl, the $today_time variable is replaced with the current server time, when the page is printed to standard output. This is actually the message that the server sends to the client.

In the client side scripts, we have the following function defined:

function handleServerResponse(message) {
  // Print the response from server inside the rectangle
  var answ = document.getElementById("ANSW");
  if (answ) {
    answ.style.color = "#fefaf0";
    answ.innerHTML = message;
    alert("Message received from server:\n\n" + message);
    // Make it disappear after 5 seconds :)
    setTimeout('normalizeAnswer();', 5000);
  }
  var iframe = document.getElementById("COMM");
  if (iframe) {
    // Here we set a different URL for the inline frame, so that it works the
    // next time when clicked
    iframe.src = "about:blank";
  }
}

Cache problems

You might wonder what the "meta" line that says "no-cache" is doing in the response page.

This tells the browser to not cache this HTML page. So, every time when the browser will need this page, it will request it directly from server, instead of just returning it from cache. It is important, because the answer is different from one second to another :).

Summary

To request a bit of information from server, an HTML page needs to contain:

An IFRAME
Other idea is to create this frame using DOM. This frame will represent the "information requester and receiver". This can be hidden by setting it's size and border thickness to zero from CSS.
A trigger to call the server
The link/button that allows an end user to trigger the call to server will request the page by setting the IFRAME's "src" attribute to the URL of the page/script that should produce the answer.
A handler function
This is a JavaScript function that will be called from the "onLoad" handler of the BODY in the page retrieved in IFRAME using "window.parent.functionName(params);". After interpreting/showing the results, this handler should also set the "src" attribute of the IFRAME to another document, e.g. "about:blank".

References

[1] Remote Scripting with IFRAME, by Eric Costello and Apple Developer Connection.

Style config