Infrequently Noted

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

Comments for How IE Mangles The Design Of JavaScript Libraries


Alex,

I took a look at that lettable hack - unbelievably clever! I never would have thought of using VBScript.

bobby

by bobby rullo at
Nice post, lots of truth, thanks.

But JavaScript Libraries developpers are not the only victims of IE. I can't recall how many time I coded something (anything) in JavaScript and made it work in Firefox in a breeze with a beautiful minimalist and high level code .. until I make it work in IE and have to thow in a bunch of ugly hacks and conditions. That is so depressing.

I wonder if one day web developpers of the world will unite for a class action against Microsoft for all these years of imposed stress, I'm sure some of us are bald now because of IE g.

by h3 at
Hey bobby:

Yeah, I'll blog more about lettable once I've got all the wrinkles ironed out. It's really painful to use because the lettable objects can't participate in the prototype chain due to the way JScript/VBscript object peering appears to work and the fact that VBScript class instances don't appear to be as mutable as JS objects.

Regardless, it may be enough...we'll see. I still haven't spent any time looking at the performance, which I expect to be terrible on IE.

Regards

by alex at
Doctype switching: We need to go further and have something like the proposed reset element. We need something such as a new CSS attribute to cause an element to become a new Initial Containing Block (and thus a new block formatting context too), along with preventing CSS inheritance from parent blocks.

Only then will HTML componentization become a reality.

by RichB at
As for your last point: http://www.w3.org/TR/xbl/ Just need implementations...
Anne:

I sort of agree...it's just that XBL is also terrible. XBL 2.0 seems to natively support inlining definitions which is an improvement. Regardless, it still forces you into the XML straight-jacket for what is a purely HTML-centric endeavor.

Frankly, most of us would just settle for the selectors API being implemented anywhere...boiling the ocean (ala XBL) might be the right end point, but the transition period needs some love. Any movement on that front from anyone would be hugely helpful.

Regards

by alex at
WebKit is implementing the Selectors API and given that it is fairly simple for UAs to implement I think other browsers will follow soon (given that the specification stays relatively stable and all).
I agree with most of that, except maybe the bit about DOM performance. I would certainly not go as far as saying IE7 DOM performance is good, but I was stunned when I recently made some perf tests on a prototype that was making heavy DOM manipulations: the slow one currently seems to be Firefox 2. Both Opera and Webkit are an order of magnitude faster than IE, but Firefox was about 30% slower than IE. Fortunately, it seems to be getting better in Firefox 3.
Hi Alex:

Great post.

Can you explain a little on the "getter/setter" issue? I am not sure of the significance, here is why:

  1. For JS toolkit developers, writing and offering an API such as "setProperty1(value") vs. your suggested approach takes similar efforts;

  2. For page authoers, using API such as "obj.setProperty1(value)" vs doing "obj.property1=value" takes similar efforts as well.

Why do you think the implicit setter/getter via accessing object properties directly is much better than an explict set/get API?

Thanks.

by Ian S. at
Hey Ian:

WRT to getters and setters, there are a couple of design-ish issues at work.

First, the DOM uses implicit getter/setter support to affect the layout and rendering of many parts of the page. If a widget is going to effectively "subclass" an element, it needs to be able to provide a similarly compact and familiar surface area. XBL and HTC provide this capability as a built-in part of their feature set...it simply boggles the mind that despite knowing it would be a critical requirement in a browser environment having done multiple revs of getter/setter bindings for the DOM, MS didn't provide a language-level construct to get it done. APIs feel more "webish" when you can just set a property and have things happen.

Secondly, many getters and setters can (and should be) auto-generated. One of the major failings with languages like Java is that their getter/setter behaviors require explicit, up-front cooperation. Python and Ruby have nice "outs" (metaclasses and method_missing, respectively) and Java could have bought it's way out of this with getter/setter decorators, but Java's annotations are too stupid to express this kind of compact "set it and forget it" default behavior creation without YAPPT (yet another pre-processing tool). This matters a lot in cases like data binding where we may not actually want to fetch the value until it's actually requested and where writing may need to be "async" and/or batched, which we can't just dish off to a thread in JS-land. Providing stubbed-out property slots allows you to defer the work until it's actually required, and pre-processing tools generally just don't work with JavaScript...sure you can go some distance, but creating a build system dependency is a fast-path to oblivion for modern, idiomatic JS toolkit code. If your tool/code requires some build system and someone else's doesn't, I'll be betting on that someone else (hence all of Dojo's build steps are entirely optional).

Lastly, size matters. You're right that for any instantaneous example, both approaches are roughly the same size and effort, but with auto-generation of getter/setter behaviors across common concerns, plus not needing the extra 5 chars for every operation, you can do a lot to reduce the size of JS code with getters/setters. That matters a ton when you're measuring increases and reductions to toolkit size in bytes (like we are with Dojo).

Maybe this is all environmental...if the DOM had a getStyle("display") method instead of style.display, maybe this wouldn't even be an issue...but we're well down the path now, and going against the grain of the web is a bad call now. It'd be great if the JScript team got with the program on this one.

Regards

by alex at
@alex
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.

You're wrong. Changing the value of a property does not change the property's attributes (DontEnum doesn't change). Why don't you try it:

var oldPop = Array.prototype.pop;

Array.prototype.pop = function() { alert("length before: " +this.length); oldPop.call(this); alert("length after: " + this.length); }; alert(Array.prototype.propertyIsEnumerable("pop"));

[1,2,3].pop();

Whether the DontEnum status is changed should not matter much for Arrays. Normal array iteration is done using a for loop for indexed props.

@alex

This means that while you can use for(var x in list){ ... style iteration,

Using for in on an Array is a bad practice. It is bad because an Array is may have enumerable properties other than its indexed properties. For example, Array.prototype.forEach is sometimes added conditionally:

if(!Array.prototype.forEach) Array.prototype.forEach = function()

If forEach didn't exist, it was added as a new property and is enumerable, by default.

var nl = new dojo.NodeList(document.getElementsByTagName("*")) nl.length; // It is always 1. nl.forEach // function...

The length is always 1, so using a for loop won't work. The dojo.NodeList keeps its items at position 0

items = nl[0];

var nl = new dojo.NodeList(document.getElementsByTagName("*")) nl[0]; nl.forEach; nl.forEach(function(o){alert(o.tagName);});

Produces one alert of "undefined".

by Garrett at
the harshest penalty for .innerHTML is tearing down whats inside before building up the new content. there's an extremely short and simple piece of JS called replaceHTML that avoids this teardown cost and yields significantly improved ability to replace content on the fly. i found replaceHTML on ajaxian.com, it's been very helpful. past that, i've found performance to be mostly a case of best practices: use precached pieces of strings and array.join.
by rektide at
Hello, what do you mean by inline HTC ? For example this has the advantage of being able to add a behaviour to nodes (depending on css selector), without the need for .htc file on the server (really) : var fStyle = document.createElement("style"); fStyle.setAttribute("type", "text/css"); document.getElementsByTagName("head")[0].appendChild(fStyle); // leave the space '( this' var fText = "div.test {behavior:expression(myFunction( this, '"+paramStr1+"','"+paramStr2+"'))}"; fStyle.styleSheet.cssText = fText;

myFunction will receive the node in the this parameter, plus some other constant parameters as strings.

by Kapouer at