Sie sind auf Seite 1von 95

Eight Weeks of Prototype: Week 1, Beginning with Prototype

Introduction

Prototype is a JavaScript framework used to help with development of cross-browser code that
is easy to maintain and extend. In this first article of "Eight Weeks of Prototype" I will teach you
the fundamentals of Prototype, which you will hopefully find useful for all JavaScript code you
write.

Firstly, I will show you how to download and install Prototype into your own web applications.

Next I will show you how to select elements from the DOM (Document Object Model). That is,
you'll learn how to access any element from your HTML document (such as a particular link or
image). There are several ways to select elements, each of which will be covered.

In the last part of this article I will show you how to create new elements real-time and add
them to the DOM. I will also show you how to remove elements from the DOM.

Downloading and Installing Prototype

Prototype is a single JavaScript file that you download and use as a file in your web site. To
make use of Prototype, you simply need to load this one file in your HTML code.

Downloading Prototype

The official home page of Prototype is http://prototypejs.org. You can download the current
version (which at time of writing is 1.6.0.2) from the download page
at http://prototypejs.org/download. The file you download is called prototype-1.6.0.2.js, which
you then need to save in your web site file structure.

For the purpose of this article (and subsequent articles in this series), I will assume you have
saved this JavaScript file to a directory called /js, and renamed the file
to prototype.js (meaning you can upgrade Prototype in the future without having to change
your HTML).

For example, if your site was www.example.com the file would be accessible
fromwww.example.com/js/prototype.js. Creating a separate directory in which to hold this file
also gives you a place to store your other JavaScript files as required.

Loading Prototype on Your Web Site

Once you have saved the Prototype JavaScript file on your web site you can use it on any of
your HTML pages simply by loading that one file. Listing 1 shows the code you would use to load
Prototype if you have saved it in the /js directory as described above.
Listing 1 Loading Prototype and viewing the loaded version (listing-1.html)
<html>
<head>
<title>Loading Prototype and viewing the loaded version</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript">
alert('Prototype ' + Prototype.Version + ' loaded');
</script>
</head>
<body>
</body>
</html>

In this code listing I have made an alert box appear which shows the version of Prototype
loaded using the internal Prototype.Version variable.

Prototype Documentation and Resources

The Prototype web site contains extensive API documentation for Prototype, which you should
refer to frequently to help with your own development. You can find this documentation
at http://prototypejs.org/api.

The $() Function

The first function we will look at is the $() function. This function behaves very similarly
todocument.getElementById() (the non-Prototype way of retrieving a DOM element using
JavaScript), except that it returns elements with extended Prototype functionality (covered in
the second article of "Eight Weeks of Prototype"). This extended functionality is a series of
methods added to each element that simplify your JavaScript development.

Although typically you will only pass one argument to $(), you can in fact pass multiple
arguments to it. If one argument is used, then a single element is returned. If more than one
argument is used, then an array of elements is returned.

To retrieve an element (or multiple elements) with $(), you can use either the ID of the element
you want to select, or the element itself. By passing an element to $(), you can ensure it has
been extended with the extra functionality Prototype provides.

Listing 2 shows an example of using $(), in which the div element with an ID of exampleDiv is
selected. The content of this div is then modified using the update() method (which is one of the
extended methods Prototype provides, covered in part 2 of this series).
Listing 2 Selecting an element with $() and updating its contents (listing-2.html)
<html>
<head>
<title>Selecting an element with $() and updating its contents</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="exampleDiv"></div>
<script type="text/javascript">
$('exampleDiv').update('Div content updated!');
</script>
</body>
</html>
In the code above we know the #exampleDiv element exists, and therefore we don't worry about
any error checking, however, you should really ensure the element is correctly returned before
trying to perform any further operations on it. If the given element was not found then null is
returned. Thus, you can use code similar to that of Listing 3 to ensure the given element was
found.
Listing 3 Ensuring the element was successfully returned before using it (listing-3.html)
<html>
<head>
<title>Ensuring the element was successfully returned before using it</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="exampleDiv"></div>
<script type="text/javascript">
var elt = $('exampleDiv');
if (elt)
elt.update('Div content updated!');
</script>
</body>
</html>

Often JavaScript classes you write with Prototype (covered in the sixth article of "Eight Weeks of
Prototype") will rely on one or more elements in your HTML document. Checking that the
element was selected successfully as in Listing 3 means you can write error-free code and
accordingly notify the user (or developer) that there was a problem if the element was not
found.

When writing your own functions you may not necessarily know whether you have been passed
an element or just an ID. Therefore, you should typically call $() on any arguments that are
supposed to be DOM elements. Listing 4 shows a user-defined function and two different (yet
identical) ways of calling it.
Listing 4 Using $() to extend function arguments (listing-4.html)
<html>
<head>
<title>Using $() to extend function arguments</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="exampleDiv"></div>
<script type="text/javascript">
function myFunction(elt, message)
{
elt = $(elt);
elt.update(message);
}

myFunction('exampleDiv', 'Div updated');


myFunction($('exampleDiv'), 'Div updated again');
</script>
</body>
</html>
While this example is somewhat trivial, it does demonstrate one of the ambiguities that may
arise with JavaScript development: whether a function accepts an element or an element ID.
Using $() as inmyFunction()above means it doesn't matter either way.

The $$() Function

One of the most powerful functions provided by Prototype is the $$() function. This function
allows you to select a series of elements from the DOM by using CSS selectors. The value
returned from calling $$() is an array containing each found element, in the order each element
appears in the HTML document.

To better demonstrate how $$() works, here are some examples:

 $$('img') - select all image elements in a document.

 $$('#container a') - select all links within the element that has the ID container.

 $$('div.someClass input[type=submit]') - retrieve all submit buttons inside a div with


the classsomeClass.

If no matching elements are found then an empty array is returned.

Listing 5 shows an example of using $$(). In this example we loop over the returned elements
and write a number to each element. In the third article in this Prototype article series we will
see an easier way to loop over arrays, but for now a simple for loop will suffice.
Listing 5 Selecting a series of elements using $$() and looping over them (listing-5.html)
<html>
<head>
<title>Selecting a series of elements using $$() and looping over them</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="someElement">
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div id="exampleDiv"></div>
<script type="text/javascript">
var items = $$('#someElement li');

for (var i = 0; i < items.size(); i++) {


items[i].update('Element ' + i);
}
</script>
</body>
</html>
Note: The above code uses the size() method to determine the number of elements in the
array. This is extended functionality for arrays provided by Prototype. This is covered more
extensively in the third article of "Eight Weeks of Prototype".
As a general rule of thumb, $$() is much more efficient when the selection is qualified by
including an element ID at the start. For example, if you want to find all elements in the
document with the class of someClass and you know all of these occur within an element with
ID someId, it is more efficient to use $$('#someId .someClass') rather than $$('.someClass').
The reason the search is more efficient is because rather than checking the class of every
element in the DOM, only elements within #someId need to be checked.

When using the $$() function, you can provide more than one argument if required. This allows
you to select elements based on multiple CSS selectors. Regardless of which selector is used to
find an element, all elements are returned in a single array in document order (just as when
using a single argument).

The select() Method

One of the extended methods provided by Prototype is the select() method (formerly
calledgetElementsBySelector()). Just like the $$() function, this method accepts one or more
CSS selectors as arguments, however the key difference between select() and $$() is
that select() only searches within the element on which it is called.

In the previous section, I discussed including an element's ID in $$() to speed up the search.
The example given was to use $$('#someId .someClass'). The equivalent of this search
using select() would be to use$('someId').select('.someClass').

Listing 6 shows an example of using select() rather than using $$(). You will find select() most
useful when writing classes or functions to which an element has been passed in which you want
to find specific elements.
Listing 6 Using select() to search for elements within a given element (listing-6.html)
<html>
<head>
<title>Using select() to search for elements within a given element</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="someElement">
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div id="exampleDiv"></div>
<script type="text/javascript">
var elt = $('someElement');
var items = elt.select('li');
for (var i = 0; i < items.size(); i++) {
items[i].update('Element ' + i);
}
</script>
</body>
</html>

DOM Traversal Methods

In addition to being able to select elements in the DOM using $$() and select(), Prototype also
allows you to select elements using the up(), down(), next() and previous() methods. These
methods help you to easily find elements relative to a given element.

Unlike $$() and select(), each of these methods is used to retrieve exactly one element (if
found). Because of this (and because each element is returned with the extended Prototype
functionality), you can chain these calls together, as you will see at the end of this section.

The up() method

To find an element further up the DOM tree from a given element (that is, to find one of its
ancestors) you can use the up() method. If no arguments are passed to up(), then the
element's parent is returned.

If you don't just want an element's parent element but rather one of its other ancestors there
are several different combinations of arguments that can be used. Firstly, you can specify a
numerical index. For instance, using up(0) will retrieve the element's parent (the same as
omitting the argument), using up(1) will return the element's grandparent, and using up(2) will
return the great-grandparent.

Alternatively, you can pass a CSS selector to up(). The first matched element is returned. For
example, if you have an image inside a HTML table (e.g. <table><tr><td> <img />
</td></tr></table>), you can useimgElt.up('table') to retrieve the table element (in this case
using just imgElt.up() might return the <td>element instead).

You can also specify a numerical index along with a CSS selector. For example, if you have an
image within two nested div elements, you can select the outer div by using imgElt.up('div',
1) (the first element from the target has an index of 0, which is the default value if the second
argument isn't specified).

Listing 7 shows some examples of how to use the up() method to find an element's ancestors.
Listing 7 Examples of using up() to find an element's ancestors (listing-7.html)
<html>
<head>
<title>Examples of using up() to find an element's ancestors</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="main" class="foo">
<table class="foo">
<tr>
<td>
<a href="#"><img src="someImage.png" id="someImage" /></a>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var img = $('someImage');

// these are all equivalent ways of retrieving the link:


var link = img.up();
var link = img.up(0);
var link = img.up('a');

// similarly, there are several ways to retrieve the surrounding cell


var cell = img.up(1);
var cell = img.up('td');

// the foo class is used in two different places


var table = img.up('.foo');
var div = img.up('.foo', 1);
</script>
</body>
</html>

One of the most useful aspects of up() is that you can easily find an element without caring
which elements lie between the element you want to find and the element you're searching on.
That is, because you can use selectors to find the parent, you don't mind whether the element is
the parent, the grandparent or otherwise.

For example, if you have a generic JavaScript class that relies on there being elements named in
a particular way (by way of element IDs or class names), you don't have to worry about the
specific structure of the elements in the DOM.

The down() method

The down() method is the opposite of the up() method, in that it searches within an element's
descendants rather than in its ancestors. That is, it looks for elements within the target element.

Just like up(), you can either specify no arguments, a numerical index, a CSS selector, or a CSS
selector with a numerical index. Specifying no arguments will result in the first child being
returned.

Using down() is very similar to using the select() method covered earlier in this article, except
that only a single element is returned using down() (remember that select() returns an array).
Because of this, we can deduce that someElt.down('.foo') is effectively equivalent
to someElt.select('.foo')[0].

The important difference is that trying to reference an particular element when using select() is
that a JavaScript error will occur if the select() call returns an empty array. This is not an issue
when using down().
Listing 8 shows some examples of using down() to find an element's descendants.
Listing 8 Selecting an element's descendants using down() (listing-8.html)
<html>
<head>
<title>Selecting an element's descendants using down()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="main" class="foo">
<table class="foo">
<tr>
<td>
<a href="#"><img src="someImage.png" id="someImage" /></a>
</td>
<td>
Second cell
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var div = $('main');

// there are several ways to find the table element


var table = div.down();
var table = div.down('table');
var table = div.down('.foo');

// you can specify an index to find a particular match


var secondCell = div.down('td', 1);

// complex selectors can be used


var image = div.down('table.foo a img#someImage');
</script>
</body>
</html>

The next() and previous() methods

You can find sibling elements (that is, any element with the same parent element as the search
target) using the next() and previous() methods. As suggested by their names, next() finds
siblings elements that appear in the document after the search target, while previous() finds
only siblings that appear before the search target.

The arguments used for next() and previous() work in the same manner as
with up() and down(). That is, you can use a numerical index or a CSS selector.

Listing 9 shows several examples of using next() and previous().


Listing 9 Using next() and previous() to find sibling elements (listing-9.html)
<html>
<head>
<title>Using next() and previous() to find sibling elements</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul>
<li id="first">First item</li>
<li id="second">Second item</li>
<li id="third">Third item</li>
<li id="fourth">Fourth item</li>
<li id="fifth">Fifth item</li>
</ul>
</div>
<script type="text/javascript">
var secondElement = $('first').next();
var secondElement = $('third').previous();

var thirdElement = $('third').previous().next();

// this call will return null since #fifth is the


// final child of the unordered list
var nullElement = $('fifth').next();
</script>
</body>
</html>

Chaining traversal calls together

Because calls to up(), down(), next() and previous() each return a single element that has been
extended with extra Prototype functionality, we can chain calls to these functions together.

For example, calling elt.down().up() will return the original element elt (note, however, that
callingelt.up().down() will not necessarily return the original element; this will depend on the
ordering of elements within elt's parent). Similarly, elt.next().previous() will also return elt.

Obviously there is little use for these examples in particular, however you may encounter
situations where chaining these calls together is extremely useful. One such example might be
to search all siblings of an element. Using elt.next(someSelector) only finds siblings before the
given element, whileelt.previous(someSelector) only finds siblings after the element. If you
wanted to search either before or after, you could do so by using elt.up().down(someSelector).

Note: Depending on your CSS selector, this may also return the search target, not just its
siblings. You may need to check for this in your code if this is a problem.

When chaining calls together, there is a risk that one of the later calls in the chain may cause an
error due to an earlier call not returning an element (for instance, calling previous() on an
element with no siblings will not return a valid element). Because of this, you should only chain
your calls together when you know it cannot fail. Otherwise, you should make each call in a
separate statement and check the return values accordingly.

Creating New Elements and Inserting Them into the DOM

New in Prototype 1.6.0 is the ability to easily create new DOM elements that will work across all
browsers. An element is created by instantiating the Element class. The first argument to this
class is the type of element to be created, while the second argument is used to specify the
element's attributes.

Listing 10 shows how you would dynamically create a new hyperlink (that is, the equivalent of
the using the HTML <a>). In this example, we call update() on the created element to set the
link text.

After an element has been created, it must be inserted into the document. This is achieved by
calling theinsert() method on another element that already exists in the document.
Listing 10 Creating a new element in the DOM (listing-10.html)
<html>
<head>
<title>Creating a new element in the DOM</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="main">
<div>Some item</div>
</div>

<script type="text/javascript">
var attrs = {
href : 'http://www.example.com',
target : '_blank'
};

var link = new Element('a', attrs);

link.update('Visit this web site');

$('main').insert(link);
</script>
</body>
</html>

Note: When you create the attributes for a new element, some attribute names need to be
quoted. For example, using { class : 'MyClass' } will cause an error in some browsers.
Instead, you should use { 'class' : 'MyClass'}.

If you were to manually create the HTML as generated by this JavaScript, it would like that in
Listing 11.
Listing 11 The HTML equivalent of the code in Listing 10 (listing-11.html)
<html>
<body>
<div id="main">
<div>Some item</div>
<a href="http://www.example.com" target="_blank">Visit this web site</a>a>
</div>
</body>
</html>

When you call the insert() method with just the new element as an argument, that new
element then becomes the last child of the target element. Alternatively, you can place the new
element before the target, after the target, or as the first child of the target. This is done by
slightly changing the argument to insert().

When we used $('main').insert(link), this was the equivalent of calling $('main').insert({


bottom : link }), meaning it was inserted as the last child (as you can see in Listing 11, since it
appears after the "Some Item" div). To make it appear before the "Some item" div, you would
use $('main').insert({ top : link }). This would result in HTML as in Listing 12.
Listing 12 The HTML equivalent of inserting the new link as the first child (listing-12.html)
<html>
<body>
<div id="main">
<a href="http://www.example.com" target="_blank">Visit this web site</a>a>
<div>Some item</div>
</div>
</body>
</html>

To insert the link before the #main div, you can instead use $('main').insert({ before :
link }). The HTML equivalent of this is shown in Listing 13.
Listing 13 The HTML equivalent of inserting the new link before the target element (listing-13.html)
<html>
<body>
<a href="http://www.example.com" target="_blank">Visit this web site</a>a>
<div id="main">
<div>Some item</div>
</div>
</body>
</html>

Finally, you can insert the new element after the #main div by using $('main').insert({ after :
link }). The HTML equivalent of this is shown in Listing 14.
Listing 14 The HTML equivalent of inserting the new link after the target element (listing-14.html)
<html>
<body>
<div id="main">
<div>Some item</div>
</div>
<a href="http://www.example.com" target="_blank">Visit this web site</a>a>
</body>
</html>

When using the insert() method, it is possible to use HTML code to create the new element (as
opposed to a DOM element created as shown as the start of this section). Listing 15 shows an
alternative to Listing 10 for creating a hyperlink and inserting it into the DOM.
Listing 15 Creating a new element using HTML code (listing-15.html)
<html>
<head>
<title>Creating a new element in the DOM</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="main">
<div>Some item</div>
</div>

<script type="text/javascript">
$('main').insert('<a href="http://www.example.com" target="_blank">Visit this
web site</a>');
</script>
</body>
</html>

Using the methods shown in this section, it is very easy to create new elements as required and
insert them wherever you need them to be in your document.

Removing Elements from the DOM

The final aspect of managing DOM elements that we will look at in this article is the removal of
elements. This is achieved by calling the remove() method on the element you want removed.

When you call this method, the element being removed is returned from the function call. This
allows you to save the element in case you want to add it to the DOM once again.

If you want to temporarily hide an element from the user, instead of removing (by
using remove()) and then re-adding it (using insert()), you should simply
use hide() and show() respectively (these are methods added by Prototype as we will see in the
second article in this series). You should use remove() when you want to get rid of an element
(and its children) completely from the current page.

Listing 16 shows an example of using remove() to remove an element from a HTML page. When
you view this HTML code in your browser, the page will be blank since the only output from the
page was removed.
Listing 16 Removing an element from the DOM using remove() (listing-16.html)
<html>
<head>
<title>Removing an element from the DOM using remove()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="main">
<div>Some item</div>
</div>

<script type="text/javascript">
$('main').remove();
</script>
</body>
</html>

Summary

In this, the first of eight articles about Prototype, we have covered many of the important
fundamentals that you will need to know for more advanced Prototype development.
We began the article by downloading and installing Prototype and learning where to find the API
documentation.

Next we learned how to select elements from the Document Object Model (DOM) using
the $(), $$(),select(), up(), down(), next() and previous() methods.

After this we learned how to create new elements, and we looked at different ways of inserting
those elements into the DOM, using the insert() method.

Finally, we learned how to remove elements from the DOM by using the remove() method.

In the next article of "Eight Weeks of Prototype", I will teach you about the functionality
Prototype adds to DOM elements, and how to manipulate elements in different ways using these
added functions.

Eight Weeks of Prototype: Week 2, How Prototype Extends


Elements
Introduction

In the first article of this series ("Week 1, Beginning with Prototype") I showed you different
methods for accessing elements in the DOM, as well as how to create new elements. Whenever
you select (or create) elements, each element is automatically extended with extra functionality
by Prototype. Essentially, this is a number of methods that help you manipulate the behaviour of
the element more easily.

In this article I will show you a selection of the most useful methods, including various examples
of using (and combining) these methods. While the methods covered here are not exhaustive,
you can find documentation about the rest of these
at http://prototypejs.org/api/element/methods.

In actual fact, we've already seen some of these methods in the first article. Namely, we have
already looked the following methods:

 update() – Used to update the inner HTML of the target element. We will look at some
more examples of this method shortly.

 select(), up(), down(), previous() and next() – Used to other elements in the DOM
relative to the target element.

 insert() – Used to add a new element to the DOM relative to the target element.

 remove() – Used to remove the target element (and its children) from the DOM.

 hide() and show() – Only mentioned in passing so far, these are used to hide an element
from the view of the user and to show it again. I will show you some examples of using
these methods.
Firstly, I will show you more about how the update() method works, used to modify the content
of an element. I will also show you how to then read that content. Additionally, you will learn
how to read and write element attributes using JavaScript.

Next, I will show you how to manage the CSS classes of elements, as well as how to manipulate
the styles of elements in real-time. Prototype also makes it easy to determine the width and
height of elements, which I will also show you.

Following this, you will learn how to show and hide elements in the user's display. I will show
you some practical examples of where it is useful to do so.

Reading and Writing an Element's Content

In this section I will show you firstly how to update the inner content of an element using
Prototype. There may occasions where you need to read the content rather than write it, so I
will also show you how to do that.

After this I will show you how to read and write attributes. Writing attributes is extremely when
including a particular attribute in your HTML source will break the page's standards compliance.
That is, this technique is most useful for managing non-standard attributes.

Updating the Content of an Element

To update the content of an element, the update() method is called on that element. This
method takes either no arguments or exactly one argument. If no arguments are specified, then
the content of the element is cleared. Otherwise, the content of the element is updated to be
whatever is specified as the first argument.

When calling update(), there are several different types of data you can use as the first
argument in order to update the content of the given element. That is, you can pass plain text,
a HTML snippet, or you can pass a JavaScript object (this includes elements created by
instantiating the Element class, as shown in the first article of this series).

Listing 1 shows a basic example of using update() to clear the contents of an element. In this
example, the div begins with some text content but when you view the page this content no
longer exists.
Listing 1 Clearing the content of an element using update() (listing-1.html)
<html>
<head>
<title>Clearing the content of an element using update()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
This content will soon be gone!
</div>
<script type="text/javascript">
$('foo').update();
</script>
</body>
</html>

If you want to change the contents of an element (rather than clearing it), you can pass the new
content of the element as the first argument to update(). As discussed above, there are several
different types of content you can pass to update.

Listing 2 shows the simplest case of using update(). In this example a plaintext string is used as
the new content of the #foo element.
Listing 2 Updating an element to display a string (listing-2.html)
<html>
<head>
<title>Updating an element to display a string</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
You won't see this content
</div>
<script type="text/javascript">
$('foo').update('You will see this content');
</script>
</body>
</html>

Similar to use using a plaintext string, you can use a string that contains HTML tags. Listing 3
shows an example of doing so.
Listing 3 Updating an element with a HTML string (listing-3.html)
<html>
<head>
<title>Updating an element with a HTML string</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
You won't see this content
</div>
<script type="text/javascript">
$('foo').update('You will see this <a
href="http://www.example.com">hyperlink</a>');
</script>
</body>
</html>

In the first article of "Eight Weeks of Prototype" I showed you how to create an element by
instantiating theElement class. Any element you create in this manner can also be passed to
the update() method. This is an alternative method to calling insert() to actually insert the
element into the DOM.

Listing 4 shows an example of creating a hyperlink and adding it to the #foo element.
Listing 4 Passing an element to update() (listing-4.html)
<html>
<head>
<title>Passing an element to update()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
You won't see this content
</div>
<script type="text/javascript">
var link = new Element('a', { href : 'http://www.example.com' });
link.update('This is the link text');

$('foo').update(link);
</script>
</body>
</html>

In fact, Prototype implements the update() method such that any class that has
a toString() method can be used as an argument to update(). I will show you exactly how to do
this in the sixth article of "Eight Weeks of Prototype".

Reading the Content of an Element

In some situations you will want to read the content of an element rather than update it. This is
achieved in Prototype by reading the innerHTML property of an element. This property is simply a
HTML string representation of the content inside an element.

Listing 5 shows an example of reading the content of an element that exists in the original HTML
source of a page, while Listing 6 shows an example of reading an element that has been
dynamically created.
Listing 5 Reading the inner HTML content of an element (listing-5.html)
<html>
<head>
<title>Reading the inner HTML content of an element</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<a href="http://www.example">Here is a link!</a>
</div>
<script type="text/javascript">
alert($('foo').innerHTML);
</script>
</body>
</html>

Reading and Writing Element Attributes

An attribute is a part of the element that appears in the opening tag of the element. An element
can have any number of attributes, each of which consists of a name and a value. For example,
in the HTML snippet <a href="http://www.example.com">My Link</a>, the tag has one attribute,
which is called href and has a value of http://www.example.com.
Prototype provides an easy way to read these attributes, as well as allowing you write your own
attributes to any element in real-time. We will now look at how to read and write attributes, as
well as covering some practical examples of doing so that you may use in the future.

To retrieve the value of an element attribute, the readAttribute() method is called on that
element. Listing 7 shows an example reading the target URL of a hyperlink. This example will
show just the URL (http://www.example.com) in an alert box.

In the fifth article of "Eight Weeks of Prototype", I'll show you how this can be useful when
creating Ajax scripts that are accessible for non-Ajax users.
Note: In this example I have also made use of the down() method covered in the first article of
this series.
Listing 7 Reading a hyperlink's URL with readAttribute() (listing-7.html)
<html>
<head>
<title>Reading a hyperlink's URL with readAttribute()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<a href="http://www.example.com">This is a link</a>.
</div>
<script type="text/javascript">
var link = $('foo').down('a');

alert(link.readAttribute('href'));
</script>
</body>
</html>

To write new attributes (or to modify existing attributes), you can use
the writeAttribute() method. This method takes two arguments, the first of which is the name
of the attribute you want to write. The second argument is the new value of the attribute.

Listing 8 shows an example of retrieving a link, then updating its target URL and link text. When
you click the link on this page you will be taken to www.phpriot.com, not www.example.com.
Listing 8 Using writeAttribute() to change the URL of a hyperlink (listing-8.html)
<html>
<head>
<title>Using writeAttribute() to change the URL of a hyperlink</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<a href="http://www.example.com">This is a link</a>.
</div>
<script type="text/javascript">
var link = $('foo').down('a');

link.writeAttribute('href', 'http://www.phpriot.com');
link.update('Visit PhpRiot');
</script>
</body>
</html>

Being able to update an element's attributes is most useful when you want to use non-standard
attributes. That is, if you use a particular attribute in your original HTML source, then you will
not be able to validate the HTML page with a validator such as the W3C Markup Validation
Service.

One such example is the target attribute, used on hyperlinks to force a link to open in a new
window. If you use XHTML 1.0 Strict, the target attribute is not allowed. Therefore, by
using writeAttribute(), you can add this attribute using JavaScript, thereby still allowing your
HTML to validate. This is shown in Listing 9.

Note: Technically, the code below will not validate since the document type and encoding are
not specified. I have done so in order to simplify the examples.
Listing 9 Changing a URL's target while not using non-standard attributes (listing-9.html)
<html>
<head>
<title>Changing a URL's target while not using non-standard attributes</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<a href="http://www.example.com">This is a link</a>.
</div>
<script type="text/javascript">
var link = $('foo').down('a');

link.writeAttribute('target', '_blank');
</script>
</body>
</html>

Another example of using writeAttribute() is to disable the auto-completion feature used by


some browsers to provide you with suggestions when filling out forms, based on what you've
entered in the past. In some situations you will want to force the browser not to use auto-
completion on a particular input.

One way to disable auto-completion is to use autocomplete="off" in your HTML source (that
is <input type="text" name="some_name" autocomplete="off" />). The problem with this is
that autocomplete is not a standard attribute. Hence, you can use writeAttribute() to disable
auto-completion instead, as shown in Listing 10.
Listing 10 Disabling auto-completion by using writeAttribute() (listing-10.html)
<html>
<head>
<title>Disabling auto-completion by using writeAttribute()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<input type="text" name="q" />
</div>
<script type="text/javascript">
var input = $('foo').down('input');

input.writeAttribute('autocomplete', 'off');
</script>
</body>
</html>

Managing an Element's Classes in Real-Time

Prototype makes it easy to manage the classes that an element belongs to by providing three
useful elements:addClassName(), removeClassName() and hasClassName(). If you were not using
Prototype, you would manually have to deal with the className property on elements; however
this is no longer necessary. These methods are especially useful when an element has (or can
have) multiple classes.

One of the most frequent occurrences of having to manage class names is when handling hover
events on elements. Although browsers that correctly implement CSS (such as Firefox) allow
you to change the look of an element when you hover over (using the :hover selector), this is
not possible in Internet Explorer (aside from hyperlinks).

Listing 11 shows an example of how CSS should work in all browsers (yet does not necessarily).
This code will draw a red box, which will turn blue when the mouse hovers over it.
Listing 11 Using CSS to change the hover style of an element (listing-11.html)
<html>
<head>
<title>Using CSS to change the hover style of an element</title>
<style type="text/css">
#foo { background : #f00; color : #fff; }
#foo:hover { background : #00f; }
</style>
</head>
<body>
<div id="foo">
Highlight me!
</div>
</body>
</html>

To make this code work in browsers that don't support this CSS, we can instead
use addClassName() andremoveClassName(). Each of these methods takes one argument: the
class name to add or remove.

Rather than defining a CSS called #foo:hover, I'll instead define one called #foo.hover. This
means we can calladdClassName('hover') on the #foo element.

Listing 12 shows how this is achieved. Note that in this code I've also used observe(), which is
used to observe events in Prototype. Event handling will be covered in detail in the fourth article
of this series. For now, all you need to know is that the first argument to observe() is the event
name (without on at the beginning) and the second argument is the function to execute when
the event is triggered. Additionally, calling this inside the handler function refers to the element
on which the event occurred.
Listing 12 Using Prototype to change the hover style of an element (listing-12.html)
<html>
<head>
<title>Using Prototype to change the hover style of an element</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<style type="text/css">
#foo { background : #f00; color : #fff; }
#foo.hover { background : #00f; }
</style>
</head>
<body>
<div id="foo">
Highlight me!
</div>
<script type="text/javascript">
var elt = $('foo');

elt.observe('mouseover', function(e) {
this.addClassName('hover');
});

elt.observe('mouseout', function(e) {
this.removeClassName('hover');
});
</script>
</body>
</html>

Using the hasClassName() method, you can check whether or not an element has a particular
class. Listing 13 shows an example of using hasClassName(). It works by checking for
the .hover class, then adding it, then checking for it again.
Listing 13 Checking whether an element has a particular class (listing-13.html)
<html>
<head>
<title>Using Prototype to check if an element has a class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<style type="text/css">
#foo { background : #f00; color : #fff; }
#foo.hover { background : #00f; }
</style>
</head>
<body>
<div id="foo">
Highlight me!
</div>
<script type="text/javascript">
var elt = $('foo');

if (elt.hasClassName('hover')) {
// does not reach this
}

elt.addClassName('hover');
if (elt.hasClassName('hover')) {
// does reach this
}
</script>
</body>
</html>

Managing an Element's Styles in Real-Time

As an alternative to changing an element's classes in real-time, you can also change an


element's CSS styles. This is done using the setStyle() method. Similarly, you can retrieve an
element's styles using getStyle().

Setting Styles with setStyle()

The setStyle() method is used to change the styles of an element. This method takes a single
argument, which is an object of property-pair values. Listing 14 shows an example of how to
define this object and apply the specified styles. In this example, the #foo div begins quite plain,
then is changed to have a red background, bold text and some padding.
Listing 14 Setting an element's styles with setStyle() (listing-14.html)
<html>
<head>
<title>Setting an element's styles with setStyle()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Change this content
</div>
<script type="text/javascript">
var elt = $('foo');

var styles = {
size : '18px',
fontWeight : 'bold',
background : '#f00',
padding : '10px'
}

elt.setStyle(styles);
</script>
</body>
</html>
Note: Some CSS properties (such as float) are reserved words, and must therefore be quoted
in the style hash. For example, you would use styles = { 'float' : 'left'; }.

Reading Styles with getStyle()

It is possible to read any element's style using getStyle(). This method takes the style property
name as its only argument. You must be careful with the return value, since different browsers
may behave slightly different with the value that is returned.
Prototype will handle the differences between internal style names and the corresponding CSS
name. For example, you can use getStyle('fontSize') or getStyle('font-size').

Note: The same does not apply with setStyle() – You must use the internal name (such
asfontSize, fontFamily or backgroundColor) to set the respective style.

Listing 15 shows an example of reading the font family that is being used on an element.
Listing 15 Reading an element's font family with getStyle() (listing-15.html)
<html>
<head>
<title>Reading an element's font family with getStyle()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Change this content
</div>
<script type="text/javascript">
var elt = $('foo');

alert(elt.getStyle('font-family'));

// returns a value such as serif or Times New Roman

elt.setStyle({ 'fontFamily' : 'sans-serif' });

alert(elt.getStyle('font-family'));

// returns sans-serif
</script>
</body>
</html>

Retrieving an Element's Dimensions

Prototype provides methods for reading the dimensions of both the viewable browser window, as
well as that of a visible element. This is done using
the getDimensions(), getWidth() and getHeight() methods.

To read the dimensions of the viewable browser area, getDimensions() is called on


the document.viewportobject (that is, by calling document.viewport.getDimensions()). To read
the dimensions of an element, you simply call getDimensions() on that element.

The getDimensions() method returns an object with two properties: width and height. These are
both integers, corresponding to the number of pixels wide and high of the respective element.

Note: For this method to work correctly, you must specify the correct document type. As you
can see in the listing below, I have specified a document type of XHTML 1.0 Strict.
In addition to getDimensions(), you can also call getWidth() and getHeight(), however, since
each these methods in turn call getDimensions(), it is more efficient to
call getDimensions() directly and read the width and height as needed.
Listing 16 shows an example of using the browser and an element's dimensions to centre that
element both horizontally and vertically on a page.
Listing 16 Determining an element's size with getDimensions() (listing-16.html)
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Determining an element's size with getDimensions()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<style type="text/css">
html, body { padding : 0; margin : 0; }
#foo {
width : 300px;
border : 1px solid #000;
padding : 10px;
background : #eee;
text-align : center;
}
</style>
</head>
<body>
<div id="foo">
This box will become centered
</div>
<script type="text/javascript">
var elt = $('foo');

// retrieve required dimensions


var eltDims = elt.getDimensions();
var browserDims = document.viewport.getDimensions();

// calculate the center of the page using the browser and element dimensions
var top = (browserDims.height - eltDims.height) / 2;
var left = (browserDims.width - eltDims.width) / 2;

// set the style of the element so it is centered


var styles = {
position : 'absolute',
top : top + 'px',
left : left + 'px'
};

elt.setStyle(styles);
</script>
</body>
</html>
Note: When setting the top and left of the element, you must remember to specify the unit
type. That is, px.

Showing and Hiding Elements

The final element extensions we are going to look at in this article are the methods using for
hiding and showing elements. Using hidden elements is a useful technique for creating pages
that react quickly to user interaction. One example of this is with dropdown sub-menus that only
appear when the mouse hovers over a main menu item.

There are three methods involved with this, each of which accepts no arguments when called.

 hide(): This method will hide an element from the display.

 show(): This method will show an element that has been hidden.

 toggle(): This method will show a hidden element, or hide a visible element.

One extremely important point when using these methods is that if you want to show a hidden
element (using either show() or toggle()), the element must not have been hidden via a CSS
stylesheet.

That is, if you want to initially hide an element, you must use an inline style attribute that
includes display: none.

Listing 17 shows a few basic examples of using each of these methods.


Listing 17 Showing and hiding elements with hide(), show() and toggle() (listing-17.html)
<html>
<head>
<title>Showing and hiding elements with hide(), show() and toggle()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="a">
This box will start visible but become hidden
</div>

<div id="b" style="display:none">


This box will start hidden and become visible
</div>

<div id="c">
This box will be visible, then become hidden, the appear again
</div>

<script type="text/javascript">
$('a').hide();
$('b').show();

var c = $('c');
c.toggle();
c.toggle();
</script>
</body>
</html>

While including inline style tags is not desirable (and is specifically one of the things you want to
avoid when creating neat and tidy markup), unfortunately it is required in this particular
situation. This is a drawback not of Prototype, but in how CSS and JavaScript work together.
To get around doing this you must do one of the following for each of the elements you want to
be hidden to begin with:

 Show the elements initially and use JavaScript to hide them once the page loads.

 Create the elements dynamically and insert them into the DOM as hidden elements.

Note that the second suggestion here is not necessarily desirable since creating elements in
real-time may affect the accessibility of your page.

Summary

In this article we have looked at some of the ways that Prototype extends each DOM element to
give you greater control in your programming. We learned that these extensions are applied
automatically when an element is retrieved using one of the element selection methods that was
covered in the first article of this series (namely, $(), $$() and Element.select()).

I firstly showed you how to update and read the content of elements, as well as how to read and
write attributes of elements and when it might be useful to do so.

Next I showed you how to manage an element's class names, as well as how to read and write
an element's styles in real-time.

Finally, I showed you how to hide and show DOM elements using the simple methods made
available by Prototype.

In the next article of this series, I will teach you about the different data types available when
using Prototype, such as Enumerable, Array and Hash.

Eight Weeks of Prototype: Week 3, Prototype Data Types


Introduction

In this, the third article in the "Eight Weeks of Prototype" series, I will teach you about the
different data types that can be used in Prototype (on top of the existing JavaScript data types).

The first data types we will look are the basic string and number types. Prototype extends these
JavaScript types to give you more control over their behaviour.

Next we will look at the Enumerable class, which is used to manage a series of values. Once we
have covered the way Enumerable works and is used, we will look at the Array and Hash data
types, both of which are examples of an Enumerable type.

In this article I will assume you have read and understand the first two articles in this series.

The String Object


The first data type we will look at is the String object. Prototype extends this object to give you
a wider range of methods that can be called on strings. Each of these methods is documented
on the Prototype web site athttp://prototypejs.org/api/string.

Each of these methods can be applied directly to a string literal, or to variables that hold strings.
For instance, one of the functions made available is strip(), used to remove whitespace before
and after a string (just as the PHP function trim() works). Listing 1 shows two different ways
you can use this method. The first way in this example is to call strip() directly on a variable,
while the second way is to apply strip() directly to a literal string.
Listing 1 The different ways to apply string functions (listing-1.html)
<html>
<head>
<title>Two different ways of applying string methods</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var string = ' test ';
var result = string.strip();
// result holds the string 'test'

var result = ' test '.strip();


// result holds the string 'test'
</script>
</body>
</html>

Rather than covering every method available, I'll cover a few basic ones to begin with, and then
give you an idea of some other useful string functions that are available when using Prototype.

String Testing

The first functions we will look at are for string testing. That is, each method returns a Boolean
value (true orfalse). Several examples of using each of the methods covered here are shown in
Listing 2.

Firstly, the empty() method. This method returns true if the target string has zero-length. An
alternative to using this method is to use the expression myVar.length == 0.

Next, the blank() method. This method returns true if the target string has no characters or
contains only whitespace. Be aware that line feeds are treated as whitespace. The alternative to
using this method is to combine the strip() and empty() methods we have already looked at.
That is, myVar.blank() will return the same value as myVar.strip().empty().

Now we will look at the include() method. Although the name is slightly misleading, this
method checks whether or not a string contains an instance of another string. You can use
regular expressions as an alternative to this method, although for a simple test this method will
work fine. For instance, if (myVar.include('bar') { ... } will work the same as if
(/bar/.test(myVar)) { ... }.
Similar to include() are the startsWith() and endsWith() methods. Rather than searching for a
substring anywhere in the string, the substring must occur at the beginning or end of the string
respectively. Once again you can use regular expressions to achieve this functionality. In the
case of startsWith(), you would use the^ symbol at the start of the regular expression (to
signify the match must occur at the start of the string), while you would use the $ symbol to
match at the end of the string. For example, if (myVar.endsWith('foo')) { ... } will function
the same as if (/foo$/.test(myVar)) { ... }).
Listing 2 Using the blank(), empty(), include() and endsWith() methods (listing-2.html)
<html>
<head>
<title>Using the blank(), empty(), include() and endsWith() methods</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var string = '';
// string.empty() returns true

var string = ' ';


// string.empty() returns false
// string.strip().empty() returns true

var string = ' ';


// string.blank() returns true

var string = 'foo';


// string.blank() returns false

var string = '';


// string.blank() returns true

var string = '\n';


// string.blank() returns true

var string = 'The quick brown fox';

if (string.include('fox'))
alert('The string contains fox!');

if (!string.endsWith('.'))
string += '.';

alert(string);
</script>
</body>
</html>

Other Useful String Methods

Some other string methods that you may find useful are listed below. For full documentation on
each of these, refer to the Prototype API guide for strings at http://prototypejs.org/api/string.

 isJSON() - Check whether a string contains syntactically correct JSON data


 camelize() - Turn a dash-separated string into camel-caps. This is useful for translating
CSS properties into their DOM equivalent. For instance, 'background-
color'.camelize() returns backgroundColor.

 capitalize() - This method formats a string so the first letter of each word is a capital
while the rest of the letters are lower case. This is the equivalent of
using ucwords(strtolower($str)) in PHP.

 escapeHTML() - This method converts certain characters into their correct HTML entities.
For instance,'&lgt;'.escapeHTML() would return the string &lt;.

 toQueryParams() - Parse the query string from a URL into an object of key/value pairs.
This is similar to the PHP parse_str() function, and will result in data similar to that of the
PHP's $_GET superglobal.

 truncate() - Shorten a string to a specified length and append a suffix to it. This is useful
when you want to show only a partial string but want to indicate there is more to it. For
example, 'The Quick Brown Fox'.truncate(12, '...') would return The Quick....

Note: This is similar to the truncate modifier in the Smarty Template Engine, except that
Smarty also has the option to append the suffix without breaking up a word, whereas this
method does not.

There are many more string functions available, so you should read over the Prototype
documentation to find out more about them.

The Number Data Type

Prototype extends the native JavaScript number type with a range of new methods, each of
which can be applied similarly to string methods. These methods are documented
at http://prototypejs.org/api/number.

For the most part, these methods are just wrappers to the normal Math object methods. For
example, theabs() method corresponds to the Math.abs() method. Similarly,
the ceil(), floor() and round() methods also correspond to the respective Math method.

Aside from these methods, the only other methods of any great use are
the toColorPart() andtoPaddedString() methods.

The toColorPart() method is used to convert a number (assumed to be in the range of 0 to 255)
to a 2 character hexadecimal number. This allows you to easily create a CSS colour string.

Listing 3 shows how toColorPart can be used. Later in this article we'll look at another way of
using this method by making use of the array methods Prototype provides.
Listing 3 Creating hex colour codes with toColorPart() (listing-3.html)
<html>
<head>
<title>Creating hex colour codes with toColorPart()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
This div will change colour!
</div>

<script type="text/javascript">
var r = 255;
var g = 100;
var b = 0;

var hex = '#' + r.toColorPart()


+ g.toColorPart()
+ b.toColorPart();

// hex will have the value #ff6400

$('foo').setStyle({ backgroundColor : hex });


</script>
</body>
</html>

The toPaddedString() method is useful for created a zero-padded string based on the target
number. For example, calling (25).toPaddString(5) will result in a string 5 characters in length.
That is, 00025.

Enumerable Data Types

An enumerable data type is one that contains a series of values over which you can loop. There
are already two such types in JavaScript (that is, arrays and objects), however Prototype
provides a united interface for accessing for accessing each of these types in the same manner.
Additionally, Prototype makes it possible for you to create your own enumerated types, all of
which extend from the Enumerable object.

For example, regardless of the type of data the your enumerated type holds, you can call
the size() method to determine the number of elements in the enumerable object.

Note: There is a slight ambiguity between JavaScript objects and the


Prototype Hash object. Hash is an extension of the normal JavaScript object, but as we will see
later in this article, values are read and written differently since Prototype 1.6.0. When you
create a new JavaScript object, it is not automatically a hash (unless arrays, which are
automatically extended).

The each() Method

Let's firstly look at the each() method, which is one of the most important functions made
available toEnumerablecode> objects. In order to use each(), you pass an iterator function as the
first argument. When the each() call is executed, the iterator function is called once for each
element in the enumerable object. The current element is passed as the first argument to the
iterator, while the second argument containers the iteration number (beginning from 0).
In order to demonstrate, let's revisit the $$() function. This function returns an array of
elements that match the CSS selector(s) passed in as arguments. Using each(), we can easily
perform operations on every matched element.

Listing 4 shows two examples of selecting all of the items in an unordered list and changing
them slightly. We've already seen how $$() and the update() method works in previous articles,
but we're now also usingeach(). In the first part of the example we define the iterator callback
inline when performing the each, while in the second part of the example it is defined as a
separate function.
Listing 4 Basic usage of each() on an array (listing-4.html)
<html>
<head>
<title>Basic usage of each() on an array</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<ul>
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
</ul>
</div>

<script type="text/javascript">
$$('#foo li').each(function(item) {
item.update(item.innerHTML + ' -- modified!');
});

function doSomething(item)
{
item.setStyle({ backgroundColor : '#f60' });
}

$$('#foo li').each(doSomething);
</script>
</body>
</html>

When using each() to loop over data, it is important to know how to control the loop. In PHP
(and other languages), you can use the break or continue expressions to do so. Using break in
PHP means the loop execution is halted there and then, while continue halts the current
iteration but continues on with the loop.

Achieving continue functionality within each() is simple. Because each iteration is a function call,
you can simply use return to emulate continue. The current function call is terminated and the
function will be called again for the next iteration.

In order to break the loop, Prototype provides a special variable called $break. If you throw this
variable as an exception, Prototype will know to end the loop. Listing 5 shows an example of
continuing and breaking a loop. This example will read the text inside each list item and write it
to the #output div.

As mentioned above, the iteration number is passed as the second argument to the iterator
callback. In this example we skip outputting a message for the third iteration, and finish the
loop after the fourth element.
Note: The iteration number is zero-indexed, so this value will be 2 for the third iteration
and 3 for the fourth iteration.
Listing 5 Breaking and continuing each() loops (listing-5.html)
<html>
<head>
<title>Breaking and continuing each() loops</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
<ul>
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
<li>Element 5</li>
<li>Element 6</li>
</ul>
</div>

<div id="output"></div>

<script type="text/javascript">
function doSomething(item, i)
{
var output = $('output');

if (i == 2)
return; // "continue"

output.update(output.innerHTML + '<br />' + item.innerHTML);

if (i == 3)
throw new $break;
}

$$('#foo li').each(doSomething);
</script>
</body>
</html>

Other Enumerable Methods

In addition to each(), there are a number of other methods that can be used with enumerable
objects. There are many different functions available (too many to cover here), but you can read
about all of them athttp://prototypejs.org/api/enumerable.
One example of one of these useful extra methods is invoke(). This method allows you to call a
single function on each element in the enumerable.

For example, if you use $$() to select a number of elements, you can hide all elements by
using invoke() to apply the hide() method. That is, you can use $$('#foo li').invoke('hide').

You can also pass arguments for the method to invoke. For example, to remove the active class
name from all elements, you could use $$('#foo li').invoke('removeClassName', 'active').

Object Context

Most of the methods in the Enumerable accept an extra argument, used to define the context of
the iterator method. Essentially what this does it define what the keyword this refers to when
used inside of the iterator.

While this is an extremely important concept in development with Prototype, it is generally only
relevant when developing your own classes in Prototype. I will discuss object binding further in
the sixth part of this article series.

Arrays

Prototype automatically extends the array type to give all of the functionality of enumerables.
That is, you can you use methods such as each() on any array that you create.

Revisiting Listing 3 (which created a CSS colour string from 3 different variables), we can define
the colours as an array then use the invoke() method (covered above) to create the string.

Listing 6 shows an example of doing so. When we call invoke with toColorPart() as the function
to execute, an array is returned with each element corresponding to the result for each element
in the input array. We can then join this value to create a single string from the returned array.
Listing 6 Using arrays and invoke to create hex colour codes (listing-6.html)
<html>
<head>
<title>Using arrays and invoke to create hex colour codes</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
This div will change colour!
</div>

<script type="text/javascript">
var colors = [ 255, 100, 0 ];

var hexColors = colors.invoke('toColorPart');


// same as [ 'ff', '64', '00' ]

var hex = '#' + hexColors.join('');


// hex will have the value #ff6400

$('foo').setStyle({ backgroundColor : hex });


// or this can be shortened further to:
var hex = '#' + colors.invoke('toColorPart').join('');
alert(hex);
</script>
</body>
</html>

Creating Custom Enumerables

It is possible to create your own enumerated objects simply by defining a function


called _each() when you create your own classes. Because we have not yet covered how to
create classes in Prototype yet, I will leave discussion of this until we do so in the sixth article of
this series.

The Hash Object

The Hash type that Prototype provides is a very useful data type that lets you use key-based
indexing of your data. That is, it is similar to an associative array in PHP.

In actual fact, it is an extension of the JavaScript "vanilla" object. However, unlike arrays, when
you create a new object in JavaScript, it is not extended unless you explicitly extend it. This can
be done using the $H()function.

In order to read and write values to or from a hash, you must use the get() and set() methods.
Both arguments accept the key name as the first argument, while the set() method accepts the
new value as the second argument.

Listing 7 shows an example of creating a normal JavaScript object, then turning it into a Hash. It
also shows how to read and update hash values.
Listing 7 Creating a hash (listing-7.html)
<html>
<head>
<title>Creating a hash</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
// create a normal object
var person = {
name : 'Quentin Zervaas',
country : 'Australia'
};

alert(person.name + ' is from ' + person.country);

// now turn it into a hash


var hash = $H(person);

// or use shorthand

var hash = $H({


name : 'Quentin Zervaas',
country : 'Australia'
});

alert(hash.get('name') + ' is from ' + hash.get('country'));

hash.set('country', 'nowhere');

alert(hash.get('name') + ' is from ' + hash.get('country'));


</script>
</body>
</html>

So now that you know how to create a Hash, how do you use it? Well, all hashes have the same
enumerable methods available (such as size(), each() and invoke()).

The most important thing to be aware of is that when you define an iterator callback (as we saw
above when learning about each()), the argument passed to the iterator is not just the element
value. Rather, it is an object with the key in the key property and the value in
the value property.

Listing 8 shows an example of how to use each() with a Hash.


Listing 8 Looping over hash values (listing-8.html)
<html>
<head>
<title>Looping over hash values</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var hash = $H({
name : 'Quentin Zervaas',
country : 'Australia'
});

alert('There are ' + hash.size() + ' values in this hash!');

hash.each(function(pair) {
alert(pair.key + ' has the value ' + pair.value);
});
</script>
</body>
</html>

Additionally, there are some other methods made available to hashes that you may find useful.
One such example of the keys() method, which returns an array of the keys in the hash. If you
were to call hash.keys()on the hash from above, an array defined as [ 'name',
'country' ] would be returned. Similarly, you can use the values() method, which will return an
array of the values in the hash.

Other operations you will find useful for hashes is to check if a value exists and also to remove a
value from a hash. To check if a value exists you can see if the return value is equal
to undefined (an internal JavaScript constant). For example, if (hash.get('foo') == undefined)
{ ... }.
To remove a value from a hash, the unset() method should be used. For
example, myHash.unset('foo') will remove the element with key foo from the Hash called myHash.

Summary

In this article we looked the different data types available in Prototype. A large number of new
methods have been added to existing JavaScript data types to make most functionality very
simple to code.

We looked extensively at enumerables and specifically, the Hash type. Enumerables are very
useful and come up frequently when developing with Prototype (such as selecting elements from
the Document Object Model with $$() or select()).

In the next article of "Eight Weeks of Prototype" we will look at how to handle events in
Prototype. This includes both native browser events as well as how to make use of custom
events in your development.

Eight Weeks of Prototype: Week 4, Event Handling in Prototype


Introduction

One of the most useful and important aspects of JavaScript developments is that of event
handling. Prototype simplifies this process by providing a number of helpful methods for doing
so. In this, the fourth article of "Eight of Weeks of Prototype", we will look at how Prototype
helps with handling events.

In addition to the native events (such as onclick or onmouseover), I will also show you how to
observe and handle custom events, which can help you dramatically when developing your
Prototype-based JavaScript.

Aside from the useful helper methods provided by Prototype, there is another excellent reason
for using Prototype to handle the events. By using Prototype, you can ensure that you don't
write over existing event handlers.

To demonstrate this, look at the code in Listing 1. Based on this code, do you know which
message will appear? This situation can occur when you use third-party libraries over which you
have no control.
Listing 1 Demonstrating event handling ambiguity (listing-1.html)
<html>
<head>
<title>Demonstrating event handling ambiguity</title>
<script type="text/javascript">
window.onload = function() {
alert('Message 1');
}
</script>
</head>
<body onload="alert('Message 2')">
</body>
</html>

When using the observe() method provided by Prototype (which we will cover shortly), this
problem does not arise, since Prototype will automatically append the new event handler to the
existing handlers.

Observing Events

In order to use Prototype to handle events, the key method to use is observe().This method is
one of the element extensions added by Prototype (as we saw in part 2 of this series). This
means you can call observe()directly on the element on which you wish to observe an event, or
you can pass that element as the first argument to Event.observe().

Listing 2 demonstrates the two different ways of observing an event on the same element. Note
that in both cases we are using the $() function which you should now be familiar with (covered
in the first article of this series). I've included some basic event handlers in this example: try
hovering your mouse over the text or clicking on the text.
Listing 2 Two different ways of observing an event on an element (listing-2.html)
<html>
<head>
<title>Two different ways of observing an event on an element</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
This is the element to observe
</div>

<script type="text/javascript">
var elt = $('foo');

Event.observe(elt, 'mouseover', function() {


$('foo').setStyle({ backgroundColor : '#fc0' });
});

elt.observe('click', function() {
alert('Click 2');
});

elt.observe('mouseout', function() {
$('foo').setStyle({ backgroundColor : '' });
});
</script>
</body>
</html>

The most important concept to take from this example is that you can use
either Event.observe(elt, ...) orelt.observe(...). In order to observe events on the page as a
whole (that is, with the window object), you must use Event.observe().
This is demonstrated in Listing 3, where an alert box is shown once the page completes its
loading. This code is the "correct" way to do what is done in Listing 1. That is, rather than
using <body onload="..."> orwindow.onload, we use Prototype's observe() method.
Listing 3 Checking for page load completion (listing-3.html)
<html>
<head>
<title>Checking for page load completion</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
Event.observe(window, 'load', function() {
alert('Page has loaded!');
});
</script>
</body>
</html>

Now that you know the two different ways of invoking observe(), let's look at the other
arguments passed to this method. Firstly, the name of the event is passed. You may have
noticed from listings 3 and 4 that on is omitted from the event name. That is, if you wanted to
observe the onclick event you would pass click as the argument. Technically click is what
happened (the event), and onclick is what happens when the clickoccurs.

The final argument passed to observe() is the function you want to execute when the event is
triggered. We will look more in depth at how to write these functions in the next section.

As usual, you can pass either a function pointer, or define the function there and then. Listing 4
shows you these different ways of defining the event handler function.
Listing 4 Two different ways of defining event handlers (listing-4.html)
<html>
<head>
<title>Two different ways of defining event handlers</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click me!
</div>

<script type="text/javascript">
// method 1: defining the function inline
Event.observe(window, 'load', function() {
alert('Page has loaded!');
});

// method 2: defining the function then passing its name to observe()

function handleFooClick()
{
alert('I was clicked!');
}
$('foo').observe('click', handleFooClick);
</script>
</body>
</html>

In general (and this is typically true for all JavaScript code you write), it's better to define the
function separately from the call to observe(). This is because your code becomes much easier
to maintain when done in this way. You will especially see how this helps in the sixth article of
this series (when I show you how to write classes with Prototype).

In some rare cases I will define an event handler inline if it is performing some extremely trivial
functionality.

Handling Events

I will now show you how to write event handlers when observing events with Prototype. I
showed you some basic examples in the previous section of handling some events, but there is
more to it than that!

When you define an event handler, the event object is passed as the first argument to the
handler. This allows you to find out more information about the event, such as which specific
element the event occurred on or to determine which key was pressed (for a key-related event).

Listing 5 shows a basic example of how to access this object. My own personal preference for
the name of the event object is to use e, since if you use event you may confuse this with the
JavaScript Event object.

Typically this variable will be used in conjunction with the Event variable. That is, rather than
methods being available to call directly on e, they are passed to the relevant Event method
(such as Event.element(e)).
Listing 5 Demonstrating how the event object is passed to handlers (listing-5.html)
<html>
<head>
<title>Demonstrating how the event object is passed to handlers</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click me!
</div>

<script type="text/javascript">
function handleFooClick(e)
{
// you can now do something with e,
// such as call Event.element(e)
}

$('foo').observe('click', handleFooClick);
</script>
</body>
</html>

Stopping an Event

Frequently you will want to stop an event from completing, since you are defining a method by
which to handle the event, and therefore don't want the browser to use its own handling
method.

The two best examples of this are for hyperlinks and forms. Firstly, let's look at links. Often you
will want to perform some action when the user clicks a link, however you don't want the
browser to follow the link.

Note: This is especially useful if you want to use Ajax to retrieve the page at the given link. This
specific example will be shown in the fifth article of this series.
Before using Prototype, you might be more familiar with returning false from the event handler.
For example, you might use <a href="..." onclick="doSomething(); return false">...</a>. By
returning false in theonclick handler, the browser knows not to follow the link in
the href attribute.

In order to achieve this same effect with Prototype, the Event.stop() method is used instead.
Returning falsefrom your handler method will have no effect. Calling Event.stop() tells
Prototype to stop event propagation and not perform the default browser action.

Note: If you have multiple handlers for a given event, all of them will be executed regardless of
whether you have called Event.stop() in any or all the handlers. I'll cover this in more detail
shortly.

Listing 6 shows an example of stopping an event if required to do so. In this example, a


JavaScript confirmation box is displayed to the user. If they click OK, then the link is followed,
whereas clicking Cancel will result in the link not being followed.

Note: Although not specifically related to this concept, you should typically use a POST form if
your action has some side-effect on your application (such as deleting data from a database)
rather than a normal hyperlink.
Listing 6 Stopping an event with Event.stop() (listing-6.html)
<html>
<head>
<title>Stopping an event with Event.stop()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<a href="/path/to/do/something.php" id="myLink">Do Something!</a>
</div>

<script type="text/javascript">
function onMyLinkClick(e)
{
var msg = 'Are you sure you want to do this?';

if (confirm(msg)) {
// user click ok, nothing to do - link will be followed as normal
}
else {
// user clicked cancel, stop the event
Event.stop(e);

// link will now not be followed


}
}

$('myLink').observe('click', onMyLinkClick);
</script>
</body>
</html>

This same concept can be useful implementing JavaScript-based form validation. By observing
the submitevent, you can then check form values before deciding whether or not to allow the
browser to submit the form.

Listing 7 shows an example of how this is achieved. In this example, I've created a form with a
single text input. The validation routine (onFormSubmit()) checks if this text input is blank (we
covered the blank()method in the third article of this series), and if so records an error.

To complete the event handler, we check if there are any errors (by checking the size of
the errors array), and if so we stop the event from propagating and display an error message.
Note: The serialize() method is a special method for forms that allows you to easily retrieve
all of the values in a single object. If true is not passed as the first argument then the data is
returned as a "get" style string (which is difficult for us to validate).
Listing 7 Validating a form before deciding whether it should be submitted (listing-7.html)
<html>
<head>
<title>Validating a form before deciding whether it should be submitted</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<form method="post" action="/some/form/target.php" id="theForm">
<div>
<input type="text" name="name" />
<input type="submit" />
</div>
</form>
</div>

<script type="text/javascript">
function onFormSubmit(e)
{
// retrieve all values to check
var values = $('theForm').serialize(true);

// placeholder array for errors


var errors = [];

// check if the name was entered


if (values.name.blank()) {
errors.push('Please enter the name');
}

// check if any errors were found


if (errors.size() > 0) {

// display the errors


alert('There were errors:\n' + errors.join('\n'));

// prevent the form from being submitted


Event.stop(e);
}
}

$('theForm').observe('submit', onFormSubmit);
</script>
</body>
</html>
Note: Even though form validation using JavaScript is very useful to the user, you should not be
relying solely on this validation in your application. You still need to validate all values on the
server-side, since it is trivial for a user to bypass the JavaScript validation.
Technically you could observe the click event on the submit button rather than the submit event
on the form, however the form might be submitted using a method other than this button. For
instance, if the user presses enter while filling out the text input the form will be submitted
(triggering the submit event but not the buttonclick event).

Eight Weeks of Prototype: Week 4, Event Handling in Prototype


By Quentin Zervaas, 21 April 2008, JavaScript, Prototype (1 comment)

Checking for Stopped Events

As mentioned in the previous section, if you have multiple event handlers, each of them will still
be executed even if you called Event.stop() at any time. You can, however, check to see if an
earlier handler has already stopped the event by reading the stopped property.

Listing 8 shows an example of doing this. In it, I have defined two different handlers for
the click event, each of which will display an alert box. However, the alert box will only be
shown if the event hasn't already been stopped.
Listing 8 Checking for stopped events (listing-8.html)
<html>
<head>
<title>Checking for stopped events</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click here
</div>

<script type="text/javascript">
function handler1(e)
{
if (!e.stopped) {
alert('Handler 1');
Event.stop(e);
}
}

function handler2(e)
{
if (!e.stopped) {
alert('Handler 2');
Event.stop(e);
}
}

$('foo').observe('click', handler1);
$('foo').observe('click', handler2);
</script>
</body>
</html>

Finding the Element on Which an Event Was Observed

In all of the event handlers in the examples so far, we have only observed events on a single
element. In the event handler we have then used the $() function to select that element again
to perform some function on it.

Using the Event.element() function, we can determine exactly which element and event was
triggered on. This is useful when an event handler might be used for a particular event that may
occur on several items.

Listing 9 shows such an example. In this listing, we use the $$() function select all of the list
items so the click event can be observed on each of them. Using the each() method (covered in
the third article of this series), we loop over each item and observe the event.

When the event is handled (that is, the onItemClick() method is called), we
use Event.element(e) to determine the specific list item that the event was triggered for. We
then update its background colour and change its text.
Listing 9 Retrieving an event's element with Event.element() (listing-9.html)
<html>
<head>
<title>Retrieving an event's element with Event.element()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>

<script type="text/javascript">
function onItemClick(e)
{
var item = Event.element(e);

item.setStyle({ backgroundColor : '#fc0' });


item.update('Clicked!');
}

$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>

Sometimes you may not want the specific element on which the event occurred, but rather one
of its parent elements. If you look at listing 9, how would we go about retrieving the
parent <ul> element rather than the<li> that was clicked?

To do so, Prototype provides the method Event.findElement(). This is similar


to Event.element(), except you specify a tag name as the second argument. That, you would
use Event.findElement('e', 'ul') to find the parent <ul> element. This is shown in Listing 10.
Listing 10 Finding an event element's ancestor with Event.findElement() (listing-10.html)
<html>
<head>
<title>Finding an event element's ancestor with findElement()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>

<script type="text/javascript">
function onItemClick(e)
{
var list = Event.findElement(e, 'ul');

list.setStyle({ border : '2px solid #fc0'});


}

$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>

The only problem with Event.findElement() though is that you can only find ancestor elements
based on their tag name. If you wanted a more complex search (such as finding an ancestor
with a particular class name), then you would want to combine Event.element() with
the up() method (we covered up() in the first article of this series).

Listing 11 shows an example of doing this. In this example we find the div with the class name
of outer. If we tried to use Event.findElement(), we would not have been able to retrieve this
element since if we passed div as the second argument, the div with class inner would have
been returned.
Listing 11 Combining Event.element() with up() (listing-11.html)
<html>
<head>
<title>Combining Event.element() with up()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div class="outer">
<div class="inner">
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>
</div>

<script type="text/javascript">
function onItemClick(e)
{
var item = Event.element(e);
var outer = item.up('div.outer');

outer.setStyle({ backgroundColor : '#cf9' });


}

$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>

Handling Keyboard Events

So far we haven't concerned ourselves with any specifics of an event that occurs, other than on
which element the event occurred. Often an event will be triggered by a particular key press or
by some movement or action with the mouse. In this section we will look at how to handle
events that are triggered by the keyboard.
To determine which key was pressed, you can read the keyCode property of the event object
passed to your event handler. Prototype defines a number of useful constants that help you
determine which key was pressed. Specifically, these are:

 Event.KEY_BACKSPACE

 Event.KEY_TAB

 Event.KEY_RETURN

 Event.KEY_ESC

 Event.KEY_LEFT

 Event.KEY_UP

 Event.KEY_RIGHT

 Event.KEY_DOWN

 Event.KEY_DELETE

 Event.KEY_HOME

 Event.KEY_END

 Event.KEY_PAGEUP

 Event.KEY_PAGEDOWN

 Event.KEY_INSERT

To demonstrate how these codes can be used, I have written a simple example, shown in Listing
12. In this example there is a text input. If you hit the escape key the text in the input is
selected, while hitting backspace results in the entire value being cleared. While this doesn't
serve much practical use, hopefully it demonstrates how you can read the key codes.
Listing 12 Determining which key was pressed (listing-12.html)
<html>
<head>
<title>Determining which key was pressed</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="text" id="foo" />
</div>

<script type="text/javascript">
function onFooKeyup(e)
{
var element = Event.element(e);
switch (e.keyCode) {
case Event.KEY_ESC:
element.activate();
Event.stop(e);
break;

case Event.KEY_BACKSPACE:
element.value = '';
Event.stop(e);
break;
}
}

$('foo').observe('keyup', onFooKeyup);
</script>
</body>
</html>

Handling Mouse Events

It is possible to return the coordinates of the mouse for a particular event using
the Event.pointerX() andEvent.pointerY() functions. You simply pass in the event object passed
to the event handler as the first and only argument to receive the integer value representing the
location of the mouse.

These methods return values relative to the entire page, not just what is currently visible to you.
In other words, if you've scrolled down on the page the returned values are still relative to the
very top of the page.

Listing 13 shows an example of tracking the mouse movement within a particular element using
the mousemoveevent. This event is triggered every time the mouse is moved whilst over this
element. In the handler function we read the X and Y position of the mouse and update the
element to display this information.
Listing 13 Reading the X and Y coordinates of the mouse (listing-13.html)
<html>
<head>
<title>Reading the X and Y coordinates of the mouse</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Move the mouse over me
</div>

<script type="text/javascript">
function onMouseMove(e)
{
var element = Event.element(e);
element.update(Event.pointerX(e) + 'x' + Event.pointerY(e));
}

$('foo').observe('mousemove', onMouseMove);
</script>
</body>
</html>

Stopping Observation

There are some cases where you will only want to observe an event for a period of time. That is,
you want to remove the event handler after some condition is met. To do this,
the stopObserving() method should be used.

Just like when using observe(), you can call this method directly on an element, or
useEvent.stopObserving() and pass the element in question as the first argument. While it
doesn't really matter which way you do it, for code consistency you should do it the same as you
did when calling observe() initially.

To successfully stop observing the event, you must pass the same arguments
to stopObserving() as you did when you called observe(). That is, you must pass the event
name and the name of the callback function.

Note: This is another good argument for not defining an anonymous function inline when
observing the event. If you do so, you will be unable to stop observing the event since you can't
reference this function any more.

Listing 14 shows an example of stopping the observation of an element. In this example, when
you click the element an alert box is shown confirming that the event occurred. At this point in
the code, the click event is no longer observed, meaning if you click the element again the alert
box will no longer be shown.
Listing 14 Stopping the observation of an event using stopObserving() (listing-14.html)
<html>
<head>
<title>Stopping the observation of an event using stopObserving()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click me
</div>

<script type="text/javascript">
function onFooClick(e)
{
var element = Event.element(e);

alert('You clicked the element!');

element.stopObserving('click', onFooClick);
element.update('Clicking me now does nothing');
}

$('foo').observe('click', onFooClick);
</script>
</body>
</html>
Using Prototype to Fire and Handle Custom Events

In addition to being able to handle the native browser events such


as click, mouseover, mouseout and other similar events, you can also define your own custom
events. The difference between custom events and native events is that you must determine
when these events should be fired, and then of course to fire them. Handling of custom events
is almost identical to native events.

So the big question is, why would you ever need to use a custom event? What advantage does it
provide over say, not using a custom event? Custom events allow different scripts on the same
page to easily indirectly interact. If an action occurs on Script A, then this script can fire a
particular event. If Script B is observing this event, then it knows the action has occurred.

Obviously this would be much clearer with an example. To demonstrate how custom events can
be used we are going to create a page that contains essentially two items. The first is a list of
items that the user can click on. The second is a status window that prints out what has
happened.

Listing 15 shows the code that uses custom events. I'll list the code here then discuss it
afterwards, including how to include extra data with your events and also how to name your
custom events.
Listing 15 Using custom events (listing-15.html)
<html>
<head>
<title>Using custom events</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<style type="text/css">
#status { border : 1px solid #000; padding : 10px; }
</style>
</head>
<body>
<div>
<h3>Items</h3>

<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>

<div id="status">
<h3>Status</h3>
</div>
</div>

<script type="text/javascript">
function onItemClicked(e)
{
var element = Event.element(e);
var memo = {
name : element.innerHTML
};

document.fire('list:item_selected', memo);
}

function onCustomEvent(e)
{
var status = $('status');
status.insert('<div>Selected: ' + e.memo.name + '</div>');
}

$$('#myList li').each(function(item) {
item.observe('click', onItemClicked);
});

document.observe('list:item_selected', onCustomEvent);
</script>
</body>
</html>

In this example, we define two functions. The first (onItemClicked()) is used to handle the
normal clickevent. The second (onCustomEvent()) is called when the list:item_selected event
is triggered.

To create a custom event, it is simply a matter of firing the event using


the document.fire() method. The first argument to this method is the name of the custom
event, while the second (optional) argument contains any custom data you would like to be
available from within the event object in the handler.

As you can see, we fire the list:item_selected event in the onItemClicked() method and pass
to it the name of the list item that was clicked. This data is referred to as the event memo.

When you name a custom event you must namespace it by including a colon in the name. This
is in order to prevent any naming conflicts with non-standard native events. In order to then
observe this event you call theobserve() method just as you would with normal events.

I've tried to keep the above example as simple as possible, so it may be difficult to see the real
value in using custom events. Here's a practical example of where custom events are really
useful. Consider an Ajax-powered address book that listed out all of your contacts. That is, you
can view, add or delete contacts on the page, all handled in the background using Ajax.

If you were to add a new contact, your Ajax routine could fire an event such as user:addedonce
it received confirmation from the server that the user was added. If the code that controls the
list of users is observing this event, it can dynamically add the new user to the display in real-
time (you can of course hold specific display information about the user in the
event's memo field).
While technically the Ajax response could interact with the code that manages the display of the
user list, using custom events it doesn't need to know anything about how that code works or
even care whether or the display of contacts is there. This allows you to use the same "Add
user" component on a different page that might not even display the list of users.

Summary

In this article I've shown you how to handle JavaScript events using the helpful methods
Prototype provides. Not only did I show you how to handle normal DOM events, but I also
showed you how to use custom events and where it may be useful to do so.

There were a wide range of examples in this article, including demonstrating how to handle
keyboard and mouse events.

In the sixth article in this series I'll show you to create classes in JavaScript, which really helps
with organising all of your event handlers together.

In the next article though, I'll show you how to perform Ajax requests using Prototype. This will
include a number of different examples and cover a wide range of topics including how to easily
communicate with server-side scripts using JSON and how to build accessible forms that use
Ajax.

Eight Weeks of Prototype: Week 5, Ajax with Prototype

Introduction

In addition to all of the other useful classes Prototype gives to developers, it also provides a
number of classes and methods for development of Ajax-enabled web applications. That is, it
allows developers to easily perform HTTP sub-requests using XmlHttpRequest and to handle the
response accordingly.

In this article I will show you how Prototype makes Ajax development for developers by covering
the functionality it provides. Additionally, I will show you how to easily transfer data between
your JavaScript code and web server using JSON data.

There are many Prototype concepts and functions used in this example that have been covered
in earlier articles in this series, such as selecting and updating elements, and event handling.

In the final article in this series, I will cover an extensive example of programming with
Prototype, which will include using Ajax to communicate with the web server.
Request Types

There are three different classes Prototype provides to perform Ajax requests. These
are Ajax.Request,Ajax.Updater and Ajax.PeriodicalUpdater.

Ajax.Request

This is the primary class that is used for Ajax, which the other two classes mentioned above use
also.Ajax.Request provides a cross-browser solution for performing Ajax requests without
requiring that you know how to specifically use XmlHttpRequest.

This class is simple to use – just instantiate it and pass to it the URL you want to send a HTTP
request to as the first argument. You can optionally pass a second argument to the constructor
which you can use to specify a number of different options. It's pretty rare that you would ever
use this class without specifying the second argument, since most of the time you will want to
use the response in some way.

Listing 1 shows an example of performing an Ajax request. For now, the options are an empty
JavaScript object, but in the next section I will cover the different options that are available for
you to specify. These also apply to the Ajax.Updater and Ajax.PeriodicalUpdater classes we will
look at shortly.

Note: If you try out this example, your sub-request will likely result in a 404 file not found error
since the requested file does not exist. Try changing the URL to a file you know exists on your
server.
Listing 1 A basic Ajax sub-request (listing-1.html)
<html>
<head>
<title>A basic Ajax sub-request</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var options = {
method : 'post'
}

new Ajax.Request('/path/to/file', options);


</script>
</body>
</html>
Note: Ajax.Request is a class, not a function, so you must remember to instantiate the class by
including the new keyword.
In the above example, the options object is used to hold the various request options. This may
include get or post data to include in the request or instructions on what to do on successful
completion (or failure) of the request. In this example, I have set the request method to
be post, simply by specifying the method option.
In this example, once the script is requested nothing will happen since we haven't defined any
callbacks for handling the response. I will cover this in the next section.

Ajax.Updater

The Ajax.Updater class is an extension of the normal Ajax.Request class, specifically used to
update a DOM element with whatever content is returned from the HTTP sub-request. The
alternative would be to useAjax.Request yourself directly, then manually handle the response
and update the required element accordingly.

Listings 2 and 3 show an example of using Ajax.Updater. In Listing 2 is some basic HTML
content that we are going to load into the page shown in Listing 3.
Listing 2 Sample content to load using Ajax.Updater (listing-2.html)
<p>
Here is some HTML content, with any element you would normally
use, such as <strong>bold</strong> and <a href="http://www.example.com">a link</a>.
</p>
Listing 3 Populating an element using Ajax.Updater (listing-3.html)
<html>
<head>
<title>Populating an element using Ajax.Updater</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>

<script type="text/javascript">
function onButtonClick(e)
{
var button = Event.element(e);

new Ajax.Updater(button.up(), 'listing-02.html');


}

$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>

When you load this code in your browser you will see a button that says "Click Me!". When you
click the button, an Ajax request is triggered to request the listing-02.html file. This occurs in
the event handleronButtonClick.

The first argument to Ajax.Updater is the element to populate with the results from the Ajax
request. The second argument is the URL to fetch. As mentioned previously, we could also
specify options to pass as the final arguments. Because this example is somewhat trivial, I have
not done so, but you will see how to do this later in this article.
If you're unsure how the event handling code works in this example, please refer to the fourth
article in this series.

Ajax.PeriodicalUpdater

The Ajax.PeriodicalUpdater class is somewhat similar to the Ajax.Updater class, except instead
of performing a single sub-request and updating an element it will continue to perform sub-
requests after a specified period, thereby continually updating the element's content.

Because this class and Ajax.Updater are extensions to the base Ajax.Request class, the
remainder of this article will focus specifically on using Ajax.Request. For more information
about Ajax.Updater andAjax.PeriodicalUpdater, refer to the Prototype API documentation
at http://prototypejs.org/api/ajax.

Request Options

There are a number of different options you can pass to Ajax.Request that allow you to control
specifically how the sub-request should be performed, or how its response should be handled.
Although there are quite a number of different options, I'll just cover the main ones here. For
details on all available options, refer to the documentation
at http://prototypejs.org/api/ajax/options.

In order to use these options, you typically create a JavaScript object in which you specify
whichever options are required. My own preference is to call this variable options, but it doesn't
really matter. You then pass this variable as the second argument to Ajax.Request (the first
argument is the URL to request).

Listing 4 shows an example of how to specify the request options to pass to Ajax.Request. My
own personal preference is to create the options separately from the call to Ajax.Request, since
it makes the code slightly easier to read.
Listing 4 How to specify options to pass to Ajax.Request (listing-4.js)
// method 1: create options separate to request

var url = '/url/to/request';


var options = {
// specify one or more options here
};

new Ajax.Request(url, options);

// method 2: specify all options inline

new Ajax.Request('/url/to/request', {
// specify one or more options here
});

Now that you know how to create the options, I'll cover the most important options available.
 method: This specifies the type of HTTP request to perform. The typical values for this
are post or get. If you don't specify this option, post is used. As always, if you are
sending data back to the server that may affect the state of the application (e.g. updating
data in the database) you should use post.

 parameters: This option specifies any form data you would like to include in your request.
This value can be a string or you can pass an object, which Prototype will automatically
serialize and escape for you. This is demonstrated below.

 postBody: If you're performing a request with a method of post, then you can use this
option to hold the post data. My own preference is just to use the parameters option, since
it will be used for a post request if postBody is left empty.

Listing 5 shows an example of specifying these options for Ajax.Request. Take note of how
the parametersoption is specified. The second method is preferred since it not only looks cleaner
but ensures your values are encoded properly.
Listing 5 Some basic examples of specifying Ajax.Request options (listing-5.js)
// method 1: create parameters as a string

var url = '/url/to/request';


var options = {
method : 'post',
parameters : 'name=Quentin&country=Australia'
};

new Ajax.Request(url, options);

// method 2: create parameters as an object

var url = '/url/to/request';


var options = {
method : 'post',
parameters : {
name : 'Quentin',
country : 'Australia'
}
};

new Ajax.Request(url, options);

Event Callbacks

The next part of specifying the request options is to specify the functions that should be called
when certain events occur. These are included in the options array just as above, except each
value should be either a function or the name of a function to call. In the next section I'll show
you how to create these functions.

The following list shows the most common callbacks that you will use. There are others
available, but not all are available on all browsers. You can find a full list of these
at http://prototypejs.org/api/ajax/options.
 onSuccess: This callback is triggered when a request is completed and the HTTP response
code is in the 200s. In the past I have seen a lot of code utilizing XmlHttpRequest that
checks specifically for a response code of 200, whereas this isn't necessarily the response
code that will be returned. WithonSuccess, this is not a problem.

 onFailure: This callback is triggered when a request completes but the HTTP response
code is not in the 200s. For instance, if a 404 File Not Found error occurs then this
callback would be used.

 onComplete: Regardless of whether a request is deemed to have succeeded or failed, this


callback is triggered upon completion of a request. This is the final callback to be
triggered. In other words, if you specify onSuccess or onFailure, then onComplete will be
used after that.

Listing 6 shows an example of specifying these callbacks in the request options. Each callback
accepts the response object (an instance of Ajax.Response) as the first argument. I will cover
how to specifically use this response in the next section.
Listing 6 Specifying callbacks for success, failure and request completion (listing-6.js)
function onRequestSuccess(transport)
{
// handle the response
}

function onRequestFailure(transport)
{
// handle the response
}

function onRequestComplete(transport)
{
// handle the response
}

var url = '/url/to/request';


var options = {
method : 'post',
parameters : {
name : 'Quentin',
country : 'Australia'
},
onSuccess : onRequestSuccess,
onFailure : onRequestFailure,
onComplete : onRequestComplete
};

new Ajax.Request(url, options);

As an alternative to using onSuccess and onFailure, you can handle specific HTTP response
codes. For instance, if you wanted a different handler for a 404 error to a 403 error, you could
achieve this easily by specifying the on404 callback and on403 callbacks. You can use any HTTP
code, in the format of onXYZ (whereXYZ is the response code).
Note however that if you do this, the onFailure or onSuccess callback will not be used for that
response code. As an alternative, you may wish to manually check for the response code and act
accordingly from within either the onFailure or onSuccess callback.

Listings 7 and 8 show example of two different ways of handling response codes. In the first
example, I have specified the on403 and on404 callbacks, while in the second I check
the transport.status value to determine the code.
Listing 7 Specifying a separate callback for different status codes (listing-7.js)
var url = '/url/to/request';
var options = {
/* other parameters if required */
on403 : function(transport) { ... },
on404 : function(transport) { ... }
};

new Ajax.Request(url, options);


Listing 8 Using a single callback and checking the status code (listing-8.js)
function onRequestFailure(transport)
{
switch (transport.status) {
case 403:
// 403 specific handler
break;

case 404:
// 404 specific handler
}
}

var url = '/url/to/request';


var options = {
/* other parameters if required */
onFailure : onRequestFailure
};

new Ajax.Request(url, options);

Typically you will only ever need to use the onSuccess and onFailure handlers to do something
with an Ajax response (although if you're lazy you may not even bother with the onFailure).
Typically you won't really need to use the onComplete or other callbacks.

In the next section I will show how to actually do something useful with the callback handlers.

Handling the Ajax Response

Now that you know how to specify the callbacks for particular events that occur when
performing an Ajax request, I will show you write to write the callback handler.

All of these callbacks are passed an instance of Ajax.Response as the first argument. This object
contains information about the request, such as the HTTP status code that resulted from the
request, and any response data sent back. Full details about this class can be found
athttp://prototypejs.org/api/ajax/response.
My own preference is to call this parameter transport. Whatever you call it, be consistent.
Functions should be self-documenting, so you should be easily able to determine what the
function is used for by its name and the naming of its arguments.

In writing a handler we are typically concerned with the response data. Typically we want to use
this data somehow on the current page, whether it's text, HTML, JSON or XML. Because of this,
when writing a response handler we are really only concerned with the onSuccess handler,
since onFailure occurs only if something went wrong with the request.

Even if the Ajax request resulted in some error condition in your application, if the Ajax request
succeeded you then need to handle your application error in the onSuccess handler.

Note: An Ajax request doesn't have to return any data. Some operations involve you simply
sending data to the server for it to process, without requiring any response. You don't really
need any success or failure handlers if you don't care about the response.

The most commonly used values that are available in the transport variable are as follows:

 status: The HTTP response code. An example of reading this value is shown in listing 8.

 statusText: The text that corresponds to the response code, as sent by the server. Since
different servers may send a slightly different string for certain response codes, you
shouldn't rely on this value.

 responseText: This is any data that is sent back from the request as a string.

 responseXml: If data was sent from the server in XML format (with the correct content
type header oftext/xml), this variable contains the response as a document object.

 responseJSON: If the data was sent back from the server in JSON format, you can read the
data from this array. In the next section I will show you how to do this. Note that the
response from the server must use the application/json content type header.

You can read any of these properties from the first argument that is sent to the request
handlers. In the previous section I called this variable transport.

Listing 9 shows identical functionality to the Ajax.Updater example in Listing 3, however, in this
example we read the responseText variable and update the element accordingly. This example
uses the same example HTML, shown in Listing 2.
Listing 9 Populating an element using the responseText property (listing-9.html)
<html>
<head>
<title>Populating an element using the responseText property</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>
<script type="text/javascript">
function onAjaxSuccess(transport)
{
$('myButton').up().update(transport.responseText);
}

function onButtonClick(e)
{
var options = {
method : 'get',
onSuccess : onAjaxSuccess
};

new Ajax.Request('listing-02.html', options);


}

$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>

Using JSON Data

I've mentioned JSON a few times in this article so far, but what exactly is it? Short for JavaScript
Object Notation, it is a way to send complex data structures between the client and server.

Essentially, it is just JavaScript code. However, it is really only the code that would be used to
create a new array or object in JavaScript. Therefore, when you read the JSON data you can
bind it to a JavaScript variable and access the response just as you would from any other array
or object in JavaScript.

For example, if you wanted to create an array of data in PHP, then make that data easily
accessible in JavaScript (let's say you wanted to send this PHP array back in an Ajax response),
then you would use JSON.

Listing 10 shows an example of some data you are representing in PHP that you want available
in JavaScript. Listing 11 shows the JSON representation of this data.
Listing 10 Some sample data in PHP that we want to use in JavaScript (listing-10.php)
<?php
$person = array(
'name' => 'Quentin',
'country' => 'Australia'
);
?>
Listing 11 The $person array represented in JSON (listing-11.js)
{
name : 'Quentin',
country : 'Australia'
}

This data is in the same format as JavaScript uses, meaning you can easily use it in your
JavaScript code. Listing 12 shows how you might access this data using
the responseJSON variable in your onSuccess handler for Ajax.Request. This assumes that
the $person array was the only data sent in the response. In this example I assigned the JSON
data to a variable called json, purely so the code is easier to read when the data is accessed.
Listing 12 Reading the JSON response (listing-12.js)
function onSuccess(transport)
{
var json = transport.responseJSON;

alert(json.name + ' is from ' + json.country);


}

PHP provides the json_encode() function to convert a PHP variable into its equivalent JSON
format. This is only available from PHP 5.2.0. I'll show you how to use this function in the next
section.

Handling Ajax Requests on the Server Side Using PHP

Now that you have some understanding of how JSON data works, I'll give you a concrete
example of performing an Ajax request which returns some JSON data. Additionally, I'll now
show you how to handle Ajax responses in PHP. The principles here apply to other languages
also, but the server-side language used is PHP.

When writing scripts in PHP to handle Ajax requests, you typically write your scripts as you
would for normal requests. The key difference is that you will typically want to set the content
type of the response data. By default, PHP uses a content type of text/html, so if you're just
sending HTML data back (as we did in listings 3 and 9), you don't need to set the content type.

Let's now look at how to send JSON data in PHP. According to RFC 4627 (which you can find
athttp://www.ietf.org/rfc/rfc4627.txt), you should use the content type
of application/json when sending JSON data.

As I mentioned in the previous section, PHP provides a function called json_encode() which you
can use to convert a PHP variable (such as an array) into JSON format. You can use header() to
send the content type, then echo the output from json_encode().
Listing 13 Sending a PHP array as JSON data (listing-13.php)
<?php
$person = array(
'name' => 'Quentin',
'country' => 'Australia'
);

header('Content-type: application/json');

echo json_encode($person);
?>

We can now build on the code from Listing 12 to actually request this data and display it
accordingly. Listing 14 shows the complete example of reading JSON from the server. When you
click the button that is displayed, the Ajax request will be initiated. The response will then be
displayed. Note however that there is no error handling in this example and it assumes that
specific data will be returned.
Listing 14 Reading JSON data from the server (listing-14.html)
<html>
<head>
<title>Reading JSON data from the server</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>

<script type="text/javascript">
function onSuccess(transport)
{
var json = transport.responseJSON;

alert(json.name + ' is from ' + json.country);


}

function onButtonClick(e)
{
var options = {
method : 'get',
onSuccess : onSuccess
};

new Ajax.Request('listing-15.php', options);


}

$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>

When handling an Ajax request on the server-side, you may want to ensure that the request did
in fact come via Ajax. Whenever a request is performed using Prototype's Ajax.Request, the
header X-Requested-Withheader is sent, with a value of XMLHttpRequest.

While this value can be manually set by a clever user, ultimately it doesn't matter too much and
simply allows you perform different functionality if a random user happened to stumble across
your Ajax request handler.

Listing 15 shows an example of checking for this header. This is a modification of Listing 13, in
which we send a different content type based on how the request is performed. Try viewing this
script in your browser. Realistically you may prefer to redirect to another page rather than what
is done here.
Listing 15 Checking the request method for the current script (listing-15.php)
<?php
$person = array(
'name' => 'Quentin',
'country' => 'Australia'
);

$isXmlHttpRequest = isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&


strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) ==
'xmlhttprequest';

if ($isXmlHttpRequest)
header('Content-type: application/json');
else
header('Content-type: text/plain');

echo json_encode($person);
?>

In PHP, you read the header from $_SERVER. PHP modifies the request header names by
prepending HTTP_, capitalizing the name and replacing hyphens with underscores. In other
words, X-Requested-With becomesHTTP_X_REQUESTED_WITH. Additionally, I like to
use strtolower() on this value so there is no confusion between,
say, XMLHttpRequest and XmlHttpRequest.

Summary

In this article I showed you how to perform Ajax requests using the Ajax.Request provided by
Prototype. This class is a wrapper to the XMLHttpRequest object that modern browsers have. I
showed you how to specify options when performing a request, including how to specify the
callback handlers to deal with the response from the HTTP sub-request.

Additionally, I introduced you to JSON data and how to send JSON data with PHP, and receive it
in the Ajax response. While the examples given were mostly conceptual, hopefully they gave
you a good idea of how you can use Ajax in your own applications. In the eighth article of this
series a larger, more concrete example will be given when I bring together all of the Prototype
functionality covered in this series.

In the next article I will show you how to create JavaScript classes in Prototype. If you prefer an
object-oriented approach to your programming, you will find this article invaluable, since
knowing how to effectively create classes will give your code a much better overall structure.

Eight Weeks of Prototype: Week 6, Writing JavaScript Classes


with Prototype
Introduction

While JavaScript is by design an object-oriented language, it hasn't always been the easiest
language to create nicely structured code that is easily maintainable. Prototype helps with this
by providing a number of useful features for creating classes, such as the ability to extend
classes and to easily group all functions together by using function binding. In this article I will
show you how to create classes using JavaScript and Prototype.

To begin with, I'll show you how to define classes and how to create the class constructor. Next I
will show you how to create child classes by extending your classes. After this I will introduce
you to function binding and how it applies to the development of JavaScript classes.

Next I will show to create a string representation of instances of your created classes, then how
to create custom enumerables (as mentioned in the third article in this series).

This article assumes you have read and understood the first five articles in this series, and also
that you have at least a basic knowledge of how object-oriented programming (OOP) works.

Declaring Classes

To create a new class with Prototype, the Class.create() function is used. This method accepts
an object as the first argument, with each element of that object corresponding to a class
method. The created class is returned from the call to Class.create().

Listing 1 shows an example of creating a new class called Person. An instance of this class
represents a single person. I will build on this particular class as we continue through this
article.

This class declares one property (name), in which the person's name is held. It also declares two
functions; one for setting the name (setName()) and another for retrieving the name
(getName()).
Listing 1 Creating a basic JavaScript class (listing-1.html)
<html>
<head>
<title>Creating a Basic JavaScript Class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var Person = Class.create({

name : null,

setName : function(name)
{
this.name = name;
},

getName : function()
{
return this.name;
}
});

var me = new Person();

me.setName('Quentin Zervaas');

alert(me.getName());
</script>
</body>
</html>

Declaring the name property is not required (you can just assign directly to the variable to create
it), but doing so makes it clearer that the property exists.

One important facet of creating classes in this manner is you must remember that each function
is an object element (the first argument to Class.create()), which therefore means you must
separate each element by comma. Think of it as Class.create({ function1, function2,
function3 }).

As you can see in Listing 1, once the class has been declared we instantiate it using
the new keyword, just as you would with most other OOP languages.

Saving and Accessing Classes

My own personal preference when saving classes is to save each class to a separate file, using
the filenameClassName.js.

Note: Additionally, when I want to namespace (that is, group classes into separate packages) I
will name the class PackageName_ClassName and save the file to PackageName/ClassName.js.

Therefore, re-working the Person class into a separate file would result in the code in Listing 2.
We can then load the class file and instantiate the class as shown in Listing 3.
Listing 2 Declaring the Person class in an external file (Person.js)
var Person = Class.create({

name : null,

setName : function(name)
{
this.name = name;
},

getName : function()
{
return this.name;
}
});
Listing 3 Loading and instantiating the Person class (listing-3.html)
<html>
<head>
<title>Loading and instantiating the Person class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Person.js"></script>
</head>
<body>
<script type="text/javascript">
var me = new Person();

me.setName('Quentin Zervaas');

alert(me.getName());
</script>
</body>
</html>

You should always be declaring your classes in external files and not inline in your HTML. In fact,
you should almost never include any JavaScript code in your HTML files – I've done so in these
examples to simplify matters, but in the seventh article in this series I'll discuss this matter
further.

Defining the Class Constructor

In Object-Oriented Programming, the class constructor is a method that is executed when the
class is instantiated. By default JavaScript doesn't use constructors when you create classes, but
Prototype allows us to do so by creating a function called initialize().

When you create a new instance of your class the initialize() function will be executed. You
can pass arguments to the constructor by including them when you instantiate the class.

To demonstrate this, I'll now modify the Person class so rather than having to set the name by
callingsetName() after you create a Person object, you can set the name in the constructor.

Listing 4 shows the new version of the Person.js file, while Listing 5 shows how you can now
instantiate the class and set the name without explicitly calling setName().
Listing 4 The Person class with a constructor which sets the name (Person.js)
var Person = Class.create({

name : null,

initialize : function(name)
{
this.setName(name);
},

setName : function(name)
{
this.name = name;
},

getName : function()
{
return this.name;
}
});
Listing 5 Defining the person's name when instantiating the Person class (listing-5.html)
<html>
<head>
<title>Loading and instantiating the Person class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Person.js"></script>
</head>
<body>
<script type="text/javascript">
var me = new Person('Quentin Zervaas');

alert(me.getName());
</script>
</body>
</html>

Inheritance

A child class is one that extends from another class, meaning the properties and methods of the
parent class now also belong to the child. Additionally, the child class can overwrite any methods
as it sees fit, or it can create its own methods that don't exist in the parent.

While JavaScript doesn't provide OOP features such as abstract classes or interfaces, Prototype
does make it fairly straightforward to create child classes.

Note: Technically it's possible to emulate abstract classes by creating a method in the abstract
class that throws an exception. This exception will not occur if the child class has its own
implementation of the method.
To create a child class, you still use the Class.create() method, but rather than passing the
class methods as the first argument, you pass them as the second argument, and instead you
pass the name of the parent class as the first argument. This is demonstrated in Listing 6.

According to the naming rules mentioned earlier in the article, my preference is to call this
classPerson_Australian (since it becomes part of the Person package) and to save it in a
directory called Person(./Person/Australian.js).
Listing 6 Creating a sub-class of Person (Australian.js)
var Person_Australian = Class.create(Person, {
// all methods are inherited, more can be defined here
});

Because Prototype isn't really designed to have class inheritance, you must make the small
concession of adding an extra parameter to each method you want to overwrite in child classes.
This extra parameter is called $super and it is the first argument to the method. It is in fact a
variable holding the parent method of the same name, meaning you can call $super().

Listings 7 and 8 demonstrate this by adding a method called getCountry() to the Person class.
In thePerson_Australian class we override this method so we can return the name of the
country. As you can see, the method accepts $super as its first (and in this case, only)
argument; however when you call this method you still don't use any arguments since Prototype
internally passes the $super argument. This is demonstrated in Listing 9 which loads and
instantiates the two classes.
Note: Since the declaration of the Person_Australian class relies on the Person class, you must
be sure to load the Person.js file before Australian.js.
Listing 7 Declaring the parent getCountry() method (Person.js)
var Person = Class.create({

name : null,

initialize : function(name)
{
this.setName(name);
},

setName : function(name)
{
this.name = name;
},

getName : function()
{
return this.name;
},

getCountry : function()
{
return 'Unknown';
}
});
Listing 8 Overriding getCountry() in the child class (Australian.js)
var Person_Australian = Class.create(Person, {

getCountry : function($super)
{
return 'Australia, not ' + $super();
}
});
Listing 9 Loading and instantiating the Person and Person_Australian classes (listing-9.html)
<html>
<head>
<title>Loading and instantiating the Person and Person_Australian classes</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Person.js"></script>
<script type="text/javascript" src="/js/Person/Australian.js"></script>
</head>
<body>
<script type="text/javascript">
var you = new Person('Some Person');
alert(you.getCountry());
// displays "Unknown"

var me = new Person_Australian('Quentin Zervaas');


alert(me.getCountry());
// displays "Australia, not Unknown"
</script>
</body>
</html>

Function Binding

When using Prototype to develop classes, you need to have an understanding of what function
binding is and how to use it. Essentially what binding does is instruct what the
variable this refers to in a function. This is especially useful for event handling and for handling
Ajax responses.

As I showed you in the fourth article of this series ("Event Handling in Prototype"), to observe
an event on an element you use theElement.observe('eventName', handler). If you are
developing a class then you want your handler function to be one of your class methods. This is
primarily so you can access other class methods when handling the event.

Consider the code in Listing 10. In this example, when the user clicks the button
the _onButtonClick function is called. Our aim is to display the string returned by
the getMessage() method when the button is clicked.

Note: My personal preference is to name event handlers using an underscore since the method
shouldn't directly be called. Typically this format is used to indicate protected or private
methods.

This code will not yet work, as explained following the listing!
Listing 10 Demonstrating the drawback of not using function binding (listing-10.html)
<html>
<head>
<title>Demonstrating the drawback of not using function binding</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>

<script type="text/javascript">
var MyClass = Class.create({

initialize : function(button)
{
button = $(button);

button.observe('click', this._onButtonClick);
},

getMessage : function()
{
return 'This is a simple function';
},

_onButtonClick : function(e)
{
var button = Event.element(e);
var message = this.getMessage();

button.up().update(message);
}
});

new MyClass('myButton');
</script>
</body>
</html>

The problem that occurs in this example is that when _onButtonClick runs,
the getMessage() method is not found. This is because the keyword this doesn't refer to the
instance of MyClass.

To solve this problem, Prototype provides two methods: bind() and bindAsEventListener(). They
are basically the same thing, the difference being that you should
use bindAsEventListener() specifically when you are observing events, because then Prototype
knows to pass in the event object to the callback handler.

Listing 11 demonstrates usage of the bindAsEventListener(). This method accepts a single


argument: the variable to bind the function to.
Listing 11 Binding a function with bindAsEventListener (listing-11.html)
<html>
<head>
<title>Binding variable with bindAsEventListener</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>

<script type="text/javascript">
var MyClass = Class.create({

initialize : function(button)
{
button = $(button);

button.observe('click',
this._onButtonClick.bindAsEventListener(this));
},

getMessage : function()
{
return 'This is a simple function';
},

_onButtonClick : function(e)
{
var button = Event.element(e);
var message = this.getMessage();

button.up().update(message);
}
});

new MyClass('myButton');
</script>
</body>
</html>

Now, inside the _onButtonClick() method, this refers to the instance of MyClass, meaning
the getMessage()method can now be called.

Binding Functions for Ajax Requests

Although function binding will be most often used for event handling
(using bindAsEventListener()), you will also need to bind functions when handling the response
from Ajax requests. Since you don't need an event handler passed in (this concept doesn't apply
to Ajax requests) we use bind() instead ofbindAsEventListener(), once again accepting the
variable to which the function should be bound.

In the previous article in this series I demonstrated a simple example of retrieving JSON data
from the server and updating an element on the page with the returned data. I will now rewrite
this same example using a class and function binding.

Listing 12 shows the PHP code used to return JSON data from the server. This is the script that
will be accessed in the Ajax request. For a further explanation of how this all works, please refer
to the previous article.
Listing 12 Sending JSON data from the server (listing-12.php)
<?php
$person = array(
'name' => 'Quentin',
'country' => 'Australia'
);

header('Content-type: application/json');

echo json_encode($person);
?>

Now comes the interesting part. In Listing 13 we define a class called MyClass which accepts a
single argument to the constructor. This is the button that we observe the click event on. We
save the button as a class property so we can access it again the Ajax response handler. Again,
we use bindAsEventListener() so we can handle the event within the class.
Listing 13 Using bind() when handling Ajax requests (listing-13.html)
<html>
<head>
<title>Using bind() when handling Ajax requests</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>

<script type="text/javascript">
var MyClass = Class.create({

button : null,

initialize : function(button)
{
this.button = $(button);
this.button.observe('click',
this._onButtonClick.bindAsEventListener(this));
},

_onButtonClick : function(e)
{
var options = {
method : 'get',
onSuccess : this._onSuccess.bind(this)
};

new Ajax.Request('listing-12.php', options);


},

_onSuccess : function(transport)
{
var json = transport.responseJSON;
var msg = json.name + ' is from ' + json.country;

this.button.up().update(msg);
}
});

new MyClass('myButton');
</script>
</body>
</html>

Now when we handle the click event (the _onButtonClick() method), we once again perform an
Ajax request. The difference between doing it now and in the last article was that now we
use bind() so we can use theMyClass instance in the _onSuccess() method.

When the button is clicked, an Ajax request is performed. When the request is
finished, _onSuccess() handles the response and updates the button's container with the JSON
data read from the Ajax request.

Binding Functions for Enumerables

In the third article in this series we covered enumerables, including the each() method. Because
a separate function (known as an iterator) is called when you use each(), once again any classes
you are using lose their context.
Consider the code in Listing 14. In the class constructor I have defined an array of different
colours. I then attempt to loop over them and call the display() method for each colour. The
problem is, this inside the each loop doesn't refer to the MyClass instance.
Listing 14 The problem with using each() in classes (listing-14.html)
<html>
<head>
<title>The problem with using each() in classes</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="container"></div>

<script type="text/javascript">
var MyClass = Class.create({

container : null,

initialize : function(container)
{
this.container = $(container);

var colors = [ 'red', 'green', 'blue' ];

colors.each(function(color) {
this.display(color);
});
},

display : function(msg)
{
this.container.insert(msg + '<br />');
}
});

new MyClass('container');
</script>
</body>
</html>

When you use the each() method, it is possible to pass a second argument in order to specify
the context. That is, you would use myEnum.each(myFunction, myObject). Or in this specific
case,colors.each(function(color) { ... }, this). This is shown in Listing 15.

Note: This second argument for binding is available on other enumerable methods, not
just each(). Look at the Prototype API guide at http://prototypejs.org/api/enumerable for full
details.
Listing 15 Binding enumerable function handlers (listing-15.html)
<html>
<head>
<title>Binding enumerable function handlers</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
var MyClass = Class.create({

container : null,

initialize : function(container)
{
this.container = $(container);

var colors = [ 'red', 'green', 'blue' ];

colors.each(function(color) {
this.display(color);
}, this);
},

display : function(msg)
{
this.container.insert(msg + '<br />');
}
});

new MyClass('container');
</script>
</body>
</html>
Note: Technically, you can use the bind() method if you prefer, rather than using this second
argument. That is, colors.each(function() { ... }.bind(this)).

Creating a String Representation of Your Class

If you want to define a string representation of an instance of your class, you can do so by
defining a class method called toString(). This method must return a string.

Effectively what this does is define what value should be returned when your object is converted
(cast) to a string. Listing 16 shows an example of how you might choose to
implement toString(). Listing 17 shows how the object is displayed when converted to a string.
Listing 16 Declaring the toString() method (Person.js)
var Person = Class.create({

name : null,

initialize : function(name)
{
this.setName(name);
},

toString : function()
{
return this.getName();
},

setName : function(name)
{
this.name = name;
},

getName : function()
{
return this.name;
},

getCountry : function()
{
return 'Unknown';
}
});
Listing 17 Converting an object to a string so it can be displayed (listing-17.html)
<html>
<head>
<title></title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Person.js"></script>
</head>
<body>
<div id="foo"></div>

<script type="text/javascript">
var me = new Person('Quentin Zervaas');
$('foo').update(me);
</script>
</body>
</html>

When you view this listing in your browser, the instance of Person will be converted to a string
so it can populate the #foo div. If you are creative you could do some other magic, such as
returning HTML code to output a photo of the person. For instance, toString : function()
{ return '<img src="..." />'; }.

Creating Custom Enumerables

In the third article of this series I discussed enumerables in Prototype, including arrays and
hashes. As mentioned in that article, it is possible to create your own enumerable types. If you
use PHP, you may be familiar with the Iterator interface. This is in effect the same thing, but for
JavaScript instead.

While an array enumerable is used for looping over each element in an array, when creating
your own enumerable you decide what constitutes each value in the enumeration.

To do so, you must extend the Enumerable class and then create a method in your class
called _enum(). This method accepts a single argument, which is the iterator function that is to
be called for each element.

Listing 18 shows an example of creating a custom enumerable, which I have called People. This
class is used to hold a collection of Person objects (refer to Listing 16). After adding
several Person objects, we loop over the People instance and show each Person on the page.
Note that we are also making using of the toString()method discussed in the previous section.
Listing 18 Creating a custom enumerable object (listing-18.html)
<html>
<head>
<title>Creating a custom enumerable object</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Person.js"></script>
</head>
<body>
<div id="foo"></div>

<script type="text/javascript">
var People = Class.create(Enumerable, {

people : [],

add : function(person)
{
this.people.push(person)
},

_each : function(iterator)
{
for (var i = 0, length = this.people.size(); i < length; i++)
iterator(this.people[i]);
}
});

var people = new People();

people.add(new Person('Quentin Zervaas'));


people.add(new Person('Joe Bloggs'));

people.each(function(person, i) {
var num = i + 1;
$('foo').insert(num + '. ' + person + '<br />');
});
</script>
</body>
</html>

Realistically in this code the enumeration is simply looping over an array anyway, but the point
is that the code calling the class doesn't care about the internal implementation; it simply knows
that when it loops over the instance of People it's going to receive an instance of Person inside
the iterator.

Summary

In this article I have shown you how to create JavaScript classes using the helper functions
Prototype provides. This included showing you how to create a class constructor and how to
extend classes using child classes.
In addition to this, I showed you how to create a string representation of your class instances by
implementing the toString() method, and how to create custom enumerable classes by
extending the Enumerable class and implement a method called _each().

One of the most important concepts to take out of this article was that of function binding,
which is extremely important if you are effectively going to develop JavaScript classes. I showed
you how to use the bind()method for Ajax request handlers and
the bindAsEventListener() method for event callbacks. Additionally, I showed you how to bind
iterator functions when looping over enumerable objects.

In the next article of "Eight Weeks of Prototype", I will cover a range of miscellaneous topics
that are useful for JavaScript development with Prototype.

Eight Weeks of Prototype: Week 7, Other Prototype Functionality

Introduction

So far in Eight Weeks of Prototype I have covered the most important aspects of developing
robust JavaScript using Prototype; however I haven't covered every available feature. In this
article I will briefly discuss other aspects of development with Prototype, that while aren't
necessarily core knowledge are still useful.

Firstly I will show you the Template class and how to make use of it in your applications. Next I
will show you different ways of reading form values (which is useful for Ajax-enabled
applications). I will then show you some of the shortcut functions Prototype provides to simplify
your development. Finally I will show you how your JavaScript code should be integrated with
your HTML code.

Some of these techniques will become much clearer after the next (and final) article in this
series which will put everything into action. Once again in this article I assume you have read
and understood the previous six articles in this series.

The Template Class

The Template class allows you to create a block of text (be it HTML, plaintext or otherwise) in
which there are placeholders. You can then evaluate the template to substitute a set of values in
for the placeholders. This is useful for doing things such as defining how data retrieved from
Ajax request should be displayed.

A placeholder is denoted in a template by using #{variable name}. For instance, if you wanted to
substitute in a variable called country you would use #{country}. The string containing the
placeholders is passed to the constructor of the Template class, as shown in Listing 1.
Listing 1 Invoking the Template class (listing-1.js)
var tpl = new Template('#{name} is from #{country}');
In order to retrieve the evaluated template with the actual values instead of the placeholders,
you call theevaluate() method on the tpl variable. This method accepts a single argument,
which is an object (or Hash, as shown in the third article of this series) containing key / value
pairs for each placeholder.

The evaluated template is returned from the call to evaluate(), which you can then use as
required. If a corresponding value isn't found for a given placeholder, an empty string is used.
Listing 2 shows an example of defining data to be used when evaluating a template. The
resultant string is used to populate the #foo div.
Listing 2 Basic usage of the Template class (listing-2.html)
<html>
<head>
<title>Basic usage of the Template class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo"></div>

<script type="text/javascript">

var tpl = new Template('#{name} is from #{country}');

var person = {
name : 'Quentin',
country : 'Australia'
};

$('foo').update(tpl.evaluate(person));
</script>
</body>
</html>

As mentioned above, the Template class is useful for handling data from an Ajax request. In the
next example we will retrieve some JSON data (just as we have done in the previous two articles
in this series), and display the returned data with the help of a template.

Listing 3 shows how we can return JSON data from PHP. This code is the same as is used in the
previous two articles. For a full explanation refer to fifth article in this series.
Listing 3 Using PHP to send JSON data (listing-3.php)
<?php
$person = array(
'name' => 'Quentin',
'country' => 'Australia'
);

header('Content-type: application/json');

echo json_encode($person);
?>

Now we can create the JavaScript code that makes use of this JSON data. In Listing 4, I have
created a class called PersonLoader which retrieves the data from Listing 3 using Ajax, then
feeds the returned values into a template created using the Template class. For more details
about Ajax requests and how classes work with Prototype, please read the previous two
examples in this series.
Listing 4 Combining an Ajax request with the Template class (listing-4.html)
<html>
<head>
<title>Combining an Ajax request with the Template class</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo"></div>

<script type="text/javascript">
var PersonLoader = Class.create({

initialize : function(container)
{
this.container = $(container);

var options = {
onSuccess : this._loadSuccess.bind(this)
};

new Ajax.Request('listing-03.php', options);


},

_loadSuccess : function(transport)
{
var tpl = new Template('<strong>#{name}</strong> is from
<em>#{country}</em>');
var content = tpl.evaluate(transport.responseJSON);

this.container.update(content);
}
});

new PersonLoader('foo');
</script>
</body>
</html>

You may notice in this example that HTML tags are used in the template. Since you can use any
text that you like, the use of the Template class can be quite powerful.

Eight Weeks of Prototype: Week 7, Other Prototype Functionality


By Quentin Zervaas, 15 May 2008, JavaScript, Prototype

Accessing Form Values

There are several ways to retrieve the value of a form element using Prototype. You can either
retrieve the form element you want to read then access its value, or use Prototype to retrieve all
values in a form then read the value that you require.
Listing 5 shows three different ways to retrieve a form value. The first way serializes the form,
allowing you to access the form using the element name. The second way retrieves the DOM
element for the form element and calls the getValue() method. Finally, we use the $F(), which is
an alias of the getValue() method.
Listing 5 Different ways to access form values (listing-5.html)
<html>
<head>
<title>Different ways to access form values</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<form method="post" id="sample-form">
<div>
<input type="text" name="country" value="Australia" />

<input type="button" id="button1" value="Display with


form.serialize()" />
<input type="button" id="button2" value="Display with
Element.getValue()" />
<input type="button" id="button3" value="Display with $F()" />
</div>
</form>
</div>

<script type="text/javascript">
$('button1').observe('click', function(e) {
var form = Event.findElement(e, 'form');

var values = form.serialize(true);

alert(values.country);
});

$('button2').observe('click', function(e) {
var form = Event.findElement(e, 'form');

var input = form.down('input[name=country]');

alert(input.getValue());
});

$('button3').observe('click', function(e) {
var form = Event.findElement(e, 'form');

var input = form.down('input[name=country]');

alert($F(input));
});
</script>
</body>
</html>
When you call the serialize() method on a form element, you need to pass true as the first
argument for an object to be returned. If you don't, a serialized URL string will be returned
instead (so in the case of this form,country=Australia would be returned).

Shortcut Functions

Prototype provides several shortcut functions to help simplify your everyday JavaScript
development. We already looked at two of these in the first article in this series ($() and $$()),
but there are a number of other useful functions available.

Retrieving a Form Element Value with $F()

In the previous section I showed you how to retrieve form values using either the
form serialize() method or by using the getValue() method. The $F() function is an alias
to getValue() (although realistically it's not much of a shortcut).

Creating a Hash with $H()

As discussed in the third article in this series, the Hash class is a Prototype-specific type which
adds extra functionality to a normal JavaScript object. A new Hash object is created using
the $H() method, which accepts a JavaScript object as its only argument. Using a hash rather
than a normal object has several advantages (such as receiving all the features of enumerable
objects). This is discussed in the third article in this series.

Listing 6 demonstrates this by creating a hash using an object, then reading and updating
values.
Listing 6 Creating a hash and reading and writing values (listing-6.html)
<html>
<head>
<title>Creating a hash and reading and writing values</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">

var person = $H({


name : 'Quentin',
country : 'Australia'
});

// retrieve a value
alert(person.get('name'));

// update a value
person.set('country', 'Somewhere else');

// remove a value
person.unset('country');

// retrieve the size of the hash


alert(person.size());

</script>
</body>
</html>

Creating a Range with $R() and $A()

One class we haven't looked at yet is ObjectRange. This is another type of Enumerable object (see
the third article in this series for more information about enumerables), used to represent a
range of values. The reason I haven't touched on it sooner is that it's probably not something
you will end up using very much.

You can create a new ObjectRange by instantiating the class, however the preferred method is to
use the$R()function. This method accepts two arguments: the starting value and the ending
value of the range.

Once you have a range you will often want to convert it to an array so it can be used just as a
normal array would be. This is achieved using the $A() function. You can think of using the
combination of $A() and $R() as simple array generation.

Listing 7 shows an example of creating a range of values using the $R() method. The range is
then converted to an array so the values can be easily manipulated.
Listing 7 Generating an array of values with $R() and $A() (listing-7.html)
<html>
<head>
<title>Generating an array of values with $R() and $A()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo"></div>

<script type="text/javascript">

var range = $R(1, 12);


var arr = $A(range);

// $A($R(1, 12)) for short

$('foo').update(arr.join(', '));
</script>
</body>
</html>

Tokenizing a String with $w()

If you want to split a string into words (using whitespace as the delimiter), you can use
the $w() method (note the lower-case w).

Listing 8 shows an example of how this function could be used. A list of element IDs is passed to
$w(), which we then use the enumerable each() method to hide and show all of those elements.
Listing 8 Using $w() for bulk element operations (listing-8.html)
<html>
<head>
<title>Using $w() for bulk element operations</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">Showing</div>
<div id="bar">Hide Me</div>
<div id="blah">I want to disappear!</div>

<script type="text/javascript">

// hide the elements


$w('foo bar blah').each(function(id) {
$(id).hide();
});

// this is simpler:
// $w('foo bar blah').each(Element.hide);

// show them again


$w('foo bar blah').each(Element.show);

</script>
</body>
</html>

Your own mileage with this function will vary. It's not typically something I use but since it's a
part of Prototype I have included it in this article.

Including JavaScript Code in Your Applications

Although many of the examples in this "Eight Weeks of Prototype" series have simply used
JavaScript inline in a HTML file, this is not the recommended way of doing things. I have done
this purely to simplify the examples so the important Prototype concepts can be discussed
easily.

Your goal when developing HTML that uses JavaScript is to include absolutely no JavaScript
expressions whatsoever in the HTML. All JavaScript code should be restricted to one or more
external files.

Note: In some cases such as when using JavaScript-based statistics tracking code (such as
Google Analytics) it is not possible to avoid doing so, but generally in these cases it's only a few
lines of code.

You may recall in the previous example I recommended saving a single JavaScript class to a
single file. Well, not only should you do this, but the code used to instantiate your classes should
also be in an external file. Typically, the external file will observe the onload DOM event and do
what is required at that time.

There are several good reasons for doing this, including:


 Your pages will load faster in the long run because the files are smaller. Realistically the
first time the external files are loaded for a single client the load time will be negligibly
longer since they do not yet exist in the client's browser cache but for subsequent pages
there will be an improvement. A by-product of this is that it may reduce your hosting
charges if you run a busy site since data transfer will be much lower.

 Both your HTML code and your JavaScript code will be easier to maintain since there is
much less clutter.

 Using external JavaScript files allows you to easily reuse your code.

 Forcing yourself to use external files also forces you to think more about how best to
structure your application.

To demonstrate this, I'll rewrite the example from Listing 4 in this article. In this code, a class
calledPersonLoader is defined, which is then instantiated in order to update the content of a div
called #foo.

The first step is to move the PersonLoader class into a separate file. As I recommended in the
previous article in this series, I will move this to a file called PersonLoader.js. In other words,
the filename corresponds to the class name (this is discussed more in the other article). Listing
9 demonstrates this. For the sake of this example I assume the file is saved to the /js directory
on your web server (the same location asprototype.js).

Note: One change I've made to this class from earlier is that it now checks for
the container element. This is to prevent any JavaScript errors from occurring just in case the
element wasn't found on the page.
Listing 9 Writing the PersonLoader class to a separate file (PersonLoader.js)
var PersonLoader = Class.create({

initialize : function(container)
{
this.container = $(container);

if (!this.container)
return;

var options = {
onSuccess : this._loadSuccess.bind(this)
}
new Ajax.Request('listing-03.php', options);
},

_loadSuccess : function(transport)
{
var tpl = new Template('<strong>#{name}</strong> is from <em>#{country}</em>');
var content = tpl.evaluate(transport.responseJSON);

this.container.update(content);
}
});
Next we define another JavaScript used to instantiate this class. In order to keep even this code
structured nicely, I like to call this file Application.js in which an object called Application is
defined. Technically this isn't a class, but I like to use it to group these single "utility" functions
together. You can almost think of it as a static class (that is, a class that cannot be instantiated).

Since we only want the startup() method (defined below) called once the page has finished
loading, we observe the window load event to ensure this. This is covered in more detail in the
fourth article of this series.

Note: You don't have to structure your code in this manner; it's only my personal preference.
You could just define a single "normal" function to run if you prefer, or put all the startup code in
the window onload handler.

Listing 10 shows the code for the Application.js file, also saved to the /js directory. Once the
page finishes loading, the Application.startup() function is called. In the startup() method, we
instantiate thePersonLoader class, passing to it the name of the container.
Listing 10 The Application class, used for handling startup and holding other utility method (Application.js)
var Application = {

startup : function()
{
new PersonLoader('foo');
}

};

Event.observe(window, 'load', function() {


Application.startup();
});
Note: The startup() method relies on the PersonLoader class being already loaded, meaning it
must be loaded in the calling HTML. This is a slight drawback since ideally files would be loaded
on demand. I have discussed a technique for dealing with this
at http://www.phpriot.com/blog/dynamically-loading-javascript.

Finally, we implement the HTML code. This is now much simpler than before. All it needs to do is
(in addition to loading Prototype) to load the two JavaScript files we created, and ensure
the #foo element exists on the page. This is shown in Listing 11.
Listing 11 With all JavaScript moved to external files the HTML is much shorter (listing-11.html)
<html>
<head>
<title>Referencing JavaScript from external files</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/PersonLoader.js"></script>
<script type="text/javascript" src="/js/Application.js"></script>
</head>
<body>
<div id="foo"></div>
</body>
</html>
This technique also goes hand-in-hand with not defining DOM events inline (besides, I showed
you a better way of doing this in the fourth article in the series) and also not using inline styles
(rather, using external stylesheets and CSS classes).

In the final article in this series you will see this in action more and how clean doing everything
in external files makes even web forms and other more complicated markup.

Summary

In this article I have discussed several miscellaneous topics to do with Prototype that have not
yet been covered in the series "Eight Weeks of Prototype".

Firstly, I showed you the Prototype's Template class can be used, which is especially useful when
using JSON response data.

Next I showed you some techniques for reading form values using Prototype. Sometimes you
will want to use the value of an element for an Ajax request without submitting the entire form,
in which case this becomes useful. For instance, when a user tries to register you might submit
just their username in the background to verify its availability.

Next I covered some of the shortcut utility methods provided by Prototype, such
as $H(), $R(), $A() and$w().

Finally, I showed you how JavaScript code should be integrated with your web site or
application's HTML code. The primary concept to take from this is that all JavaScript code should
be accessed from external files and you should almost never have to include any JavaScript
statements inline with your HTML.

In the next and final article of Eight Weeks of Prototype, we will create a mini application to
demonstrate the many aspects of Prototype covered in this series. The idea is to show you how
different concepts fit together, such as Ajax, classes and events.

Eight Weeks of Prototype: Week 8, A Complete Prototype


Example

Introduction

So far in this series, "Eight Weeks of Prototype," I have shown you some of the extensive
features the Prototype JavaScript framework provides. In this, the final article of the series, I
will bring many of these features together into a simple example.

The example I show you will be an extremely simplified contact manager (address book,
whatever you like to call it), in which you can view all contacts and add new contacts.
The example includes element selecting, event handling (including custom events), JavaScript
classes and Ajax. Additionally, hopefully the example will serve to show you how JavaScript files
can be structured and accessed in a scalable web application.

In order to use this code yourself you will need access to a web server with PHP 5 and MySQL.
Note, however, that the important concepts in this article relate to the JavaScript code, so
adapting the PHP code to instead use your preferred server configuration is not a problem.

Application Structure

As mentioned in the introduction, the sample application created in this article is that of a simple
contact manager. This will be presented to the user in the form of a single HTML page with
primarily two sections: a list of existing contacts and a form with which to add a new contact.

A separate JavaScript class will be created for each of these two primary functions, with another
JavaScript file used to instantiate these classes. Additionally, server-side scripts are required to
return a list of contact and to process a submitted contact and save it to the database.

Therefore, the following files are required:

 index.html – The HTML file that the user visits in their web browser.

 /js/Application.js – The JavaScript file that creates the contact manager once the page
has loaded.

 /js/AddressBook/Contacts.js – The JavaScript file containing


the AddressBook_Contacts class, used to display the list of existing contacts. Contacts are
retrieved from the server using Ajax.

 /js/AddressBook/Creator.js – The JavaScript file containing the Address_Creator class,


used to save contacts to the MySQL database using Ajax.

 contacts.php – The PHP script that lists existing contacts

 create.php – The PHP script that new contact data is submitted to so it can be saved to
the database.

 styles.css – A basic stylesheet used to make the main page look nicer.

In addition to these main files, we are also going to create some utility PHP scripts:

 ContactManager.php – This file will declare the ContactManager PHP class, used to retrieve
contacts from the database and to save new contacts to the database.

 database.php – This file will make the connection to the database so it can be used in
theContactManager class.
If you were going to create a more complex PHP application, you would perhaps consider using
something like the Zend Framework's Zend_Controller_Front class to structure your code (using
the MVC design pattern); however in an effort to keep the example concise I've simplified things
as much as possible.

Creating a Database and Connecting To It

Before we begin, you will need to create a new MySQL database in which to save the contacts in
your application. For the purpose of this article we will use a database called prototype8.

Assuming you are able to login via the command using a super-user account, you might use the
commands shown in Listing 1 to create the database setup a user to access it. You may want to
use a different username and password.
Listing 1 Creating the database and corresponding output (listing-1.txt)
mysql> create database prototype8;
Query OK, 1 row affected (0.00 sec)

mysql> grant all on prototype8.* to prototype8@localhost identified by 'myPassword';


Query OK, 0 rows affected (0.05 sec)

Once you have created the database and connected to it (if you're still logged in from creating
the database, just enter the statement use prototype8;), you must create the database tables.
For this article we only use a single table, shown in Listing 2.

This database table is fairly simplistic: we will only be saving their name, email and location but
if you were to use this for a real-world application you would likely want a different set of fields.
Listing 2 Creating the contacts database table (listing-2.sql)
create table contacts (
contact_id serial not null,
name varchar(255) not null,
email varchar(255) not null,
location varchar(255) not null,

primary key (contact_id)


);

As mentioned in the previous section, we need to create a PHP file called database.php which we
use to connect to the database server and select the prototype8 database. To simplify matters I
have not included error handling, even though you should definitely do so in your real-world
applications.

Listing 3 shows the code for the database.php file, which we will use in the other PHP scripts we
create in this article.
Listing 3 Connecting to the prototype8 database (database.php)
<?php
mysql_connect('localhost', 'prototype8', 'myPassword');
mysql_select_db('prototype8');
?>
Now that we are able to connect to the database using the database.php script we need the
ability to read and write contacts from the database. To do this, we are going to create a class
called ContactManager.

The ContactManager class will contain two methods: getContacts(), which is used to retrieve a
list of all contacts in the database; and saveContact(), used to save a new contact to the
database. As mentioned when creating the database, the actual contact details we are saving
are fairly simplistic, but will hopefully still give you a good idea of the key concepts of developing
JavaScript applications.

Listing 4 shows the code used to create the ContactManager class. Firstly, I have defined a
constant calledTABLE which holds the name of the database table we use to hold contact data.

The getContacts() method retrieves all contacts order by their name then writes them to an
array called$contacts which is returned. If no contacts are found then an empty array is
returned.

The saveContact() method accepts the name, email and location as arguments and inserts them
into the database. The mysql_real_escape_string() method is used to prevent SQL injection
from occurring.

Note: When developing PHP code you should always ensure that
the register_globals andmagic_quotes_gpc settings are disabled.
Listing 4 The ContactManager class (ContactManager.php)
<?php
class ContactManager
{
const TABLE = 'contacts';

public function getContacts()


{
$query = sprintf('select * from %s order by lower(name)', self::TABLE);
$result = mysql_query($query);

$contacts = array();

while ($row = mysql_fetch_assoc($result)) {


$contacts[] = $row;
}

return $contacts;
}

public function saveContact($name, $email, $location)


{
$query = sprintf("insert into %s (name, email, location) values ('%s', '%s',
'%s')",
self::TABLE,
mysql_real_escape_string($name),
mysql_real_escape_string($email),
mysql_real_escape_string($location));
mysql_query($query);

$id = mysql_insert_id();

return array(
'contact_id' => mysql_insert_id(),
'name' => $name,
'email' => $email,
'location' => $location
);
}
}
?>

The saveContact() method returns the details that have been inserted into the database, as well
as thecontact_id value assigned to the new record in the database. We will be returning this
data to an Ajax request as JSON data.

Ajax Request Handler to Return Contacts

The next step is to write a PHP script that returns a list of the existing contacts in JSON format.
This script will be requested by the JavaScript code we write later in the article.

Essentially, all this script needs to do is connect to the database, retrieve a list of contacts using
the thegetContacts() method of ContactManager, then sent it back as JSON data. The code used
to achieve this is shown in Listing 5.

If you recall from the fifth article in this series, to send JSON data you send
the application/json content-type header, then using json_encode() to convert a PHP variable
to JSON data.
Listing 5 Sending a list of contacts as JSON data (contacts.php)
<?php
require_once('database.php');
require_once('ContactManager.php');

$cm = new ContactManager();

$json = $cm->getContacts();

header('Content-type: application/json');
echo json_encode($json);
?>

In the fifth article in this series I also showed you how to detect if a HTTP request occurred using
Ajax or using a "normal" request. You may wish to include such a check in this file just in case a
user tries to access the file directly from in their browser.
Ajax Request Handler to Save a Contact

The final server-side script to create at this point is the one used to save a new contact based on
submitted form data. This script reads the data submitted (via the "post" HTTP method) and
calls the saveContact()method of the ContactManager class to save it to the database.

Listing 6 shows the code used to achieve this, which includes comments. It begins by connecting
to the database (by including the database.php file), then initialize default values for the new
contact. The corresponding values are then read from the form data (that is, the $_POST variable
in PHP). Note that we also sanitize the value by calling trim() and strip_tags() on the value.

Next we loop over the values to check that each value has been submitted. If a value is not
specified an error message is written to the $errors array.

Finally, we check if any errors have occurred, and if they haven't the saveContact() method is
called. If you recall from Listing 4 the saved contact is returned from the method call, which we
then use to include in the JSON response.
Listing 6 Processing the submitted form data for a new contact (create.php)
<?php
require_once('database.php');
require_once('ContactManager.php');

$cm = new ContactManager();

// initialize the values


$values = array(
'name' => '',
'email' => '',
'location' => ''
);

// loop over the fields and retrieve them from the form data.
// also, sanitize the data so malicious data is not saved
foreach ($values as $field => $value) {
if (!array_key_exists($field, $_POST))
continue;

$values[$field] = trim(strip_tags($_POST[$field]));
}

// if an error occurs it will be stored in this array


$errors = array();

// loop over all values and ensure they have been specified
// this is fairly rudimentary data validation
foreach ($values as $field => $value) {
if (strlen($value) == 0)
$errors[] = 'This field is required';
}
// create the JSON return data
$json = array('errors' => $errors);

// if no errors occurred, save the contact


if (count($errors) == 0) {
$contact = $cm->saveContact($values['name'],
$values['email'],
$values['location']);

// write the new contact to the return JSON data


$json['contact'] = $contact;
}

// send the JSON response


header('Content-type: application/json');
echo json_encode($json);
?>

The beauty of this code is that it will still work even if the user does not have JavaScript enabled
in their browser. Technically you should then use a different response rather than sending back
JSON data (since browsers don't have a way to represent this), but this is a fairly trivial change
and checking if a request occurred via Ajax is discussed in the previous section and in the fifth
article in this series.

Creating the Primary Web Page

Now that we have all of the server-side code in place, it's time to build the client-side aspect of
this site. As mentioned near the start of the article, the application consists of a single HTML file
with a series of JavaScript files.

As I showed you in the previous article in this series, the goal when developing web applications
that use JavaScript is to try and include none of the JavaScript code in your HTML file. Rather,
you should use external JavaScript files.

Listing 7 shows the code for the index.html file, which is the file that users of the contact
manager load in their web browser. Note that for now the file is trying to load JavaScript files
that we have not yet created.
Listing 7 The HTML file used to load the application (index.html)
<html>
<head>
<title>Prototype Address Book</title>
<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/Application.js"></script>
<script type="text/javascript" src="/js/AddressBook/Creator.js"></script>
<script type="text/javascript" src="/js/AddressBook/Contacts.js"></script>

<link rel="stylesheet" type="text/css" href="styles.css" />


</head>
<body>
<div>
<h1>Prototype Address Book</h1>

<div id="creator">
<form method="post" action="create.php">
<dl>
<dt>Name:</dt>
<dd><input type="text" name="name" /></dd>
<dt>Email:</dt>
<dd><input type="text" name="email" /></dd>
<dt>Location:</dt>
<dd><input type="text" name="location" /></dd>
</dl>

<div>
<input type="submit" value="Create Contact" />
</div>
</form>
</div>

<h2>Contacts</h2>

<div id="contacts"></div>
</div>
</body>
</html>

The two key elements of this file are the form used to create a new contact (the #creator div),
and #contactsdiv, used to hold all existing contacts.

When we implement the JavaScript code for this application we will read the values from the
form and submit them using Ajax, however the beauty of this code is that if the user's browser
does not support JavaScript then the form will be submitted normally. Even when using Ajax we
will read the form action and method from the HTML code.
Note: We are going to populate the #contacts div with contacts that are retrieved using Ajax. If
you really want your application to be accessible to users who do not have JavaScript, you would
realistically want to pre-populate this div when the page is loaded rather than using JavaScript. I
have chosen not to do so in this example since the point of the article is to demonstrate
development of JavaScript code.
The other file referenced in this HTML is the styles.css file. There's nothing overly complex in
here, but I've still included it below in Listing 8. All this code does it format each contact in the
list of contacts – obviously you can format this further if required.
Listing 8 Styling the contact list (styles.css)
.contact { margin : 5px 0; }
.contact .name { font-weight : bold; }
.contact .email { color : #999; }
.contact .location { color : #008000; }
Starting the JavaScript Application

The next step is to implement the Application.js file, used to bootstrap the application. That is,
it is responsible for instantiating the contact manager JavaScript classes. The technique used
here of creating an object in which to hold useful application functions is shoed in the previous
article in this series.

Listing 9 shows the code for Application.js, which I assume you store in the /js directory on
your web server.
Listing 9 The application JavaScript bootstrap file (Application.js)
var Application = {

startup : function()
{
new AddressBook_Creator('creator');
new AddressBook_Contacts('contacts');
}
};

Event.observe(window, 'load', Application.startup);

Note that if you load the index.html file (from Listing 8) in your web browser now an error will
occur since theAddressBook_Creator and AddressBook_Contacts classes have not yet been
defined.

Saving a New Contact Using Ajax

Now we will define the AddressBook_Creator class, the purpose of which is to send the details of
a new contact to the server using Ajax so that it can be saved to the database.

This class works by observing when the form created in Listing 8 is submitted and retrieving the
values and submitting them using Ajax. Additionally, we must prevent the browser from
submitting the form as normal. This is achieved using the Event.stop() method.

Listing 10 shows the code for the Creator.js, which should be saved to
the /js/AddressBook directory.
Listing 10 Creating a new contact using Ajax (Creator.js)
var AddressBook_Creator = Class.create({

initialize : function(container)
{
this.container = $(container);
this.form = this.container.down('form');

this.form.observe('submit', this._onFormSubmit.bindAsEventListener(this))
},

_onFormSubmit : function(e)
{
// stop the browser from submitting the form normally
Event.stop(e);
// create request options
var options = {
method : this.form.readAttribute('method'),
parameters : this.form.serialize(true),
onSuccess : this._onSaveSuccess.bind(this)
}

// clear the form values so it can be re-used


this.form.reset();

// initiate the ajax request


new Ajax.Request(this.form.readAttribute('action'), options);
},

_onSaveSuccess : function(transport)
{
var json = transport.responseJSON;
var errors = $A(json.errors);

if (errors.size() > 0)
alert('Error adding contact');
else if (json.contact) {
// this condition means the contact was added, so trigger the custom event
document.fire('contacts:added', { contact : transport.responseJSON.contact
});
}
}
});

The concepts covered in this code have all been covered in previous articles in this series,
however perhaps the most interesting part of this code is the case when a new contact is added.
In this situation, we trigger a custom event called contacts:added (remember that in Prototype
you must namespace your custom events with a colon). When firing a custom event you can
also include memo data as the second argument to fire(), as done in this code with the new
contact.

In the contact manager class (covered in this next section) we will observe this event so we can
add any newly created contacts to the list without the page needing to be reloaded.

The other thing this code does is to read the form method and action straight from the HTML,
allowing it to use the same handler script for the form as if Ajax wasn't used. This means that if
you want to change the script that handles the form you don't have to change the JavaScript
code.

Retrieving and Displaying the List of Contacts

Finally we must implement the AddressBook_Contacts, which is used to load the existing contacts
using Ajax and populate them on the index.html page (in the #contacts div).

Listing 11 shows the code for this class, which we store in the Contacts.js file in
the /js/AddressBookdirectory. We begin the class by defining a basic HTML template that is used
to format each contact. We use the Prototype Template class to do so (as covered in the previous
article in this series). Next we observe the custom event (contacts:added) then load the existing
contact.
Listing 11 Loading the existing contacts and displaying new contacts (Contacts.js)
var AddressBook_Contacts = Class.create({

// define the template used to display each contact


template : new Template(
'<div class="contact">'
+ ' <span class="name">#{name}</span>'
+ ' <span class="email">(#{email})</span>'
+ ' <div class="location">'
+ ' #{location}'
+ ' </div>'
+ '</div>'
),

initialize : function(container)
{
this.container = $(container);

// observe the custom event so we know when a new contact is created


Event.observe(document, 'contacts:added',
this._onContactAdded.bindAsEventListener(this));

// load existing contacts


this.load();
},

load : function()
{
var options = {
method : 'get',
onSuccess : this._onLoadSuccess.bind(this)
};

// submit request to retrieve contacts


new Ajax.Request('contacts.php', options);
},

_onLoadSuccess : function(transport)
{
var json = transport.responseJSON;

// clear the contacts container


this.container.update();

// now populate the container with each contact


json.each(function(contact) {
this.container.insert(this.template.evaluate(contact));
}, this);
},

_onContactAdded : function(e)
{
this.container.insert(this.template.evaluate(e.memo.contact));
}
});

Now that all the files have been created you should be able to load the index.html file in your
browser and create contacts. When you create a new contact it should be submitted via Ajax
then added to the list of contacts without reloading the page.

If you find that your code isn't work as intended, I highly recommend you make use of the
Firebug plug-in for Firefox. It allows you to easily debug your JavaScript as well to see any HTTP
sub-requests that are performed (that is, Ajax requests), including request and response
headers and the response body.

Summary

In this article we have created a simple contacts manager, which allows you to create contacts
using a HTML form. The contacts are submitted via Ajax to the server which then processes the
form values and saves the new contact to the database.

Additionally, the contacts manager retrieves a list of existing contacts using Ajax and displays
them on the page. When a new contact is added we use custom events so it can be added to the
list of contacts.

While the application isn't overly useful as it currently is (since it lacks several key features such
as being able to edit and delete contacts), it does demonstrate the general thought process and
structure of a larger application.

This now concludes the Eight Weeks of Prototype series, which I hope has not only helped you
learn the key aspects of the Prototype JavaScript framework, but also given you further ideas of
how to develop JavaScript in general.

Das könnte Ihnen auch gefallen