» Publishers, Monetize your RSS feeds with FeedShow: More infos (Show/Hide Ads)
Well folks, today is my last day at Sun. It's been challenging, aggravating, educational, terrifying, boring, exciting, and overall fun. I've worked with some absolutely amazing people and made several friends along the way. I've stared into the eyes of the Hydra—a multi-corporate, multi-national, multi-language, multi-architecture website—and kept my sanity. (At least that's what the voices in my head keep telling me.) So far as I'm concerned, that's an accomplishment.
So farewell, people of Sun. Oracle, I never knew ye, but you've got a tiger by the tail, and I'll be curious to see what research and innovations the new combined company brings to bear. What a tech portfolio that will be!
Contact info
Linkedin: http://www.linkedin.com/in/gregreimer
Facebook: http://www.facebook.com/people/Greg-Reimer/603647710
Non-Sun Blog: http://gregreimer.wordpress.com/
Email: gregreimer ~at~ gmail ~dot~ com
I'm sure that my list of virtual OSes will grow in the future.Anybody who's ever implemented or used event delegation knows first-hand the bitter pain of IE's lack of bubbling on those events. Bitter, bitter pain.
Well, the reglib trunk has just received experimental beta support for those events. The technique is of course to co-opt other events that *do* bubble, and that are almost certain to be executed prior to the non-bubbling event. Within the co-opted, bubbling event, the non-bubbling event handler is added.
The fly in the ointment is the "almost" in the above phrase, "almost certain." At the very least, these events can be triggered programatically, without any prior events firing. But I thought what the heck, might as well build it and see how useful it is in the real world.
Of course many browsers do support bubbling on these events, so they get reglib's natural event delegation. Although I'll probably need a more sophisticated test, if(document.all&&!window.opera) seems a bit crude, but I wanted to get this into the trunk and in front of people. I'm sure I'll wake up in a cold sweat tonight and realize I need to recode the whole darn thing.
Here are the new methods:
reg.submit(selector,function); reg.reset(selector,function); reg.select(selector,function); reg.change(selector,function);
More info: all reglib posts, reglib feed, download reglib
Update: So far the results are pretty promising, better than I had hoped even. Early iterations of the code were a bit unweildy and bug-prone, then I had an epiphany and about 90% of the code and complexity evaporated, leaving a solution that seems pretty tight, altogether.
Basically, it sets up reglib's existing delegation on the fly, directly on the element that receives the non-bubbling event. E.g. for the change event, it more or less does reg.focus('select',setDelegationOnThis) to ensure the delegation gets set up prior to a change event. Of course this is only done on IE. Everything bubbles normally for modern browsers. They are quite boring, those modern browsers.
Looks like Opera 10 will support the selector API. Now if only there were an Element.is(selector) DOM method for us event delegation geeks...
UPDATE: I've removed reg.pause() and reg.resume() from the trunk. Sean Hogan makes a good point in the comments below. Plus, now that reglib is doing delegation on form events, things get complicated maintaining the pause/resume system alongside the branch of code that simulates bubbling for form events in IE. One can still easily disable an event by using a selector like this: "body.on a@href" and then adding/remove 'on' from the body classname as necessary.
reglib has reached 1.0.6. Updates include:
- methods for pausing and resuming event behavior:
reg.pause()andreg.resume() - mouseenter/mouseleave behavior as posted earlier
reg.pause() and reg.resume() follow a similar pattern to reg.removeEvent():
// handle hover on all links
var hoverLinks = reg.hover('a@href',function(e){
// handle mouseenter
},function(e){
// handle mouseleave
});
// stop handling hover on links
reg.pause(hoverLinks);
// resume handling hover on links
reg.resume(hoverLinks);
More info: all reglib posts, reglib feed, download reglib
I decided that mouseover/out is never the behavior I want, whereas mouseenter/leave is always the behavior I want. Practically always. So I've changed over reglib's reg.hover() to behave like mouseenter/leave.
For Example
Say you have this HTML structure:
<div>
<p>
<span>
<a>
mouse over me
</a>
</span>
</p>
</div>
Which I tend to visualize as a ziggurat-like object:
...and then you have this event handler:
// note, the think() function is purely fanciful
reg.hover('div',function(e){
think(e.type, e.target);
},function(e){
think(e.type, e.target);
});
Up until now, reglib behaved like a typical mouseover/out event handler, which in a nested element/bubbling situation isn't what you really want. The mouse movement is represented by the little arrow:
So anyways, as I said, this is a bit wonky and unnecessary. Therefore the next version of reglib will (if all goes to plan) behave thusly:
The title of this post is a bit misleading. The mouseenter/leave events are never used. It's all still accomplished via mouseover/out, but there's enough information available in the handling element, event.target and event.relatedTarget elements--and their positions relative to each other--to know whether to execute the handler. Since the actual mouseenter/leave events don't bubble, and in any case are IE-only, reglib wants nothing to do with them. This code is checked in to the trunk, and with a little more testing will be released as reglib version 1.0.5.
One thing also, I'd be curious to know if anybody knows of a reason this would be *undesirable* behavior.
For more info: see all reglib posts, grab the reglib feed, reglib on google code
reglib is up to 1.0.4. Some bugs are fixed and some features are added since the 1.0 release:
- new x-browser event function
removeEvent() - new x-browser event function
cancelBubble() - now supports event delegation with mousedown, mouseup, and dblclick
- now supports multiple class selection, e.g.
.foo.bar
Here's an example of removeEvent():
var el = reg.gebi('myid');
// mem is just an id
// similar to that returned by setTimeout()
var mem = reg.addEvent(el, 'click', function(e){
// do stuff here
});
// later on, remove the event
// similar to clearTimeout()
reg.removeEvent(mem);
Personally, I don't use these direct-attachment functions very much because I prefer to use reglib's built-in event delegation, but it's nice to have them around.
More info: all reglib posts, reglib feed, download reglib
Read the reglib tag trail for more about reglib, or subscribe to the feed of all things tagged reglib.
Let me first of all stress that I'm not trying to bust on JQuery here. JQuery does something that needs to be done, and it does it just about as well as can be done given the tool-set browsers have collectively placed at our disposal.
With that said, I'm going to go ahead and pimp the reglib way of doing things, by which I mean declarative, over the load-traverse-modify methodology, which JQuery makes so easy.
The demo page linked below has two identical interactive widgets; one wired up using JQuery, and the other wired up using reglib.
The page is rigged like a science experiment, with a control followed by several tests in which you observe differences in behavior between the two widgets in response to various stimuli. The goal is to demonstrate reglib's resilience under duress (as it were). Enjoy.
The reglib is now published to Google Code and available under the MIT license.
Project Home: http://code.google.com/p/reglib/
Download: http://code.google.com/p/reglib/downloads/list
The backstory - Almost a year ago1 I made all kinds of big talk about releasing a JavaScript lib I was developing for sun.com. This library obviates (some of) the need for what I call the load-traverse-modify methodology of unobtrusive JavaScript:
- Load: make it your business to know when the DOM has loaded.
- Traverse: use a query to scan said DOM, returning a list of elements.
- Modify: attach event handlers to, and otherwise manipulate, those elements.
Although LTM is quite common in JavaScript development, I believe it's an antipattern. The reglib tries to get closer to the CSS way of doing things: declare what kinds of elements get what behavior, and have that declaration take immediate effect at a global level, regardless of subsequent mutations to the DOM.
// JAVASCRIPT CODE:
reg.click("a.popup", function(e){
window.open(this.href);
return false;
});
/*
ENGLISH TRANSLATION:
I declare, forthwith, that all clicks on <a> elements with
class "popup" shall be handled thusly...
*/
So there you have it. Feel free to check it out (just check it out or actually check it out), read the documentation wiki, download, leave comments, etc. The lib also has a bunch of convenience functions for DOM manipulation, plus it has tools for doing LTM stuff because let's face it, sometimes there's no other way.
(Previous posts on this topic can be found here and here.)
1 It took so long because, in a nutshell: 1) Sun has an open source review process, you can't just release code willy-nilly, 2) During the process, I failed to be a squeaky wheel, and in fact the lawyers waited on my responses as much as I waited on their responses. However development over that interval has been constantly active, and reglib is being used on sun.com.
I built a loop benchmarking test suite for different ways of coding loops in JavaScript. There are a few of these out there already, but I didn't find any that acknowledged the difference between native arrays and HTML collections. Since the underlying implementations are different (HTML collections for example lack the pop() and slice() methods, etc), benchmarks that don't test against both are probably missing important information.
My suspicions were confirmed. Accessing the length property is more expensive on HTML collections than on arrays, depending on the browser. In those cases, caching it made a huge difference. However, HTML collections are live, so a cached value may fail if the underlying DOM is modified during looping. On the other hand, HTML collections will never be sparse, so the best way to loop an HTML collection might just be to ignore the length property altogether and combine the test with the item lookup, since you have to do that anyway:
// looping a dom html collection
for (var i=0, node; node = hColl[i++];) {
// do something with node
}
Another interesting result is that with HTML collections, hColl.item(i) is 2-6x slower than hColl[i], except in Safari where it's about the same. I wonder what the extra overhead is?
I've posted the results of all my benchmarks here, however my IE7 is really IE8 in IE7 emulation mode, so I mistrust these results as representing IE7's original JavaScript/DOM engine. If anybody wants to run the benchmarks in true IE7 and post the results, I'll update this post accordingly. Thanks to Kyle Simpson for the benchmarks. Also, I'm sure there are ways of looping that escaped my imagination, so if you know of any, post it in the comments and I'll add them to the test page.
[edit] To prevent overly-helpful "this script is taking too long" warnings from skewing the results, in IE you might have to edit your MaxScriptStatements registry setting. In Gecko it's dom.max_script_run_time under about:config.
Firefox 3.0.1 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 4ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 4ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 3ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 9ms |
| for ... in loop | for (var i in arr) {} | 28ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 93ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 3ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 6ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 4ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 5ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 51ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 51ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 81ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 35ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 79ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 32ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 22ms |
| for ... in loop | for (var i in sarr) {} | 30ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 96ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 178ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 769ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 119ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 119ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 3ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 2ms |
| for ... in loop | for (var i in hColl) {} | 97ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 163ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 114ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 189ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 95ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 235ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 98ms |
Firefox 2.0 / WinXP / 1.95GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 47ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 16ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 31ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 15ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 281ms |
| for ... in loop | for (var i in arr) {} | 375ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 906ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 32ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 62ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 31ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 391ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 422ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 453ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 203ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 469ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 218ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 172ms |
| for ... in loop | for (var i in sarr) {} | 328ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 875ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 750ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 6000ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 891ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 16ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 906ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 15ms |
| for ... in loop | for (var i in hColl) {} | 625ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 1188ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 453ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 797ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 437ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 1438ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 437ms |
Camino 1.6.1 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 8ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 4ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 9ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 85ms |
| for ... in loop | for (var i in arr) {} | 44ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 163ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 10ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 14ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 10ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 11ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 111ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 105ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 103ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 45ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 99ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 42ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 33ms |
| for ... in loop | for (var i in sarr) {} | 52ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 151ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 217ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 1338ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 270ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 4ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 270ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 3ms |
| for ... in loop | for (var i in hColl) {} | 106ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 224ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 119ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 220ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 115ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 397ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 114ms |
Mozilla 1.7.12 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 40ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 14ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 22ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 13ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 9ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 388ms |
| for ... in loop | for (var i in arr) {} | 263ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 1126ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 27ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 48ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 26ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 32ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 666ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 148ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 264ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 157ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 255ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 180ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 103ms |
| for ... in loop | for (var i in sarr) {} | 236ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 1027ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 1076ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 1881ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 1888ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 18ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 1855ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 17ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 24ms |
| for ... in loop | for (var i in hColl) {} | 578ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 1455ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 1182ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 2072ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 1102ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 2542ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 1115ms |
Safari 3.1.2 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 5ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 6ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 18ms |
| for ... in loop | for (var i in arr) {} | 42ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 80ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 5ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 8ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 5ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 7ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 11ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 19ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 54ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 31ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 71ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 45ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 19ms |
| for ... in loop | for (var i in sarr) {} | 41ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 77ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 285ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 482ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 8ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 8ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 2ms |
| for ... in loop | for (var i in hColl) {} | 47ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 86ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 58ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 107ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 59ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 53ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 60ms |
Opera 9.51 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 7ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 7ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 7ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 7ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 3ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 28ms |
| for ... in loop | for (var i in arr) {} | 58ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 299ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 9ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 18ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 9ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 13ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 54ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 27ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 94ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 76ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 92ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 74ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 42ms |
| for ... in loop | for (var i in sarr) {} | 63ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 286ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 230ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 442ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 28ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 5ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 28ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 6ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 4ms |
| for ... in loop | for (var i in hColl) {} | 113ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 380ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 36ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 60ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 37ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 175ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 41ms |
IE 7.0.5730.11 / WinXP SP2
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 31ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 15ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 32ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 15ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 0ms |
| do ... while loop in reverse. | var i = arr.length-1; do { } while (i--); | 16ms |
| for loop in reverse. | for (var i=arr.length; i--;) { } | 0ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 2907ms |
| for ... in loop | for (var i in arr) {} | 140ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 438ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 31ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 63ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 46ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 719ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 406ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 344ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 140ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 328ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 157ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 78ms |
| do ... while loop in reverse. | var i = sarr.length-1; do { } while (i--); | 94ms |
| for loop in reverse. | for (var i=sarr.length; i--;) { } | 93ms |
| for ... in loop | for (var i in sarr) {} | 110ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 359ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 2453ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 10922ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 2172ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 15ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 2125ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 15ms |
| do ... while loop in reverse. | var i = hColl.length-1; do { } while (i--); | 0ms |
| for loop in reverse. | for (var i=hColl.length; i--;) { } | 16ms |
| for ... in loop | for (var i in hColl) {} | 172ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 484ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 1219ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 2797ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 1406ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 3203ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 1453ms |
IE 8 Beta 1 / WinXP / 1.95GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 46ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 32ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 47ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 31ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 15ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 188ms |
| for ... in loop | for (var i in arr) {} | 203ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 734ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 47ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 62ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 47ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 610ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 203ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 657ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 281ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 656ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 266ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 156ms |
| for ... in loop | for (var i in sarr) {} | 156ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 641ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 4656ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 3781ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 3110ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 31ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 3109ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 31ms |
| for ... in loop | for (var i in hColl) {} | 281ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 938ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 453ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 875ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 453ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 3906ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 484ms |
IE 6.0 (JScript 5.6) / WinXP / 750MHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 121ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 60ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 110ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 60ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 50ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 16694ms |
| for ... in loop | for (var i in arr) {} | 781ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 2013ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 141ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 250ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 140ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 170ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 2945ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 1331ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 1241ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 671ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 1242ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 671ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 471ms |
| for ... in loop | for (var i in sarr) {} | 611ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 1762ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 11737ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 13359ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 6349ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 71ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 6339ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 70ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 50ms |
| for ... in loop | for (var i in hColl) {} | 2984ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 5017ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 4497ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 8863ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 4516ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 10415ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 4587ms |
I've often wished browsers would offer native SAX implementations. SAX is lightweight and fast. Not only that, SAX is easy because it lets you ignore what's not interesting, unlike DOM, where you have to traverse the whole mess and keep it hanging around in memory. SAX also uses callback functions, which any JavaScript programmer should feel comfortable with.
A native SAX implementation in JavaScript would for example let you grab data from RSS feeds over Ajax without loading the entire RSS document into a DOM tree. Or, assuming your XHTML was well-formed, it would let you rapidly query the current document. (Although it wouldn't be able to return references to existing DOM nodes.)
After reading Search and Don't Replace over at John Resig's blog, it got me wondering if you could use that technique as the basis for a SAX parser in JavaScript. Of course there's nothing stopping you from building a SAX parser from scratch in JavaScript, but (methinks) the string tokenizer part of it would be a bit of a beast. However, by taking advantage of the optimization built into JavaScript's RegExp replacement engine, you might just be able to work a nice little souped-up tokenizing engine out of the deal.
So I thought I'd give it a try. What I came up with is nowhere near anything resembling a real-world, valid XML parser. All it knows how to deal with are elements, text nodes and character entities. And not all of the error messages are as helpful as a real world implementation should be. And I'm sure there are plenty of bugs since I banged this out in less than an afternoon. But it ran like scalded cats on a 422kb file. (YMMV depending on what browser you use.) Try it out on this simple XML fragment:
The SAX function looks like this:
doSax(stringToParse,doStartTag,doEndTag,doAttribute,doText);
The callback functions for this example are:
function doStartTag(name){alert("opening tag: "+name);}
function doEndTag(name){alert("closing tag: "+name);}
function doAttribute(name,val){alert("attribute: "+name+'="'+val+'"');}
function doText(str){
str=str.normalize();
if(!str){str='[whitespace]';}
alert("encountered text node: "+str);
}
And here's the code: sax.js. I think that with a little work (i.e. the ability to handle namespaces, comments, and other declarations) this could potentially be usable--maybe not as a full-fledged SAX parser--but a quick and dirty utility for reading XML via Ajax. Hmm, a tag soup SAX-style parser might be nice to have too.
The black and white object you're looking at will look different depending which browser you're using, but it should look like a circular target of some sort. It was created using nothing more than CSS's border-radius property and a couple of span tags. All of that to say this: It looks like Safari/Webkit browsers are rendering border-radius quite nicely these days, and Gecko-based browsers aren't far behind, if Firefox's latest public beta means anything. To illustrate this, I've posted some screenshots of a test page I built. This wasn't an exhaustive test for CSS3 border-radius compliance, just an attempt to see where the browsers were with basic support. Only one background image is used (shown below), the rest is pure CSS. (All tests done on the Mac.)
(this is the background image)
Camino 1.5.1 (shown below): This pretty much sums up the entire Mozilla family as of Feb 27th, 2008 in terms of border-radius support. Note the lack of anti-aliasing and the irregular edges. Also note how the background image ignores the boundaries of the rounded corners. Still, it's a commendable effort considering CSS3 isn't actually finalized.

Firefox 3 beta 3 (shown below): What an improvement. The radius forms a nearly perfect, anti-aliased circle (radius=15, width=30) and the background image respects the boundaries now.

Safari 3.0.4 (shown below): Can "perfecter" be used as an adjective? Safari, as usual, is an overachiever, already having released a final version to the public that has decent support for border-radius. In fact, Firefox 3's implementation looks nearly identical to this. (I hope Firefox 3's support isn't just a Mac thing.)

Opera 9.26 (shown below): For reference, this is what it looks like if the browser doesn't support border-radius at all.

For reference, here's the test page.
(A followup to my last post.) We're on the brink of releasing a new JavaScript mini-library to sun.com, which we call the reg library. It provides an object named, naturally enough, reg. It stands for register. With it, you can register behaviors, like this:
reg.click('ul.foo > li > a.bar', myFunction);
Once that bit of code runs, regardless of whether the entire DOM has finished loading into the browser, click events on elements matching ul.foo > li > a.bar will be handled by myFunction, which is passed a reference to the active element and the event object. This happens without any DOM traversal, and without any events ever being attached to any <a> elements. Even if the entire contents of document.body are subsequently overwritten by innerHTML, all those new element nodes implicitly inherit the correct behavior. No re-walking of the new DOM subtree ever occurs. No re-attachment of events ever occurs.
How is this even possible?
Two facts conspire to make this feasible. 1) The <body> (document.body) is available almost immediately. 2) Most events bubble. All you need to do is to stand on document.body, and you're privy to almost every event that occurs on the page, right out of the gate. No need to go seeking out the elements you need, they literally bubble their way to you. All you do is grab the event's target and ask the question, does this element, or one of its ancestors, match 'ul.foo > li > a.bar'? If so, run the associated function, if not, ignore it. This is really just event delegation, and it's nothing new, but we've made little use of it on Sun.COM before now.
Limitations, caveats, dangers
I harbor no illusions this is a perfect solution. There are limitations. Besides the fact that not all events bubble, much of time, behavior depends exclusively on preprocessing, especially if you're doing progressive enhancement. You don't want non-JS users to see useless expand/collapse signs or widgets laying around, so you build the widgets only if scripting is enabled. And the only time to build the widgets is onload. And the only way to build the widgets in the right place is... *sigh* traversal. Some of this can fortunately be avoided by relying as much as possible on CSS and making your widget styles depend on a dynamically-added class name, such as body.jsenabled, so there's some workaround potential at least.
There's also an inherent performance limitation. Sitting in the driver's seat on document.body isn't always a relaxing cruise through the countryside. It can easily turn into rush-hour gridlock, with a flood of events each demanding to be checked. For that reason, I dare not use this technique to handle mousemove events. That would cause a veritable torrent of events. Even mouseover events are iffy. We have the capability and it appears to work reasonably well, but time will tell whether it's really viable. Click events, on the other hand, because of their relative infrequency, are a good candidate. Of course, as we develop this thing further, we'll be looking for ways to mitigate performance risks.
So there you go
It isn't live yet, but hopefully will be soon. After this code has been chugging away out there in the wild and woolly world of production Sun.COM for a while, I may post more about this, what works, what doesn't, unexpected wins and losses, etc. Stay tuned.
As cool as unobtrusive JavaScript is for building the behavioral layer, it's nevertheless based on some pretty kludgy foundations, especially when contrasted with CSS's rather elegant method of declaratively building the presentation layer.
Kludge #1: Waiting for DOM load.
It seems unavoidable. If you want to be unobtrusive, there will be a delay before your functionality is available. Furthermore, DOMContentLoaded and friends don't truly solve the problem. Interestingly, a parallel problem exists in the CSS world: the FOUC (Flash Of Unstyled Content). FOUC is generally not a problem in modern web clients. I wish I could say the same for the "flash of behaviorless content" problem.
Kludge #2: Procedural DOM traversal and event attachment.
Why do I have to seek out the elements that interest me and add behavior, element by element? What a pain. On sufficiently complex pages this means walking the entire tree. If you add new elements as you walk the tree, hall-of-mirrors-style infinite loops and other pitfalls plague you. If innerHTML gets added during the course of the page's life, you have to go back and re-walk those parts of the DOM, re-attaching events as needed. Once again, contrast this with CSS, where the user agent abstracts away the traversal and the attachment of styles, allowing you to declare once--up front--which elements get which styles. Pity we can't do it this way for the behavior layer too.
So what?
Of course these are well known problems. Such is the hand we've been dealt, and tools and techniques exist that make these problems less painful, so why the fuss? In my mind anyway, leaky abstractions on top of kludges aren't an ideal state of affairs, and so I rant. But I also wanted to establish a little background for a future post where I'll describe some tools we're about to deploy on sun.com that, in certain instances, avoid these problems altogether.
[Update]: I've posted a followup with a bit of information about how our new library works.
We have a function called hasClassName(el, cls) that checks whether a given DOM element has a given class. More and more, we're relying heavily on this function, and on certain pages it can run thousands of times. In these situations a little performance tuning can go a long way, as demonstrated here:
// BEFORE
function hasClassName(el, cls){
var exp = new RegExp("(^|\\s)"+cls+"($|\\s)");
return (el.className && exp.test(el.className))?true:false;
}
// AFTER
(function(){
var c={};//cache regexps for performance
window.hasClassName=function(el, cls){
if(!c[cls]){c[cls]=new RegExp("(^|\\s)"+cls+"($|\\s)");}
return el.className && c[cls].test(el.className);
}
})();
This simple change cut the function's average runtime in half and reduced the page's overall onload setup time by over 25%! (According to Firebug.) All because it caches regular expressions.
Edit: removed unnecessary ?: operator per Nate's comment.
Python is my spare time language. I wish I had opportunity to use it during my day job, but alas, JavaScript is my day job language. Not that there's anything so terrible about JavaScript, but I get wistful sometimes switching back and forth between being able to use list comprehensions and not being able to use list comprehensions.
So anyway, I wrote a breeder program in Python that breeds little Farkle-playing babies and sets them all off playing each other. The ones that win the most games survive to the next round and get to have little Farkle-playing babies, passing on their Farkle-playing genes with various mutations. The ones that don't win, sadly, become the victims of the garbage-collector. After a few hundred generations or so they got pretty good at Farkle, starting at about 20 rounds per game on average and working their way to about 16 rounds per game on average. (The idea is to get to 10000 in as few turns as possible.) Because it's ultimately based on random numbers (rolls of the dice), Farkle is one of those games where skill level (good risk-tolerance sensibilities) only gives you a marginal advantage, so in retrospect it perhaps isn't the best candidate for an EA.
One thing that struck me about Python versus JavaScript is that with Python, my tendency is to sit and think for a long time, then write a few lines of code. Whereas with JavaScript, my tendency is to spew code prolifically, thinking as I type. I don't know if this speaks more about my skill level in the respective languages, or the nature of the languages themselves. It definitely feels like I burn more keytypes in JavaScript getting ready to do, whereas in Python I burn more keytypes simply doing. Maybe we web developers should push to have Python implemented in web clients as an alternative to JavaScript, if for no other reason than to save our hands from the strain of typing all day.
Check out this atmospheric phenomenon that appeared around the sun the other day. Do ice crystals in the higher clouds do this?
I originally took the picture because of the bright spot, but in the final pictures the entire halo showed up rather well.
Woo. Leopard arrived yesterday. Gonna install that bad boy as soon as I get a chance.
Strange things are afoot in IE-land. Remember our old friends quirks mode and (almost) standards-compliance mode? There's reason to believe IE8 will introduce yet another rendering mode, and that developers will need to opt-in to this rendering mode if they want IE to break backwards compatibility with its long-entrenched bugs. The question is, how will the opt-in be triggered? A <meta> tag? A version number in the opening <html> tag? An HTTP response header? A conditional comment? The mind boggles.
[Update]: Chris Wilson of the IE team promises more details soon regarding this very thing.
[Update jan/25/08]: And we have a winner: A <meta> tag and/or an HTTP response header. I have no opinion. Too many people smarter than I have already weighed in, and it remains to be seen just exactly how this will all shake out.







