Prototype

  • December 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Prototype as PDF for free.

More details

  • Words: 27,770
  • Pages: 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)



Loading Prototype and viewing the loaded version <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> alert('Prototype ' + Prototype.Version + ' loaded');

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)

Selecting an element with $() and updating its contents <script type="text/javascript" src="/js/prototype.js">
<script type="text/javascript"> $('exampleDiv').update('Div content updated!');

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)

Ensuring the element was successfully returned before using it <script type="text/javascript" src="/js/prototype.js">
<script type="text/javascript"> var elt = $('exampleDiv'); if (elt) elt.update('Div content updated!');

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)

Using $() to extend function arguments <script type="text/javascript" src="/js/prototype.js">
<script type="text/javascript"> function myFunction(elt, message) { elt = $(elt); elt.update(message); } myFunction('exampleDiv', 'Div updated'); myFunction($('exampleDiv'), 'Div updated again');

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)

Selecting a series of elements using $$() and looping over them <script type="text/javascript" src="/js/prototype.js">
<script type="text/javascript"> var items = $$('#someElement li'); for (var i = 0; i < items.size(); i++) { items[i].update('Element ' + i); }

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)

Using select() to search for elements within a given element <script type="text/javascript" src="/js/prototype.js">
<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); }

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.
), you can useimgElt.up('table') to retrieve the table element (in this case

using just imgElt.up() might return the 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)

Examples of using up() to find an element's ancestors <script type="text/javascript" src="/js/prototype.js">


<script type="text/javascript"> var img = $('someImage'); // these var link var link var link

are all equivalent ways of retrieving the link: = img.up(); = img.up(0); = 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);

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)

Selecting an element's descendants using down() <script type="text/javascript" src="/js/prototype.js">
Second cell
<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');

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)

Using next() and previous() to find sibling elements <script type="text/javascript" src="/js/prototype.js">

  • First item
  • Second item
  • Third item
  • Fourth item
  • Fifth item
<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();

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 ). 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)

Creating a new element in the DOM <script type="text/javascript" src="/js/prototype.js">
Some item
<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);

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)



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)

Visit this web sitea>
Some item


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)

Visit this web sitea>
Some item


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)

Some item
Visit this web sitea>

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)

Creating a new element in the DOM <script type="text/javascript" src="/js/prototype.js">
Some item


<script type="text/javascript"> $('main').insert('Visit this web site');

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)

Removing an element from the DOM using remove() <script type="text/javascript" src="/js/prototype.js">
Some item
<script type="text/javascript"> $('main').remove();

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)

Clearing the content of an element using update() <script type="text/javascript" src="/js/prototype.js">
This content will soon be gone!
<script type="text/javascript"> $('foo').update();



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)

Updating an element to display a string <script type="text/javascript" src="/js/prototype.js">
You won't see this content
<script type="text/javascript"> $('foo').update('You will see this content');

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)

Updating an element with a HTML string <script type="text/javascript" src="/js/prototype.js">
You won't see this content
<script type="text/javascript"> $('foo').update('You will see this hyperlink');

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)

Passing an element to update()

<script type="text/javascript" src="/js/prototype.js">
You won't see this content
<script type="text/javascript"> var link = new Element('a', { href : 'http://www.example.com' }); link.update('This is the link text'); $('foo').update(link);

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)

Reading the inner HTML content of an element <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> alert($('foo').innerHTML);

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 My Link, 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)

Reading a hyperlink's URL with readAttribute() <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> var link = $('foo').down('a'); alert(link.readAttribute('href'));

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)

Using writeAttribute() to change the URL of a hyperlink <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> var link = $('foo').down('a'); link.writeAttribute('href', 'http://www.phpriot.com'); link.update('Visit PhpRiot');



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)

Changing a URL's target while not using non-standard attributes <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> var link = $('foo').down('a'); link.writeAttribute('target', '_blank');

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 autocompletion on a particular input. One way to disable auto-completion is to use autocomplete="off" in your HTML source (that is ). 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)

Disabling auto-completion by using writeAttribute() <script type="text/javascript" src="/js/prototype.js">


<script type="text/javascript"> var input = $('foo').down('input'); input.writeAttribute('autocomplete', 'off');

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)

Using CSS to change the hover style of an element <style type="text/css"> #foo { background : #f00; color : #fff; } #foo:hover { background : #00f; }
Highlight me!


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)

Using Prototype to change the hover style of an element <script type="text/javascript" src="/js/prototype.js"> <style type="text/css"> #foo { background : #f00; color : #fff; } #foo.hover { background : #00f; }
Highlight me!
<script type="text/javascript"> var elt = $('foo'); elt.observe('mouseover', function(e) { this.addClassName('hover'); }); elt.observe('mouseout', function(e) { this.removeClassName('hover'); });

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)

Using Prototype to check if an element has a class <script type="text/javascript" src="/js/prototype.js"> <style type="text/css"> #foo { background : #f00; color : #fff; } #foo.hover { background : #00f; }
Highlight me!
<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 }

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)

Setting an element's styles with setStyle() <script type="text/javascript" src="/js/prototype.js">
Change this content
<script type="text/javascript"> var elt = $('foo'); var styles = { size fontWeight background padding }

: : : :

'18px', 'bold', '#f00', '10px'

elt.setStyle(styles);

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)

Reading an element's font family with getStyle() <script type="text/javascript" src="/js/prototype.js">
Change this content
<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

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)

Determining an element's size with getDimensions() <script type="text/javascript" src="/js/prototype.js"> <style type="text/css"> html, body { padding : 0; margin : 0; } #foo { width : 300px; border : 1px solid #000; padding : 10px; background : #eee; text-align : center; }
This box will become centered
<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);

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)

Showing and hiding elements with hide(), show() and toggle() <script type="text/javascript" src="/js/prototype.js">
This box will start visible but become hidden
This box will be visible, then become hidden, the appear again
<script type="text/javascript"> $('a').hide(); $('b').show(); var c = $('c'); c.toggle(); c.toggle();

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)

Two different ways of applying string methods <script type="text/javascript" src="/js/prototype.js"> <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'

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)

Using the blank(), empty(), include() and endsWith() methods <script type="text/javascript" src="/js/prototype.js"> <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);

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, 'backgroundcolor'.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 <. 

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)

Creating hex colour codes with toColorPart()

<script type="text/javascript" src="/js/prototype.js">
This div will change colour!
<script var var var

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

var hex = '#' + r.toColorPart() + g.toColorPart() + b.toColorPart(); // hex will have the value #ff6400 $('foo').setStyle({ backgroundColor : hex });

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)

Basic usage of each() on an array <script type="text/javascript" src="/js/prototype.js">
  • Element 1
  • Element 2
  • Element 3
  • Element 4
<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);

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)

Breaking and continuing each() loops <script type="text/javascript" src="/js/prototype.js">
  • Element 1
  • Element 2
  • Element 3
  • Element 4
  • Element 5
  • Element 6
<script type="text/javascript"> function doSomething(item, i) { var output = $('output'); if (i == 2) return; // "continue" output.update(output.innerHTML + '
' + item.innerHTML); if (i == 3) throw new $break; } $$('#foo li').each(doSomething);

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)

Using arrays and invoke to create hex colour codes <script type="text/javascript" src="/js/prototype.js">
This div will change colour!
<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);

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)

Creating a hash <script type="text/javascript" src="/js/prototype.js"> <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'));

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)

Looping over hash values <script type="text/javascript" src="/js/prototype.js"> <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); });

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)

Demonstrating event handling ambiguity <script type="text/javascript"> window.onload = function() { alert('Message 1'); }



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)

Two different ways of observing an event on an element <script type="text/javascript" src="/js/prototype.js">
This is the element to observe


<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 : '' }); });

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 orwindow.onload, we use Prototype's observe() method. Listing 3 Checking for page load completion (listing-3.html)

Checking for page load completion <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> Event.observe(window, 'load', function() { alert('Page has loaded!'); });

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)

Two different ways of defining event handlers <script type="text/javascript" src="/js/prototype.js">
Click me!
<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);

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)

Demonstrating how the event object is passed to handlers <script type="text/javascript" src="/js/prototype.js">
Click me!
<script type="text/javascript"> function handleFooClick(e) { // you can now do something with e, // such as call Event.element(e) } $('foo').observe('click', handleFooClick);



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 .... 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)

Stopping an event with Event.stop() <script type="text/javascript" src="/js/prototype.js"> <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);

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)

Validating a form before deciding whether it should be submitted <script type="text/javascript" src="/js/prototype.js">
<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);

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)

Checking for stopped events <script type="text/javascript" src="/js/prototype.js">
Click here
<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);

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)

Retrieving an event's element with Event.element() <script type="text/javascript" src="/js/prototype.js">
  • Click Me
  • Click Me
  • Click Me
  • Click Me
  • Click Me
  • Click Me


  • Click Me
<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); });

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
    element rather than the
  • 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
      element. This is shown in Listing 10. Listing 10 Finding an event element's ancestor with Event.findElement() (listing-10.html)

      Finding an event element's ancestor with findElement() <script type="text/javascript" src="/js/prototype.js">
      • Click Me
      • Click Me
      • Click Me
      <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); });



      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)

      Combining Event.element() with up() <script type="text/javascript" src="/js/prototype.js">
      • Click Me
      • Click Me
      • Click Me
      <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); });

      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)

      Determining which key was pressed <script type="text/javascript" src="/js/prototype.js">
      <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);

      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)

      Reading the X and Y coordinates of the mouse <script type="text/javascript" src="/js/prototype.js">
      Move the mouse over me
      <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);



      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)

      Stopping the observation of an event using stopObserving() <script type="text/javascript" src="/js/prototype.js">
      Click me
      <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);

      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)

      Using custom events <script type="text/javascript" src="/js/prototype.js"> <style type="text/css"> #status { border : 1px solid #000; padding : 10px; }

      Items

      • Item 1
      • Item 2
      • Item 3
      • Item 4

      Status

      <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('
      Selected: ' + e.memo.name + '
      '); } $$('#myList li').each(function(item) { item.observe('click', onItemClicked); }); document.observe('list:item_selected', onCustomEvent);

      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 realtime (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)

      A basic Ajax sub-request <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript"> var options = { method : 'post' } new Ajax.Request('/path/to/file', options);

      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)

      Here is some HTML content, with any element you would normally use, such as <strong>bold and a link.



      Listing 3 Populating an element using Ajax.Updater (listing-3.html)

      Populating an element using Ajax.Updater <script type="text/javascript" src="/js/prototype.js">
      <script type="text/javascript"> function onButtonClick(e) { var button = Event.element(e); new Ajax.Updater(button.up(), 'listing-02.html'); } $('myButton').observe('click', onButtonClick);

      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 subrequests 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)

      Populating an element using the responseText property <script type="text/javascript" src="/js/prototype.js">


      <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);

      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)

      '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)

      '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)

      Reading JSON data from the server <script type="text/javascript" src="/js/prototype.js">
      <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);

      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)

      '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)

      Creating a Basic JavaScript Class <script type="text/javascript" src="/js/prototype.js"> <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());

      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)

      Loading and instantiating the Person class

      <script <script <script var

      type="text/javascript" src="/js/prototype.js"> type="text/javascript" src="/js/Person.js">

      type="text/javascript"> me = new Person();

      me.setName('Quentin Zervaas'); alert(me.getName());

      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)

      Loading and instantiating the Person class <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript" src="/js/Person.js"> <script type="text/javascript"> var me = new Person('Quentin Zervaas'); alert(me.getName());

      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)

      Loading and instantiating the Person and Person_Australian classes <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript" src="/js/Person.js"> <script type="text/javascript" src="/js/Person/Australian.js"> <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"



      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)

      Demonstrating the drawback of not using function binding <script type="text/javascript" src="/js/prototype.js">
      <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');

      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)

      Binding variable with bindAsEventListener <script type="text/javascript" src="/js/prototype.js">
      <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');

      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)

      '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)

      Using bind() when handling Ajax requests <script type="text/javascript" src="/js/prototype.js">

      <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');

      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)

      The problem with using each() in classes <script type="text/javascript" src="/js/prototype.js">
      <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 + '
      '); } }); new MyClass('container');

      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)

      Binding enumerable function handlers <script type="text/javascript" src="/js/prototype.js">


      <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 + '
      '); } }); new MyClass('container');

      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)

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

      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 ''; }.

      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)

      Creating a custom enumerable object <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript" src="/js/Person.js">
      <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 + '
      '); });

      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)

      Basic usage of the Template class <script type="text/javascript" src="/js/prototype.js">
      <script type="text/javascript"> var tpl = new Template('#{name} is from #{country}'); var person = { name : 'Quentin', country : 'Australia' }; $('foo').update(tpl.evaluate(person));

      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)

      '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)

      Combining an Ajax request with the Template class <script type="text/javascript" src="/js/prototype.js">
      <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} is from <em>#{country}'); var content = tpl.evaluate(transport.responseJSON); this.container.update(content); } }); new PersonLoader('foo');

      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)

      Different ways to access form values <script type="text/javascript" src="/js/prototype.js">
      <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)); });

      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)

      Creating a hash and reading and writing values <script type="text/javascript" src="/js/prototype.js"> <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());

      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)

      Generating an array of values with $R() and $A() <script type="text/javascript" src="/js/prototype.js">
      <script type="text/javascript"> var range = $R(1, 12); var arr = $A(range); // $A($R(1, 12)) for short $('foo').update(arr.join(', '));

      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)

      Using $w() for bulk element operations <script type="text/javascript" src="/js/prototype.js">
      Showing
      Hide Me
      I want to disappear!
      <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);

      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} is from <em>#{country}'); 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)

      Referencing JavaScript from external files <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript" src="/js/PersonLoader.js"> <script type="text/javascript" src="/js/Application.js">


      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 name varchar(255) email varchar(255) location varchar(255)

      not not not not

      null, null, null, 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)



      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)


      mysql_query($query); $id = mysql_insert_id(); return array( 'contact_id' 'name' 'email' 'location' );

      => => => =>

      mysql_insert_id(), $name, $email, $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)

      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)

      'email' => 'location' => );

      values '', '', ''

      // 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)

      Prototype Address Book <script type="text/javascript" src="/js/prototype.js"> <script type="text/javascript" src="/js/Application.js"> <script type="text/javascript" src="/js/AddressBook/Creator.js"> <script type="text/javascript" src="/js/AddressBook/Contacts.js">

      Prototype Address Book

      Name:
      Email:
      Location:

      Contacts



      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 .contact .contact .contact

      { margin : 5px 0; } .name { font-weight : bold; } .email { color : #999; } .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( '
      ' + ' <span class="name">#{name}' + ' <span class="email">(#{email})' + '
      ' + ' #{location}' + '
      ' + '
      ' ), 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.

Related Documents

Prototype
December 2019 63
Prototype
November 2019 57
Prototype 2
August 2019 24
Soluzione Prototype
May 2020 10
Prototype Jquery
July 2020 11
Prototype Model
May 2020 15