Many developers I know don’t like working with IE because it is so “incompatible” with Firefox and Safari. This is true, but what most people don’t know is that 90% of IE’s problems are actually isolated to just one part of it’s JavaScript implementation and if you work with that one part correctly, you can largely avoid the potholes that make cross-browser such a pain.
A Tail of Two Doms
The DOM is the in-memory representation of an HTML document. Most people know the DOM from its familiar JavaScript API like insertBefore(), removeChild(), cloneNode() and so on. But actually, the DOM is much more than that. Whenever a web browser reads in an HTML document, it builds an in-memory representation of that document — a graph of objects called the “Document Object Tree”, but which people usually refer to as the “DOM”*.
Now the real DOM used by wedding browsers is not really JavaScript; it’s native code, written in C++. This makes the browser very fast and parsing and displaying HTML but it presents a problem for the JavaScript. How do JavaScript scripts get access to the objects in the DOM? It turns out that IE and FireFox/Safari/Opera diverge significantly on this point and it is the single biggest cause of browser incompatibilities that I know.
The Peer Model
Intentional or not, both Webkit and Firefox as I understand handle this problem in a similar way.** They create, in effect, a parallel tree of semi-pure JavaScript objects that match exactly the C++ DOM objects used to render and display the page. The figure below shows how this works. (Using my handy Skitch whiteboard…)

(Interesting aside, btw, most browsers actually have a third parallel tree of peer objects called Display Tree or something like that. This is a set of native objects written in C++ that actually handle drawing the DOM objects in your window. A web page is really much more sophisticated than it looks!)
It turns out that the peer model for JavaScript is actually a very nice one because the DOM objects you see when you write JavaScript are real JavaScript objects. They have prototypes, inherit methods, throw exceptions, and can even have their properties overridden. This makes it easy to do nifty tricks like enhancing the root HTMLElement class to add the $() method like jQuery and Prototype do. It also means that DOM objects tend to have largely the same behavior as other objects so you can reliably predict how they will behave in most situations.
The Wrapper Model
IE, on the other hand, takes a much different approach to exposing the DOM in JavaScript. DOM objects in IE are essentially “wrapped” by a thin veneer in the JScript engine that makes it possible for you to query and set properties and call methods on these native C++ objects through script. The figure below shows the difference with this model.

Now, actually, wrapping the DOM elements like this in IE does have its advantages. In some ways you are much closer to the metal of the browser so to speak and therefore able to exert a bit more control. The dreaded hasLayout property for example exposes some good internal information about how IE will treat the rendering of an element that would be useful in other browsers when trying to deal with complex layouts. (The programming by side-effects model required to manipulate this property, of course, is another matter.)
On the downside, however, wrapping DOM elements makes IE subject to the effect of leaky abstractions. Since DOM objects are not real JavaScript elements, sometimes they don’t behave just like you expect. In fact, if you’ve ever hit one of these doozies, you’ve been bitten by the DOM-Wrapper IE bug:
- You set a property on a DOM object and that property mysteriously changes to some other value; even if it is not part of the DOM’s published API.
- You tried to modify the prototype of the DOM element (i.e. HTMLElement) and discover it doesn’t show up on existing DOM objects.
- You added a method to the Function prototype, yet certain functions on DOM objects seem not to have them.
- You try to iterate through the key/value pairs on a DOM element and certain keys you expect to see are not included (actually this one can happen in FF at times because of their own leaky-abstractions.)
- You store a JS objects in some property on a DOM element and now it leaks when you leave the page. Yep, the dreaded IE memory leaks are caused by wrapping behavior in IE.
The list goes on and on. Weird, seemingly unexplainable behavior suddenly makes sense when you realize the object you are working with is not JavaScript, even though it pretends to be so; it is a native C++ object only posing as one instead.
How to Program IE with Less Pain and More Joy
The models used by Firefox/Webkit and IE are plainly very different when it comes to the DOM. How can we use this power to our advantage? The answer is quite simple: treat all DOM objects like foreign objects. Forget that they are JavaScript in Firefox and Safari. Don’t add properties or methods to DOM elements. Don’t fiddle with their prototype definitions. Only work with their published APIs.
Use the best practice followed by the browsers and create a peer tree of pure JavaScript objects that can mange your DOM elements for you. These view objects can have all of your special high-level methods for manipulating the DOM build in and manage the DOM objects in your tree for you. Conveniently, this also creates a nice abstraction layer you can use to hide other browser differences as well.

This is the strategy used by SproutCore Views. Each SC.View manages a single DOM object and sometimes a few of its children. They reimplement much of the API actually used by the DOM. This way DOM objects remain pure DOM elements manipulated by a pure JS layer defined by the view.
Unfortunately, most JS libraries today follow exactly the opposite approach. Almost all of them try to modify the root prototypes of elements to add their own methods. This isn’t really possible to do in IE since DOM objects are not real JavaScript so instead these libraries will try to infect each element they come in contact with with their methods to try to simulate the effect instead. This is not only slow but leads to a wide variety of bugs.
Less DOM, more JS
So there you have it. There of course are a lot of other minor differences between how the JavaScript language in FireFox and Safari and JScript in IE work, but most of them are easy to paper over. The real tricky bugs that keep you up all night, however, are often driven from the differences in the DOM models. To solve this problem, simply treat your DOM objects like foreign objects. Follow the published APIs on these objects but avoid adding your own properties or enhancements on top.
Or use a framework that does this for you like SproutCore.
Either way, if you understand this difference you’ll find debugging IE is not only easier but sometimes it a little bit fun. Like learning a foreign dialect of your own native tongue; a little weird but mostly harmless and interesting,
* OK, for the pedants out there, technically the Document Object Model, or DOM, is the API defined by the W3C that the Document Object Tree (DOT?) conforms to. Most people I know however simply refer to the Document Object Tree as the DOM, so I’m going to stick with that convention here as well.
** Also for the pedants: I am aware that both FireFox and Safari do not strictly keep a “peer” tree of JavaScript objects for all DOM elements in page. However, this is the direction they appear to be moving conceptually and from a developer’s standpoint it’s easier to keep this mental model in the head than to deal with the actual complexity of the situation that is FireFox and Safari both blend the peer-tree and the wrapper models to some extent, mostly for historical reasons. Either way if you follow my suggestion and always
Tags: Uncategorized by charles
11 Comments »