Adding Persistence to DHTML Effects

Originally published at Netscape Enterprise Developer, now archived at the Wayback Machine

Dynamic HTML (DHTML), implemented in Netscape Navigator 4.x and Microsoft Internet Explorer 4.x, gives the Web page visitor the ability to alter the position, format, or visibility of an HTML element. However, the effects that are created with DHTML are transitory in that the next time the page is refreshed, the current DHTML state is not maintained and the page opens showing the same content layout as when the page is first loaded. Any changes to the content based on DHTML are not maintained. Sometimes this isn’t a problem, but other times this is irritating to the reader. This article covers how to add persistence to a DHTML page. Examples should work with all forms of Netscape Navigator 4.x and Internet Explorer 4.x, but have only been tested with IE 4.x and Netscape Navigator 4.04 in Windows 95.With DHTML, developers can provide functionality that hides or shows whichever layer the Web page reader wants to view. Developers can also add functionality that lets readers move content. The problem is that DHTML effects are not session persistent, which means that they aren’t maintained between page reloads. Following a link to another site and returning to the page can trigger a reload, which wipes out the effect and annoys the user,. especially if he or she has spent considerable effort achieving the current DHTML state.

For example, you can use DHTML to let your reader position your page contents for maximum visibility in her browser and get the page look just right. If she decides to leave your site to check Yahoo’s news for a few minutes and then comes back, her settings will have been erased.

So, what’s the solution to these problems? It’s relatively simple — add document persistence using Netscape-style cookies.

Using cookies 
Despite the name, you won’t find Netscape-style cookies at your local bakery. Cookies are bits of information in the form of name-value pairs that are stored on the client’s machine for a set period of time or until the browser is closed. For security, cookies are created and accessed using specific syntax, are limited to 300 cookies total within the cookie database at 4K per cookie, and are limited to 20 cookies per domain (the URL where cookie was set).

Cookie syntax consists of a string with URL characters encoded, and may include an expiration date in the format:


Netscape has a complete writeup on cookies (see our Resource section at the end), including functions that can be copied and used to set and get cookies. I created modified versions of these functions to support my DHTML effects. As I don’t want to add to overly burdened cookie files, I don’t set the expiration date, which means the cookie does not get stored in the persistent cookie database or file. This also means that the DHTML effect only lasts for the browser session. However, this fits my needs of maintaining a persistent DHTML state in those cases where the reader moves to another site or hits the reload button.

DHTML state cookie functions
I created a JavaScript source code file called cookies.js that has two functions. One function sets the cookie by assigning the value to the document.cookie object. More than one cookie can be set in this manner, as cookie storage is not destructive — setting another cookie does not overwrite existing cookies, it only adds to the existing cookie storage for the URL. The cookie setting function is shown in the following code block:

// Set cookie name/value
function set_cookie(name, value) {
   document.cookie = name + "=" + escape(value);

Next, the function to get the cookie accesses the document.cookie object and checks for a cookie with a given name. If found, the value associated with the cookie name is returned, otherwise an empty string is returned. The code for this function is:

// Get cookie given name
function get_cookie(Name) {
  var search = Name + "="
  var returnvalue = "";
  if (document.cookie.length > 0) {
    offset = document.cookie.indexOf(search)
    if (offset != -1) { // if cookie exists
      offset += search.length
      // set index of beginning of value
      end = document.cookie.indexOf(";", offset);
      // set index of end of cookie value
      if (end == -1)
         end = document.cookie.length;
      returnvalue=unescape(document.cookie.substring(offset, end))
  return returnvalue;

That’s it to set and get cookie values. The next section shows how to create two Web pages with simple, cross-browser DHTML effects. Then each page is modified to include the use of cookies to maintain DHTML state.

_BREAK1 Creating the DHTML Web pages
To demonstrate how to add persistence to DHTML pages, I created two Web pages implementing simple DHTML effects. The first page layers content and then hides and shows the layers based on Web-page reader mouse clicks. The second page has a form with two fields, one for setting an element’s top position and one for setting an element’s left position. Changing the value in either field and clicking an associated button changes the position of a specific HTML element.

Adding DHTML persistence for layered content
For the first demonstration page, a style sheet was added to the top of the page that defines positioning for all DIV blocks, formatting for an H1 header nested within a DIV block, and three named style sheet classes. The complete style sheet is shown here:

<STYLE type="text/css">
        DIV { position:absolute; left: 50; top: 100; width: 600 }
        DIV H1 { font-size: 48pt; font-family: Arial }
        .layer1 { color: blue }
        .layer2 { color: red }
        .layer3 { color: green }

Next, three DIV blocks are used to enclose three layers, each given the same position within the Web page. Each block also contains a header (H1), with each header given a different style-sheet style class. The HTML for these objects is:

<DIV id="layer1">
<H1 class="layer1">LAYER--BLUE</H1>
<DIV id="layer2" style="visibility:hidden">
<H1 class="layer2">LAYER--RED</H1>
<DIV id="layer3" style="visibility:hidden">
<H1 class="layer3">LAYER--GREEN</H1>

That’s it for the page contents. To animate the page, I created a JavaScript block that contains a global variable and two functions. The global variable maintains the number of the layer currently visible. The first function is called cycle_layer; this function determines which layer is to be hidden and which is shown next, and then calls a function that performs the DHTML effect:

<SCRIPT language="javascript1.2">

// current layer counter
current_layer = 1;

// assign document clicks to function pointer
document.onclick = cycle_layer;

// cycle through layers
function cycle_layer() {
   var next_layer;
   if (current_layer == 3)
        next_layer = 1;
        next_layer = current_layer + 1;
   switch_layers(current_layer, next_layer);
   current_layer = next_layer;

The scripting block also assigns the function cycle_layer as the event handler function for all mouse clicks that occur within the document page. By doing this, clicking any where in the page document area that doesn’t include any other content results in a call to the function to change the layer.

The switch_layers function is the DHTML effects function, and it uses the most uncomplicated technique to handle cross-browser differences: it checks for browser type and then runs code specific to the browser. Other techniques can be used to handle cross-browser differences, but these are outside of the scope of this article. All that the function does is hide the current layer and show the next layer in the cycle, as shown here:

// hide old layer, show new
function switch_layers(oldlayer, newlayer) {
   if (navigator.appName == "Microsoft Internet Explorer") {
        document.all.item("layer" + oldlayer).style.visibility="hidden";

        document.all.item("layer" +
   else {
        document.layers["layer" + oldlayer].visibility="hidden";
        document.layers["layer" + newlayer].visibility="inherit";

Try out the first sample page. Clicking on the document background, not the text, changes the layer. Try setting the layer to the green or red layer, accessing another site using the same browser window, and returning to the sample page. When you return, the page is reset to the beginning DHTML effect, showing the blue layer.

To correct the loss of the DHTML effect, we can add persistence to the page by storing which layer is showing when the page unloads. This information is captured in a function called when the onUnLoad event fires.

To add persistence, I added the cookies Javascript source code file to the page, using an external source code reference:

<!-- add in cookie functions -->
<SCRIPT language="javascript" src="cookie.js">

Next, I added the function to capture the DHTML state:

// onunload event handler, capture "state" of page (article)
function capture_state() {

When the page reloads, the onLoad event is fired, and a function is called from this event to “redraw” the DHTML effect. The function is called start_page, and it pulls in the cookie containing which layer should be shown:

function start_page() {
// get cookie, if any, and restore DHTML state
  current_layer = get_cookie("current_layer");
  if (current_layer == "")
        current_layer = 1;
        switch_layers(1, current_layer);

Finally, the two event handlers, onUnLoad and onLoad, are added to the BODY HTML tag:

<BODY onload="start_page()" onunload="capture_state()">

You can try out the second sample page, which has DHTML persistence — again, change the layer, open some other site and then return to the sample page. This time, the DHTML effect persists during a page reload.

Sometimes an effect requires more than one cookie, as the next example demonstrates.

_BREAK2 Adding DHTML Persistence for Positioned Content
In the next example, I used a style sheet again to define CSS1 and CSS-P formatting for the DIV block that is going to be moved in the page:

<STYLE type="text/css">
        DIV { position:absolute; left: 50; top: 100; width: 600 }
        DIV H1 { font-size: 48pt; font-family: Arial }
        .layer1 { color: blue }

Next, I created a form that has two text elements and associated buttons. One text and button element pair is used to change the DIV block’s top position, one pair is used to change the DIV block’s left position. The entire form is shown here:

<DIV id="first" style="position:absolute; left: 10; top: 10; z-index:2">
<form name="form1">
Set new Left Value: <input type="text" name="newx" value="50">
<input type="button" onclick="newleft(newx.value)" value="Set New
Set new Top Value: <input type="text" name="newy" value="100">
<input type="button" onclick="newtop(newy.value)" value="Set New Top">

Next, the positioned DIV block is created:

<DIV id="layer1" style="z-index: 1">
<H1 class="layer1">LAYER--BLUE</H1>

Once the page contents have been created, you can add code to animate the DIV block based on whether the top or left position is changed. The function to change the top position is:

// change top value
function newtop(newvalue) {
   if (navigator.appName == "Microsoft Internet Explorer") = parseInt(newvalue);
   else = newvalue;

Again, the least complex technique to handle cross-browser differences is used, which is to check for the browser being used and run the appropriate code. The function to change the left position is identical to the top position function, except the CSS-P attribute “left” is changed rather than the CSS-P attribute “top”:

// change left value
function newleft(newvalue) {
   if (navigator.appName == "Microsoft Internet Explorer") = parseInt(newvalue);
        document.layer1.left = newvalue;

That’s it for this page. You can try it out on the third sample page, where you can change the position of the DIV block by changing the left position, the top position, or both. Again, open another site in the browser and then return to the example page. Notice how the position of the DIV block does not persist between page reloadings.

Adding persistence to this page is very similar to adding persistence to the layered example page, except two values are maintained: the top and left values. The start_page and capture_state functions for this DHMTL effect are:

function start_page() {
// get cookie, if any, and restore DHTML state
  var tmpleft;
  tmpleft = get_cookie("currentleft");
  if (tmpleft != "") {
        currentleft = parseInt(tmpleft);
        currenttop = parseInt(get_cookie("currenttop"));
        if (navigator.appName == "Microsoft Internet Explorer") {
        else {
           document.first.document.forms[0].newx.value = currentleft;
           document.first.document.forms[0].newy.value = currenttop;

function capture_state() {
        set_cookie("currentleft", currentleft);
        set_cookie("currenttop", currenttop);

To see how it works, try out the fourth sample page, move the DIV block, open another Web site in the browser and return to the example page. This time the DHTML effect does persist between page reloadings.

Summing up
In a nutshell, the steps to add persistence to a page are:

  1. Create the DHTML page
  2. Determine the values that must be maintained to re-create an existing effect
  3. Add the Netscape-style functions to the page
  4. Capture the onUnLoad event and call a function to capture the DHTML effect persistence values
  5. Capture the onLoad event and call a function to restore the DHTML effect from the persisted values

Using Netscape-style cookies to maintain DHTML effects won’t help with some DHTML persistence problems. For example, DHTML effects can be lost when a page is resized. In addition, if a page is resized between the time the DHTML persistence values are stored and the page is reloaded, the values may no longer work well with current page dimensions. However, for most effects and for most uses, the use of Netscape-style cookies is a terrific approach to DHTML persistence.

Using Dynamic HTML to create an animated menubar

Originally published in Netscape World, now archived at the Wayback Machine

Microsoft and Netscape decided to use two different techniques for Dynamic HTML. Microsoft bases its approach on exposing CSS1 (Cascading Style Sheets) attributes to script, with a little help from some interesting new built-in, lightweight, and windowless ActiveX controls. Netscape bases much of its approach on the new LAYER tag.

At first glance, the approaches seem incompatible — using one browser’s techniques will not work with the other. However, you can use both browsers’ techniques within the same page and generate the exact same result.

In this article, I create an animated menu bar using a combination of CSS1 style sheets, the PATH ActiveX control, and dynamically modifying HTML elements to work with IE 4.0. I create the exact same effect using CSS1 style sheets and the LAYER tag for Navigator 4.0. Note, though, that both IE 4.0 and Navigator 4.0 are works in progress, and this example may need to be modified, slightly, for future versions of either product.

Creating style sheetsThe first section of the example page is the CSS1 style sheet settings. Both IE 4.0 and Navigator 4.0 process these style sheets, though Navigator does not seem to like setting images using the IMG tag at this time.

This example image uses headers for the text-based portion of a menu bar. To create the effect I want, I don’t want the standard underline that occurs with hypertext links, and I do want to modify the font size and color. As I don’t want either of these changes to impact on the rest of the document, I use the cascading effect of CSS1 to apply these changes only to links contained within <H2> header tags:

h2 A { text-decoration: none; color: gold } 
h2 A:link { color: gold } 
h2 A:visited { color: gold }

Next, I create the style sheet definitions for the menu images, for use with IE 4.0 only. I use absolute positioning to place the images exactly where I want on the page, and use the visibility property to hide the images when the page is first opened:

#menu1 { position: absolute; left: 0; top: 0; 
	visibility: hidden}
#menu2 { position: absolute; left: 142; top: 0; 
	visibility: hidden}
#menu3 { position: absolute; left: 284; top: 0; 
	visibility: hidden}
#menu4 { position: absolute; left: 192; top: 0; 
	visibility: hidden}
#menu5 { position: absolute; left: 142; top: 192;
	visibility: hidden }
#menu6 { position: absolute; left: 284; top: 192; 
	visibility: hidden}

The final part of the CSS1 style sheet definition creates the text-based menu components of the menu bar. These show as soon as the page is opened, and if the Web page reader does not want to wait for the images, or has image downloading turned off, they can access the links via the text menu. The size of the area for the menu text item should be large enough to contain the complete text, otherwise odd behavior will result with Internet Explorer: It does not display the text correctly when you click on a text-based menu item. Another text-based style sheet rule is provided for a message that is displayed while the images are loading. Whenever you create content that may be invisible or not displayed for a time, provide some kind of feedback to the user about what is happening:

This is the final section of the CSS style sheet definitions.

Adding images to a Web pageThe next section in the example page is the scripting, but I will talk about that a bit later. For now, I will add the images to the Web page using two different techniques, one for IE 4.0, and one for Navigator 4.0. IE 4.0 uses the style sheet settings defined for the images, and references the appropriate definition by using the identifier of the style sheet rule. The Navigator technique is to embed the images within layers that are hidden when the page first loads. IE 4.0 ignores the LAYER tag, and Navigator ignores the image style sheet rules:

Note that a hypertext link reference is applied to the image, and the border is set to 0. As the image style sheet rules seem to be ignored by Navigator, important image information such as widthheight, and border are specified within the image tag.

Next, the text objects are added to the menu bar. This includes a text-based alternative that labels each image when it is displayed, or can be accessed directly if the image is not displayed. This also includes a message stating that the images for the menu bar are being loaded:

The “waiting…” text is placed within a LAYER tag as I need to add script to modify it later, and only layer-based elements can be modified with Navigator at this time.

The final objects to add to the page are six ActiveX PATH objects, to control the menu bar animations for IE 4.0. A path object can be used to specify both X and Y coordinates at specific tick marks and moves the visual object associated with the control along that path. Two of the parameters for the control are the X path coordinates and the Y path coordinates. Another parameter could also control the timer, but I want to synchronize my image movement, so I use a different script-based approach later:

Notice that the X, Y coordinates for each path object are different, but all of them have the same number of timing ticks, which is the first parameter given in each of the coordinate pairings. The tick timings given are in microseconds.

The PATH ActiveX controls are the last elements placed into the page. Next comes the fun part, animating the menu bar.

Adding the script to animate the menu barThe animation is started when the page loads by trapping the onLoad event for the document. This calls a function called cycle. This function is implemented in both VBScript and JavaScript, and the appropriate browser picks the appropriate implementation, with Microsoft grabbing the VBscript version, Navigator grabbing the JavaScript version.

The first script controls the menu bar for IE 4.0. This is written entirely in VBScript, though JavaScript could be used if browser differences are handled correctly. The cycle function sets the Target properties of the path objects to the style sheet rules of the menu bar images. This associates each path control with its visual object. Next, the images are set to be visible, and the “waiting..” text is hidden.

The Play method for each path control is called, beginning the animation process, though no movement occurs yet. Why? No movement occurs because no timing mechanism has been associated with each control. The timing is handled by using the setTimeout function and calling a second subroutine that handles the image animation. The complete text of the cycle subroutine is here:

I use my own timer to synchronize the images, a technique that Microsoft recommends when using more than one PATH control on the same Web page.

The MoveIEObjects subroutine calls the Tick method for each of the path controls, moving each of the objects to the next coordinate defined for the specific control. It issues another setTimeout function call to continue the animation process, after first testing to see if the last movement of the path has occurred (the last of the ticks, of which there are eight for this particular demonstration). Note that I did not necessarily have to test the tick movement because after the path has completely played, it no longer moves unless it is rewound. However, I want to make sure the timer is not still operating. The complete text of MoveIEObjects is given next:

Next, I add the JavaScript to control the animation for Navigator 4.0. The cycle function for Navigator is similar to that for IE 4.0, except that I need to create my own arrays of X and Y coordinates. These arrays, in turn, are set to hold other arrays holding the actual coordinates. After the arrays are created, the images are displayed and the timer is started, as seen next:

The final script is for the MoveObjects function for Navigator. The LAYER tag method moveTo is used to move each of the images to the new coordinate for this timer event. Note that the timer values are set to be much larger than the ones set for IE 4.0 as the PATH control does run a bit slower than using the LAYER tag approach, and I wanted both effects to be as similar as possible. MoveObjects can be seen next:

Note that resizing Navigator can misalign the images. Netscape recommends a blank document.write statement in JavaScript to compensate for this type of problem, but this does not work for this example. I will continue to investigate this and will post a fix to my Web site when one appears. In the meantime, if you resize the Web page you need to reload the example. No problem occurs when you resize IE.

That’s it for the scripting. You can test the example yourself if you are using either IE 4.0 or Netscape 4 (this script will not work on earlier browsers). View the complete source by using the appropriate “View Source” option of your browser.