Get Started Today Free Trial
April 07, 2006 - by jason
One of the real benefits of using a framework like Ruby on Rails for development is that there are new, agile features being added all the time. One of the challenges in using a framework with such an accelerated curve of improvement is… well, keeping up. Luckily it's fun to do, and the benefits are real. The next update to the Joyent platform has been on edge Rails for a while and we've been able to simplify + improve our code a lot using '1.1' features like polymorphic associations, with_scope, integration tests and .rjs templates (my personal favorite).
Another big, constantly improving 'feature' of Rails is Sam Stephenson's incredible Prototype JavaScript library (live large with version 1.5.0_rc0!), which has become a vital part of the framework. It almost single-handedly changed my entire mindset about JavaScript. There was a time (4-5 years ago…) when I would have done absolutely anything to avoid using any JavaScript whatsoever. Not exactly one of those people who turned JavaScript off in my browser or anything, but close.
But JavaScript's always been the same, versatile language it is today; it even shares a lot in common with Ruby. They're both object-oriented and interpreted. Both allow objects to be modified extensively at runtime. JavaScript supports closures. And it's the only language that runs client-side within the browser across platforms, so we're kind of stuck with it too.
And that's okay, as the language seems to have finally recovered from the one-two punch of browser incompatibilities and the lack of a standard library. Time has (mostly) solved the former, and prototype.js has done much to take care of the latter. And while the library is small enough to be easily digestible, here are some favorite must-use features and some related must-read articles that can help you modernize your JavaScript skills.
Sometimes you want an event to occur when the document has finished loading. In the past you might have added an onload attribute with the corresponding code to the document's body tag. One problem with this approach was that you (or another library whose internals you are unaware of) could inadvertently overwrite this code elsewhere. So Prototype provides a simple way to register n callbacks whose declarations won't interfere with each other.
Event.observe(window, 'load', function(event){ Calendar.setupEdit(); });
Any event for any element can be observed, too, such as blur, mousemove, or keypress. Useful for cleaning up your code or writing more complex behavior that just doesn't work with inline event handlers. If you're running Safari and aren't using WebKit ('golden Safari') read up on an easy workaround for Event.stop, which is kind of the new-school way of returning false. For a more advanced example see the always excellent Justin Palmer's article about events in Prototype.
Much like John Resig's jQuery, Prototype now has a DOM-inspecting selector that queries for elements based on the criteria you specify. This is exciting stuff. You want to toggle all the checkboxes in a fieldset with the class of 'toggleable' inside your form with the id of 'itemList'? No problem.
$$('form#itemList fieldset.toggleable input[type=checkbox]').each(function(input){
input.checked = !input.checked;
});
The $$() function performs a document query based on the HTML tag types, JavaScript ids, CSS classes, and HTML attribute keys + values and returns the matching DOM elements. It's not always fast on the client-side, but there's no easier way to get a handle on document elements that match arbitrarily complex criteria. You even get =, ~=, |= and != operators for matching HTML attribute values.
You may have noticed in that last example the use of the very Ruby-like, and very un-Javascript-like each method. Well, Prototype adds the ability to treat arrays and 'hash' objects as enumerables, just like Ruby.
$A(myArray);. Or declare your arrays from the get-go like this: var myArray = $A();. The newly 'mixed in' methods you'll recognize as being from Ruby, or at least Ruby-inspired, include:
all, any, collect, detect, each, entries, find, findAll, grep, include, inject, invoke, map, max, member, min, partition, pluck, reject, select, sortBy, toArray, zip
You can get your Ruby on in JavaScript like so:
// show a nice, friendly, sorted, comma-delimited string of all the events tagged 'awesome'
var Events = $A([
{name: 'Yesterday', tags: $A(['not so awesome', 'snow'])},
{name: 'Tomorrow', tags: $A(['awesome', 'warm'])},
{name: 'Today', tags: $A(['awesome', 'sunny'])}
]);
var awesomeEvents = Events.findAll(function(event){
return event.tags.detect(function(tag){
return tag == 'awesome';
});
}).sortBy(function(event){
return event.name;
}).collect(function(event){
return event.name;
}).join(', ');
alert(awesomeEvents);
Whew! That was awesome, non? A little messy with all the anonymous functions all over the place, but awesome nonetheless. Thank goodness ugly for loops are a thing of the past. The best write-up on these Enumerable features lives over at Encytemedia (where you can also find the swanky Vibrant Ink TextMate theme).
Of course we can't forget the AJAX support that put Prototype on the map, the ever-handy $() function, bootstrap exception handling, and the foundation laid for the script.aculo.us effects + UI library. For a comprehensive reference (currently covering only 1.4.0) see Sergio Pereira's excellent developer notes. And enjoy writing joyful JavaScript code, something that wasn't possible but is now.