The Flurry Continues: More Prototype Updates

—Tuesday, September 05 2006

The last Prototype update was nice, but it doesn’t compare to this update in terms of overall awesomeness. I think Sam worked on labor day or something, because we’ve got a host of new additions and fixes to play with now.

Before we get started, I’m gonna be using this chunk of html for the examples:

  <div id="sidebar">
    <ul id="menu">
      <li><a id="cool" href="#" title="Cool">Cool</a></li>
      <li><a href="#" title="This Rocks">This Rocks</a></li>
      <li id="selected"><a href="#">I am selected</a></li>
    </ul>
    
    <ul id="actions">
      <li><a href="#" title="Delete this">Delete</a></li>
      <li><a href="#" title="Edit">Edit this</a></li>
    </ul>
  </div>

Events with a side of arguments

In days past bind was great at accepting additional arguments, however, bindAsEventListener didn’t get this love until now. We can pass those additional arguments to bindAsEventListener with ease:

  var Clicker = Class.create();
  Clicker.prototype = {
    initialize: function(link) {
      Event.observe(link, 'click', this.onClick.bindAsEventListener(this, 'red'));
    },
    
    onClick: function(event, color) {
      Event.element(event).setStyle({color: color});
      Event.stop(event);
    }
  }
  
  new Clicker('cool');

Notice how I passed red to the onClick callback? This little class observes the element with the id of cool, and when clicked set’s it’s color to whatever value we pass as the color. If we wanted to add additional arguments we could.

Traversing the DOM

What a pain eh? Not anymore! We now have some really cool methods for Element to help us traverse the DOM: up, down, previous, and next. Let’s fire up Firebug and test:

   console.log($('menu').up()); //<div id="sidebar">
   console.log($('menu').down()); //<li> (First child li element of menu)
   console.log($('menu').next()); //<ul id="actions">
   console.log($('menu').previous()); //undefined because menu has no previous siblings

You can also pass tag names and integer indexes (or both) to these methods making them super powerful:

   console.log($('menu').down(2)); //Grabs second li element (index starts at 1)
   console.log($('menu').down('li', 0)); //Grabs first li element(index starts at 0)

After testing this, it seems there might be a bug or just an inconsistency with this. When you don’t supply a tag name, the index starts at 1, if you do supply a tag name, the index starts at 0.

Siblings, Descendants, and Ancestors

There is even more DOM goodness now with these additional methods to Element: ancestors, descendants, previousSiblings, nextSiblings, and siblings. I’m sure most of you can guess what they do, but here it is:

   console.log($('sidebar').descendants()); //Every descendant, even nested descendants
   console.log($('selected').previousSiblings()); //<li><li>  (Both li's before #selected)
   console.log($('actions').ancestors()); //sidebar, <body>, <html>
   console.log($('actions').siblings()); //menu

On the Horizon

There is much more that went into this update, so be sure to checkout the source and give it a run.

While I’ve got your attention I also want to say there is some internal changes happening at camp Prototype. There is a Prototype Core team in the works and we are laying out our ideas for the upcoming versions of Prototype, documentation efforts, and many other things. The paint still isn’t quite dry on this yet so I’ll wait before I talk more on this.

On the documentation front: We have something in the works. We have the API about 80% documented and will have this up for public consumption as soon as we can. This will start off as very basic API docs, but we plan on putting a lot of effort in this as time goes by. On that note, you can send your thanks to Andrew Dupont considering he has worked very hard on the docs.

Until next time!