Prototype 1.7 RC1: Sizzle, layout/dimensions API, event delegation, and more
We've just tagged the first release candidate of Prototype 1.7: a major new version with some major new features.
Sizzle as the selector engine (or mix in your own)
With Prototype 1.7, we've finally realized our long-held goal of moving to Sizzle, the middleware selector engine used by jQuery and others. I wrote our previous selector engine, used since 1.5.1, but nevertheless I'm excited to switch to a more robust engine that's shared between frameworks.
So Sizzle is the new default. But there's more to it than that. In moving to Sizzle, we've modularized the selector engine entirely. If you want to use Diego Perini's NWMatcher library in place of Sizzle, you can. Just check out the source code and build like so:
rake dist SELECTOR_ENGINE=nwmatcher
If you're a sentimentalist, you can use the legacy Prototype selector engine by specifying SELECTOR_ENGINE=legacy_selector. Or add your own selector engine by creating a subdirectory in vendor/ and following some simple conventions.
Element#on
Element#on is a new way to access the Prototype event API. It provides first-class support for event delegation and simplifies event handler removal.
In its simplest form, Element#on works just like Element#observe:
$("messages").on("click", function(event) {
// ...
});
An optional second argument lets you specify a CSS selector for event delegation. This encapsulates the pattern of using Event#findElement to retrieve the first ancestor element matching a specific selector. So this Prototype 1.6 code...
$("messages").observe("click", function(event) {
var element = event.findElement("a.comment_link");
if (element) {
// ...
}
});
...can be written more concisely with Element#on as:
$("messages").on("click", "a.comment_link", function(event, element) {
// ...
});
Element#on differs from Element#observe in one other important way: its return value is an object with a #stop method. Calling this method will remove the event handler. (Technically, this is an instance of a new class called Event.Handler.) With this pattern, there's no need to retain a reference to the handler function just so you can pass it to Element#stopObserving later.
For example, in Prototype 1.6, where you'd need to write something like...
start: function() {
this.clickHandler = function(event) {
// ...
};
$("messages").observe("click", this.clickHandler);
},
stop: function() {
$("messages").stopObserving("click", this.clickHandler);
}
...you can now write:
start: function() {
this.clickHandler = $("messages").on("click", function(event) {
// ...
});
},
stop: function() {
this.clickHandler.stop();
}
Also note that the Event.Handler class has a corresponding #start method that lets you re-attach an observer you've removed with #stop.
So, to review, Element#on is both a new approach to event observation and an implementation of event delegation. Feel free to eschew Element#observe and use Element#on exclusively; or use Element#on just for event delegation; or keep using Element#observe the way you always have.
Element.Layout: Your digital tape measure
The second major feature in 1.7 is Element.Layout, a class for pixel-perfect measurement of element dimensions and offsets.
Now you don't have to decide between properties like offsetWidth (which return numbers, but not the numbers you want) or retrieving computed styles (which have their own set of quirks and require a call to parseInt).
The simple case
If you want a one-off measurement of an element, use the new Element#measure:
$('troz').measure('width'); //-> 150
$('troz').measure('border-top'); //-> 5
// Offsets, too:
$('troz').measure('top'); //-> 226
The argument passed to measure is one of a handful of intuitive names, most of which are derived from their CSS equivalents. So width means the width of the content box, just like in CSS — but we throw in extra properties (e.g., padding-box-width, margin-box-height) for some common measurements. This approach gives you far more granularity than common DHTML properties like offsetWidth and clientHeight.
These measurements are guaranteed to be in pixels. Even in IE. (In fact, Prototype works around a handful of IE quirks that would ordinarily result in inaccurate measurments.) It can even measure elements that are hidden, as long as their parents are visible. (Like when you want to animate an element from a hidden state and need to know how tall it will be.)
The complex case
If you need to measure several things at once, though, Element#measure is not the most efficient way to do it. Often an element will need a bit of manipulation before it reports its dimensions accurately, which means measurements can be costly.
The Element.Layout class tries to minimize that cost. It's a read-only subclass of Hash that remembers values in order to avoid re-computing.
First, use Element#getLayout to obtain an instance of Element.Layout:
var layout = $('troz').getLayout();
Now use Element.Layout#get to retrieve values, using the same property names you used for Element#measure:
layout.get('width'); //-> 150
layout.get('height'); //-> 500
layout.get('padding-left'); //-> 10
layout.get('margin-left'); //-> 25
layout.get('border-top'); //-> 5
layout.get('border-bottom'); //-> 5
layout.get('padding-box-width'); //-> 170
layout.get('border-box-height'); //-> 510
layout.get('width'); //-> 150
Here's where the remembered values (or memoization, if you prefer) come in. When I ask for width, Prototype measures the element – which, as we discussed, is a costly operation — and returns a value. A few lines later, I ask for width again, and I get the same value. But this time it didn't do any measuring. It remembered the value from last time.
There's more. When I ask for border-box-height, Prototype knows that's just height plus border-top plus border-bottom. All three of those properties are already memoized, since I asked for them earlier, so it skips the measurement phase and just gives me the sum.
How does it know when an element's dimensions change? It doesn't. Don't hang onto an instance of Element.Layout for too long; it's meant for short-term efficiency, not long-term caching. You can grab a new instance by calling Element#getLayout again.
Believe it or not, this is the short version. Read the documentation to learn more.
JSON fixes, ES5 compliance
The JSON interface slated for ECMAScript 5 is already being implemented in major browsers. It uses many of the same method names as Prototype's existing JSON implementation, but with different behavior, so we rewrote ours to be ES5-compliant and to fall back to the native JSON support where possible. A few other methods, like Object.keys, received similar treatment.
And, of course, bug fixes
Consult the CHANGELOG for further details.
Download, report bugs, and get help
- Download Prototype 1.7 RC1
- View the API documentation
- Check out the Prototype source code on GitHub
- Submit bug reports to Lighthouse
- Get prototype help on the mailing list or #prototype IRC channel
- Talk to the core team on the prototype-core mailing list
As always: thanks to the many contributors who made this release possible!
Sorry, comments are closed for this article.


Comments
Element#on, my new favorite method!
Modular selector engines FTW.
Sexy, can’t wait for the final!
Awww everything covered in this post sounds soooo good!
Can’t wait to get my hands on these :) great work guys.
Why didn’t you guys just embed the memoization logic inside the – measure – method?
I have been wondering where you guys have been! 1.7 looks like it is going to be hot (insert Sizzle pun here) – keep up the good work! This is a great start to the month of April!
Yay! Impressive work, as always. You’re the best <3>
Gabriel: Because we wouldn’t know when to “expire” the memoized value. What happens when the element’s dimensions change?
Great work – #on looks amazing! Cannot wait to see what you guys have planned for 2.0 :)
Andrew: Fair enough. A parameter indicating if you want the live values defaulting to no would look a lot more coherent, though.
In the new prototype there is a: var layout = element.get(‘layout’); shouldn’t it be: var layout = element.getLayout();
Great news. But as one who is still going to be using the older version for a while in production shouldn’t the on-line documentation show the new/changed stuff somehow? Otherwise, how do I know?
I agree with @bugrain. I missed up the documentation for the currently stable version of prototype. Please make it available again since I have multiple projects built with prototype 1.6.1.
Is this release considered to be compatible with the latest version of Scriptaculous?
Looks great. I was excited when I saw Element.Layout in the API docs the other day, but disappointed that it wasn’t implemented yet.
Why oh why does these pages have Version: ‘1.5.2_pre0’? I wanted to check it out in Firebug immediately :)
I found the following small error:
$H({foo: ‘bar’}).toJSON() //-> “[Object Object]”
firejune: That isn’t an error, it follows the specs.
toJSON()methods are requested to return an object (or primitive) ready for serialization withObject.toJSON.Try this:
Great!! When can I get the final version??
Removing the documentation for stable version in this movement you-better-use-1.7_RC1-right-now-or-die is very annoying and I hope you fix it soon. At least you should support a transition phase between developers using 1.6.1 in their current production projects and those that will learn 1.7 and eventually 2.0 (me included) in their development projects.
Meanwhile I found documentation for prototype 1.6.0 located here http://globalmoxie.com/projects/prototype-pdf/index.shtml
Tobie Langel: Yeah, but earlier versions had it like:
It is still specified like that in the Introduction to JSON turorial page. Version 1.7 will break a lot of code (including mine,) so, is it really gonna be like this?
@superruzafa: We’re working on the doc issue (sorry for the glitches).
@Raul: Yes, it will. The impact on existing code should be minimal, as most users are using
Object.toJSON(…)rather than calling thetoJSONinstance method of hashes or arrays.Documentation for Prototype 1.6.1 is now available here.
this is neat… VIVA PROTOTYPE =), awesome job.. the same question, do you have in mind the release date… I’m so excited to have it in my hands…
my favourite javascript library just got a new hat and boots, looking snazzy!!!
andy
Nicely done, looking forward to using 1.7 and (hopefully soon!) 2.0!
Thanks for the effort in make available the 1.6.1 doc again. I’m expecting the final 1.7 release. Good job!
Veejay approves of this.
nice work, thanks so much!
Very exciting, and glad to see the code get divided into modular components! Thank you.
My feature request is an easy way to omit the ajax code from prototype to reduce file size. Or can I do that already?
@Erik: You’ll see something related make its way in Prototype 2.0
It’s always great to know a new Prototype version is out.
I’ve found a problem with ‘nth-last-child’ selector. It is not working at all on 1.7.rc1
@Lisandro: that’s most probably an issue with Sizzle. You should report the issue there.
Really great move with moving to Sizzle AND leaving other engines optional, the on() method was really missed ( or something similar ), and the measuring methods are ( hopefully ) impressive, I haven’t tried it yet.
Good job people, now prototype IS ( it always was ? ) the best JavaScript framework.