custom events from anywhere in your code; you can also listen for custom events withthe same API that you’d use to listen for native browser events.. Since custom events are handled diff
Trang 1custom events from anywhere in your code; you can also listen for custom events with
the same API that you’d use to listen for native browser events.
The First Custom Event
Prototype itself fires a custom event called dom:loaded It fires at a specific time in the
page’s life cycle: after the page’s DOM tree is fully accessible to scripts, but before the
win-dow’s loadevent, which doesn’t fire until all external assets (e.g., images) have been fully
downloaded
Use dom:loadedwhen you want to work with the DOM in that narrow window of
time before the page appears on the screen fully rendered In nearly all cases, it’s better
to assign to dom:loadedthan load—unless your handler depends upon having
every-thing downloaded and rendered
This is also a good time to talk about the naming scheme for custom events You’ve
probably noticed that dom:loaded, unlike native events, contains a colon This is by
design—all custom events must contain a colon in their names Since custom events are
handled differently under the hood, Prototype needs a way to distinguish them from
native browser events (which number in the hundreds if all major browsers are
consid-ered) Embrace the convention
Broadcasting Scores
The data stream we built in Chapter 4 will power a large portion of our fantasy football
site It would be wasteful and silly for each JavaScript component to make its own Ajax
requests, so let’s write some general code with the specific task of “asking” for scores
from the server, and then “announcing” these scores through some sort of public
address system
Create a new file called score_broadcaster.jsand place this code inside:
var ScoreBroadcaster = {
setup: function() {
this.executer = new PeriodicalExecuter(this.update.bind(this), 30);
this.update();
},
update: function() {
this.request = new Ajax.Request("scores.php", {
onSuccess: this.success.bind(this)
});
},
C H A P T E R 5 ■ E V E N T S 109
Trang 2success: function(request) {
document.fire("score:updated", request.responseJSON);
}
};
document.observe("dom:loaded", function() {
ScoreBroadcaster.setup();
});
First, notice the design pattern—we’re creating a ScoreBroadcasterobject to act as our namespace Next, jump to the bottom—we’ve hooked up ScoreBroadcaster.setupto run as soon as the DOM is ready This function schedules a new Ajax request every 30 seconds; successful requests will call another function that will fire a custom event with our data
Now look in the middle—we call document.firewith two arguments This method fires custom events, naturally, and exists on all elements (Element#fire) and on the documentobject, too You’ve just learned two things about this method:
• The first argument is the name of the event to be fired As we discussed, the name needs to have a colon in it, so let’s call it score:updated The noun:verbednaming scheme is just a convention, but it’s a useful one
• The second argument is an object that contains any custom properties for attach-ing to the event object Just like native browser events, custom events pass an event object as the first argument to any handler Alongside familiar properties like target, custom events have amemoproperty on their event objects The second argument of Element#firegets assigned to this property In short, we’re attaching the score information so that handlers can read it
As we covered in Chapter 4, we’re using Prototype’s special responseJSONproperty on the Ajax response object—useful because it automatically unserializes the JSON payload Using the application/jsonMIME type gets us this property for free
That’s one fewer link in the chain we have to worry about When we write compo-nents, we won’t have to deal with the boring details of getting the data Score updates will be dropped upon them as though they were manna from heaven
Listening for Scores
To illustrate this point, let’s write some quick test code to make sure the custom event is working right Add this to the bottom of score_broadcaster.js:
C H A P T E R 5 ■ E V E N T S
110
Trang 3document.observe("dom:loaded", function() {
document.observe("score:updated", function(event) {
console.log("received data: ", event.memo);
});
});
We listen for a custom event the same way we listen to a native event: using
Event.observe Custom events behave much the same way as native events: they bubble
up the DOM tree, and they have their own event objects that implement all the
proper-ties and methods we’ve already covered
Here we listen for our custom score:updatedevent and log to the Firebug console
whenever it fires Include this script on an HTML page and observe the result Every
30 seconds, one of the lines shown in Figure 5-12 should appear in your Firebug console
Figure 5-12.This line should appear in your Firebug console twice a minute.
In subsequent chapters, we’ll write code that hooks these stats up to the interface
Summary
To revisit the theme of the chapter, events should make simple things simple and
com-plex things possible Prototype’s event system doesn’t make everything simple, but it
does manage the unnecessary complexities of modern browser events.
The watchword for this chapter has been normalization: making different things
behave uniformly Prototype makes two different event systems (Internet Explorer’s
and the W3C’s) behave uniformly; it also makes native events and custom events
behave uniformly Keep this concept in mind while we look at DOM traversal in the
next chapter
C H A P T E R 5 ■ E V E N T S 111
Trang 4Working with the DOM
Now that you’ve got the foundation you need to explore advanced concepts, it’s time to
learn about Prototype’s powerful helpers for working with the DOM
About the DOM API
As we discussed in the last chapter, the DOM is specified in a series of documents
released by the W3C
DOM Level 1 outlines the basics you’re probably used to Levels 2 and 3 specify a
series of enhancements and expansions to the DOM API, such as events, node traversal,
and style sheets Level 1 enjoys support in all modern browsers, but the other levels
can-not be counted on
Despite its obvious power and utility, at times the DOM API just doesn’t feel very
JavaScript-y Methods have annoyingly long names Some methods take a lot of
argu-ments, and some methods expect their arguments in an unintuitive order
This is an unfortunate but necessary result of the DOM’s language independence
Though the most famous implementation of the DOM is JavaScript’s, the APIs are
designed to be implemented in nearly any modern programming language This
approach has its drawbacks (the DOM can’t leverage any of JavaScript’s dynamism, since
it has to work in more static languages like Java), but it also has the advantage that the
DOM works the same way in any language: learn once, write anywhere
Still, we’re writing our code in JavaScript, so let’s make the most of it Prototype
con-tains a large number of extensions to the browser’s DOM environment, so developers can
have their DOM and eat it too
Node Genealogy
The strange world of the DOM is filled with jargon and terms of art In order to minimize
confusion, let’s look at a few of them up front
113
C H A P T E R 6
Trang 5Think of the DOM as an unseralizer It takes a linear stream of HTML, parses it into different types of nodes, and arranges those nodes in a tree according to their relationships
That may not have been too helpful, so I’ll let the code talk (see Figure 6-1):
<p><u>Colorless</u> <i>green <u>ideas</u></i> sleep <b>furiously</b>.</p>
Figure 6-1.The DOM translates a stream of HTML into a tree of nodes This paragraph has both element nodes and text nodes as descendants.
The resemblance of a tree like this to a family tree is convenient—it lets us borrow
the jargon of genealogy
Take the ptag at the top It has five children:u,i,"sleep",b, and "." The two in quo-tation marks are text nodes The other three are element nodes, and those elements have children of their own And "ideas"is p’s great-grandchild, so to speak; it’s in the third level
of descendants from p
The distinction is useful, then, between children and descendants, and between parent and ancestor Children are all elements that are exactly one level descended from
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
114
Trang 6a certain node Descendants are all elements that a node contains—the sum of all the
node’s children, its children’s children, and so on
Likewise, a node can have only one parent, but can have many ancestors
Prototype’s DOM Extensions
The DOM is broad, sterile, and built by committee In the interest of creating a “safe”
API that can be used by many different languages, it maintains a cordial distance from
the features of JavaScript Its API chooses verbose method names like getElementByIdand
getAttributeNode—as with natural language, the cost of eliminating all ambiguity is to
double the number of words
Prototype establishes a bridge between the DOM and the commonest use cases
of the typical developer As a result, the code you write will be shorter and far more
readable
Prototype’s DOM API is broad, so let’s divide it into rough categories based on task:
modifying, traversing, collecting, and creating
Modifying
These methods modify properties of a DOM node or report information about a node for
later modification
The hide, show, visible, and toggle Methods
These are the most commonly used element methods They control whether the element
is visible or hidden (whether its CSS displayproperty is set to none, thereby hiding it from
view)
Controlling element display is a staple of web applications Think of a message that
should disappear after the user dismisses it Think of a listing of items, each with a
sum-mary that should only be shown when the user mouses over that item Think of a view
stack—a group of elements that should occupy the same area of the page, with only one
visible at a time
Element#hideand Element#showcontrol element display:
var foo = $('foo');
foo.hide();
foo.style.display; //-> 'none';
foo.show();
foo.style.display; //-> 'block';
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 115