I've considerably changed my navigation frame since the writing this page. The code on this page still works, but I added a few features to my navigation frame which aren't described on this page. Study the code for the details.
The setNav() function doesn't work in Safari 1.0.
On this page I explain the navigation script I use in the navigation frame. See there for an example.
I knew exactly what I wanted when I wrote this script:
I devised the following code:
The pairing of labels and content blocks was very simple: a label is always
followed by its paired content block, so label.nextSibling always accesses it (excluding
empty text node problems).
<div class="label"> JavaScript </div> <div class="content"> <a href="js/intro.html" >Introduction</a> <a href="js/contents.html" >Table of contents</a> <a href="js/links.html">Links</a> <div class="label"> Building blocks </div> <div class="content"> <a href="js/placejs.html" >Placing JavaScript</a> <a href="js/support.html" >Object detection</a> [etc] </div> </div>
The CSS isn't particularly complicated, see navi.css. The only thing I needed were some extra selectors to give a nested label a subtly different style from a first level label:
div.label {
styles
}
div.content div.label {
overrule a few styles
}
I wrote the following script:
window.onload = function () {
var x = document.getElementsByTagName('div');
for (var i=0;i<x.length;i++)
{
if (x[i].className == 'label')
x[i].onclick = clickNav;
}
closeNav();
if (top.setNav)
setNav(top.setNav,'currentPage');
}
function closeNav()
{
var x = document.getElementsByTagName('div');
for (var i=0;i<x.length;i++)
{
if (x[i].className == 'content')
x[i].style.display = 'none';
}
}
function clickNav(e)
{
if (!e) var e = window.event;
if (e.target) var tg = e.target;
else if (e.srcElement) var tg = e.srcElement;
while (tg.nodeName != 'DIV') // Safari GRRRRRRRRRR
tg = tg.parentNode;
var nextSib = tg.nextSibling;
while (nextSib.nodeType != 1)
nextSib = nextSib.nextSibling;
var nextSibStatus = (nextSib.style.display == 'none') ? 'block' : 'none';
nextSib.style.display = nextSibStatus;
}
function setNav(page,newID)
{
var test = page.indexOf('#')+1;
if (test)
page = page.substring(0,test-1);
var x = document.getElementsByTagName('a');
var i;
for (i=0;i<x.length;i++)
{
if (x[i].href == page)
{
x[i].id = newID;
break;
}
}
if (i < x.length && newID == 'currentPage')
{
var parDiv = x[i];
while (parDiv.parentNode.tagName == 'DIV')
{
parDiv = parDiv.parentNode;
parDiv.style.display = 'block';
}
}
}
As soon as the page is loaded we prepare the navigation.
window.onload = function () {
We go through all divs on the page
var x = document.getElementsByTagName('div');
for (var i=0;i<x.length;i++)
{
If a div has a class label we set the clickNav event handler.
if (x[i].className == 'label') x[i].onclick = clickNav; }
We close the entire navigation (see below)
closeNav();
Finally we see if the top frame has a variable setNav. This variable is set by any page
loaded into the content frame. If it exists, we call the setNav() function to give the link
to this content page a special style (see below).
if (top.setNav) setNav(top.setNav,'currentPage'); }
Closing the entire navigation is extremely simple. We go through all divs, and if one has
a class content we set its display to none.
function closeNav()
{
var x = document.getElementsByTagName('div');
for (var i=0;i<x.length;i++)
{
if (x[i].className == 'content')
x[i].style.display = 'none';
}
}
The function clickNav() is executed whenever the user clicks on a label. It first finds the
target of the event (see the Events properties page for details).
function clickNav(e)
{
if (!e) var e = window.event;
if (e.target) var tg = e.target;
else if (e.srcElement) var tg = e.srcElement;
Defeat a Safari peculiarity: in this browser the target is the text node you clicked on, not the element. So
while the target is not a div we go to its parentNode.
while (tg.nodeName != 'DIV') // Safari GRRRRRRRRRR tg = tg.parentNode;
Then we take the nextSibling of the target. This is always the related content block because of the structure of the HTML. We continue until the nextSibling is an element node, because some browsers count the empty text node between the two divs.
var nextSib = tg.nextSibling; while (nextSib.nodeType != 1) nextSib = nextSib.nextSibling;
If the content block has display: block it should get display: none and vice versa.
Find out which display it should get and change it.
var nextSibStatus = (nextSib.style.display == 'none') ? 'block' : 'none'; nextSib.style.display = nextSibStatus; }
setNav() sets or removes the special style for the link to the page that's currently shown in
the content frame.
It receives two arguments: the location.href of the page and the new ID the link should get.
This last variable can have two values: 'currentPage' to set the special style, or '' (empty
string) to remove it.
function setNav(page,newID)
{
First we remove any hash ('#somewhere') because it'd confuse our script.
var test = page.indexOf('#')+1;
if (test)
page = page.substring(0,test-1);
Then we go through all a elements in the navigation frame.
var x = document.getElementsByTagName('a');
var i;
for (i=0;i<x.length;i++)
{
If the href of a links is equal to page (which contains the location.href
of the page in the content frame), we set the id of the link and break the for{}
loop.
if (x[i].href == page)
{
x[i].id = newID;
break;
}
}
One more thing: if we set the special style (but not if we remove it) the link should become visible. We must set the
display of all its ancestors to block.
First see if we actually found the link and if we set the special style instead of removing it:
if (i < x.length && newID == 'currentPage')
If so we start at the link and go up through the document tree. Any ancestor of our link that is a div element
gets display: block
{
var parDiv = x[i];
while (parDiv.parentNode.tagName == 'DIV')
{
parDiv = parDiv.parentNode;
parDiv.style.display = 'block';
}
}
}
I added a 'Clean up' function. It closes the entire navigation, except for the divs containing the link
to the current page. First I close the navigation by calling closeNav(). Then
I call setNav() to open the link to the current page and its ancestors.
function cleanNav()
{
closeNav();
setNav(top.setNav,'currentPage');
}