views:

224

answers:

4

Why doesn't the following code work?

var f = document.getElementsByTagName;
var x = f('div');

I get "TypeError: Illegal invocation" in Chrome, "TypeError: Type error" in Safari. I don't get an error in Firefox, but it doesn't work. I haven't bothered testing in IE or Opera yet.

A: 

Try this:

function f(divName){
  return document.getElementById(divName);
}

var x = f('div');

You are trying to call a function, by using the parenthesis. The problem is that 'f', in your code is a variable, not a function.

Moshe
+7  A: 

In Javascript there is no such thing as a "bound method" (to borrow the term from Python, which I hope you already know or the explanation may need to be longer). When you grab a reference to "document.getElementsByTagName" you are merely getting a reference to a function, not a method associated with the document object. When you call it, "this" is set to the window, not the document, so it doesn't work.

Technically doing this will get you what you want, but as you can probably see it's pointless:

var x = f.call(document, 'div')

(It's pointless because it's less readable and not as fast as calling document.getElementsByTagName(). Using a closure is similarly pointless.)

Peter Hansen
Makes perfect sense. Thanks.
Brian
+2  A: 

because in javascript, methods get their this from the object on which they're called, and calling a method stored in a separate variable makes that this be the global context (or window, in browsers). this should work:

var f function ()
{
    return document.getElementsByTagName.apply(
        document
      , arguments
    );
}
var x = f('div');
just somebody
IE doesn't support this.
Crescent Fresh
You can do this without `apply` for two reasons: 1. `getElementsByTagName` receives only *one argument*, 2. Since you are calling the function on the `document` object the context will be *implicitly set*, there is no reason to set it again with apply.
CMS
A: 

The reason is that getElementsByTagName needs to be called on an object. You could say that inside the function definition, it uses this to figure out what element to look for the tags in. When you copy the function and then call it, it will be given a new scope, which means that this no points to a different object. To call the function on document, try this:

var f = document.getElementsByTagName;
var x = f.call(document, "pre");
alert(x[0]);
Marius