views:

280

answers:

3

So, I using this snippet <tr onclick="this.toggleClassName('selected')"> on a web page. (a Prototype.js function) It works great. All I do is click on a table row, and it appears to get selected. It works everywhere, except IE and Opera. This is just a convenience, so I don't really care if it works, but in IE it throws an error, and asks the user if he/she would like to debug.

The error message I get is "Object doesn't support this property or method." Which object or which method might it be referring to? Any thoughts on an easy way to implement this functionality in IE? How about just not throwing error messages out to the user?

Thanks, Dave

(http://math.davehampson.net)

+1  A: 

Try using this instead:

$(this).toggleClassName('selected')

Detailed reason why here: http://prototypejs.org/learn/extensions/

Nick
A: 

the 'this' keyword is a javascript fundamental and in your example would return the tr dom node. i suspect that prototypejs is attaching methods to the dom nodes, and for some reason this isn't done/working for IE and Opera.

pstanton
+4  A: 

Yeah, this is basically the main problem with Prototype.

Prototype works differently on different browsers. On browsers that let you play with the prototypes of the host objects (like HTMLTableRowElement), it adds its own functions like toggleClassName to those prototypes, so that every time you get hold of a <tr> you can call that method on it directly. Woohoo, how convenient!

Unfortunately, being able to alter the prototypes of DOM Nodes is something that no ECMAScript or DOM standard actually endorses. It happens to work in Firefox because Mozilla are nice like that, but you can't expect it to work in all browsers; it certainly won't work in IE.

So for other browsers you have to tell Prototype to add (‘augment’) its own methods onto every single object that you want to deal with:

Element.extend(this);

Now you can call this.toggleClassName safely on all browsers.

Once you've augmented that particular <tr>, every time you access it in future, it will still be augmented, so you can still call toggleClassName on it. What's more, if you happen to access an element through Prototype methods — such as $('mytrid') — it will automatically augment it for you.

This is not a convenience feature: this is a trap. It encourages you to write code that happens to work on your browser (that supports prototype-hacking) but will fail elsewhere. It encourages you to write code that happens to work if you interact with the page elements in a certain order, that ensures they all get augmented before the augmented methods are called, but then falls over if you interact with the elements in a different order.

This is a debugging disaster, and it is Why I Do Not Use Prototype.

(Next week, for extra flamebait fun, Why I Do Not Use jQuery Either.)

bobince
It's more or less why I stopped using Prototype too, but I should note that if you always do `$(this).whatever()` then Prototype is smart enough to not re-extend an element that has already been extended. This can mitigate the dependence on things happening in a certain order.
Joel Mueller
OK, I'll bite (even though this is off-topic), why don't you use jQuery? I promise I won't flame you ;-)
Matthew Crumley
Thanks! I don't understand 100% yet, but it did the trick!
the Hampster
@Matthew: I'll do a proper rant at some point ;-) but for the moment these questions (http://stackoverflow.com/questions/1357052/is-jquery-html-buggy-in-jquery-1-3-2 , http://stackoverflow.com/questions/1462649/jquery-memory-leak-with-dom-removal ) uncover a few places where I believe jQuery to be deeply flawed. See also the `clean:` function for some extreme unpleasantness.
bobince
I also have issues with the combination of fluent interfaces, combined gettersetters and extreme overloading (of both arguments and even whole methods) which makes it very easy to write code that doesn't do what it looks like it does. Also there is a heavy emphasis on handling HTML as strings instead of objects, resulting in a rash of client-side XSS exploits (just as we were starting to tackle the server-side ones), and string handling for selectors, eg. the simplest case `$('#'+id)`, which is both much more browser-work than just calling getElementById and will go wrong for `.` or `:` in id.
bobince
Also, `Element.extend(this);` is guaranteed to not work in IE if a script happens to have turned expandos off for the document (`document.expando = false;`)
Tim Down
@bobince: Those are all good points. For me though, they don't outweigh the convenience that jQuery provides when I'm forced to write client-side JS. I switched to jQuery from Prototype for the reason you mentioned (among other things) and never had much reason to try anything else. Sorry if I caused you any additional trauma by reminding you of regex-based HTML parsing :-)
Matthew Crumley