Netscape is a hated and loved piece of coding in the web building community for many reasons. I dislike it for one of the following reasons, it has one of the most twisted and evil DOM's in the history of browsing. This all started back before standards, and when the browser makers tried to build a DHTML DOM. IE, as it's probably well know, succeded, while Netscape released their version 4 browser. This had a unique DOM, usually called DOM level 0.5.

What's wrong with Netscape 4's DOM

It's simple. A DOM is a document object model, it's a way to structure pages, if you give a page structure through the HTML, and then you add presentational looks through CSS, and finally you can manipulate the whole thing via Javascript, or also VBScript in IE win32's case. Needless to say, when you make webpages, "write once, work anywhere", is, and has always been the creed. The netscape DOM is so incompatible with other DOM's that it's not even funny. Other DOM's included the new DOM1, used by Mozilla, and Netscape 6, and also older level 0 DOM's, used by netscape 2-3, and IE3. Also the IE4 DOM is wastly different.

Now, the whole problem arises when you start nesting these elements. Good old school DHTML usually relies on so called Layers, or Div markup elements. The thing that Netscape 4 does is to create a entire document, just for every single one of these elements. This is not needed, and troublesome, you can reference a layer directly like so, and also how to write inside of that layer:

document.layers['nameOfLayer']
document.layers['nameOfLayer'].document.write('This Text will appear inside the element')

But, if you nest a layer inside this layer, you have to go through the parent layer first. Say you have a page like this:

<div id="outerLayer">
	<div id="innerLayer"></div>
</div>

This is a simplified case, but the point remains. If you have the above structure in your page, in all other 4+ browsers, you can go directly to the inner element, but not in Netscape 4.

For Netscape 4: 
document.layers['outerLayer'].document.layers['innerLayer']

For Internet Explorer 4:
document.all['innerLayer']

For DOM1 browsers:
document.getElementById('innerLayer')

This is obviously a major headache when it comes to building various DHTML applications for Netscape 4, especially if you don't know what or how a layer will be nested.

So, the core problem is that elements are not visible throughout the entire DOM tree, we need a way to scour through the DOM tree, and automatically pick the correct element when we encounter it.

What the script does

What my script does it to automatically look a whole page's DOM through, looking for the element. That is, it will by it self go through child layers, sister layers, looking for the layer in question. It also looks for images, since that's pretty useful too. Notice that my code also fetches a object reference in Mozilla, IE, so you will not need to do branching of your code, to get an object reference.

An example

Open example

Open the example, and type in the name of any of the following elements:

Layers:

Layers:

Layers:

Images:

DOM structur of a deeply nested page.
The above picture shows the exmaple page I have made. It shows how these elements are nested, and what is what. It is navigating this DOM that NS4 just cant do on it's on, so we do it for it.

code

here is the code in one big bite, for those who want it like that:

function browserCheck() {
	this.ns4 = (document.layers)? true:false;
	this.ie = (document.all&&(!window.opera))? true:false;
	this.dom = (document.getElementById)? true:false;
	this.ns6 = (window.sidebar)? true:false;
	this.moz = (window.sidebar||navigator.userAgent.indexOf('Gecko')!=-1)? true:false;
	this.opera = (window.opera)? true:false;
	this.mac = (navigator.userAgent.indexOf('Mac')!=-1)? true:false;
}
is = new browserCheck();

var Obj;

function getObjectName(nameOfObject){

	Obj = null;

	if (is.ie) Obj = document.all[nameOfObject]
	else if (is.dom) Obj = findDOMObject(nameOfObject)
	else if (is.ns4) findLayer(window,nameOfObject);

	if (!Obj || ( is.ns4 && Obj == window ) ) Obj = "Object not found"
	
	return Obj;
}

function findDOMObject(nameOfObject) {
	for (var i = 0; i < document.images.length; i++) {
		if (document.images[i].name==nameOfObject) return document.images[i]
	}
	return document.getElementById(nameOfObject)
}

function findLayer(node,nameOfObject) {
	
	if ( node.name == nameOfObject ) Obj = node;
	
	for ( var counter = 0; counter < node.document.images.length; counter++ ) {
		if (node.document.images[counter].name==nameOfObject) Obj = node.document.images[counter];
	}
	
	for ( var i = 0; i < node.document.layers.length; i++ ) {
		findLayer(node.document.layers[i],nameOfObject);
	}
}

How to use it

And here is how you call it. Say you want to get to a image called "mouseOver02", then you would call it like so:

moObj = getObjectName('mouseOver02');

The moObj will contain a direct reference to the image in question. You can then proceed and use any normal code with this object.

moObj.src = 'gfx/mouseOverOff.gif'

Code walk-through

  1. First we need a browser checker. I use this object sniffer, which I find to work enough for my purposes.

    What it does is, to make a browserCheck object, which will assign itself new properties (that's what this.ie does), and set the values to true or false, depending on how the browser qualifies. Next I assign the is object as a new browserCheck object, in the future, is.ie will tell me, with a true or false, if a browser is Internet Explorer or not.

    function browserCheck() {
    this.ns4 = (document.layers)? true:false;
    this.ie = (document.all&&(!window.opera))? true:false;
    this.dom = (document.getElementById)? true:false;
    this.ns6 = (window.sidebar)? true:false;
    this.moz = (window.sidebar||navigator.userAgent.indexOf('Gecko')!=-1)? true:false;
    this.opera = (window.opera)? true:false;
    this.mac = (navigator.userAgent.indexOf('Mac')!=-1)? true:false;
    }
    is = new browserCheck();        	
    
  2. First we create a variable Obj, to hold our object reference.

    The function getObject, is the one that we call to get an object reference, see the above demonstration of usage. The argument nameOfObject, is a string which gets passed, to the rest of the function. First off, we reset the Obj object, if the function has been called already, the Obj object will already contain a valid reference, and the script will simply return that instantly.

    Next we run a series of check, to see which browser we're running the code in.
    If it's in IE, it's very simple, simple set Obj = document.all[nameOfObject], since the all array in IE contains both layers, and images in the whole DOM, we will find it instantly (I like IE).
    Second, we check to see if it's in Mozilla, or any other DOM1 browser, we set Obj = findDOMObject(nameOfObject), where findDOMObject() is a method, which finds objects in the DOM1 DOM, this part is mainly for Mozilla, though other compliant browsers should be able to use this part also.
    Lastly, we run the check for Netscape 4, and we do the same, we run the function findLayer(window,nameOfObject), the reason I dont set Obj to equal this function, is because, unlike the other methods, I couldn't get findLayer to return a value, and therefore I had to make a global Obj, not as nice object code, but I'm still learning.

    After all the code has been run, the calls to the other functions finished, we check to see if we found nameOfObject. Here's a small piece which may puzzle, and that's the line is.ns4 && Obj == window, there, we say, if the browser is Netscape 4, and Obj is an object, but it's the window Object, we still return a "Object not found".

    var Obj;
    
    function getObjectName(nameOfObject){
    
    Obj = null;
    
    if (is.ie) Obj = document.all[nameOfObject];
    else if (is.dom) Obj = findDOMObject(nameOfObject);
    else if (is.ns4) findLayer(window,nameOfObject);
    
    if (!Obj || ( is.ns4 && Obj == window ) ) Obj = "Object not found"
    
    return Obj;
    }        	
    
  3. Now, this little piece of code is for DOM1 browsers. First we loop all images in the old DOM0 images collection, to see if any of them matches the string name we're looking for. If we happen to find it, we return the image reference.

    If not, we simply assume that it's a ID element, and we return the getElementById method, which exists only in the new DOM1

    function findDOMObject(nameOfObject) {
    for (var i = 0; i < document.images.length; i++) {
    if (document.images[i].name==nameOfObject) return document.images[i]
    }
    return document.getElementById(nameOfObject)
    }
    
  4. Finally, we explain the Netscape object locator. this is the real code, the part that is intersting. What we do is simple programming logic, but I had to consult another co-worker, as to how to make it work, so that says something about my skills.

    The program is a loop, that looks at the current object, and sees if it has any child nodes, and if it does, it calls itself, with the child node as the new node, while keeping the string nameOfString passed as an argument also.

    the first we do, is to see if the node.name equals the nameOfObject, if it does, we set Obj = node, and we still let the function run, as return false, wont work.

    After we've entered a node, we loop all the image in the layer first, as the images can't contain more layers (being contained elements).

    After that, we loop all layers, or rather, we move to the first node, which has a child nodes, and we move into that layer, and call the same function, the point being, that we will loop all nodes, and each function call will exist only in it's name space.

    function findLayer(node,nameOfObject) {
    
    if ( node.name == nameOfObject ) Obj = node;
    
    for ( var counter = 0; counter < node.document.images.length; counter++ ) {
    if (node.document.images[counter].name==nameOfObject) Obj = node.document.images[counter];
    }
    
    for ( var i = 0; i < node.document.layers.length; i++ ) {
    findLayer(node.document.layers[i],nameOfObject);
    }
    }
    

A rough non programmers guide to how this works

This is a walk through of the function findLayer.

  1. I have a layer. does this node match the one I'm looking for?
    If yes, I make a note, remembering the layer. No matter what, move to next point.
  2. Ok, I'm in this layer, lets look at all the images... Does any of the images names match the name we're looking for? If so, make a note of it. No matter what, move to next point.
  3. Hm, nothing so far. Ok, lets see if this layer has any child layers. So it does. It has three child layers (exmaple). For each child layer, I'll start another call to this very function. Each child layer will start from the first point in this listing.

When the current running functions sees that it doesn't have any child layers, the function that called that one, will initiate the next call, calling the function, with the sister layer, of the function, that just exited. It's programming, and pretty fun.

caveats

NONE! No really, I'm not aware of any bugs with the code. It's usefull code, but I hope for the Netscape 4 DOM to go away soon...