"Object doesn't support this property or method"

The Problem

Sometimes when you're trying to use some of Prototype's nifty Element extensions, they work on Firefox/Gecko, Opera, and Safari/WebKit, but Internet Explorer gives you the error "Object doesn't support this property or method."

The Short Answer

Wrap the element instance IE's complaining about using $() (or retrieve it using one of Prototype's methods rather than a raw DOM method).

The Long Answer

Consider this code:

function showContent(html)
{
    var container;
    var inside;
 
    container = $('container');
    inside = container.firstChild;
    inside.update(html);
}

…which might be used with this HTML:

<div id='container'><div>This is the inside div</div><div>Some other div following it</div></div>

The intent of the showContent function is to update the first div inside the 'container' div with whatever HTML is passed to the function. It does this by retrieving the container by its ID using Prototype's $() method, then it goes to the first child of that element using the raw DOM property firstChild, and updates its content via the Prototype update() method. (This is a contrived simple example; in reality it's usually a more complex situation.)

If we call showContent('New content');, we expect the text "This is the inside div" will be replaced with "New content". And it is — on Firefox, Opera, and Safari. But not on Internet Explorer. What's going on?

The problem is that on Internet Explorer, the variable 'inside' refers to a raw HTMLElement instance, not a Prototype-extended one, because it was retrieved using a raw DOM method or property. When it can, Prototype extends the DOM in a highly-efficient way by extending the HTMLElement prototype, which is used to create all HTMLElement instances. It can do that on Firefox, Opera, and Safari, meaning that all HTMLElements are automatically extended, even the ones coming back from raw DOM methods and properties. But Internet Explorer won't let it do that, it doesn't allow adding properties to the HTMLElement prototype. So to get the extensions, each HTMLElment instance has to be individually extended. That's what the $() function does when you give it an element instance rather than a string: It checks to see if that element has been extended and extends it if not. There's a more in-depth discussion of this on the Protoype website.

So how to fix it? A generic answer is just pass the element instance through $():

function showContent(html)
{
    var container;
    var inside;
 
    container = $('container');
    inside = $(container.firstChild);  // <= Modified line
    inside.update(html);
}

That solution will work in pretty much all situations.

But there's also another approach: Use a Prototype method to get the instance in the first place, because Prototype always gives you back extended elements — the only reason the earlier code didn't work was that it was using the raw DOM firstChild property.

Prototype provides a huge number of very useful methods for navigating the DOM. In this case, we might rewrite the code like this:

function showContent(html)
{
    var container;
    var inside;
 
    container = $('container');
    inside = container.firstDescendant();  // <= Modified line
    inside.update(html);
}

Prototype's firstDescendant method returns an extended element for the first child element inside 'container'. It does a couple of things for us in this situation: It extends the element, and it skips text nodes (which firstChild does not), and so we could change our HTML to make it more readable:

<div id='container'>
    <div>This is the inside div</div>
    <div>Some other div following it</div>
</div>

Doing that would break the first version of showContent because firstChild would reference a text node with the whitespace between the opening of the 'container' div and the "inside" div. firstDescendant skips text nodes and goes to the element instead.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License