Section B. Finding elements


B. Finding elements

The first step in any DOM script is finding the elements you want to do something with. There are two ways of finding elements:

  • Through the getElementById() and getElementsByTagName() methods. This is the best choice for searching long-distance through the entire document.

  • Through the parentNode and similar properties of an element. This is the best choice for short-distance travels from node to node.

Long-distance travel

When you start up your script, you usually want to find some HTML elements somewhere in the document. The two methods getElementById() and getElementsByTagName(), which we've already seen in many code examples in the previous chapters, can find any HTML element in the entire document, provided you give them correct instructions.

These two methods largely ignore the document structure. If you want to find all <p>s in the document, it doesn't really matter if they're direct children of the <body> or children of <div>s or other elements; you just want to get them all. Similarly, getElementById() always returns the correct element, wherever it may be hidden in the document structure. Therefore, these two methods are the ones you need for "long-distance" searching: they give you the HTML elements you need regardless of where they are in the document.

getElementById

The getElementById() method returns the element that has the ID specified in the argument:

document.getElementById('someID'); 


Not in XML

getElementById() is the single W3C DOM method that doesn't always work in XML. In an XML document, it searches through attributes that have the type (and not the name!) id, and this type must be defined in the XML document's Document Type Definition (DTD).


getElementsByTagName

The getElementsByTagName() method returns a nodeList (and not an array!) of all elements with the desired tag name that are descendants of the element on which you use the method. We'll discuss nodeLists in detail in 8I. Once we have a nodeList, we go through all elements in it and do something with them, for instance, checking if they have a certain attribute, or setting an event handler on them.

getElementsByTagName() can be used on any HTML element, as well as on the document. Both of these calls are therefore valid:

document.getElementsByTagName('p'); document.getElementById('someID').getElementsByTagName('p'); 


The first call returns a nodeList of all <p> elements in the document, while the second one returns a nodeList of all <p> elements that are descendants of the element with ID="someID".

We usually store such a nodeList in a variable:

var pars = document.getElementsByTagName('p'); 


Now pars contains a list of all individual <p>s in the page, and we can access them by their index numbers. The first <p> has index 0, the second index 1, etc. The most common use of getElementsByTagName() is requesting all tags of a certain type in order to loop through them.

var pars = document.getElementsByTagName('p'); for (var i=0;i<pars.length;i++) {     // do something with pars[i]; ie. with every paragraph } 


To access the fifth <p> in the page you can also say:

var fifthP = pars[4]; 


This syntax is useful only if you know exactly which <p> you want to access, and are certain that it hasn't been moved or removed by another part of your script. Therefore it isn't often used.

getElementsByTagName('*');

Finally, you can use getElementsByTagName('*'), which gives you all element nodes in the document in the order they appear in the source code. This method is not used often, since you rarely need all the elements in the entire document. Besides, it is not supported by Explorer 5.5 and earlier.

Examples

For an example, let's look at Usable Forms. When the script starts up, it should go through all tags of a certain kind; if they contain a rel attribute, they should be removed from the document. The script does this through the getElementsByTagName() method:

[Usable Forms, lines 1 and 25-28]

var containerTag = 'TR'; var containers = document.getElementsByTagName(containerTag); for (var i=0;i<containers.length;i++) {     if (containers[i].getAttribute('rel')) {             // remove element             // containers[i] refers to the <tr>             // we're currently studying     } } 


containers becomes a nodeList that contains all <tr> tags in the entire document. The script goes through them one by one and sees if they have a rel attribute. If so, it removes them from the document and puts them in a waiting room, where they remain until the user checks or selects the appropriate form field.

Sandwich Picker needs a slightly different approach. Initially I only want to go through all <tr>s in the third table on the page, the one that contains the sandwiches. The two other tables also have a few <tr>s, but these contain static information such as the search field and the order form.

So the script goes through the <tr>s in the third table, which has ID="startTable" (well, actually, its <tbody> has this ID, for reasons we'll discuss in 8E).

[Sandwich Picker, lines 32-33]

var containers = document.getElementById('startTable').getElementsByTagName('tr'); for (var i=0;i<containers.length;i++) {     // initialize     // containers[i] refers to the <tr>     // we're currently studying } 


Short-distance travel

Every node in the document has five properties and two nodeLists that allow you easy short-distance travel. The five properties are parentNode, firstChild, lastChild, previousSibling, and nextSibling, and they each refer to the node that is the parent node, first child, etc. of the node you use them on.

parentNode, firstChild, lastChild, previousSibling, nextSibling

Take this bit of HTML from Sandwich Picker:

<tr>     <td ><input /></td>     <td >Spicy roast beef</td>     <td >With our own mix of herbs and spices</td>     <td >home made</td> </tr> 


Figure 8.3. The DOM tree of the <tr> with the short-distance properties sketched in.


The first <td> is the firstChild of the <tr>; the last <td> is the lastChild. When we start at the second <td>, the <tr> is its parentNode, the first <td> is its previousSibling, and the third <td> is its nextSibling. Almost all nodes contain these five properties, although text nodes cannot have child nodes and document nodes cannot have parent nodes.

childNodes[] and children[]

Every node has two properties that contain nodeLists: childNodes[] and children[]. The difference between the two is that childNodes[] contains all children of a node, while children[] contains only those children that are element nodes themselves, and not the text nodes. children[] is the more useful of the two, since you're rarely interested in text nodes when you go through the children of an element, but unfortunately it is not a part of the W3C DOM specification, and at the time of writing is not supported by Mozilla.

Previoussibling, Nextsibling, and Childnodes[] are Useless

previousSibling, nextSibling, and childNodes[] appear to be useful properties, but unfortunately they are not nearly as handy as they seem. In the example scripts I use nextSibling twice, and I don't use previousSibling and childNodes[]at all.

The reason is that they generally don't refer to the element nodes you'd expect, but to empty text nodes. We'll study these nuisances in 8H.


Short distance only

Although it's theoretically possible to use these properties to go from any node to any other node, that's not recommended. Take, for instance, this path:

var x = [a node]; var y = x.parentNode.lastChild.childNodes[2].firstChild. nextSibling; 


The code is unreadable; no other JavaScript programmer will intuitively understand what you're doing. Besides, even though the path may be correct at the moment you create the script, it doesn't account for changes in the document tree, and changing the document tree is the whole point of W3C DOM scripts.

Therefore, I use these properties exclusively for short-distance travel. In general I restrict myself to two, or at most three, of these properties per statement, because if you need more of them you're usually better served by getElementById() or getElementsByTagName().

Examples

By far the most common use of firstChild is in accessing the text of an element:

<p>I am a JavaScript hacker.</p> 


If you want to find the text contained by the paragraph, you have to travel to the text node it contains and read out its nodeValue:

var par = [the paragraph]; var text = par.firstChild.nodeValue; 


You'll also use parentNode a lot. For instance, take this bit of Sandwich Picker:

[Sandwich Picker, lines 106-133, condensed]

function orderSandwich() {     // more stuff     var x = this.value * 1;     if (isNaN(x) || x == 0)            removeFromOrder(this);     else            moveToOrder(this); } function moveToOrder(obj) {     obj = obj.parentNode.parentNode;     obj.className = 'highlight';     // more stuff } function removeFromOrder(obj) {     obj = obj.parentNode.parentNode;     obj.className = '';     // more stuff } 


orderSandwich() is called whenever the user enters text in a form field. The script first checks if the text is a number. If it's not a number, it calls removeFromOrder(); if it is a number, it calls moveToOrder(). this (the form field in which the user entered data) is sent as an argument to both functions.

One of the jobs of the two functions is to highlight or un-highlight the <tr> that contains the form field, and it does so by giving its className a new value (see 9B). Before that's possible, though, the script has to access the correct <tr>. This is the HTML of one sandwich <tr>:

<tr>     <td ><input/></td>     <td >English sandwich</td>     <td >bacon, cheese, lettuce, tomato</td>     <td >freshly fried</td> </tr> 


Therefore, seen from the form field (obj), the <tr> can be accessed by parentNode.parentNode. The form field's parentNode is the <td>, and the <td>'s parentNode is the <tr>.

Since this is short-distance travel, and since the script moves entire <tr>s through the document but doesn't touch the internal structure of the <tr>s, using parentNode.parentNode is safe.

You'll also use parentNode often when you change the document structure, because, as we'll see in 8D, all methods you need for structure changes are defined on the parent node of the node you want to change. Suppose you want to remove the node with ID="testID" from the document:

var x = document.getElementById('testID'); x.parentNode.removeChild(x); 


First you access the node (long range!), but in order to execute the removeChild() method, you have to move to its parent first.

Root nodes

Finally, there are two special document properties that allow access to the <html> and <body> tags: document.documentElement and document.body. The first property exists in all XML documents, since every XML document must have a root element from which all other nodes descend. In HTML pages this is obviously the <html> tag.

document.body is a special addition for HTML pages, since it is often useful to directly access the <body> tag.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net