Tutorial: How To Use Prototype.js On Your Site

prototype.js is an amazing JavaScript framework that I have been using for some years now. This tutorial shows you how to work with prototype.js and what some best practices are. It won’t go into details on every available method but it will give you a quick introduction.

I apologize for the coding style but the WordPress plugin seems to eat spaces and tabs.

Obtaining Prototype.js

You can download the latest version of the prototype library from the download page and include this on your page like this if you have saved it as prototype.js in your document root directory:

1
2
3
4
5
6
7
8
9
<html>

<head>

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

...

</html>

There are also some packaged and minimized versions of the file available because the file itself is quite large.

Using Google To Load Prototype.js

Another option is using Google to include the Prototype JavaScript Library on your page thus offloading traffic from your server to Google. You can use Google’s AJAX Libraries API for that purpose. If you want to use Google which has the benefit of distributed servers around the world therefore likely speeding up the download this is how it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script src="http://www.google.com/jsapi"></script>

google.load("prototype", "1.6.0.3");

google.setOnLoadCallback(function() {

// What to do once the file has been loaded

....

}

</script>

Throughout this tutorial I am assuming that you are loading the library without using Google as this makes initializing your own code more complicated.

JavaScript Coding With Prototype.js

After you have included the prototype.js library it’s time to start implementing your JavaScript code. With prototype.js I have come to use classes exclusively because this allows for a object-oriented architecture of your JavaScript code and encapsulates the code.

Create a new file – for demonstration purposes in this tutorial named test.js – and save it in your document root (or in any other directory like /js, just make sure to change the path accordingly in the <script> tag). Now add the following code to it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Create the class

var myTestClass = Class.create({

initialize:function()

{

// Is called when the page has finished loading by the Event.observe code below

alert('Works!');

}

});

// Global variable for the instance of the class

var myTest;

// Creating an instance of the class if the page has finished loading

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

myTest = new myTestClass();

});

Now you need to create an HTML file named test.html containing the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<html>

<head>

<title>prototype.js Test</title>

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

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

</head>

<body>

<h1>prototype.js Test</h1>

<div id="container" style="display:none"></div>

<ul id="list">

<li>Item 1</li>

<li class="highlight">Item 2</li>

<li>Item 3</li>

<li>Item 4</li>

</ul>

<a href="http://www.dzone.com/" target="_blank" id="demoLink">Click me!</a>

</body>

</html>

If you then load this file in your browser a JavaScript alert box will pop up saying “Works”.

By creating an event hook for the window’s load event handler you can be sure that every element on the page has been loaded. If you are not using this code and leave out the Event.observe code in the test.js file it may happen that some elements on your page may not yet have loaded so the user may receive a JavaScript error message if you are trying to access elements on the page that do not yet exist.

You can also use ‘dom:loaded‘ instead on ‘load‘ in the Event.observe call which will be executed once the DOM tree of the HTML document has been fully created. This means that individual elements may not have been loaded (e.g. an image) but you can already access all of the elements on the page.

The Constructor

Whenever a new instance of a class is created the initialize() method of the class is called automatically (just like PHP’s __construct() method). You can of course define an arbitrary number of parameters as well. Just modify the line to see how it works:

1
2
3
4
5
6
7
8
9
initialize:function(url)

{

// Is called when the page has finished loading by the Event.observe code below

alert('Works @ '+url);

}

and

1
2
3
4
5
Event.observe(window, 'load', function() {

myTest = new myTestClass(location.href);

});

Methods

Defining methods within the class is very easy. In contrast to other programming languages you cannot declare specific methods (functions) as private, protected or public thus they can be accessed from the outside anyways even if you do not desire it.

Let’s create a new method and modify the class so that it looks like in this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var myTestClass = Class.create({

initialize:function(url)

{

// Is called when the page has finished loading by the Event.observe code below

alert('Works @ ' + this.toUppercase(url));

},

toUppercase:function(str)

{

return str.toUpperCase();

}

});

Please note the specific syntax for creating new methods. Every method must be separated by the one before with a comma and the last method in the class must not be followed by a comma (Internet Explorer does not like this).

You can simply call methods by prefixing them with a “this.” from within the class.

Accessing Elements

The most often used function from my experience is the $() function which allows you to access any element that has an id. The HTML code contains a link to dzone.com which has an id. To access that element you simply need to write

1
$('demoLink')

No need to write the long document.getElementById(‘demoLink’) anymore. Here is an example you can add to the initialize() method at the very end:

1
alert($('demoLink').getAttribute('href'));

This will show an alert box with http://www.dzone.com in it. Likewise you can set specific attributes easily:

1
2
3
$('demoLink').setAttribute('href', 'http://www.saschakimmel.com/');

alert($('demoLink').readAttribute('href'));

This will change the href attribute pointing to this blog’s homepage.

Now you may feel tempted to give any element that you may wish to access a specific id attribute. But wait – that’s not required! The Prototype library contains the $$() method which allows you to use CSS3 selectors (yes, even if the browser does not yet support it). So just as you might do with CSS you can do with prototype.js:

1
$$('#list li.highlight')[0].update('I was Item 2');

The $$() method always returns an array. Because our code only contains a single LI element with the given class “highlight” in an unordered list with the id “list” this code works.

You could even use something like this to achieve the same result:

1
$('list').down('li.highlight').update('I was Item 2');

The Element.up() and Element.down() methods return only a single element which is the first element found from the element given and also support CSS3 selectors.

The Element.select() method is similar to $$() but whereas the $$() method searches globally the select method searches from the given element within the DOM tree so you could also have used this code as well:

1
$$('ul')[0].select('.highlight')[0].update('I was Item 2');

This code may look a bit complex at the beginning but if you start using the library you will quickly recognize the different methods. I just urge you not to give every single element an id so that you can access it by using the $() method. You should use ids on semantic blocks like #topbar, #navigation and so on and use classes within the code wherever possible.

This way you are not blowing up your code by only using ids for accessing elements with JavaScript. It’s just not necessary if you’re using the prototype.js library.

Add more semantics to your code.

Updating And Modifying Elements And Classes

As you have already seen in the examples above you can use the Element.update() method to update element contents. If you wish to modify attributes you should use the Element.setAttribute() method (which is a native browser method).

You can also add, edit and remove class names at runtime easily.

To add a CSS class use the Element.addClassName() method:

1
$('demoLink').addClassName('democlass');

To find out whether a specific element has a specific class use Element.hasClassName():

1
2
3
4
5
6
7
$('demoLink').addClassName('democlass');

if ($('demoLink').hasClassName('democlass')) {

alert('Yikes!');

}

Loops

Forget those annoying for loops you have learned when using JavaScript. Prototype has its own methods that ease the implementation of loops. There are some pitfalls however which I am going to explain here.

We now want to loop over the <li> elements of the list and wrap a <strong> around the current contents of each <li> element. This is how it works:

1
2
3
4
5
$$('#list li').each(function(el) {

el.update('<strong>'+el.innerHTML+'</strong>');

});

In fact this is a method that is a bit dirty because we are using HTML code as a string in the update() method. This is a cleaner method but it’s very long in contrast to the method before:

1
2
3
4
5
6
7
8
9
10
11
12
13
$$('#list li').each(function(el) {

var oldContent = el.innerHTML;

var strongEl   = new Element('strong');

el.update('');

el.appendChild(strongEl);

strongEl.update(oldContent);

});

Most of the time I’m sticking with the shorter method above.

Using bind()

Now let’s call the toUppercase method we created in our class. Because the each() method has been implemented by prototype.js using the this keyword within the each loop would reference the each method itself so this will throw the error “this.toUppercase is not a function”:

1
2
3
4
5
$$('#list li').each(function(el) {

el.update('<strong>'+this.toUppercase(el.innerHTML)+'</strong>');

});

So in this case we need to bind the this keyword to our class. This can be achieved by using the bind() method:

1
2
3
4
5
$$('#list li').each(function(el) {

el.update('<strong>'+this.toUppercase(el.innerHTML)+'</strong>');

}.bind(this));

Now our code works! Whenever you are using specific prototype.js methods where you can use a this inside the method you need to bind it to your class in that way.

Event Handlers

Remember this code?

1
<a href="..." onclick="doThis()">Click here</a>

Or even this code?

1
<a href="<a href="javascript:doThis">javascript:doThis</a>()">Click here</a>

You don’t need this with prototype.js anymore as you can use the unobtrusive event handlers it provides. If a link is only useful and functional with JavaScript enabled only you should add a CSS style of “display:none” to the element and make it visible in the initialize() method.

Now let’s add an event listener to the link:

1
$('demoLink').observe('click', function() { alert('This link has been disabled!'); });

If you reload the page and click on the link you will see an alert box and if you click on the OK button the link will be opened in a new window/tab anyways. To prevent this you need to stop the event. Just modify the code to look like this:

1
$('demoLink').observe('click', function(e) { alert('This link has been disabled!'); Event.stop(e); });

As you see now the link is no longer opened. However using anonymous functions inline is not a good programming style so let’s create an additional method in our class:

1
2
3
4
5
6
7
8
9
showDisabledMessage:function(e)

{

Event.stop(e);

alert('This link has been disabled!');

},

You also need to modify the Event observer:

1
$('demoLink').observe('click', this.showDisabledMessage.bindAsEventListener(this));

Just as you did before with the call to the bind() method when using the each method when using an event listener you need to use the bindAsEventListener() method to bind the function to your class.

Here is some more code for you to see some more of what is possible with event listeners:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$('demoLink').observe('mouseover', function(e) { Event.element(e).setStyle({

'color': 'red',

'fontSize':'30px'

}); });

$('demoLink').observe('mouseout', function(e) { Event.element(e).setStyle({

'color': 'blue',

'fontSize':'12px'

}); });

I won’t go into the details on these methods so please have a look at the documentation.

AJAX

I know I have to show some demo code for AJAX usage when writing an article on the Prototype.js JavaScript framework so here we go. Create an HTML file named ajax.html and add the following code to it:

1
2
3
4
5
<div style="border:5px solid red;padding:15px;">

<h1>Loaded via AJAX!</h1>

</div>

We now want to update the <div> with the id “container” with this content after the ajax.html file has been loaded via AJAX after clicking on the link. Let’s clean up the code of the initialize() method:

1
2
3
4
5
6
7
initialize:function(url)

{

$('demoLink').observe('click', this.loadContent.bindAsEventListener(this));

},

Now add this method to the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loadContent:function(e)

{

Event.stop(e);

new Ajax.Updater('container', 'ajax.html', {

onComplete:function() {

$('container').show();

}

});

},

This method stops the event (i.e. it won’t open the dzone.com homepage in a new window/tab), sends an AJAX request to the ajax.html file, updates the div with the id “container” (the first parameter) and when the request has finished makes the container visible with the show method as it has an inline style reading “display:none”.

As you see you don’t even need any dynamic code on the server to use AJAX.

In this article I have only scratched the surface of what prototype.js has to offer. In encourage you to read the documentation and just play around with the methods. Prototype.js has so much in store it’s really fun developing. As you may have noticed in the article I am using unobtrusive JavaScript which means that there is not a single line of JavaScript code in the HTML file (except the two <script> elements of course).

Download the source code

Be Sociable, Share!
Sascha Kimmel - Living The Web Experience Since 1996 by tricosmedia