Note: This script is superseded by the new display: block navigation script. This page is no longer maintained.

This script is DHTML, so it won't work in Version 3 and lower browsers. In addition, it does not work in Netscape 4.

Display: block

On this page I explain what I call the display: block script. It runs on this page. Click on the first paragraph to open it.

Display: block/none

Display is a very useful CSS property. In essence, it tells the browser that the elements that have a style 'display' can be added to or removed from the page.

If you switch the style.display of an element to 'none', the browser removes the element from the page and makes the other elements on the page flow into the gap. If you switch it to 'block' again, the element is restored and the other elements are moved so that the restored element fits into place nicely.

All this can be done by a simple JavaScript. However, there is one major problem: Netscape 4 does not support display. First I thought that I had written a Netscape 4 workaround, but testing revealed that many Netscape 4's crashed on the script.

Explorer 4/5 and Netscape 5

But let's start with the easy part: the browsers that do support display.

First of all, you need to put each part of the page that you want to block inside a <DIV>. Each <DIV> has its own unique ID. I called them number1, number2 etc.

In addition, the <DIV>'s all have a CLASS="para". Since in this example we start with all blocking elements closed, we add a little style sheet:

.para {display: none;}

Then we add the links that open and close the paragraphs. I put them in the <H3>'s that I use as headers throughout this site:

<H3 CLASS="head" ID="header1">
 <A HREF="#"
 onClick="blocking('number1');
  return false;">Display: block/none</A></H3>

The CLASS and ID are for Netscape 4's sake, the other browsers don't need them.

As you see, the link executes the script blocking() which does the actual blocking. The link hands the script the name of the <DIV> that should be (un)blocked.

function blocking(nr)
{
 if (document.getElementById) <-- Netscape 5 and Explorer 5
 {
  current = (document.getElementById(nr).style.display == 'block') ? 'none' : 'block';
  document.getElementById(nr).style.display = current;
 }
 else if (document.all) <-- Explorer 4
 {
  current = (document.all[nr].style.display == 'block') ? 'none' : 'block'
  document.all[nr].style.display = current;
 }
 else alert ('This link does not work in your browser.');
}

We have one section for the Version 5 browsers and one for Explorer 4. As you can see, the only difference is how the <DIV>'s styles are accessed. For the Version 5 browsers we say

document.getElementById(nr).style.display

and for Explorer 4 we say

document.all[nr].style.display

In both cases, nr is the name of the <DIV> as defined in the ID.

current = (document.getElementById(nr).style.display == 'block') ? 'none' : 'block';

First of all we see what the current status of the block is and change it from 'none' to 'block' or vice versa.

document.getElementById(nr).style.display = current;

Then we command the browser to change the display.

 }
 else alert ('This link does not work in your browser.');
}

Finally a courtesy to browsers that can't handle this script. If they click on the link (which is visible to all browsers), nothing happens. It's best to tell them that nothing is supposed to happen. After two or three times they'll get the idea (and maybe even get the feeling that they should upgrade).

That's it. If the script doesn't need to work in Netscape 4 you're ready now.

Netscape 4 - the problems

If you do need a script that works in Netscape 4, you're out of luck.

First of all, this script may be BUGGY in Netscape 4 and may even cause a crash, although it works perfectly on my Netscape 4.05's at work (128M WinNT mega-Pentium) and at home (8M Win3.1 486).

When I just wrote this script I decided to use it in the navigation frame on the left. Testing by WDF members and my colleagues at NetlinQ (thanks all) revealed a distressing tendency of certain Netscape 4's to crash when the navigation frame was loaded. So I switched the navigation frame for Netscape 4 back to a safe page.

Since the script in this page also crashes some Netscape 4's, everything below is purely theoretical. I'm going to rewrite this page to describe the navigation frame.


In the other browsers you just give the command to change the display and the browser itself attends to the gory details like moving part of the content to create enough space for the block and calculating just how much the content should be moved.

For Netscape 4 we have to write a script that tells the browser exactly what to do, when to do it and how to do it. The basic idea is that as soon as the user clicks a blocking link, a script starts up that put the blocks in its place makes them visible and invisible. In addition, all elements below the link are to be moved, one by one.

So all elements (the headers with the links and the actual blocks) need to be in a separate layer that you can move around by changing its top property as we did on the cross-browser DHTML page.

In addition, we need to position these elements at an absolute point on the page. Normally, with normal text pages and also in the script for the other browsers, the elements are position: relative which means that they get to be positioned wherever they're needed (= below the previous block of content). I found out through trial and error, however, that for this script we need position: absolute for Netscape 4. This means that we also have to calculate the starting position of each element on the page.

Netscape 4 - the preparations

We start by defining some variables. They are for Netscape 4 only, though the shown array may also be useful for the other browsers.

var max = 5;
var headerheight = 60;
var heights = new Array(170,920,470,90,90);
var shown = new Array();
var safe = headerheight*max;
for (i=0;i<max;i++)
{
	safe += heights[i];
	shown[i+1] = false;
}

max is the total number of blocks. headerheight is the height of the headers, plus whitespace until the next header, in pixels. The heights array gives the heights of the actual blocks. When your page is finished you should set these variables by trial and error.

Then we initialize the array shown which keeps track of which blocks are shown and which ones are hidden.

Then we calcuate safe which gives the starting position of the invisible blocks. safe is simply the added heights of all headers (headerheight*max) and all blocks (safe += heights[i];). We put the blocks there initially to keep them out of the way and also to make the page long enough to contain all blocks when displayed.

In the for-loop we also set shown to false since we start with all blocks closed.

Now for the style sheets. We need different style sheets for Netscape 4 and the other browsers.

if (document.getElementById || document.all) <-- Explorer 4/5 and Netscape 5
{
 document.write('<STYLE TYPE='text/css'><!--');
 document.write('.para {display: none}');
 document.write('--></STYLE>');
}
else if (document.layers) <-- Netscape 4
{
 document.write('<STYLE TYPE='text/css'><!--');
 document.write(".para {position: absolute; visibility: hide; top: " + safe + "}");
 document.write(".head {position: absolute; visibility: hide;}}");
 document.write('--></STYLE>');
}

For the other browsers we need display: none for the .para elements (the blocks). But Netscape 4 unfortunately does support display: none: it takes this command to mean that it should hide the blocks and never show them again, no matter what the user does. So only the other browsers should have this style sheet.

Netscape 4 on the other hand, needs both the blocks (.para) and the headers (.head) to have a position: absolute, which messes up the whole script in the other browsers. So only Netscape 4 should have this style sheet.

In addition, in Netscape 4 we need to make the blocks invisible (visibility: hide;) and put them at the bottom of the page (top: " + safe + "}"). We have just calculated the bottom of the page and put the number in safe.

I also make the headers invisible, because when I don't I get a very ugly effect: first they are all placed on top of each other and only when the page is completely loaded do they take their correct positions.

Finally, we need to put the headers in their correct position and make them visible. I do this by the following script which I call onLoad:

function NN4()
{
 if (document.layers)
 {
  pos = 280;
  for (i=1;i<=max;i++)
  {
   eval("document.layers['header" + i + "'].top = " + pos);
   eval("document.layers['header" + i + "'].visibility = 'show'");
   pos += headerheight;
  }
 }
}

First of all, we only execute this function in Netscape 4:

 if (document.layers)
 {

We tell the browser where to put the first header. This may be at the top of the page, but in this case I have some overhead first (compatibility info, the page header and the first paragraph), so I only put the first header at 280 px from the top.

 pos = 280;

Then we go through all headers.

  for (i=1;i<=max;i++)
  {

The headers need to be placed in their proper position (each on headerheight pixels from the previous one) and to be made visible. I do this by the eval method, which allows me to create a string and then run it as a JavaScript command.

   eval("document.layers['header" + i + "'].top = " + pos);
   eval("document.layers['header" + i + "'].visibility = 'show'");

Finally, increase pos by headerheight to give the position of the next header.

   pos += headerheight;
  }
 }
}

That's it. Page is initialized, now we only need the script that does the actual blocking.

Netscape 4 - the script

Now we need to add a Netscape 4 specific part to our function blocking(). (For the complete script, see the next block).

 else if (document.layers)
 {

Execute if the browser is Netscape 4.

  var i = parseInt(nr.substr(nr.length-1,1));

First, get the number of the block to be displayed or hidden. Suppose nr is number3, we need to get the last character of nr, so i becomes 3.

Note that the present script only allows for 10 blocks (0-9). If you need more, change the this line to:

  var i = parseInt(nr.substr(nr.length-2,2));

and name your blocks and headers something like number08, number09, number10 etc.

  var replacing = heights[i-1];

Take replacing from the array heights. replacing is the height of the block to be (un)blocked, so it is also the number of pixels all layers should be moved.

  if (shown[i])
  {

If the block is currently shown (shown[i]=true) and needs to be hidden

   shown[i] = false;

set shown[i] to false

   replacing = -replacing;

make replacing a negative number (the layers have to be moved up instead of down)

   document.layers[nr].visibility = 'hide';

make the block invisible

   document.layers[nr].top = safe;

and put it in the safe position way down the page.

  }
  else
  {

If the block is hidden and needs to be shown

   shown[i] = true;

set shown[i] to true

   document.layers[nr].visibility = 'show';

make the block visible.

   var tempname = 'header' + i;

Now we need the name of the header above the block. We put it in tempname.

   document.layers[nr].top = document.layers[tempname].top + headerheight;
  }

We put the newly shown block just below the header, headerheight pixels away. If you think the block is placed too high or low, add or substract a number of pixels here. This should be done by trial and error.

Now we have arranged for the block itself to be hidden or shown. What remains is moving the headers and blocks below it, to create the true illusion of display: block.

  for (j=(i+1);j<=max;j++)
  {

Go through all headers and blocks, starting at the one below the block to be (un)blocked.

   name1 = 'header' + j;

We need the name of the current header and put it in name1.

   document.layers[name1].top += replacing;

Move the header by replacing pixels.

   if (shown[j])
   {

If the block is also shown, do the same

    name2 = 'number' + j;
    document.layers[name2].top += replacing;
   }
  }
 }

Done! Now Netscape 4 accurately mimics the display: block capabilities of Explorer 4/5 and Netscape 5.

The complete script


// Variables, mainly for Netscape 4

var max = 5;
var headerheight = 60;
var heights = new Array(170,920,470,90,90);
var shown = new Array();
var safe = headerheight*max;
for (i=0;i<max;i++)
{
	safe += heights[i];
	shown[i+1] = false;
}

// Write in style sheets according to browser

if (document.getElementById || document.all) // Explorer 4/5 and Netscape 5
{
 document.write('<STYLE TYPE='text/css'><!--');
 document.write('.para {display: none}');
 document.write('--></STYLE>');
}
else if (document.layers) // Netscape 4
{
 document.write('<STYLE TYPE='text/css'><!--');
 document.write(".para {position: absolute; visibility: hide; top: " + safe + "}");
 document.write(".head {position: absolute; visibility: hide;}}");
 document.write('--></STYLE>');
}

// The blocking function

function blocking(nr)
{
 if (document.getElementById) // Netscape 5 and Explorer 5
 {
  current = (document.getElementById(nr).style.display == 'block') ? 'none' : 'block';
  document.getElementById(nr).style.display = current;
 }
 else if (document.all) // Explorer 4
 {
  current = (document.all[nr].style.display == 'block') ? 'none' : 'block'
  document.all[nr].style.display = current;
 }
}
 else if (document.layers) // Netscape 4
 {
  var i = parseInt(nr.substr(nr.length-1,1));
  var replacing = heights[i-1];
  if (shown[i])
  {
   shown[i] = false;
   replacing = -replacing;
   document.layers[nr].visibility = 'hide';
   document.layers[nr].top = safe;
  }
  else
  {
   shown[i] = true;
   document.layers[nr].visibility = 'show';
   var tempname = 'header' + i;
   document.layers[nr].top = document.layers[tempname].top + headerheight;
  }
  for (j=(i+1);j<=max;j++)
  {
   name1 = 'header' + j;
   document.layers[name1].top += replacing;
   if (shown[j])
   {
    name2 = 'number' + j;
    document.layers[name2].top += replacing;
   }
  }
 }
 else alert ('This link does not work in your browser.');
}

// The initialization function for Netscape 4

function NN4()
{
 if (document.layers) // Netscape 4
 {
  pos = 280;
  for (i=1;i<=max;i++)
  {
   eval("document.layers['header" + i + "'].top = " + pos);
   eval("document.layers['header" + i + "'].visibility = 'show'");
   pos += headerheight;
  }
 }
}

Home