Infrequently Noted

Alex Russell on browsers, standards, and the process of progress.

Extending dojo.query()

As you probably know, Dojo is layered, extensible, and our philosophy of "build with, not on" means that we give you all of the same tools and extension points that we use in our code for use by your app.

Dojo's got 2 "sides", namely the bits that make working with the DOM better and the bits that make writing idiomatic JavaScript faster and easier. Where the package and language utilities enable you to extend and modularize your JavaScript work, the widget, behavior, and query systems make working with the DOM similarly extensible. Writing widgets and behaviors is well-understood in the Dojo community, but extending the results of a dojo.query() call haven't seen as much attention. To rectify that, here's the two-minute version of how to write your own dojo.query() extension.

Step 1: grok dojo.NodeList

dojo.NodeList is the Array subclass which all dojo.query() calls return an instance of. Therefore, to extend the results of a dojo.query(), we really want to extend the dojo.NodeList class. Both dojo.query() and dojo.NodeList are available as soon as dojo.js is included in your page.

Step 2: extend NodeList the old-skool way

Instances of dojo.NodeList pick up properties from the prototype object of the dojo.NodeList class. Lets add an inspect() method which logs out the innerHTML of the nodes in the list to the Firebug console:


dojo.NodeList.prototype.inspect = function(){
    this.forEach("console.debug(item.innerHTML);");
    return this;
}

// now we can call it: dojo.query("#container > p").inspect();

// or via a direct instance: var nl = new dojo.NodeList(dojo.byId("container"), document.body); nl.inspect();

A couple of small things to note:

Step 3: modernize and package it up

We want to be able to use dojo.require() to manage this module, so we'll assume that our Acme module lives in the acme/ directly which is a peer of the dojo/ and dijit/ directories from the Dojo distribution.

Lets add a provide() so that we can require() our module and use a bit of Dojo's language tools to extend dojo.NodeList more tersely:


// this file located in:
//    acme/ext-dojo/NodeList.js

dojo.provide("acme.ext-dojo.NodeList"); // require() statements go here

dojo.extend(dojo.NodeList, { inspect: function(){ this.forEach("console.debug(item.innerHTML);"); return this; }, ... });

We can now include this module at runtime via dojo.require("acme.ext-dojo.NodeList") and use it as a part of a build which will significantly improve the performance of the application which needs the file. Dojo's infrastructure makes doing what's easy pay off in the long run for your application.

Note that the file name is acme/ext-dojo/NodeList.js, which might seem a bit odd at first glance, but the intermediate "ext-dojo" directory makes it blindingly obvious to anyone who sees the require() statement what's going on. You can't dot-access a variable named "ext-dojo", so we know that this isn't a "normal" module. Instead, it's an extension, which extends the dojo.NodeList class. Subclassing is often more trouble than it's worth, and discouraging people from extending the Dojo core objects seems lame. Instead, we use this convention to make it easy to understand what's happening when you look at any given Dojo project.

And that's it! Writing powerful extensions for query results is easy, and because your project is using Dojo, you can structure your extension as a module and gain the benefits of optimization for deployment via the Dojo build system and the ease of use that comes from not having to worry about what order you're including your files in. It's good to be using a tool that you can build with, not just on.

Big Questions On IE8's Big Progress

So IE is the first browser out of the gate to do something sane about rendering engine locking to content…and good on them for it.

Now we need to know a couple more details to see if it’s going to have real legs:

These questions need to be answered, and soon. If the IE team has just replaced one scarce resource for another, we’re not much better off over the long haul. It’s great news that the IE team is really implementing the “renderer selection switch” which many of us have dreamed of for so long…but having it continue to be global to the page or in other ways encourage contention on yet another single element in the document wouldn’t be stepping forward enough.

Roxer Goes Live!

So once upon a time, back when I had a "real" job, I used to do security (specifically webappsec) for a living. One of the shining lights in that world for a long time has been Jeremiah Grossman, and as I moved out to the Bay Area, I was lucky enough to meet him in person. We've kept up here and there, and while his company has grown at a furious rate, he's somehow found time to continue to publish important original research, travel more than any human really should, and generally kick ass....oh...and build an awesome Dojo-based product on the side.

Jeremiah and Lex must really not sleep, 'cause their new Roxer app makes me all giddy to play with. Yes, yes, you should separate style from content...oh fuck it all. When it's this easy and fun to build a web page, who cares? I loathe wikis, mostly because traditional wikis try to be "half-pregnant" about how what you write gets displayed. Roxer solves that essential failure in one easy step, and makes it enjoyable to boot.

Think of it as the anti-blogging tool. It's not set up with expectations that you'll be doing this or that with it, it's there for you to do stuff. I literally cannot wait until it's hooked up with some dojox.data love to access web services and the like. It's not a programming platform, and maybe that's what's great about it. Can't wait to see how it evolves and how they'll open up the (apparently pluggable?) content type system.

Great work guys!

Dojo Needs Your Projector (and room, and network, and...)! (updated)

The votes have just come in, and the next set of Dojo Developer Days will be in the San Francisco Bay Area Feb 7-8 or 8-9, but as of now we don't have a venue.

In years past, IBM and AOL have graciously hosted these events, provided network connections and projectors, and generally made us feel at home.

This is where you come in! If you're working at a Bay Area company who is using or otherwise benefits from the work we're doing in Dojo and can spare a room for 20-30 people (with working network) for a couple of days, this is a great chance for you to meet the community of folks building the toolkit, put faces to names, and do your bit to help ensure that Dojo continues to succeed.

If you can spare some space on either of those sets of dates (Feb 7-8 or 8-9), please send me mail.

Update: Our friends at Google and Mozilla have both generously offered their spaces, and so it looks the Feb DDD will be in Mt View. I'll update this post once more once dates and locations are final. Thanks Google/Mozilla!

Update 2: Thanks to Google, Dojo Dev Day will be Feb 7-8th at Google's Mt. View campus. The agenda is being constructed here, and we can use all the help and feedback we can get on what to discuss, particularly if you plan on attending. It's always hard trying to address every concern at these meetings, so now's the time to make your voice heard! We'll be posting logistics details and the RSVP address to that page in the next couple of days.

This DDD is going to rock! Can't wait to see everyone there.

How IE Mangles The Design Of JavaScript Libraries

A lot of hyperbole gets thrown around about how painful IE 6 and 7 make the world of JS development, and so I thought I'd do a bit of cataloging to help those using Dojo understand why it's built the way it is and indeed, why all JS widget libraries suffer similar design warts. I know the good folks up in Redmond are working hard at delivering something better, but the fact of the matter remains that until they outline when we're going to get it (and the version after) and how it's going to be distributed, IE 8 only serves to make the current situation look as shabby as it really is. Here are but 5 examples of how IE makes your toolkit of choice less elegant than it probably should be.

  1. Array's Can't Be Usefully Subclassed (test case)

    At first blush, this seems wrong. You can use the Array base-class as the prototypal delegate for any user-defined class you wish. Methods are correctly delegated to and hash-style indexes work fine. Almost everything works right...except when you try to use the built-in array manipulation methods like push, pop, and shift. They dutifully change the internal contents of the subclass instance's indexed attributes, but they don't manipulate the length property. This means that while you can use for(var x in list){ ... style iteration, you can't do anything *aside from key iteration* to know how many items are in the array. Obviously, one could try to wrap the intrinsic functions and detect how they manipulate the length property, but then you've ruined their [DontEnum] status and now they end up in the iterable surface area of instances. Ugg.

    Arrays without a working length property are nearly useless, and JScript mangles the design of toolkits as a result.

    So how do we get dojo.NodeList to be a "real" array with extra methods then?

    As you might expect, it's a giant hack. When you use the "new" keyword with the dojo.NodeList function, you expect that the system will create a new instance and do it's normal "stamp with a constructor" business. Instead, we resort to creating (and populating) a regular Array instance and "NodeList-ifying" it by copying named attributes from the class prototype into the instance as member properties. The "constructor" function then explicitly return a new object, bypassing the "new" keyword's create/stamp machinery, at which point the return of the new operator becomes our explicit return and not the object which it would have otherwise implicitly returned.

    In Dojo 0.9 we had used an even more aggressively hackish workaround for IE which involved creating a sub-document and mapping its interpreter's intrinsic Array class into the parent document at a different name. Both are slow for different reasons but we eventually switched to the create-and-mix-in style because some popup blockers were interfering with the old method.

    Lest you think that Dojo is a dirty, dirty toolkit for doing this kind of thing, consider the janktastic "it's not really an array" thing that JQuery resorts to instead. By giving up all "[]" index operations, JQuery manually maintains it's internal length property by re-implementing all of push, pop, etc. functions. This has the benefit of allowing prototypal delegation to work of pre-existing instances when new features are added to the base prototype, but at the expense of no longer being able to think about a dense list of things as an array. Dojo's approach is painful, but so are all the alternatives today.

    I think it's safe to say that both toolkits would subclass Array directly to save code, were it a reasonable thing to do.

  2. Where Art Thou Getters/Setters?

    As JavaScript toolkits get pushed out of their current workhorse tasks of plastering over JavaScript and DOM implementation gaffes by positive browser progress and Moore's Law, they increasingly take on application construction tasks (e.g., dojo.data). As the toolkits have approached these tasks, we've collectively started to hit some very serious usability limitations due, in large part, to JScript's lack of progress.

    Toolkits like Dojo have widget systems because HTML just hasn't kept up. That means that these toolkits have a responsibility to keep as many of the positive aspects of the "native" web as they can. From CSS customization to accessibility all the way through implementing declarative creation and DOM-ish JavaScript APIs, the better a job a toolkit can do in making the abstraction feel more solid, the better the toolkit is. Widgets are essentially a way to "subclass HTML elements".

    In many places, DOM allows you to affect the behavior of the visible document using "setter"-style syntax. For example:

    
    document.getElementById("foo").style.border = "5px dotted black";
    

    Custom widget classes can have the same behavior on every browser except IE.

    This means, of course, that JavaScript toolkits can't really implement the behavior, backing JavaScript programmers up against a wall when they design their tools. Instead of providing the natural property-oriented behavior, it forces class authors to write getSomeProperty/setSomeProperty method pairs on their classes should they want to do anything when values are gotten or set. The resulting code feels a lot more like Java than JavaScript, which is usually a sign that something is horribly wrong in a browser.

    As bad as this problem is for visual widgets, it's worse for data binding systems. API's like dojo.data would be designed in fundamentally different ways if getters and setters were available everywhere. Instead of the rigamarole of making users fetch an item reference and then fetch attribute values using the opaque item handle and the property name, we'd just set up getters and setters on the properties themselves and defer the implementation of fetching those values down to the data store. Further, assigning a linkage between a dojo.data query or store and a widget which represents it could be as simple as assigning a property to the widget object.

    So are workarounds to this possible? I think they are, and I'm testing some of them out for use in Dojo 1.1 right now. I'll post more about them should they pan out. Every avenue which looks potentially workable right now involves gigantic hacks which also deeply constrain API designs. Fundamentally, this problem can't be solved without good language-level support.

    It's perhaps folly to assume that this will be addressed in IE 8, but given the enormous back-pressure of nearly every JavaScript toolkit author demanding this feature and the embarrassment of every other browser beating them to the punch, I have some hope that we could see getters and setters for JScript in the near future. It won't matter much, though, unless the JScript team ships their new engine to all IE versions when they release IE 8. Not bloody likely.

  3. Performance

    Kudos are in order to the JScript team for fixing their long-b0rken GC heuristic and pushing it out to everyone...but it's the tip of the iceberg.

    Performance is one of those areas where differences in implementations can tightly circumscribe what's possible despite exacting spec conformance. On this front, JScript's raw VM-level execution time leaves a lot to be desired, but the true travesties really show up when you hit the DOM for computed style information or try to do anything reasonably complicated that involves string operations.

    Most non-trivial blocks of JS code today rely on innerHTML to bootstrap some new chunk of DOM in response to user action due in large part to the cross-browser speed and size advantages of innerHTML vs. raw DOM methods for equivalent DOM structures. This reality pushes IE's string performance woes to the fore as more and more client-side systems push far enough to hit the new "wall".

    Similarly, getting computed box model calculations out of IE is not for the faint of optimization foo. When we profiled Dojo's widgets to plan our attack for 0.9 and 1.0, we noted very quickly that getting box-model data out of the browser for any element is hugely costly on every browser, but on IE the cost was not just big... it was enormous. Our best guess right now is that the properties on the currentStyle property are re-calculated when they're requested from script and not cached in the bound object when layout happens. The resulting performance penalty requires that developers nearly never manage layout in code, severely constraining the layouts which are attempted by toolkits like Dojo.

    Across the board, from DOM performance to raw JScript execution speed, IE is a dog, and the odds are good that whatever toolkit you're using spends a lot of time working around that reality.

  4. Doctype Switching

    Doctype switching to toggle box-model behavior is perhaps the single most limiting implementation error in IE. Saner browsers allow you to use a CSS property to affect the layout model in use in a particular section of a document. This makes tons of sense in a templated world where most of the markup your system generates starts and ends in the "news hole". Today, that covers nearly everyone. A quick line count in any HTML document shows that doctypes are a scarce resource whose scarcity is made problematic when it's semantics are overloading. I've been on product teams where the idea of changing the doctype would require months to recover from. That kind of cost related to what should be simply a markup dialect change (not a formatting policy change) implies strongly that the doctype is a terribly brittle way to control several independent concerns.

    Instead of giving devs fine-grained layout system control, IE makes it all-or-nothing. The global flag approach backs toolkit developers into doing script-based layout calculations or "just throw it in another div" solutions where we'd really rather not. Both are slow and both may be required since it's completely impractical to dictate to users which doctype they'll be using. While any app may be able to be disciplined enough to not care, toolkit developers must work everywhere. Hilarity ensues.

    I fear this is going to get even worse with IE8 as the IE team looks to implement some of HTML 5 and hopefully many of CSS 2.1's clarifications. The sooner they abandon the global switch, the better...but I'll wager it's pain they just don't feel. Building a browser is a very different pursuit from building portable apps to run inside it.

  5. HTC's Can't Be Inlined (Even With Hacks)

    Modern browsers have built-in widget systems. On IE, it's HTCs + Viewlink and on Firefox it's XBL. Even a cursory reading through the docks for both is enough to illuminate the gigantic overlap. Alas, no one is yelling at them to standardize and the result is a terrible mess in which both sub-optimal formats limp along with nearly zero Open Web usage.

    So why do I single out IE for whipping here when XBL is just as lame and similarly b0rken with regards to single-file embedding? Well, on Mozilla, you have a lot more "outs". I strongly suspect that you can use "data:" urls to generate and evaluate component definitions for FF, which would enable compiling down from a single (more sane) format in the running page environment. IE prevents any such useful code-loading approaches, meaning you have to generate files on disk in their b0rken-ass format in order to be able to use them. Given how far we've gotten with non-builtin widget systems, it's pretty clear that toolkit authors aren't going to contort themselves into requiring a build step that splats files all over disk just so that we can give IE and Mozilla different views of the same component description. Instead, we all limp along on our own class hierarchies without any of the benefits of element subclassing, getter/setter handling, and inline (scoped) method description that these browser-provided systems would allow. It's kind of pitiful, really.

    IE8 may include strong "data:" URL support given that it's needed to pass Acid2, but I'm not holding my breath. I strongly suspect that HTC's are a dark, unloved corner of the Trident codebase that none of the current engineering team are really fired up about fixing (which they could have done by just allowing Data Islands to contain HTC definitions...but I digress). The takeaway here is that we probably shouldn't even need JS toolkits to build widget systems, but it's too late now. We're an abstraction short and a decade late and now the Flex frankenstein is beating HTML at it's own game.

In a vacuum of feature data or builds to work with, it's prudent to assume that IE 8's DOM and JavaScript implementations will continue to warp attempts at building useful, idiomatic JavaScript libraries to ease the problems that HTML + CSS aren't effectively solving. So next time you wonder why your toolkit of choice is built the way it is or why it's even necessary, just remember that in many cases they are protecting you from a decade or more of bad decision making.

From that perspective, they're worth every penny.

Older Posts

Newer Posts