Tải bản đầy đủ (.pdf) (6 trang)

Practical prototype and scipt.aculo.us part 14 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (93.49 KB, 6 trang )

You might recognize this pattern from Element#setStyle, discussed earlier in the
chapter.
There’s only one gotcha:
class and for are reserved words in JavaScript (they have
special meaning), so be sure to wrap them in quotes, as in the preceding example.
Later in the chapter, you’ll use this pattern as part of a powerful element-creation API.
Traversing and Collecting
The tasks we’re about to cover could broadly be defined as “getting nodes from other
nodes.” Traversal is getting from one node to another; collection is asking for a group of
nodes that relates to the node you’re on in some way.
Navigating Nodes
Since traversal is all about swiftly navigating complex node structures, let’s create a com-
plex node structure:
<table id="cities">
<caption>Major Texas Cities</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col" class="number">Population (Metro Area)</th>
<th scope="col">Airport Code</th>
</tr>
</thead>
<tbody>
<tr id="dallas">
<td>Dallas</td>
<td class="number">6003967</td>
<td class="code">DAL</td>
</tr>
<tr id="houston">
<td>Houston</td>
<td class="number">5539949</td>


<td class="code">IAH</td>
</tr>
<tr id="san_antonio">
<td>San Antonio</td>
<td class="number">1942217</td>
<td class="code">SAT</td>
</tr>
CHAPTER 6 ■ WORKING WITH THE DOM128
<tr id="austin">
<td>Austin</td>
<td class="number">1513615</td>
<td class="code">AUS</td>
</tr>
<tr id="el_paso">
<td>El Paso</td>
<td class="number">736310</td>
<td class="code">ELP</td>
</tr>
</tbody>
</table>
Nothing too fancy here—just an ordinary data table (see Figure 6-2). But I’ve taken a
couple liberties with the styling. Anything with a
class of number gets right-aligned (so
that all the digits line up); anything with a
class of code gets center-aligned.
Figure 6-2. A simple, contrived data table
Drop this code into your
index.html file. We’re done with the breakfast log, sadly, so
you can clear that stuff out, but leave the
script tag that loads Prototype.

The up, down, next, and previous Methods
Visualize a DOM tree. The root element, document, is at the top, roughly corresponding to
the
html tag in your markup. And from there the tree branches to reflect the complex rela-
tionships of nodes in the document. A
table node branches into thead and tbody; tbody
branches into several trs; each of those trs branches into several tds.
Now imagine you’re a lowly
td tag in the body of our table. You know that your parent
node is a
tr, and that you’ve got a reference to that table row in your parentNode property.
td.parentNode; //-> <tr>
CHAPTER 6 ■ WORKING WITH THE DOM 129
You don’t know who your grandparent node is, but you can ask the tr:
td.parentNode.parentNode; //-> <tbody>
And so on, up the line:
td.parentNode.parentNode.parentNode; //-> <table>
Tedious, isn’t it? Makes me want to be an orphan. But it’s even worse in the opposite
direction: imagine you’re a
table element and you want to find one of your td grandchil-
dren.
The DOM foresees these needs, but addresses them only partially. We need better
ways to jump from one node to another, no matter which direction we’re going.
With
Element#up, Element#down, Element#next, and Element#previous, we have the fine
control that the DOM lacks. Each method returns one element in the specified direction.
Imagine we’ve got a reference to the
td with the content “Houston.” From there, we
can traverse with ease:
td.up(); //-> <tr>

td.next(); //-> <td class="number">
td.previous(); //-> null
td.down(); //-> null
Calls to up and next return the parent node and the next sibling, respectively. Calls to
down and previous return null because no element is found in that direction.
■Note These four methods ignore text nodes entirely. When you call next, you’re asking for the next
element sibling, which may not be the same as the node’s nextSibling property.
Now let’s jump up one level:
var tr = td.up();
tr.up(); //-> tbody
tr.next(); //-> tr#san_antonio
tr.previous(); //-> tr#dallas
tr.down(); //-> td
CHAPTER 6 ■ WORKING WITH THE DOM130
This time, we get results in all four directions: next and previous point to table rows 1
and 3, while
up and down point to the parent node and the first child node.
To repeat, each of these methods returns one result. If there is more than one ele-
ment to choose from, it will pick the first one that satisfies its search.
These traversal methods become even more useful when given arguments. All four
take two types of arguments:
A CSS selector string: Checks potential matches against the selector; accepts the same
wide range of selectors as
$$.
A numeric index: Specifies how many matches should be skipped. (Or think of it this
way: if all the matches were returned in an array, this would be the index of the one
you want.)
tr.up('table'); //-> table
tr.next(1); //-> tr#austin
tr.down('td.code'); //-> td.code

tr.down('td', 1); //-> td.number
As you can see, both arguments are optional, and the order is flexible. You can pass in
an index or a selector as the first argument; but if you pass both, the selector needs to
come first.
The select Method
Don’t forget about our old friend from Chapter 2. $$, the CSS selector function, searches
the entire document, but it has an instance-method counterpart (
select) that can search
any subset of a DOM tree.
Element#selector works like Element#down, except it returns an array of elements.
tr.select('.code');
// -> [td.code, td.code, td.code, td.code, td.code]
tr.up('table').select('.number');
// -> [th.number, td.number, td.number,
td.number, td.number, td.number]
tr.up('table').select('td:last-child');
// -> [td.code, td.code,
td.code, td.code, td.code]
CHAPTER 6 ■ WORKING WITH THE DOM 131
The ancestors, descendants, and immediateDescendants Methods
These methods correspond to the groups searched by Element#up and Element#down:

ancestors returns all ancestors of the node, starting with its parent node and end-
ing with the
html element.

descendants returns all element descendants of the node in depth-first order. This
is equivalent to calling
getElementsByTagName('*') in the node’s scope.


immediateDescendants returns all element children of the node in the order they
appear. This is equivalent to the element’s
childNodes property—but with all text
nodes filtered out. (
children would be a better name, but Safari uses that property
name for something else.)
The siblings, previousSiblings, and nextSiblings Methods
These methods correspond to the groups searched by Element#previous and Element#next:

previousSiblings and nextSiblings return all the element nodes that come before
and after the given node, respectively. The returned collections are ordered based
on proximity to the original element, meaning that
previousSiblings will return a
collection in
reverse DOM order.

siblings returns the union of previousSiblings and nextSiblings arranged in DOM
order. This collection does not include the original node itself. This is equivalent to
calling
immediateDescendants on the node’s parent node, and then removing the
original node from the returned collection.
Creating Nodes
Individually, all these methods are simply helpers—convenience methods for repetitive
tasks. But when they combine, they form a strong platform that allows for a whole new
level of coding. The
Element constructor is the Captain Planet of the Prototype DOM
extensions—a force greater than the sum of its parts.
Think of the example we’ve been using in a specific context. Suppose we were build-
ing a site where a user could select any number of cities and compare some of their
qualities. To make the UI snappy, we’d load city data via Ajax and stuff it into our compar-

ison table dynamically. The data itself has to come from the server, but we can offload
some of the easier stuff to the client side.
Most of this falls outside the scope of this chapter, but there’s one part we can extract
into a simple example. Let’s say we want to build a new row at the bottom of our table—
CHAPTER 6 ■ WORKING WITH THE DOM132
one that will add up all the populations of the cities and display a total. In HTML form,
the row would look something like this:
<tr class="total">
<td>Total</td>
<td class="number">15,736,058</td>
<td class="code"></td>
</tr>
We’ll give the tr its own class so we can style it to look different from the other rows.
And we’ll leave the last cell empty because it’s not applicable to this row, of course.
But now we’ve got to decide between two equally ugly ways of generating this HTML
dynamically. We could use
Element#insert with a string:
var html = "<tr class='total'>";
html += "<td>Total</td>";
html += "<td class='number'>" + totalPopulation + "</td>";
html += "<td class='code'></td>";
html += "</tr>";
$('cities').down('tbody').insert(html, 'bottom');
But I hate building long strings like that. It’s a syntax error minefield. So, we could
stick to the DOM way:
// First, create the row.
var tr = document.createElement('tr');
// We need to "extend" the element manually if we want to use
// instance methods.
$(tr).addClassName('total');

// Next, create each cell individually.
var td1 = document.createElement('td');
td1.appendChild(document.createTextNode('Total'));
var td2 = document.createElement('td');
$(td2).writeAttribute('class', 'number');
td2.appendChild(document.createTextNode(totalPopulation));
var td3 = document.createElement('td');
$(td3).writeAttribute('class', 'code');
CHAPTER 6 ■ WORKING WITH THE DOM 133

×