The DOM¶
As we know by now, JavaScript programs are included with a web page's HTML. With JavaScript, we can alter the document structure and content and change element attributes, but how do we get to those elements in the first place? To answer this, we need to take a look at how HTML, CSS and JavaScript relate to each other in a web page.
Given an HTML document like the following:
1 <html>
2 <head>
3 <title>Book Club</title>
4 </head>
5 <body>
6 <h1>My books</h1>
7
8 <ul class="books">
9 <li class="book unread">War and Peace</li>
10 <li class="book unread">The Origin of the Species</li>
11 <li class="book read">101 Really Funny Jokes</li>
12 </ul>
13 </body>
14 </html>
We can imagine the elements (tags) as a tree, where tags appearing "inside" other tags create the branches:
- html
- head
- title (Book Club)
- body
- h1 (My books)
- ul
- li (War and Peace)
- li (The Origin of the Species)
- li (101 Really Funny Jokes)
- head
This tree structure is called the Document Object Model, or DOM for short, and represents a web page's structure. Each element you see on the page exists "inside" of another element. For example, if you're reading this in a web browser, this text exists inside a paragraph element (<p>) which exists inside the <body> element. This link is an <a> element inside the paragraph. If you open this page's source (View - Source on most browsers - do a search for "ereshkigal" to find it), you will see something like this:
1 [...]
2 <p>
3 This tree structure [...] <a href="http://www.railstutor.org/">This link</a> is an [...]
4 </p>
5 [...]
The DOM is not just useful for understanding how the elements we see on a page correspond to the HTML for that page; CSS and JavaScript also use the DOM to access the same elements. For example, to make the book titles from the examples above red, we could use this CSS:
1 ul.books li.book {
2 color: red;
3 }
The "ul.books li.book" CSS rule tells the browser to find elements using the tag <li> belonging to the class "book" that are child elements ("inside") of a <ul> element with the class "books". In the same way, we can use this information in JavaScript to get elements with a certain tag name, class or id attribute. While this is possible using only basic JavaScript, it's quite cumbersome, so most people use a helper library such as Prototype. Using Prototype, we can use the exact same syntax as we do for CSS. Let's look at a few examples..
1 //Prototype has a function called $$ (double dollar) which
2 //uses CSS syntax to fetch elements from the DOM
3 var pageTitle = $$('title')[0];
The above example fetches the page's title element and puts it in the pageTitle variable. Because the $$ function always returns an array, we use the bracket syntax to get the first (and only) element. Note that the element we now have in the pageTitle variable is a JavaScript object and not simply the text (as a string) content of the title tag. To get the element's contents we can use the object's innerHTML property:
1 alert(pageTitle.innerHTML)
Now let's see if we can get all the book (<li class="book">) elements. This should be pretty easy, as we've already used the syntax in the CSS example above.
1 // Exactly the same
2 var books = $$('ul.books li.book');
If you remember the HTML example from the top of the page, each book also had a "read" or an "unread" class depending on whether the book has been read or not. Let's see if we can get all unread books and print their titles.
1 //First, get all the unread book elements by adding the "unread" class to "li.book"
2 var unreadBooks = $$('ul.books li.book.unread');
3
4 //Then, create a new array to contain only the titles
5 var pageTitles = [];
6
7 //Next, we will iterate through all the books, extract the title and add it to pageTitles
8 var i = 0; //Keep track of the current index
9
10 //The while loop will run once for each book in unreadBooks, or "while the index is less than the length of the unreadBooks array"
11 while (i < unreadBooks.length) {
12 pageTitles.push(unreadBooks[i].innerHTML); //"push" the book title of the current book onto the array
13 i = i + 1; //Increase the index variable by 1 to go to the next book
14 }
15
16 alert(pageTitles.join(',')); //The "join" method adds each element to a string with the parameter (a comma) in between
That's a lot of code to do so little, isn't it? Don't worry, there are much shorter ways to do this, but it's important to understand what's really happening. Just to not discourage you, let's see how easy this could be once you know all the tricks:
1 var pageTitles = $$('ul.books li.book.unread').pluck('innerHTML');
2 alert(pageTitles.join(','));
