[This article originally appeared in August of 2000 when, I assure you, it made more sense. I add it here for posterity as it was, by far, the most popular piece of content I ever developed (as sad as that may be).]
With this technique you can create adaptive forms, gain detailed control over session time out, better deal with user error, and other things we haven’t thought of.
How it Works
We’ll explore a simple example. The user will be presented two form fields. Every five seconds whatever is currently entered in the upper field will be sent to the server and the value of a cookie will be output to the second field. The server will then prepend that string to the current date and time and populate our cookie with the result.
Note that in this case we use the onLoad event handler in the body tag to kick off the action. The onLoad handler ensures that all the page assets, including our img tag, are loaded before triggering prevent “object not found” errors. However, depending on your need, you may use a user trigger event such as a button click or an image mouse over to launch your communications.
The GetServerData() function actually sends the data to the server. Line 8 builds the URL with two variables: “Message”, containing the escaped (escape() is equivalent to URLEncodedFormat() in CFML) content of the Message form field. The other variable, “TimeSeed” is simply the current browser time. Something like TimeSeed is needed to ensure that every URL created is unique and not pulled from the browser cache. Of course your seed can be just a simple counter or random number. The function finishes by calling the UpdateForm() function and restarting the timer with the SetTimer() function.
One thing to understand is that in this code, since the form is updated immediately after the graphic’s source is changed, it actually takes two cycles for the new information to show up. Once the first cycle of the data is sent to the server and immediately before the server is able to respond, the value of the cookie is displayed. Then, on the next cycle the same thing happens, but now the cookie has the new value.
Now let’s look at (much simpler) ColdFusion code that replaces our graphic:
<!--- Default the Message Variable ---> <cfparam name="URL.Message" default="" type="string"> <!--- Set the Time Cookie ---> <cfset RawMessage = "Your Message:(" & #URL.Message# & "); Server Time:(" & #TimeFormat(Now(), "hh:mm:ss")# & "," & #DateFormat(Now(), "mm/dd/yy")# & ")"> <cfcookie name="ServerMessage" value="#RawMessage#"> <!--- Write out the real GIF ---> <cfcontent type="image/GIF" file="d:WWWRootTestSessionTimeSpacer.gif">
The last line is the trick. We use cfcontent to send an actual GIF image to the browser. In this case the gif is a single pixel, transparent graphic that won’t be seen by the user. You can, of course, use any image(s) that you like.
New and Improved Script (Added April 14, 2001)
Although all the above code works just fine and gets the point across there has been one additional feature requested by several people: the ability to “know” when the server response has been received. The code above works perfectly well when the client has no need to examine the server response (for example to maintain session state). A problem arises however when the client needs to react to the server response as quickly as possible. This updated code addresses that problem.
The primary addition is the CheckCookie() function (beginning on line 25) which is called (from within SendServerData() ) immediately after information is sent to the server (line 10). CheckCookie() takes the date/time seed created in SendServerData() as a transaction identifier. At its essence this function simply checks in the cookie for this transaction identifier until it finds it (which means that the cookie has been returned with updated information from the server) or times out.
As written the function recursively calls itself every 50 milliseconds (as defined in line 35) up to 500 times (as defined in line 29). These settings work out to a 25 second timeout. You should modify them to meet your needs. If the cookie changes properly before the timeout the “else” clause at line 38 is executed (which, in the example, runs the UpdateForm() function) if the timeout value is reached the “if” clause at line 30 is executed (which currently does nothing).
Another small modification from the original code is the use of the “valueOf()” method on the DateSeed variable (line 8). This converts the date/time object to a simple, smaller numeric value.
The updated client-side code:
The only change to the ColdFusion file is the addition of the DateSeed to the returned cookie:
<!--- Default the Message Variable ---> <cfparam name="URL.Message" default="" type="string"> <!--- Set the Time Cookie ---> <cfset RawMessage = "Your Message:(" & URL.Message & "); Server Time:(" & TimeFormat(Now(), "hh:mm:ss") & "," & DateFormat(Now(), "mm/dd/yy") & "); DateSeed" & URL.DateSeed> <cfcookie name="ServerMessage" value="#RawMessage#"> <!--- Write out the real GIF ---> <cfcontent type="image/GIF" file="d:WWWRootTestSessionTimeSpacer.gif">
Special thanks to James Pecora who collaborated with us on this code.
Ideas for this Technique
The above code may illustrate the technique but it doesn’t offer much in the way of useful functionality. Here are some ideas for using this technique to solve actual problems.
Gain Control over Session Timeouts
One continual problem with session-based CFML applications is the server-centric basis of customer usage. By default ColdFusion times out a user after 20 minutes of “inactivity”. However, inactivity in this case is limited to the retrieval of a CFML page marked as a member of the application in use. Often an application may present a task that requires more than twenty minutes of time between requests. A form could easily take more than twenty minutes to complete and some content could easily take more than twenty minutes to read.
Form fields could be made, using the onChange or onfocus handlers, to populate a variable with the time of the event, a “LatestEvent” tracker. The page could be set to check the value of that variable against the current time every, say, 5 minutes. If an event has occured in the past five minutes the script could launch a function that updates the graphic source. As long as the CFML template called is a member of the application, the user’s session will be properly maintained.
Another option is to present a pop-up window to the user informing them that their session is about to timeout. Their choice determines whether you modify the graphic src or not.
Manage Forms Better
The uses of the technique and forms are nearly endless. Although somewhat limited by the amount of data possible in the URL or Cookie there are still endless options. One of the easiest is adaptive forms that change as the user fills them out. A selection made in a select box could be sent to the server for processing and data to populate a second select box could be returned. Although special care needs to be taken to ensure that the lag in communication doesn’t affect the user experience, this option is very powerful when complex databases or calculations must be consulted to populate other options.
Another option is proactive data processing. For example, you may validate a credit card number via the graphic while the user is entering other information. This is where using visible graphics may come in handy. You can present an animated “checking credit card” graphic, then have the CFML template send either a “Credit Accepted” or “Credit Refused” graphic to the browser. The same idea can be used to the same effect for reverse DNS lookups, logins and other complex validations.
Generally Deal with the User more Intelligently
Using this technique it would be possible to, for example, spell check the values entered into a “search” field at the server before submitting the page. A a pop-up window or dynamic HTML routine could be used to present the corrected options before proceeding. Otherwise you either interrupt the process with another page to present the options or run the search anyway and possibly waste system resources and user time looking for a misspelled term. You could create a FAQ page that sent the internal (named) links that the user selected back to the server. This would allow you to better manage information and present more important items up front. Lacking a technique like this, those internal document links are lost and cannot be used to improve your user experience.
There are of course other ways to create in-page browser/server communication. Here are a few:
- The WDDX SDK available from [dead link] contains a free Java Applet, URLPipe, that does everything I’ve described and can deal with larger datasets. However, it does require that Java and scripting of Java applets be enabled.
- A hidden frame or layer could be used to send and retrieve data (and this technique would in fact be much better for larger, more complex data). Brent Ashley has, in fact, authored an entire library to do just this. It’s a great resource and available from Brent’s Remote Scripting Site. Thanks to Patrick Whittingham for pointing us to Brent’s work.
- Another option is Microsoft’s [dead link]. This works in much the same way as the URLPipe applet available in the WDDX SDK, but with many more options and documentation. Thanks to Patrick Whittingham for pointing us to this as well.
We hope that you’ll explore the potential of this technique. If you come up with something really slick you may want to consider doing a write up for it (or allowing us to). We’d be happy to share your idea with other readers!