// Now append each cell to the row...tr.appendChildtd1; tr.appendChildtd2; tr.appendChildtd3; // ...and append the row to the table body.. tr.appendChild new Element'td'.update'Total' ;
Trang 1// Now append each cell to the row
tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
// and append the row to the table body
$('cities').down('tbody').insert(tr, 'bottom');
Three times as many lines! As is often the case, one approach is easy but sloppy, and the other is harder but more “correct.” Can’t we split the difference?
// First, create the row
var tr = new Element('tr', { 'class': 'total' });
// Next, create each cell and append it on the fly
tr.appendChild( new Element('td').update('Total') );
tr.appendChild( new Element('td',
{ 'class': 'number'}).update(totalPopulation) );
tr.appendChild( new Element('td', { 'class': 'code' }) );
// Now append the row to the table body
$('cities').down('tbody').insert(tr, 'bottom');
We haven’t talked about this technique yet, but it ought to seem familiar anyway The glue in the middle, the new Elementpart, takes a tag name as its first argument, creates that element, calls Element.extendon it (to give it the Prototype instance methods), and then returns the element
The rest of it is stuff we’ve already covered:
• The optional second argument to Elementis an object with the attribute/value pairs the element should have.Element#writeAttributetakes care of that part
• Instead of the annoying document.createTextNode, we can use Prototype’s own Ele-ment#updateto set the text content of a new element
• The last step is the same in all cases We use Element#downto hop from the tableto its tbody, and then we use Element#insertto place our new element after all the other rows
Trang 2The wrap Method
Let’s take a time-out from the example to talk about Element#wrap
Sometimes you’ll want to create a new element to act as a container for something
that’s already on the page This is one mode of attack for browser bugs—rendering issues,
for example, can sometimes be defeated with a “wrapper” element
Prototype’s Element#wrapis shorthand for creating an element and specifying its
con-tents all at once Its arguments are identical to those of the Elementconstructor:
// wrap a TABLE in a DIV
var someTable = $('cities');
var wrapper = someTable.wrap('div', { 'class': 'wrapper' });
Here, since the tablealready exists in the DOM, we don’t have to explicitly append it
somewhere The divis created at the spot occupied by the table; the tablethen gets
added as a child of the div
Wrapping an element that isn’t yet in the document, of course, is a different matter:
// add a link to a list
var li = new Element('a', { href: "http://google.com"
}).update("Google").wrap("li");
$('links').insert(li);
Like most methods on Element,wrapcan be chained But, as these examples show, it
returns the wrapper, not the original element.
Putting It Together
Back to the example Let’s write a function that, when given a table of the sort we’ve
writ-ten, will automatically calculate the total and insert a new row at the bottom Insert this
block in the headof the page, right below where Prototype is loaded:
<script type="text/javascript">
function computeTotalForTable(table) {
// Accept a DOM node or a string
table = $(table);
// Grab all the cells with population numbers in them
var populationCells = table.select('td.number');
C H A P T E R 6 ■ W O R K I N G W I T H T H E D O M 135
Trang 3// Add the rows together.
// (Remember the Enumerable methods?)
var totalPopulation = populationCells.inject(0, function(memo, cell) {
var total = cell.innerHTML;
// To add, we'll need to convert the string to a number
return memo + Number(total);
});
// We've got the total, so let's build a row to put it in
var tr = new Element('tr', { 'class': 'total' });
tr.insert( new Element('td').update('Total') );
tr.insert( new Element('td',
{ 'class': 'number' } ).update(totalPopulation) );
// Insert a cell for the airport code, but leave it empty
tr.insert( new Element('td', { 'class': 'code' }) );
table.down('tbody').insert(tr);
}</script>
This code does a lot of stuff, so let’s look at it piece by piece
First, we use the $function on the table argument so that we can be sure we’re work-ing with a DOM node Then we use Element#selectto grab all the table cells with a class of number—there will be one per table row
Now that we have all of the table cells that contain population figures, we add them together with Enumerable#inject We start off with 0, adding the value of each cell to that figure as we loop through the cells The returned value is the sum of all the numbers con-tained in the cells
Now that we have the number, we need to lay the DOM scaffolding for it We build a
trwith a class of total, and then three tds with contents that correspond to that of the existing cells Our total population figure gets inserted into the third cell via
Element#update We insert each cell as a child of the trupon creation; since Element#insert adds new nodes at the end, by default, these elements will appear on the page in the order they’re inserted
All that’s left to do is test it! Open index.htmlin Firefox, and pass our population table
to the computeTotalForTablefunction (see Figure 6-3):
computeTotalForTable('cities');
Trang 4Figure 6-3.JavaScript can add better than I can.
Summary
There’s a lot of meat to Prototype’s DOM support—primarily because that’s where most
of your headaches come from The DOM’s verbosity and uneven browser support are the
proverbial “rock” and “hard place” of JavaScript development
An exploration of the DOM, however, isn’t complete without attention paid to events
The next chapter will exemplify the power of Prototype’s element traversal methods in
the context of handling standard (and not-so-standard) browser events
C H A P T E R 6 ■ W O R K I N G W I T H T H E D O M 137
Trang 5Advanced JavaScript:
Functional Programming and
Class-Based OOP
JavaScript is a multi-paradigm language No matter how well you think you know how
to use it, you’re destined to find some style of writing code that confuses the hell out of
you
This is good news, if you’ll believe it It means that there’s often a better, shorter,
more secure, or easier-to-understand way of doing something
Earlier chapters introduced you to object-oriented programming and functional
programming So you’re probably familiar with what they are, but may not realize yet
why they’re useful In this chapter, we’ll revisit these techniques, exploring advanced
use cases for both
Object-Oriented JavaScript Programming
with Prototype
The term object-oriented programming (OOP) has become nearly meaningless with
over-use, but I’ll try to wring out a few final drops of meaning JavaScript itself is an
object-oriented language, as we’ve discussed, but most of the common OOP concepts—
class definitions, clear inheritance chains, and so on—aren’t built into the language
Prototype builds a class-based facade around the prototypal OOP model of
JavaScript In this chapter, you’ll learn how to use classes the Prototype way
Why OOP?
The jaded developer might wonder whether I’m espousing OOP as the alpha and omega
of programming styles As Steve Yegge once quipped, advocating “object-oriented
139
Trang 6programming” is like advocating “pants-oriented clothing”; it elevates one architectural model to an overimportant position
In the DOM scripting world, OOP is often—but not always—the right tool for the job Its advantages mesh well with the realities of the browser environment
Cleanliness
JavaScript 1.x has no explicit namespacing and no package mechanism—no comprehen-sive way to keep different pieces of code from stepping on each other If your web app needs, say, 50 lines of JavaScript, this isn’t a problem; if it needs three external libraries and a bunch of behavior code, you’ll run into naming problems Scripts share the global space
In other words, if I have a function named run, I’ll need to make sure that none of the scripts I rely upon defines a runfunction, or else it will be overwritten I could rename my function to something unique—like myRunor dupont_Run—but this is just a stall tactic Ensuring uniqueness of function names becomes exponentially harder as the number of functions increases (PHP’s standard library is an extreme example of the sort of clutter that can accumulate this way.)
OOP helps solve this It lets me define methods in more appropriate contexts Instead of a global function called stringReplace, JavaScript defines replaceas an
instance method on strings
Sharing a scripting environment is like sharing an apartment If you don’t help keep the place clean, your roommates will think of you as an inconsiderate bastard Don’t be that guy If you must be messy, be messy in your own room Common areas need to remain tidy
Encapsulation
OOP helps keep things organized through bundling An object contains all the methods and properties associated with it, minimizing clutter and affording portability
Information-Hiding
Many clocks rely on an elaborate system of gears to tell time, but humans see very little of what goes on inside a clock We don’t have to figure out the time from the positions of the gears; we can look at the hands on the clock face In other words, the gears are important
to the clock, but they’re not important to us.
Most real-world objects have a “public” interface (the way the outside world uses it) and a “private” interface (the way the object does the tasks it needs to do) OOP works the same way, letting a developer produce code that is easier to understand and more rele-vant on the line where it’s used
C H A P T E R 7 ■ A D VA N C E D J AVA S C R I P T: F U N C T I O N A L P R O G R A M M I N G A N D C L A S S - B A S E D O O P
140