This technique is outdated. Do not use. Eventually this page will disappear.
It does not work in iCab.
There are some minor bugs in the style sheets in Explorer 4 on Mac, Netscape 4 and Omniweb. The script itself works fine, though.
The pseudo-mouseover effect does not work in Netscape 4 and Opera 6-.
See this script in real live action at the World Press Photo site I coded.
On this page I explain my version of the ever more popular DHTML navigation script. This is no doubt one of the best things you can do with DHTML, if you design the graphics, interaction and information structure of your site well, this navigation can double as sitemap and users can quickly find their way through complex, multi-layered sites.
As usual the script runs in this page. The links with the arrows > open sub menus. The menu should of course fit into the design of the page (it doesn't fit in mine, but this is only a demo anyway).
After you haven't done anything for 5 seconds I close all navigation layers, because then I assume you've lost interest in the navigation and want to read the actual page. Since the navigation layers divert attention from the page, it's best to close them.
I gave the example 4 levels of navigation, but this script can handle an unlimited number of levels (though your users probably can't).
You'll need specific HTML, style sheets and JavaScript code for this script. You're free to change the style sheets, of course, but you shouldn't change too much in the HTML because this script expects certain HTML structures.
var remember = new Array();
var remember2 = new Array();
var checkIt;
var DHTML = (document.getElementById || document.all || document.layers);
function show(name,lvl,obj)
{
if (!DHTML) return;
checkUserInput();
if (remember[lvl] && remember[lvl] == name) return;
if (remember[lvl])
{
closeAll(lvl);
}
if (name)
{
var x = getObj(name);
x.visibility = 'visible';
}
remember[lvl] = name;
if (obj.parentNode) y = obj.parentNode;
else if (obj.parentElement) y = obj.parentElement;
else return;
if (y.className) return;
y.className = 'over';
if (remember2[lvl]) remember2[lvl].className = '';
remember2[lvl] = y;
}
function closeAll(lvl)
{
for (i=remember.length - 1;i>=lvl;i--)
{
if (remember[i])
{
var x = getObj(remember[i]);
x.visibility = 'hidden';
}
remember[i] = null;
if (remember2[i])
{
remember2[i].className = '';
remember2[i] = null;
}
}
}
function checkUserInput()
{
if (checkIt) clearTimeout(checkIt);
checkIt = setTimeout('closeAll(1)',5000);
}
function getObj(name)
{
if (document.getElementById)
{
return document.getElementById(name).style;
}
else if (document.all)
{
return document.all[name].style;
}
else if (document.layers)
{
return document.layers[name];
}
else return false;
}
The navigation menus themselves look like this:
<DIV CLASS="navilayer" ID="base" STYLE="visibility: visible; left: 20px">
<DIV><A HREF="#"
onMouseOver="show('',1,this)">Home</A></DIV>
<DIV><A HREF="#"
onMouseOver="show('about',1,this)">About ></A></DIV>
<DIV><A HREF="#"
onMouseOver="show('products',1,this)">Products ></A></DIV>
<DIV><A HREF="#"
onMouseOver="show('',1,this)">Contact</A></DIV>
<DIV> </DIV>
</DIV>
The empty DIV at the bottom is for the last horizontal line. The subsequent menus may also have one or more of these DIVs:
<DIV CLASS="fill"><A> </A></DIV>
This is to make sure that the first link in the menu is at the same height as the link that opened the menu. I give them a separate class because the border color should be the same as the background color. This doesn't work in Netscape 4 for obscure reasons.
Finally the style sheets are the most difficult part of this menu, because you'll find that anything you think of won't work in a certain browser (usually, but not always, Netscape 4). The extra pseudo-mouseover effect (mouseovered link becomes blue bg and white text) does not work in Netscape 4 and Opera 5. Even worse, at first I did
a {display: block}
to make the links blocks on their own. It turned out that Explorer 4 doesn't support this, which forced me to put extra DIV's around each link.
As you see each layer has its own left position, I don't define this in the general style sheet.
In addition the first menu has a visibility: visible because it should always be shown
when the user comes to the page.
I do not further explain the style sheets on this page, for details please view the source.
First of all we define a few variables that we'll need:
var remember = new Array(); var remember2 = new Array(); var checkIt; var DHTML = (document.getElementById || document.all || document.layers);
The arrays are for storing the current status of the menu: which menus are visible, which
links are mouseovered. See below for more information. checkIt is for the timeout,
DHTML is to see if the browser supports DHTML.
Furthermore we need our faithful DHTML micro-API page. Then the preparations are finished, we can start using the menu.
Each link in every menu should have a mouseover event handler, for instance:
show('about',1,this)
show('',3,this)
As you see, each call hands three variables to the function show().
this which contains the link object itself because the script
must access the exact link the user mouses over. (I could have given each link an ID, then
passed the ID to the function, but this solution is far simpler).These variables are handed to the function show(), which is the central processing
function of this script:
function show(name,lvl,obj)
{
First of all, check if the browser supports DHTML. If it doesn't, end the function immediately.
if (!DHTML) return;
Secondly, we call the function checkUserInput() to tell it that the user has
moused over a link. I explain this function below.
checkUserInput();
Thirdly, we see if a menu is already opened on this level. To do this we use the array remember[].
This array is filled with the names of the menus that are currently opened. remember[1] is the
first menu opened (the about menu or the products menu), remember[2] is the second one etc. If
no menu is opened on a certain level remeber[lvl] is empty.
Now we check if a menu is opened on the current level and if the name of this menu is the same as the name of the menu that should be opened now. If it is the same, the user has moused over the same link once again. Now nothing should happen, so we end the function.
if (remember[lvl] && remember[lvl] == name) return;
When we pass this last check we're certain that the user has moused over a new link and things
should start to happen. First of all we see again if remember[lvl] contains a name
(if a menu is already opened on this level). If it does,
this layer and all menus below it should be closed, since the user has un-selected the link that
contains these levels. To close everything we use the special function closeAll() which
is explained below.
if (remember[lvl])
{
closeAll(lvl);
}
Then we see if the event handler has passed a name of a new layer to be opened. If it has, we
get the object with that name and set its visibility to visible: the menu layer magically
opens.
if (name)
{
var x = getObj(name);
x.visibility = 'visible';
}
We make note of the opened level in the array remember[]. We also do this when no
name is given, then remember[lvl] should become empty.
remember[lvl] = name;
If it's necessary, a new menu layer has now been opened. There is a second effect, however. When
the user mouses over a link, it should get a mouseover colour (blue background and white text). Note
that I cannot use a a:hover here: I want the mouseover colour to remain when the user
goes to a newly opened menu so that the user always knows where he is. What I do is change the
CLASS of the DIV around the A (I'd rather have changed the CLASS of the A
itself, but as said before the DIVs are necessary for Explorer 4).
The mouseover event handler hands this to the function, so it knows which link the user
went over. This link is now stored in obj. However, we want to change the class of the
parent node of this link, the DIV that's around it. So first we should search for the parent node. For the
Level 1 DOM browsers we search for obj.parentNode
and store it in y:
if (obj.parentNode) y = obj.parentNode;
For Explorer 4 we search for obj.parentElement and store it in y.
else if (obj.parentElement) y = obj.parentElement;
If the browser supports neither parentNode nor parentElement (Netscape 4) we cannot apply the mouseover and we end the function.
else return;
If the parent node already has a class, end the function now: nothing needs to change.
if (y.className) return;
If it hasn't, now change the class to over so it gets the blue background and the white
text, as defined in the style sheet. (In Opera 5 the class name is changed
but the browser doesn't apply the new styles to the DIV)
y.className = 'over';
If another link on the same level had the mouseover colours, it should change back now. We keep
track of this in the array remember2[] which contains the objects that have class over.
If an object is currently in remember2[], empty its class name so that it returns to standard
styles.
if (remember2[lvl]) remember2[lvl].className = '';
Finally we store the new object with class over in remember2[].
remember2[lvl] = y; }
To close layers (make them invisible) I wrote a special function. After all, if you close a certain layer all layers below it should also become invisible.
This function is called from the main function show() and from the special function
checkUserInput(). Each time a level is passed to the function as argument. The layer on
this level and the layers on all lower levels should be closed.
function closeAll(lvl)
{
We go through the levels that should be closed in reverse order. We start at the level with the highest number and go back in the navigation to the level that's been passed to the function.
for (i=remember.length - 1;i>=lvl;i--)
{
If the array remember[] has a value at the current level (if a layer is opened on this level)
we close it by taking the correct object and setting its visibility to hidden.
if (remember[i])
{
var x = getObj(remember[i]);
x.visibility = 'hidden';
}
After that we empty this level of remember[], after all there's no opened layer anymore
at this level.
remember[i] = null;
Secondly we see if any object on the current level has the mouseover style. If it has, it's stored
in remember2[]. We have to switch the special style off, otherwise the user will find one
link with the special style when he opens the layer again. This can be mightily confusing, since
the style marks the path through the navigation the user chooses.
So if remember2[] is filled at this level, we remove the class of the object and afterwards remove the object from remember2[].
if (remember2[i])
{
remember2[i].className = '';
remember2[i] = null;
}
Then on to the next level until we reach the level that has been passed to the function.
} }
Finally a special function for closing all layers. I want to close down the entire navigation if the user does nothing for 5 seconds.
Each time the user mouses over any link the function checkUserInput() is called. This
function sets a timeout that calls closeAll() 5 seconds later.
function checkUserInput()
{
This function uses the variable checkIt in which the timeout is stored.
First of all we see if checkIt is filled with a previous timeout from the previous mouseover
event. If it is, we cancel the timeout. After all the user is still active so the menus shouldn't
be closed.
if (checkIt) clearTimeout(checkIt);
Then we set the timeout once again. After 5 seconds, we call closeAll(1) which closes
all navigation layers so that only the topmost starting layer is shown. The navigation is now
completely closed.
checkIt = setTimeout('closeAll(1)',5000);
}
Of course, if the user mouses over another link this timeout is canceled and a new one is set. The net result is that all layers close 5 seconds after the last mouseover event.