Creating classes with Prototype
August 27th, 2008 . by LorisIf you use Script.aculo.us or some other library written on top of Prototype, you probably have already encountered the Class object. Every time you call a new Effect.Appear() or a new Draggable() you are, in fact, instantiating a Prototype class.
Just like software developers abandoned procedural languages like C or Pascal for object oriented ones like C++ all the way up to C# and Java, the same concept applies to the web: with (web)applications growing in complexity, it’s better to keep a modular approach and have a finite number of classes instead of a million of functions.
A *very* basic example
For this example, we are going to create a new class that observes an element and adds the ‘hover’ class name to it when the mouse is over it. If you are impatient, you can see the end result on this demo page.
Let’s start by declaring the new class. The class must be declared with the var keyword. The function Class.create takes a javascript object and transforms it into a Prototype class.
var HoverObserver = Class.create({ initialize: function(element) { } });
The initialize function is very important, as it will be the constructor for our class.
Let’s add some more code:
var HoverObserver = Class.create({ initialize: function(element) { this.element = element; element.observe('mouseover', this.addClass.bindAsEventListener(this)); element.observe('mouseout', this.removeClass.bindAsEventListener(this)); }, addClass : function(event) { this.element.addClassName('hover'); }, removeClass : function(event) { this.element.removeClassName('hover'); } });
Now that our class is actually doing something, we can start using it:
document.observe('dom:loaded', function(){ new HoverObserver($('someDiv')); });
You can see that the element we are passing to the constructor is actually passed by Prototype to the initialize function.
Adding options
Another nice thing we can do is to add an options parameter to the initialize function, so that we can pass one or more additional parameters to the constructor. For example, we can add an option to specify the class name to use instead of the default ‘hover’:
var HoverObserver = Class.create({ initialize: function(element, options) { this.options = Object.extend({ hoverClass : 'hover' }, options ||{}); this.element = element; element.observe('mouseover', this.addClass.bindAsEventListener(this)); element.observe('mouseout', this.removeClass.bindAsEventListener(this)); }, addClass : function(event) { this.element.addClassName(this.options.hoverClass); }, removeClass : function(event) { this.element.removeClassName(this.options.hoverClass); } });
This is how we can use the options parameter:
document.observe('dom:loaded', function(){ new HoverObserver($('someDiv'), {hoverClass: 'highlighted'}); });
The nice thing about this approach is not that we can specify every single parameter in the options argument, but that we don’t have to, since every parameter we don’t specify will use its default value.
Common (and not-so-common) mistakes explained
Problem: I can’t use my object’s properties or methods, it says they are undefined!
Solution: You forget to add a .bind(this) or .bindAsEventListener(this) to a function called by an event handler or an iterator.
// wrong element.observe('click', this.doClickAction); elements.each(this.doSomething); // right element.observe('click', this.doClickAction.bindAsEventListener(this)); elements.each(this.doSomething.bind(this));
Problem: Everything works fine in Firefox, Opera and Safari, but IE refuses to run my script. It looks like IE doesn’t even recognize it as JS code!
Solution: You forgot a comma next to the last function declaration.
var MyClass = Class.create({ initialize: function(){ }, method1: function(){ }, lastMethod: function(){ }, // <-- this is the evil bastard });
In this particular case none of the browsers I tested throws an error (they should!): IE simply refuses to parse the javascript file while the others ignore the syntactical error.