with Ges Seger

STRAAANGE COOODE

Today's Episode:

Poor Man's Database

Webmasters, like Starfleet engineers, always like to change things. I'm no exception to this. Most of this past Christmas break, for instance, was spent with the orange barrels set up around the Road Geek section of the family website solely because I felt it needed to be changed. Among other things I was rewriting, I was seriously dissatisfied with how I had the road trip pages set up. I didn't like the trip-based way I had used, and a rewrite to make it more road-based only seemed to make it worse. I wanted to put the various road segments in a database and generate the pages dynamically, but server-side databases don't seem to be something Earthlink offers to its residential users (you can all stop the smart-assed comments on getting a real ISP, okay?). I was stuck.

Or was I?

SOLUTION

A couple of different client-based techniques in a unique combination immediately suggested themselves in an effort to streamline the road trips database. The previous week, I had done an initial workup for an April Fool's STRANGE CODE episode that returned a random "Are You Pondering What I'm Pondering?" scene from Pinky and the Brain on repeated page loads. The technique there involved the (mis)use of Math.random(), <TBODY>, and the display property. Okay, so I could load the whole database and toggle visibility on its elements. What I now needed was a filtering technique more sophisticated than a simple random number generator.

Horror of horrors, my episode on form validation provided the perfect data filtering mechanism in its abuse of the className property. For those too lazy to click on the provided link, I looped over certain form elements upon submission and checked their data for validity based on which class was specified for that element. If I had a textfield that only accepted integer numbers, I set its class equal to "integer" and the validation script knew from that particular className setting which regular expression to use in order to validate that element's input.

For the road trip database, I wasn't concerned about data validation. No, my stroke of mad genius was in using the className property as a key field or index field. I mixed this with the Ponder-O-Matic, threw in a dash of child node manipulation straight from the recesses of the DOM, stirred, and presto! Instant database!

WALKTHROUGH

Let's go to the code. To start with, this application is divided into two HTML pages: the Application page (which will be the one the luser loads) and a Data page. The HTML for the Application page will roughly resemble the following structure:

The Data page will contain blocks and blocks of <DIV> containers, each holding a single data record and all initially having a display property set to 'none'. If we check the className properties for each data record, we will find that in addition to a class setting default style rules, we have extra classes corresponding to each search term that could apply to that particular record. All our controlling Javascript on the Application page then has to do is find each data record which has a className property matching the luser's search criteria and flip its display property to 'block'.

Here's the Javascript:

 0     function dbquery(menu) {
 1       var choice = menu.options[menu.selectedIndex].value;
 2       var db     = window.results.document.body;
 3	 document.body.style.cursor = 'wait';
    

The function takes one argument, menu, which is a reference to the calling object (in this case, a <SELECT> form control). Line 1 caches the luser-selected value from menu, and line 2 caches a reference to the Data page. Line 3 changes the default cursor to whatever your browser uses to indicate a long operation in progress, because what I'm about to do will take a few seconds. It's bad form to not give the luser some feedback that the browser's busy doing something.

 4       for (var i=0; i<db.childNodes.length; i++) {
 5         var entry = db.childNodes[i];
 6         if (entry.nodeType != 1) continue;
 7         entry.style.display = (entry.className.indexOf(choice) == -1)? 'none': 'block';
 8       }
    

The heavy lifting is done in this block. We loop over all the child nodes on the Data page and take action on each of them. Line 4 caches a reference to the current node. Like 5 checks to see if it's a Element node (as defined by the W3C. I do try to follow their standards sometimes...) and aborts to the next iteration if it isn't. Line 6 checks the className property of the current node and toggles its display property depending on whether it meets current search criteria. If the data record has been made visible by a previous search but doesn't meet current search criteria, we don't want to see it this time -- hence, the toggling.

 9       document.body.style.cursor = 'auto';
    

Finally, we need to clean up after ourselves. Remember where I set the cursor to indicate a long operation in progress back in line 3? Line 9 sets the cursor back to its default setting in the document, so the luser won't get angry at me for supposedly freezing their browser.

USAGE

Use dbquery() in an onchange handler of my <SELECT> menus as follows:

	<select name="highway" onchange="dbquery(this)">
    

BUGS FEATURES

As currently written, dbquery() does not support multiple criteria for searching. Nor does the active query menu blank out the other menu to make it more obvious what the search criteria are.

I fully expect W3C shocktroopers to be knocking on my door any day now for my flagrant abuse of the className property. I know it works for filtering data, but that doesn't mean it's right...

On initial page load, the entire database comes to your browser in one throw. Depending on your database size, this may be a problem in more technologically-Amish settings with no broadband access (such as Clan Seger World HQ, but it's not like I'm bitter or anything). On the other hand, you don't have to access the server each time you make a query.

Data will be returned from any search in the order which it was found within the document. As currently written, there is no capability to return data in any other sort order.

Setting the cursor at the <BODY> node of the document doesn't cascade down over all its child nodes.

EXAMPLE

I'm not going to provide one here when I can send you over to the Road Geek Section of the website and ask you to View Source on a working application based on this technique. Enjoy.