DOM hierarchy

Hey guys,

I am a fledgeling wed developer who is just trying to make sense of the DOM. I have worked extensively in C and C++ and presumed upon seeing javascript’s similar syntax that I knew how to use it, only to be brought crashing down to earth when I realized that there is a very structured object hierarchy involved with this language.

Anyway I went back to the books and spent a bit of time researching it, getting to grips with parents, children, siblings etc and it is now clearer. However, I have a few questions:

Taking a div tag with a three img elements inside as an example, is it the order in which I add these elements to the div (ie the inline positions in which they are placed) which dictates their hierarchy in the childNodes array ?

So if i wanted to select the second img object of this div would it be document.div1.childNodes[1] for the second img or [0] for the first img?
Is this understanding correct and does anyone have either a good tutorial on this sort of thing or even better a reliable piece of code with a function which allows me to pass an element’s ID which then document.writes its relevant hierarchical position within a parent node?

thx

Of course in this case it’s better to just use getElementsByTagName. You’d only need to walk through all the elements inside ‘container’ if you didn’t know what sort of elements were in there, or you knew they were different (e.g. <img> and <span>), and therefore were unable to use getElementsByTagName.

yes, that textnode thing caused me a lot of grief until after a lot of googling I found out about nodeType.

I’ve gone the nodeType route to filter out unwanted nodes.

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
 
window.onload=function() {
 //get all children of "container"
 var allChilds = document.getElementById("container").childNodes;
 
    //now get just the img elements
    var imgO = new Array(); //contains just the img elements
 
    for(var i=0; i<allChilds.length; i++) {
     if(allChilds[i].nodeType == 1) imgO.push(allChilds[i]);
    }
    alert('Total children = '+allChilds.length+"\
Number of img elems = "+imgO.length);
}
 
</script>
</head>
<body>
 
<div id="container">
    <img src="pic1.jpg" />
    <img  src="pic2.jpg" /> 
    <img  src="pic3.jpg" />
</div>
 
</body>
</html>

Hey thats excellent, very informative, I shall check out those tutorials too, thanks kindly
Will

Taking a div tag with a three img elements inside as an example, is it the order in which I add these elements to the div (ie the inline positions in which they are placed) which dictates their hierarchy in the childNodes array ?
Yes. But it’s a bit more complicated than that, because normally there is whitespace in between elements. That whitespace counts as a textNode.

<body>
  <div id="d">
    <img src="1.jpg">
    <img  src="3.jpg"> 
    <img  src="2.jpg">
  </div>
</body>

In the HTML above, the DIV has three images in it, but it has 7 children - 3 IMG elements and 4 text nodes. You could access the second image in a multitude of ways:


document.getElementById('d').getElementsByTagName('img')[1];
document.body.getElementById('d').getElementsByTagName('img')[1];
document.body.getElementById('d').getElementsByTagName('img')[1]; // *
document.getElementsByTagName('img')[1];
document.getElementById('d').childNodes[3];
document.getElementById('d').firstChild.nextSibling.nextSibling.nextSibling;
document.getElementById('d').lastChild.previousSibling.previousSibling.previousSibling;

// or you can "save" the DIV in a variable:
var div = document.getElementsByTagName('div')[0];
var div = document.getElementById('d'); // *
var div = document.body.firstChild.nextSibling;
var div = document.getElementsByTagName('div')[0];

//and then access the img using the div var:
div.childNodes[3];
div.firstChild.nextSibling.nextSibling.nextSibling;
div.getElementsByTagName('img')[1]; // *
div.getElementsByTagName('img')[0].nextSibling.nextSibling;

This is why childNodes can be problematic, and also nextSibling/previousSibling. Using getElementsByTagName negates this issue. The whitespace stuff is a bit of an annoyance when you need to go through every child of an element, so people often write wrapper functions that check the nodeType. There’s a property called nextElementSibling which is more useful because it ignores the textNodes, but unfortunately doesn’t enjoy adequate browser support yet.

Of course another solution is to simply strip out the whitespace between elements in your HTML. This, however, makes it less legible. It is better to write your code in a way where it doesn’t matter whether there’s whitespace there or not.

Obviously some of those examples above are silly. The ones I marked with an asterisk are probably the best. They’re shorter and won’t break if things change in the DOM.

It’s worth noting that document.body is a special case. It exists because the body is a single item that is useful to be able to access easily rather than having to document.getElementsByTagName(‘body’)[0].

As for tutorials, this is a classic introduction to the DOM, and I like [URL=“http://www.howtocreate.co.uk/tutorials/javascript/dombasics”]this too. It’s also worth just spending lots of time investigating the [URL=“https://developer.mozilla.org/En/DOM/”]Gecko DOM reference over at the MDC once you get the hang of things.

a function which allows me to pass an element’s ID which then document.writes its relevant hierarchical position within a parent node?
Try to get out of the habit of using document.write. It’s on its way out. Use alert() to quickly get the value of things, or console.log with Firebug.

Firebug is very good for having a good look at the DOM. You can just click something and it’ll show you where in the tree it is. If you install the Firefinder plugin for Firebug (google it), you can type in a CSS selector like “#d img + img” and then it’ll highlight it on the page and find it for you in the DOM tree.

yes, for this particular case I agree.

The case causing me grief initially was a situation where I had children divs and grandchildren divs inside a container div and I wanted only the direct descendant divs of the the container div.

I didn’t think of using getElementsByTagName() but to my understanding it would have returned the direct descendents and the grand children divs of the container div which is not what I wanted.

Ah yes, good point. getElementsByTagName goes as deep as it can, whereas childNodes guarantees that you stay in the same generation. I guess this is where document.querySelectorAll comes in, or XPath or (gasp!) jQuery and friends.