Introduction

JavaScript’s increasing popularity throughout the web makes it more important than ever to make sure our client side code is implemented with a nice mix of stability, speed, and reusability. One of the best ways for accomplishing this is to use a simple library and syntax to use as a foundation for every project. Thankfully, Sam Stephenson created an amazing library of functions that we can rely on called Prototype.js to ease our JavaScript development practices.

After our readers pointed out in an previous feature that our frequent use of Prototype syntax was making things a bit confusing, we decided it would be best to create a quick reference page for the library to help ease the learning curve for a lot of developers out there. The following tutorial will explain the most common methods used in Particletree projects with simple examples of how to implement them. This guide is meant to be used hand-in-hand with the more exhaustive unofficial Prototype documentation by Sergio Pereira, and the script.aculo.us documentation wiki, which I highly recommend every serious JavaScript / Prototype developer read.

Getting Started

After you have downloaded the files and placed them in your preferred directory, all you need to do is include them in your html document like so:

<script src="/scripts/prototype.js" type="text/javascript"></script>

Boom. JavaScript just got 10x easier to develop. Now, let’s looks at some of the cool new weapons you just acquired.

Note - This tutorial is based off of version 1.3.1.

$() Function

The most used and convenient function, $() , provides an easy way of getting a handle on a DOM element. Normally, if you would like to access an element in the DOM, it would look like this:

node = document.getElementById("elementID");

Using $(), we can shorten it up.

node = $("elementID");

Other than being short and sweet, the $() function is also more powerful than document.getElementById() because the ability to retrieve multiple elements is built into the function.

allNodes = $("firstDiv", "secondDiv"); for(i = 0; i < allNodes.length; i++) { alert(allNodes[i].innerHTML); }

In this example, we see the $() function now returning an array of our elements, which can then be accessed with a simple for loop.

Form Helper Functions

Yes, not only are forms a pain in the ass from an HTML and CSS perspective, but also on the JavaScript side of things. Prototype.js provides useful and creative functions that make dealing with forms almost fun.

The $F() function returns the value of the form element or ID passed in. If we put together the following HTML fields:

<input type="text" id="textfield" name="textfield" /> <textarea rows="5" cols="5" id="areafield" name="areafield"></textarea> <select id="selectfield" name="selectfield"> <option value="1" selected>One</option> <option value="2">Two</option> </select> <input type="checkbox" id="checkfield" name="checkfield" value="1" checked />

We can then access the values in the form easily by using the $F() function.

$F("textfield"); // returns the value of the text input $F("areafield"); // returns the value of the textarea $F("selectfield"); // returns the selected value of the select $F("checkfield"); // returns undefined if not checked, or the value

The ability to get a value regardless of the control makes processing forms incredibly easy in most circumstances. There are only two drawbacks I could find with this function: 1) there is no easy way of accessing the selected value of a radio group (only the individual value of a single radio element) and 2) it is not possible to pass in multiple ID’s, like you can with the $() function.

*Another function, Form.getElements() will return an array of every form element, regardless of the type.

allNodes = Form.getElements("myform"); for(i = 0; i < allNodes.length; i++) { //do something to each form field }

In this example, we’re getting every element from a form with the id of myform . If you want to add an onclick effect, or a help window popup to each form field, you can loop through as shown above.

The next method we will look at is Form.serialize() . When building an Ajax request, you often need to format your own post string to pass the data. When the form is submitted, that string is built. serialize() makes the process easy.

allNodes = Form.serialize("myform");// returns field1=value1&field2=value2&field3=value3 and so on...

Building the string for us helps out, but what makes the method even nicer is that it is not biased towards the type of field. We saw before that $F() had some problems with radio groups, but serialize() processes all of the values correctly for any type of field. These are not the only form methods available, so go check out the Sergio’s documentation for the rest.

getElementsByClassName

Why getElementsByClassName() is not already built into JavaScript is beyond me, but it’s not and so Prototype had added to it to the arsenal as an extension of the document object. It behaves exactly like document.getElementsByTagName() , the only difference being that it checks for className.

allNodes = document.getElementsByClassName("red"); for(i = 0; i < allNodes.length; i++) { alert(allNodes[i].innerHTML); }

An array is returned containing all elements that match the given className. This will also work with elements that have multiple classNames, which is nice. getElementsByClassName() has become a function used in nearly every project around here, mainly to attach DOM events so I suggest every developer give it a try.

Element Helper Functions

The Element Object provides a load of helper functions (increasing with each release) that assist in common DOM manipulation practices. Some of these functions create no new ease, while others simplify 10+ lines of code into one call. Let’s take a look at some examples.

Retrieving the height of an element without the helper:

$("first").offsetHeight

And now with the helper:

Element.getHeight("first")

In this case, the helper arguably provides no benefit. Now, what if we wanted to remove a className from an element? Here is the long way (taken from the Prototype.js source code):

element = $(element); if (!element) return; var newClassName = ''; var a = element.className.split(' '); for (var i = 0; i < a.length; i++) { if (a[i] != className) { if (i > 0) newClassName += ' '; newClassName += a[i]; } } element.className = newClassName;

And now with the helper function:

Element.removeClassName("elementID", "red");

Nice, eh? Unlike the first example, most of the helper functions save a lot of time and effort by making common tasks easy. And for the sake of consistency, it may be best just to use the Element syntax throughout the project. For a full list of helper functions and how to use them, check out Sergio’s Prototype documentation.

Try.these Function

Try.these() is a great function for helping developers create code that will work regardless of the different JavaScript implementations across browsers. Instead of doing object or browser detection on your own, this function will attempt to execute one path of code until it encounters an error, and then switch to the next path.

return Try.these( function() { alert("first"); jkgjhgjhg //intentional error alert("firsterror"); return 1; }, function() { alert("second"); return 2; } );

In the example above, the first path will stop executing at the intentional error. Knowing that, it is important to be cautious with our code because everything before the error will get executed, we must be careful not to execute code twice (once in each try). Overall, Try.these() is not a function we use often around here, but it is nice to know it exists and how it functions.

Ajax Support

There is no shortage of Ajax support functions in this library, and I would like to give you a look at how we primarily create Ajax applications with the help of Prototype.js. Taken from the documentation, we can see a normal Ajax request can be made as follows:

var myAjax = new Ajax.Request( url, {method: 'post', parameters: data, onComplete: ajax_response} );

Where method is post or get, parameters is the name/value paired query string, and onComplete is the function that should be called when everything is finished. Once the core functionality is understood, it is easy to make repetitive Ajax calls by creating our own functions that utilize the library. First, a simple function to process the Ajax request.

function ajax_request(url, data) { var myAjax = new Ajax.Request( url, {method: 'post', parameters: data, onComplete: ajax_response} ); }

And after the request is finished, send it over to ajax_response() .

function ajax_response(originalRequest) { if(!bHasRedirect) { //process originalRequest.responseText; } else { bHasRedirect = false; ajax_request(originalRequest.responseText, ""); } }

After you make an Ajax request, the response is always sent to ajax-response() . From there, another Ajax request will be made if bHasRedirect is set to true (a global variable), and if not then the proper code will be executed based on a global array of functions and originalRequest.responseText() (the return value).

PeriodicalExecuter

Once the PeriodicalExecuter object is initialized, it repeatedly calls a desired function at a given interval. This comes in handy when you wish to auto update an Ajax portion of your site.

function periodicalUpdate() { new PeriodicalExecuter(refreshNews, 10); }function refreshNews() { //Ajax code to grab news and update DOM }

The PeriodicalExecuter constructor expects the function to call as its first parameter, and the time interval as its second. Don’t get confused with the time though - the common setInterval() is handled with milliseconds, but in this function we’re dealing with seconds. Also note that while this example assumes Ajax is involved, it can update the page for any reason. Prototype.js also has a Ajax.PeriodicalUpdater class that can ease the process when dealing solely with Ajax.

Additional Enhancements

While I can’t cover every single function or method that Prototype.js offers, it is still important to emphasize some of the ones not covered here (all of which can be found in the documentation).

observe - This method functions like addEvent(), and should be used to unobtrusively attach events to the DOM.

User Interaction - You can find built in globals, such as KEY_TAB to evaluate what key presses the user is making. Additionally, you can find out the coordinates of the mouse, and if it has been clicked.

Class Creation - Why stop with what Prototype.js provides? Using the same syntax and functions, we can build our own classes to keep things consistent. Adding a constructor and additional methods has never been easier. Lookup Class.create() in the documentation.

Wrap It Up

Is it acceptable to use public code/libraries when you do not know the author and do not intimately understand what happens behind the scenes? My answer is yes, as long as you thoroughly test the code and trust the person/community that is behind the development of it. In the case of Prototype.js, trust is built from two sources. First, Ruby on Rails has integrated prototype support. Since Ruby on Rails has a respectable developer base, it is likely that many bugs have been found and ironed out to make Prototype.js more stable. Second, the developer works for 37signals, who happen to employ the creator of Ruby on Rails. Not only do I trust the development practices of the company, but I trust that Prototype.js will continue to be tested and improved upon. Given that, and testing within my own projects, I confidently use this library in nearly all my projects.

Prototype.js more than doubles the functionality listed in this tutorial, and it is definitely worth checking out. If you’re scared of the file size (it’s 30k as of this writing), remember that you can always take out classes that you don’t use (minus the few that have dependencies) and/or compress your JavaScript files with PHP before serving them out to your user. Also, once you have tried a few of the methods, the rest are easy to learn, so the learning curve is very minimal. Basically, there is no excuse not to give it a try. The examples shown above are how we handle things at Particletree, and should not be used without testing. Remember, this is meant to be an introduction to Prototype.js, so always reference and give credit to the unofficial Prototype documentation and the script.aculo.us documentation wiki for all of the hard work put in to find out the various methods. As always, if you find any mistakes or possible improvements, please point them out.