agaskar.com

Prototype.js, IE, and method not found

Prototype has two ways of calling a prototype method on an object. The first is the one most people will use: the object is -- to borrow a phrasing from a co-worker -- 'prototype-ized', and then methods can be directly called on it using dot syntax. In the second, static* methods may be called on an object by accessing the method via the class definition and passing in the object you'd like to operate on. To give an example:

Prototype-ized Style:

my_object.addClassName('my_class');

Static Method Style:

Element.addClassName(my_object,'my_class');

Now, in theory**, prototype will automagically prototype-ize any object you may be working on. Ie, when you try to send addClassName to my_object above, it checks my_object to see if it is a Prototype object (rather than just a regular javascript object), an if not, adds the methods. Turns out this can occasionally fail in IE. Yes, of course, just IE. 6 and 7. Turns out this is a HUGE pain to debug, especially since JSUnit seems unable to provide a line number when this occurs (my guess? The line number is left out because the actual error is occurring IN the minified Prototype include). In our case, this happened on when we were trying to operate on a Select element in a form, but I suspect this problem is not necessary limited to particular elements.

Recommendation? Think about calling ALL prototype methods in the second fashion unless an object has explicitly been given the prototype methods via dollar syntax. Yes, it's a LOT of extra typing, but even then, it probably still wouldn't have been eight hours worth of typing, which is how long it took us to track down this bug (although hopefully next time, we'll think of this first).

* This designation is a presumption on my part and may be technically incorrect. You get the idea.
** More like wild-assed guess, but again, from observation of the code I worked on I presume prototype did this under the hood.

Comments

FYI, Prototype has no way to

FYI, Prototype has no way to magically wrap your objects. What prototype does is guarantee that objects returned from prototype methods are wrapped.

In Firefox and Safari, which both provide access to modify the prototypes of the core DOM objects directly, Prototype can set things up at startup and not worry about it again. In IE, however, it has to manually extend individual DOM objects because IE provides read-only access to the core DOM prototypes.

In short, the right fix for this is to always wrap any element you need to access Prototype functions in in $(). $() is a shortcut for getElementById, but, if it receives an element, it just wraps it. It's your guarantee that your object has the properties it needs. See http://prototypejs.org/api/utility and http://prototypejs.org/learn/extensions for more on this.