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.
Quick Guide to Prototype
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.
Getting Started
After you have download 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.
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.
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 37 signals,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.
|