Uli's Web Site
[ Zathras.de - Uli's Web Site ]
Other Sites: Stories
Abi 2000
Stargate: Resurgence
Lost? Site Map!
     home | blog | moose | programming | articles >> blog

 Blog Topics

15 Most Recent [RSS]

 Less work through Xcode and shell scripts
2011-12-16 @600
 iTunesCantComplain released
2011-10-28 @954
 Dennis Ritchie deceased
2011-10-13 @359
 Thank you, Steve.
2011-10-06 @374
 Cocoa Text System everywhere...
2011-03-27 @788
 Blog migration
2011-01-29 @520
 All you need to know about the Mac keyboard
2010-08-09 @488
 Review: Sherlock
2010-07-31 @978
 Playing with Objective C on Debian
2010-05-08 @456
 Fruit vs. Obst
2010-05-08 @439
 Mixed-language ambiguity
2010-04-15 @994
 Uli's 12:07 AM Law
2010-04-12 @881
 Uli's 1:24 AM Law
2010-04-12 @874
 Uli's 6:28 AM Law
2010-04-12 @869
 Uli's 3:57 PM Law
2010-04-12 @867


Using DOM and JavaScript for dynamic Web UI

A while ago, Mike Zornek showed me a project he'd been working on. Basically, it was a web site that was built to replace a desktop application. One of the problems was getting it to be responsible enough so the users wouldn't notice a difference between their app and the site. Since responsiveness has traditionally been a problem with client/server software (due to each action requiring a transmission to the server and back over the Internet), he used a very clever trick: JavaScript and manipulating the DOM interface to move some of the page changes client-side, e.g. when people are using an interface where you can add or remove items. This gives great responsibility and speed improvements.

Since Mike is kinda busy right now learning Ruby on Rails and my boss reminded me that JavaScript is something most people turn off for security reasons (so I'm not very likely to be using this approach at work), I decided to research this thing on my own time. A quick Google brought up an article at Apple that details the general concepts, and soon I had it going. Since I'd love to see more pages offer this feature, I thought I'd throw together a script that does the basics:

The first thing we do is create a new HTML page containing an (empty) table to which we'll add our rows later:

        <table width="100%" id="rowlist">
            <!-- Our JavaScript will insert the rows here. -->
        <img src="plus_btn.png" onclick="newRow();">
This page will require two images: "plus_btn.png" and "minus_btn.png", which are icons for the "add row" and "remove row" buttons, like Finder's smart search folders or the Login panel have them. If you don't have any icons, you can also use a <button>+</button> tag with the onclick attribute instead.

So, this page now contains a pointlessly empty table with an id of rowlist (so we can refer to it later from our JavaScript code), and an image or button to add a new row. When clicked, this thing calls the JavaScript function newRow(). So, now might be a good time to define this new function. Add the following somewhere in the body section of the page:
<script language="JavaScript" type="text/javascript">
    var gUniqueRowID = 0;    // So each row gets a unique id.
    var gRowText = "<td>#ROWID#:</td><td>Two</td>"
            + "<td><img src=\"minus_btn.png\" "
            + "onclick=\"removeRowWithID('queryItem#ROWID#');\"></td>";
    function newRow()
        var table = document.getElementById( "rowlist" );
        var newRow = document.createElement( "tr" );
        table.appendChild( newRow );
        gUniqueRowID += 1;
        newRow.id = "queryItem" + gUniqueRowID;
        str = gRowText.replace( /#ROWID#/g, ""+gUniqueRowID );
        newRow.innerHTML = str;
gUnqiueRowID is a global where we keep a global ID for each new row. gRowText is the HTML used for each new row (I'm doing it this way since it is easier to customize that way). A placeholder #ROWID# will be replaced with the current value of gUniqueRowID when the row is added. That way, if your rows contain form fields, you can append the row ID to their names to later be able to access each field and row separately in PHP, Perl or whatever CGI language you're using.

Now, on to the actual newRow() method. The first thing it does is get the table and add a new tr-element to it. Then it increments the unique row ID and gives the new row a unique ID based on that ("queryItem1" etc.), so the JavaScript to delete a row later will be able to access this element and remove it again. Finally, it sets the innerHTML property of the table row to the HTML in gRowText, after replacing all occurrences of the #ROWID# placeholder with the unique ID. The g in the regular expression passed to replace() simply means replace all occurrences. innerHTML is an extension to DOM supported by the common browsers that lets you set or retrieve all children of an element as an HTML string, and thus changing this is equivalent to doing document.createElement() and newRow.appendChild() several times to manually add the table columns.

Note that gRowText also contains the code for the minus-button for removing the row again (which is analogous to the plus-button, except that it calls removeRowWithID() with the row's ID as the parameter). The one thing that's still missing is now the removeRowWithID() function to remove a row again. here it is:
function removeRowWithID( theRowID )
    var table = document.getElementById( "rowlist" );
    var rowToDelete = document.getElementById( theRowID );
    table.removeChild( rowToDelete );
There's not much to this function. It looks up the element with the ID passed and our table and them removes the row from the table.

And now, go and add this cool, speedy feature to all your web interfaces, so everyone using them can have the performance of a desktop application! :-)
Created: 2005-08-21 @664 Last change: 2005-08-21 @729 | Home | Admin | Edit
© Copyright 2003-2023 by M. Uli Kusterer, all rights reserved.