Article

Painless JavaScript Using Prototype

Page: 1 2 3 4 Next

Function Binding

Prototype also adds to the Function object two really useful methods: bind and bindAsEventListener. These are used mainly to bind a function to a particular object so that the this keyword points to that object. This is incredibly useful when you're setting event handler functions. Imagine you try something like this:

var myObject = new Object();  
myObject.message = "Hello!";  
myObject.eventHandler = function() {  
 alert(this.message);  
}  
 
$("mydiv").onmouseover = myObject.eventHandler;

Traditionally, you'd get an error because, when the event triggers the handler function, this refers to the mydiv element, not myObject, so this.message is undefined. You can solve this problem using the bind method like so:

$("mydiv").onmouseover = myObject.eventHandler.bind(myObject);

Now it all works fine, because the this keyword is bound to myObject. Further to that, bindAsEventListener does that same thing, though it passes the event object through to your function in a cross-browser compatible way, so you no longer need to worry about window.event in IE. Try this:

myObject.eventHandler = function(event) {  
 alert(event.srcElement.nodeName);  
}  
 
$("mydiv").onmouseover = myObject.eventHandler.bindAsEventListener(myObject);

Now our eventHandler function has access to the event object. Much more detail on these two methods is available at their creator's site.

New String and Number Methods

Prototype has added an enormous number of useful methods to the built in String object. Let's have a quick look at some of the best.

// "backgroundColor"  
"background-color".camelize()

camelize turns hyphenated strings to camel case strings that you can use to work with CSS properties.

// "I am a piece of HTML"  
"I am a piece of <strong>HTML</strong>".striptTags()  
 
// {a : 10, b: "thing"}  
"a=10&b=thing".toQueryParams()

Prototype adds a great method to Number, too. Say goodbye to your for loops!

// alerts "1", "2", "3" ... "50"  
50.times(function(n) {  
 alert(n);  
}};

Here, the times method takes a function that will be called the given number of times, and passes in the current iteration number as an argument. This use of an iterator function is common when using Enumerable, which we'll discuss next.

Iterating the Ruby way: Enumerable and Hash

One of the hidden gems of Prototype is the Enumerable mix-in and the Hash object, which have been poached straight out of Ruby. If you're not familiar with Ruby, don't worry. I'll explain it all here.

We'll start with Enumerable. In short, when we add Enumerable to an object using Object.extend, it gives the object in question lots of really useful functions for working with its properties. Enumerable has been added to Array's prototype, so any array has these new methods. Here are a few examples of what you can do with the new "enumerated" arrays:

// alerts "a is at 0" then "b is at 1" then "c is at 2"  
["a", "b", "c"].each(function(item, index) {  
 alert(item + " is at " + index);  
});  
 
// [80,50]  
[1, 80, 3, 50].select(function(item) {  
 return (item > 20);  
});

select creates a new array that contains only the elements that make the function return true.

// ["A", "B", "C"]  
["a", "b", "c"].invoke("toUpperCase");

invoke calls the specified method of each element of the array and returns the resulting array.

// ["cat", "rat"]  
["cat", "dog", "rat", "mouse",].grep(/at/);

grep returns all elements that match the given regular expression.

Enumerable offers a large number of incredibly powerful functions that can make many tedious DOM scripting tasks a breeze. I strongly suggest you have a good look at the Enumerable methods in Sergio Pereira's extremely useful developer notes.

There's a small problem here, though. In JavaScript, you can come across many types of objects that, to all intents and purposes, act like arrays but aren't Array objects. Objects such as DOM NodeLists and function arguments won't have Enumerable available to them automatically. This is easy to rectify, though; to add the Enumerable functions to any array-like object, use $A:

// add Enumerable to childNodes  
var children = $A($("mydiv").childNodes);  
 
// sets class="highlighted" for all child nodes of "mydiv"  
children.each(function(child) {  
 child.setAttribute("class", "highlighted");  
});

To create a hash, call the magic function $H on any object. This turns all the properties of the object into a set of key-value pairs with Enumerable mixed in. Let's take hashes for a spin:

// create a hash by feeding an object to $H  
var contact = $H({  
 name : "Dan Webb",  
 email : "dan@danwebb.net",  
 address : "None of your Business, London",  
 postcode : "a111111"  
});  
 
// ["name", "email", "address", "postcode"]  
contact.keys()  
// ["Dan Webb", "dan@danwebb.net","None of your Business, London", "a111111"]  
contact.values()  
// "name=Dan Webb&email=..."  
contact.toQueryString()

Hash extends Enumerable as well, so all those useful methods are also available...

// alerts "name contains Dan Webb" and so on  
contact.each(function(item) {  
 alert(item.key + " contains " + item.value);  
});

At first, if you're not a Rubyist, Enumerable and Hash may seem a bit of a hassle but I can assure you, once you start using them, you'll wonder why you ever bothered getting RSI writing all those for loops! When you use one or more of them together, you'll realise the massive power of these new methods. You can read about Enumerable and Hash in more detail at Encyte Media.

The Event object helps to provide what, to many, is the holy grail of JavaScript: simple, cross-browser event handling:

function eventHandlerFunction(e) {  
 // the element that triggered the event  
 var element = Event.element(e);  
 // gets the mouse position  
 var mouseX = Event.pointerX(e),  
     mouseY = Event.pointerY(e);  
 // stop default behaviour and event propagation  
 Event.stop(e);  
}  
 
// register eventHandlerFunction to the onclick of myObject  
Event.observe(myObject, "click", eventHandlerFunction, false);  
 
// removes the event handler  
Event.stopObserving(myObject, "click", eventHandlerFunction, false);

In a rather pleasant way, Prototype tries to avoid those pesky memory leaks in IE by automatically removing every observer when the page unloads.

In my opinion, though, this is a rather under-developed event handling solution at the moment, so it might be worth considering using something a bit richer like Dean Edwards's addEvent for the time being.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links