For this reason, a lesson in using Prototype begins not with an in-depth look at any particular portion, but rather with a whirlwind tour of many of the problem-solving con-structs you’l
Trang 1Prototype Basics
JavaScript libraries don’t start out as libraries They start out as “helper” scripts
It’s possible, but impractical, to do DOM scripting without the support of a library
Little things will start to annoy you from day one You’ll get tired of typing out
and Firefox have different event systems, so you’ll write a wrapper function for adding
events Then you’ll become irritated with a specific oversight of the language itself, so
you’ll use JavaScript’s extensibility to write some code to correct it
Then one day you’ll notice your “helper script” is 35 KB When organized and divided
into sections, you’ll realize you’ve got a library on your hands All JavaScript libraries start
out as caulk between the cracks
For this reason, a lesson in using Prototype begins not with an in-depth look at any
particular portion, but rather with a whirlwind tour of many of the problem-solving
con-structs you’ll use most often In this chapter we’ll take that tour
Getting Started
add some content to the page’s body:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="prototype.js"></script>
<title>Blank Page</title>
</head>
17
Trang 2<h1>Blank Page</h1>
<ul id="menu">
<li id="nav_home" class="current"><a href="/">Home</a></li>
<li id="nav_archives"><a href="/archives">Archives</a></li>
<li id="nav_contact"><a href="/contact">Contact Me</a></li>
<li id="nav_google"><a href="http://google.com"
rel="external">Google</a></li>
</ul>
</body>
</html>
We’re adding an unordered list with links as list items—the typical structure for a web site’s navigation menu Each lielement has an ID The final aelement has a relattribute with a value of external, since it links to an external site This convention will be quite useful later on
We’ll add more markup to this page over the course of the chapter, but this is enough for now Nearly all of the code examples from this chapter can be used on this page with the Firebug shell, so feel free to follow along
The $ Function
DOM methods have intentionally verbose names for clarity’s sake, but all that typing gets tiresome very quickly Prototype’s solution is simple but powerful: it aliases the
instead of
document.getElementById('menu'); //-> <ul id="menu">
with Prototype, you can write
$('menu'); //-> <ul id="menu">
attribute
name It will be the function you’ll use most often when scripting with Prototype In addition, the dollar sign is a valid character in object names and has little chance of having the same name as another function on the page
Trang 3$ Can Take Either Strings or Nodes
ID matches the string If it’s passed an existing node reference, though, it will return that
refer-ences nearly identically For example
var menuElement = document.getElementById('menu');
Element.remove(menuElement);
// can also be written as
Element.remove('menu');
Either way, the element is removed from the page Why? Listing 2-1 shows how
Listing 2-1.Prototype Source Code
Element.remove = function(element) {
element = $(element);
element.parentNode.removeChild(element);
return element;
};
It makes code very flexible at a very small cost
argu-ment in any Prototype method that expects a DOM node can receive either a node
reference or a string reference to the node’s ID.
$ Can Take Multiple Arguments
node because an ID is supposed to be unique on a page, so if the browser’s DOM
engine finds an element with the given ID, it can assume that’s the only such element
on the page
number of arguments If passed just one, it will return a node as expected; but if passed
more than one, it will return an array of nodes.
Trang 4var navItems = [document.getElementById('nav_home'),
document.getElementById('nav_archives'), document.getElementById('nav_contact')];
//-> [<li id="nav_home">, <li id="nav_archives">, <li id="nav_contact">]
var navItems = $('nav_home', 'nav_archives', 'nav_contact');
//-> [<li id="nav_home">, <li id="nav_archives">, <li id="nav_contact">]
Prototype’s constructs for working with DOM collections represent a large portion of its power In Chapter 3, you’ll learn about how useful this can be
$ Enhances DOM Nodes with Useful Stuff
Core JavaScript data types can be augmented with user-defined functions All JavaScript
shared by all instances of that object For instance, Prototype defines a method for strip-ping leading and trailing whitespace from strings:
String.prototype.strip = function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
};
" Lorem ipsum dolor sit amet ".strip();
//-> "Lorem ipsum dolor sit amet"
All strings get this method because they’re all instances of the Stringobject
In an ideal world, it would be this easy to assign user-defined functions to DOM objects:
HTMLElement.prototype.hide = function() {
this.style.display = 'none';
};
This example works in Firefox and Opera, but fails in some versions of Safari and all versions of Internet Explorer There’s a gray area here: the DOM API is designed to
be language independent, with its JavaScript version just one of many possible
as built-ins like Stringand Array To get this sort of thing to work across browsers requires a bit of voodoo
Prototype takes care of this behind the scenes by defining custom element
Trang 5methods to nodes on demand in browsers that don’t Any Prototype method that
returns DOM nodes will “extend” these nodes with instance methods to enable this
handy syntactic sugar
Once a node has been extended once, it does not need to be extended again But to
extend all nodes on page load would be prohibitively costly, so Prototype extends nodes
on an as-needed basis
Let’s illustrate this in code:
var firstUL = document.getElementsByTagName('ul')[0];
firstUL.hide();
//-> Error: firstUL.hide is not a function
You’ll get this error in Internet Explorer A node must have been extended by
Prototype before you can be sure it has these instance methods So there are a few
options here:
• Use the generic version of the method Every instance method of a DOM node is
also available on the Elementobject:
var firstDiv = document.getElementsByTagName('ul')[0];
Element.hide(firstUL);
• Instead of using the native DOM method, use a Prototype method that does the
same thing
var firstUL = $$('div')[0];
firstUL.hide();
• Extend the node just to be safe, using Element.extendor $
$(document.getElementsByTagName('ul')[0]).hide();
In other words, $isn’t just an alias for document.getElementById, it’s also an alias for
learn much more about this in Chapter 6
Object.extend: Painless Object Merging
The object literal is part of JavaScript’s terseness and expressive power It allows one to
declare an object with any number of properties very easily
Trang 6var data = {
height: "5ft 10in",
weight: "205 lbs",
skin: "white",
hair: "brown",
eyes: "blue"
};
But in JavaScript, it’s possible to add any number of properties to an existing object
at any time So what happens when we want to extend this object?
if (person.country == "USA") {
data.socialSecurityNumber = "456-78-9012";
data.stateOfResidence = "TX";
data.standardTaxDeduction = true;
data.zipCode = 78701;
}
We can’t define new properties en masse—we have to define them one by one It gets even more frustrating when extending built-in classes, as Prototype does:
String.prototype.strip = function() {
//
};
String.prototype.gsub = function() {
//
};
String.prototype.times = function() {
//
};
String.prototype.toQueryParams = function() {
//
};
This is a direct road to carpal tunnel syndrome There’s got to be a better way—we need a function for merging two different objects
Prototype gives us Object.extend It takes two arguments, destinationand source,