CGI Communication

Although DHTML in the version 4 browsers is not specifically geared to interact with a server-side process, using some tricks it can be accomplished. This is particularly useful for building true applications using DHTML. You could create a wide range of applications such as some really nifty shopping carts, or a nice DHTML interface to a database, or possibly even a chat room if you're inclined to put in the time to write one. Following the guidelines I will explain here these things are all theoretically possible.

The basic technique to use is really just a derivative of the load external files technique. You load in external files that are generated by a CGI script or equivalent server-side process like ASP, PHP, Java Servlets, or any number of web database languages like ColdFusion, Domino, Oracle etc. The fun part is, you can't send any information to the CGI by submitting HTML Forms, or by changing the location of the page (the way CGI scripts usually work). The whole point of developing a DHTML interface is to make the page static, but keep the information contained in it dynamic and updatable. This means you cannot ever change the location of the page, you must load files (containing new information generated by the CGI) into layers contained within the static page.

The only other alternative to a pure DHTML-to-CGI process, would be to create a Java Applet, and let that communicate with a CGI, or a Java Servlet to bring in your new information. Although I speculate that could get extremely complated and may not even work to begin with.

To communicate with a server using a regular CGI process there is only one solution: query strings!!!.

Your CGI script must be able to accept query strings instead of regular form parsed values. We will use query strings to pass all the relavent data to the CGI - I will be using a simple Perl script to accomplish this task.

Gathering the Data

The easiest way to gather your data is through HTML Forms. But you must realize that this is not the only way to gather data. You could create your own GUI elements to switch widget-like images on and off or any other crazy ideas you have. For simplicity in this example I'll just use a simple form that asks what your favourite operating system is:

<form name="myform">
<p>My Favourite Operating System is:

<p><input type="Radio" name="os" value="win9x">Windows 95/98
<br><input type="Radio" name="os" value="winnt">Windows NT 3.5/4.0
<br><input type="Radio" name="os" value="mac">MacOS 7/8
<br><input type="Radio" name="os" value="linux">Linux
<br><input type="Radio" name="os" value="solaris">Solaris
<br><input type="Radio" name="os" value="freebsd">FreeBSD
<br><input type="Radio" name="os" value="beos">BeOS
<br><input type="Radio" name="os" value="handheld">Handheld (PalmOS/WinCE)
<br><input type="Radio" name="os" value="otherunix">Other Unix-based
<p>
<input type="button" value="Submit" onClick="submitForm()">
</form>

Notice there is not ACTION associated with the Form tag, that's a no-no. You must create a JavaScript function of some sort to collect your data into individual variables. In my case, I just need to know which operating system was selected. So I created a submitForm() function to get that value when the Submit button is clicked:

function submitForm() {
	for (var i=0;i<document.myform.os.length;i++) {
		if (document.myform.os[i].checked) {
			var os = document.myform.os[i].value
			break
		}
	}
	alert(os)
}

View cgicomm1-form.html to view the form.

Getting The CGI Process To Work

Before we go head first into writing a big Perl script, we better do a little test to make sure this will in fact work. What I did was create a "results" layer which will contain the Perl-generated external page. To load the page I'll use the DynLayer load() method. Because the load() method uses an IFrame called "bufferFrame" we must include that as well. If you didn't read the DynLayer load() section yet, I recommend you do so now to understand what I'm doing.

The CSS (auto-generated via the css() function):

writeCSS (
css('resultsText',250,30)+
css('resultsDiv',250,50,200,100,'#c0c0c0')
)

The Div's:

<iframe style="display:none" name="bufferFrame"></iframe>

<div id="resultsText"><b>Results Layer:</b></div>
<div id="resultsDiv"></div>

In order to use the DynLayer load() method, we must have both the dynlayer.js and the dynlayer-common.js (which contains the load function) in the page:

<script language="JavaScript" src="../dynlayer/dynlayer.js"></script>
<script language="JavaScript" src="../dynlayerext/dynlayer-common.js"></script>

The resultsDiv layer must be initialized (DynLayerInit() can be used) and the load() method applied to it:

function init() {
	DynLayerInit()
	results.load = DynLayerLoad
}

When the form is submitted, we must change the location - by using the load() method - directly to the Perl script:

function submitForm() {
	for (var i=0;i<document.myform.os.length;i++) {
		if (document.myform.os[i].checked) {
			var os = document.myform.os[i].value
			break
		}
	}
	results.load("/cgi-bin/dynduo/cgicomm-test.pl")
}

For this "test" case, the cgicomm-test.pl script can be simple, it just writes out a simple page that does what all external files must do - call back to the layer it's being loaded to, to complete the loading sequence.

#!/usr/local/bin/perl

print "Content-type: text/html\n\n";
print "<html*gt;<body onLoad=\"parent.results.loadFinish()\"*gt;\n";
print "This text came from a perl script!";
print "</body*gt;</html*gt;\n";

View cgicomm2-cgitest.html to view a preliminary test of the DHTML-to-CGI communication technique. View Source Code

As you can see this process works fine, so lets finish it up...

Finalizing The Perl Script and Query Strings

For this example the Perl script doesn't really do anything except gather the query strings (only one string actually - "os"), and then prints out a page.

#!/usr/local/bin/perl

# Get the query strings
@qsets = split (/&/,$ENV{'QUERY_STRING'});
foreach $qset (@qsets) {
	@qsetpart = split(/=/, $qset);
	$qstr{$qsetpart[0]} = $qsetpart[1];
}

# make a list of the full names for the OSes
$osNames{'win9x'} = "Windows 95/98";
$osNames{'winnt'} = "Windows NT 3.5/4.0";
$osNames{'mac'} = "MacOS 7/8";
$osNames{'linux'} = "Linux";
$osNames{'solaris'} = "Solaris";
$osNames{'freebsd'} = "FreeBSD";
$osNames{'beos'} = "BeOS";
$osNames{'handheld'} = "Handheld (PalmOS/WinCE)";
$osNames{'otherunix'} = "Other Unix-based";

# get the full name of the OS that was selected and sent as a query string 'os'
$os = $osNames{$qstr{'os'}};

# print the page
print "Content-type: text/html\n\n";
print "<html><body onLoad=\"parent.results.loadFinish()\">\n";
print "This text came from a perl script!";
print "<p>You have chosen:<br>$os\n";
print "</body></html>\n";

To test this script out you could point the browser to the script with a query string manually attached: /cgi-bin/dynduo/cgicomm.pl?os=win9x. It'll cause a JavaScript error when it tries to find the "results" layer, but you can see the script works fine.

There's only one small change we need to make in the JavaScript to finish everything up. We need to send the "os" variable to to the perl script when you submit the form:

if (os) results.load("/cgi-bin/dynduo/cgicomm.pl?os="+os)

And Voila! We have DHTML and JavaScript working together with Perl!

View cgicomm3-final.html to view a DHTML-to-CGI communication example. View Source Code

As you can see it's really not that difficult. This general idea could be extrapolated significantly to open up a wide range of possibilities. I will be doing some more work on this, I may try to build a DHTML interface to my Forum. And I'll expand on this technique further in the future.

Home Next Lesson: DHTML Buttons
copyright 1998 Dan Steinman