I really hate to start out with the hairy hocus-pocus, but if I'm going to demonstrate JavaScript properly I don't have much of a choice. So bear with me. Scripting works with and manipulates something known as the Document Object Model (DOM for short), which is basically a model of what elements have what children, attributes, and so on. Knowing how this works is essential to understanding scripting.
A note here: From now on, when I talk about an HTML DOM or document, I will be talking about the DOM of a document with the .html
or .htm
extension, regardless of the actual markup language used. When I talk about XHTML, I will be talking a a document with an .xhtml
, .xht
, or .xml
extension, since that means the document is being read as XML. If I simply say DOM
, then what I'm talking about refers to either.
A DOM is made up of nodes. The DOM tutorial on the W3Schools website explains nodes well:
According to the DOM, everything in an HTML document is a node.
The DOM says:
- The entire document is a document node
- Every HTML element is an element node
- The text in the HTML elements are text nodes
- Every HTML attribute is an attribute node
- Comments are comment nodes
(http://www.w3schools.com/htmldom/dom_nodes.asp)
To demonstrate a DOM, I'm going to show you a simple HTML document. You might remember this webpage from Your First Webpage.
Building on what we learned in the portion on (X)HTML, I gave all the descendants of the body
element id
attributes like I did in Introduction to CSS along with a title
attribute for the h1
element. I also added a script
element, which we'll use to explore the DOM.
Your First Webpage
So let's pick this page apart. The first picture is a diagram of what is nested in what. Remember that the meta
element is an empty element, and all its content
is contained in its start tag. Next is what is known as a node tree. It shows what a webpage's DOM looks like, and you can use it like a map. The third is a key telling you what node is what type (I lump document and document type together here).
Document is, of course, the root node (this is very important). The Document Type node corresponds to the Doctype, which is rarely referenced in JavaScript, but the rest of the nodes—or at least nodes of those types—are quite often accessed by JavaScript.
I left out text nodes that contain whitespace and nothing else. I did this for a few reasons:
Specifically, the HTML DOM does not include a text node between the html
and head
start tag, and according to Internet Explorer, a text node that includes only whitespace doesn't exist. The DOM of an XML document does not have this peculiarity: if there's anything other than coding between two tags, there's a text node there.
I did not omit a text node between the start tag of the first p
element and the strong
element: if you look at the code, you'll see the start tags for these elements are adjacent, with no intervening text, not even whitespace. Therefore, there is no text node there.
Some of the text for the second paragraph was cut off because it wouldn't fit in the rectangles. But looking at this, you should be able to figure out which nodes are which.
There are 12 distinct types of nodes, 4 of which are commonly used in JavaScript.
Each type of node has a specific numerical value which can be used to determine which type of node it is (handy when your script may have to deal with several types).
NodeType numerical value: 9
The document node is the root node. Therefore, every time you want to access a node, you have to start with the document node.
NodeType numerical value: 1
As I said back in Chapter 2, elements are the building blocks of any webpage, so they are one of the most important types of nodes in any DOM.
NodeType numerical value: 2
Attribute nodes are always child nodes of element nodes.Wherever there is an attribute, there is an attribute node.
NodeType numerical value: 3
If you have whitespace (which includes spaces, tabs, or new lines), letters, or numbers between two tags of any kind, that's a text node. Seriously. The only type of markup document that wouldn't have a text node at all would be one that read:
That's assuming, of course, that root
is the root element. If the two tags on different lines (as shown below), then it would have a text node containing whitespace.
Usually, JavaScript is used to manipulate the text nodes of block or inline elements.
If you have a paragraph with some text, then an em
element (with its own text), then some more text, that paragraph will have two text nodes: one before the em
element and one after. The text inside the em
element element is not considered part of either of the paragraph's text nodes.
p
Element's Text NodeThe first paragraph has only two child text nodes as well—remember that the start tag of the strong
element directly follows the start tag of the first p
element:
NodeType numerical value: 4
It is entirely possible that you may want to use the script to refer to an internal stylesheet, script, or content you don't want confused with markup. Of course, this is only used in XML (in this case, XHTML) documents.
NodeType numerical value: 10
Just in case you were curious. :-)
You can't do anything with an element, attribute, or text node unless you can get at it. Each node has its own special way of being accessed.
By the way, habit alert: I usually end lines of JavaScript code with a semicolon (;
). You may use these or leave them off as you please (for the most part). I use them because I regularly code with PHP, which requires them.
All nodes in the DOM of any markup document (not just (X)HTML) are accessed through the document node, using the keyword document
.
document
;Accessing element nodes sometimes requires a method, which I quickly defined back in Dynamic Behavior And Scripting as a procedure associated with an object (in this case, a DOM node). There are two methods that access element nodes:
getElementById
getElementsByTagName
Remember when I said the id
attribute was really, really important? This is another reason why. The method getElementById
accesses a single element with that specific, unique id
value.
This method must be preceded by the keyword document
and a period. It might sound confusing but I assure you it's true. Suffice to say for now, if I wanted the element with the ID of p_1
(which is the first paragraph in the sample page), I would do it like this:
document
.getElementById
("p_1"
);See those quotation marks? Those are important. They tell that browser that p_1
is the literal string of text that it's looking for. Oh, and yes, you can use single quotes as well:
document
.getElementById
('p_1'
);The other method is to get a list of nodes by their tag name. That list
part is important, and I'll explain how to work with it soon. But for now, say you wanted to get a list of all em
elements in the webpage. The way to do it is like this:
document
.getElementsByTagName
("em"
);Those quotation marks have the same importance as with getElementById
. You may also use this method with getElementById
, if you wanted to get a list of elements nested within another specific element. For example, for a list of all em
elements nested within the second paragraph, you would do this:
document
.getElementById
("p_2"
).getElementsByTagName
("em"
);And that would give you a list of every single em
element nested within the element with the ID p_2
—in this case, emphasis on single
.
There is a way to choose an element from an element list. This entails following the getElementsByTagName
with a number contained in square brackets. But you must remember: most programming languages (JavaScript included) start counting at zero. Which means if you wanted the first em
element in a webpage, you would have to say you wanted #0
; #1
would be the second.
em
Element In A Webpagedocument
.getElementsByTagName
("em"
)[0];Now, can you use that method to get, say, the first em
element in the second p
element?
em
Element From The Second p
Elementdocument
.getElementsByTagName
("p"
)[1].getElementsByTagName
("em"
)[0];Yes. Yes you can.
document
.getElementsByTagName
("p"
).getElementsByTagName
("em"
);You can only get a list of nodes from a specific element node or the document node, not from a list of nodes.
document
.getElementsByTagName
("p"
)[1].getElementById
("em_2"
);document
.getElementById
("p_2"
).getElementById
("em_2"
);You can only use getElementById
with the document
node. Besides, since IDs are unique, it's redundant to do it this way, and doing it this way is redundant.
Accessing an attribute uses the method getAttribute
. Because attribute nodes are always children of element nodes, to access an attribute node, you have to specify which element node is the parent. Because of this, my demonstration will use getElementById
, which gets a single element.
In the example below, I am getting the title
attribute of the element with the ID h1_1
.
title
Attributedocument
.getElementById
("h1_1"
).getAttribute
("title"
);Yes, quotation marks are needed again.
What this says is:
h1_1.
title.
Using the alert
function I mentioned in Dynamic Behavior And Scripting, we can cause the contents of the h1
element's title
attribute to show up in a popup box.
alert
(document
.getElementById
("h1_1"
).getAttribute
("title"
));The steps are:
h1_1.
title.
alert
function.This can actually be used as a working script:
alert
(document
.getElementById
("h1_1"
).getAttribute
("title"
));The result is:
And now that you know what else can go between (
and )
, you should realize why quotation marks are important. Not everything is plain text.
Internet Explorer treats the class
attribute a little differently than most other browsers. Most browsers treat the class attribute like any other attribute. Internet Explorer uses the property className
. This is one of the differences between JavaScript and JScript that I mentioned earlier.
Manipulating the text in an element is one of the primary uses of scripting, but getting at the text is a little complex. Here are the steps:
document
getElementById
to get the element with the ID h1_1.
text node
. The keyword firstChild
is often used, but it works only if the desired text node is indeed the first child node of the specified element node. For sake of simplicity, that will be the case here.data
The resulting code would be:
h1
Elementdocument
.getElementById
("h1_1"
).firstChild
.data
Again, this can be used in a similar fashion with the alert
function to create a working script.
h1
Element In A Popup Boxalert
(document
.getElementById
("h1_1"
).firstChild
.data
);The result is:
Extracting the text in a text node is not the only thing JavaScript is used for. Putting text there is even more common. There are a number of properties and methods that allow you to manipulate the DOM, but I am not going to go into great deal about these before explaining more JavaScript to you (this hocus pocus is hairy enough!). I will, however, show you how to manipulate a text node. Let's play around with the h1
element, and change its text to Hello JavaScript World
. Remember the keyword data
? That's what we'll be working with.
h1
Elementdocument
.getElementById
("h1_1"
).firstChild
.data
= "Hello JavaScript World"
This actually sets the text to what we want. The result is below.
I'm saying this now because this is how most of my examples in the following chapters will work.
I'm going to make a few cautionary notes, because JavaScript has more than a few surprises up its sleeve, some of which have thrown me for a loop. First, and most importantly, always remember that a node must actually exist before you do anything with it.
The above warning is why the placement of a script
element is important. If your script uses p
elements, but is placed in the head element, you'll get an error, because the script
element (which is in the head) will load before the p
elements (which are in the body) do—which means your browser will try to access elements that don't exist yet!
For this reason, website developers usually place their script
element(s) at the end of the body
element. There are ways around this; I'll get to those.
A common error with text nodes is to have no text node where you want one. For example, look at the following code:
This gives
nothing to work with, because the element with the ID document
.getElementById
("JS"
).firstChild
.data
JS
does not have a text node. For most browsers, a single space is sufficient to fix this problem. HOWEVER...
According to Internet Explorer, a text node containing nothing but white space does not exist.
No matter how many tab characters, spaces, and new lines you have in a text node, if it's whitespace only, Internet Explorer will not acknowledge it as an existing node. If you are wondering Will that make a difference with
, the answer is firstChild
, lastChild
, childNodes
, and nextSibling
yes, and thank you oh so much for dredging up such pleasant memories.
If you want a space as a placeholder, you have to use something known as a non-breaking space, which has the entity reference
and the entity code of  
( 
if you're using UNICODE codes).
tbody
Element And HTML DOMSubtitle: And How They'll Make You Tear Your Hair Out
The HTML DOM has one major peculiarity when it comes to tables: it always assumes the tbody
element is a child of each table
element whether the tbody
tags are there or not. Because of this, if you intend to use a client-side scripting language (JavaScript isn't the only language with this issue; I think VBScript has it as well) to manipulate the rows of a table, you must take into account that if tr
elements are seemingly child elements of a table
element, they are actually the children of a tbody
element, never a table
element. Otherwise, you may be in for a world of frustration.
Try giving some styling using the selector tbody td
when the page has no tbody
elements; you'll see what I mean.
With an XHTML DOM, this is not a problem.
When you look at the series of commands that allow you to access a text node, it becomes clear that typing
over and over again will quickly become tedius. So how to make it easier? Use variables, of course. That's in the next chapter.document
.getElementById
("JS"
).firstChild
.data