On my final editing pass of our upcoming book, The JavaScript Anthology, I’ve spotted what appears to be a previously unrecorded bug in Safari:
function preload(url) {
var img = new Image();
img.onload = function() {
alert(this); // What is this?
};
img.src = url;
}
preload('chewbacca.jpg');
In well-behaved browsers like Firefox, Internet Explorer 6 and Opera 8.5, the above script loads the image and then displays some variation of “[object HTMLImageElement]”, which indicates that this refers to the image object for which the load event has just fired.
In Safari 1.3 and 2.0, however, the alert displays “[object window]”, because this refers instead to the window object within which the script is running.
Naughty Safari! Google didn’t seem to know about this bug when I asked it. Anyone seen this behaviour reported before?
Until this bug gets fixed, you should use a closure to refer to an image object from within its onload the event handler. That is, refer to a variable in the handler function’s enclosing scope.
function preload(url) {
var img = new Image();
img.onload = function() {
alert(img);
};
img.src = url;
}
preload('chewbacca.jpg');






January 31st, 2006 at 2:01 am
Well, I know that in almost any browser, when you alert(this) on anchor elements eg: the ‘a’ tag, you receive the href source. Something I’ve never understood when technically we should be seeing something like HTMLAnchorElement or something like that. If I wanted the ‘href’ I would reference it by saying this.href.
Wierd.
January 31st, 2006 at 4:18 am
It’s a fairly well-known gotcha that simply assigning onclick, onload, etc. in javascript is dangerous.
Stick with addEventListener and attachEvent. Only directly bind event handlers when you absolutely must support old browsers.
January 31st, 2006 at 4:36 am
That bug goes away if you use
var img = document.createElement('img');. This relates to a known Safari bug: http://bugzilla.opendarwin.org/show_bug.cgi?id=3869I would tend to disagree with Etnu on the dangers of onclick etc - I’ve found them to be far more predictable in cross-browser situations than the more modern addEventListener and attachEvent. The benefit of the latter is that they allow multiple handlers to be attached to a single event, but if you don’t need that ability onclick tends to work as well (if not better) with far less browser tricks required.
January 31st, 2006 at 5:15 am
You do see an anchor element Polvero, though it’s not obvious. alert() shows the string representation of an object as returned by toString(). Most HTML elements don’t have toString. Anchors return their href.
Proof that you’ve really got a link object:
<a href="ftp://test" onclick="alert(this.protocol)">Click me</a>January 31st, 2006 at 3:03 pm
MrDuke, I guess I was just expecting something more like HTMLAnchorElement. I was a bit confused by seeing the href attribute being returned.
February 3rd, 2006 at 2:22 am
Using onclick is a bad idea if you plan to distribute your code because your onclick event may overwrite another script’s onclick. However if you’re operating in a relatively closed environment and know that there won’t be interferance, onclick provides a nice and elegant way of assigning events.
February 5th, 2006 at 4:18 pm
Maybe the example got mangled or over-simplified, but there is nothing in the code that defines ‘this’, so therefore it _should_ refer to the top-level ‘window’ object.
If that is different from other browsers then I suspect that they are the ones that have got it wrong, not Safari.
February 5th, 2006 at 8:16 pm
Omnicity,
I’m afraid you need to do some more homework, here.
thisis a special keyword in JavaScript that points to the object through which the currently executing method was invoked. Even Safari does this correctly in most cases.thisdoes not need to be defined; in fact, because it is a JavaScript keyword, attempting to define it would be an error.February 6th, 2006 at 7:22 am
If I’m wrong, then I’m certainly not the only one: first result in Google:
http://www.quirksmode.org/js/this.html
My terminology was probably not perfect though; I agree that
thisdoes not need to be defined like a normalvarbut it does require context, which is often an object constructor, or else anonClick(this)construct. However your example is neither of these, but implies that it one of them was intended.February 6th, 2006 at 7:03 pm
Omnicity,
The page you cite is absolutely right, and it’s saying the exact same thing I’m saying. Read the section under the heading “Copying”.
Their example:
When
doSomethingis called as a method ofelement,thiswill refer toelementin the function body.My example:
This is analogous in structure to the code above, except for the fact that the function is created anonymously–without a name–and is assigned directly as a method of
img(thereby providing the context of which you speak). When the function is called as a method ofimg,thiswill refer toimgin the function body.The page you cite actually has an example of this form under Examples - copying:
February 7th, 2006 at 6:08 am
No, the exact example is shown under examples - referring.
I think you are being confused by the fact that you are effectively using a compound statement. If you re-write the code to use a non-anonymous function you will see why ‘Window’ is the correct context.
February 7th, 2006 at 11:09 pm
Omnicity,
Here’s the example you refer to in full, along with the declaration of
doSomething():In this case,
thisdoes refer towindow, becausedoSomethingis called not as a method of some object, but as an ordinary function.Here’s how my example above would look if it were written the same way:
In this case too,
thiswould refer to thewindowobject, becausedoAlertis being called as a function (doAlert();), not as a method of a particular object (object.doAlert();).But my example above isn’t coded this way. It’s actually coded using the same structure as this example from Examples - copying:
In this case, because the function is being called as an event handler of
element(and thus as a method ofelement),thisrefers toelement.Likewise, in my code:
Because the function is called as a method of
img,thisrefers toimg.Since you think it’s the anonymous function that’s confusing me, here’s how I’d rewrite the code to use a non-anonymous function, and still have
thisrefer toimg:Again, because
doAlertis getting called as an event handler (and thus a method) ofimg,thisrefers toimginside the function.This is again confirmed on the page you cited (see “The difference”).
February 8th, 2006 at 12:08 am
Kevin (and Omnicity):
I think the bottom line that both of you have not mentionned is that the language is interpreted as statements. As such:
var myFunc = doSomething; //1and
var myFunc = doSomething(); //2Are two completely different statements. The first one assigns a function ‘pointer’, or a function call ‘context’ (including possibly a closure) to myFunc, whereas the second one calls doSomething’2 and assigns myFunc the result of that function call.
This might seem trivial, but it’s essential: you can name a function without calling it. And by assigning by name you make a copy of the value of the variable (in this case a function).
Anonymous functions are also a way of naming a function without giving it a name. They do not execute the function, just describe it.
As you will notice in the examples, copying is via:
element.onclick = doSomething element.addEventListener('click',doSomething,false) element.onclick = function () {this.style.color = '#cc0000';} <element onclick="this.style.color = '#cc0000';">and refering via:
element.onclick = function () {doSomething()} element.attachEvent('onclick',doSomething) <element onclick="doSomething()">The first samples are all naming a function, whereas the last ones are all calling it. Except for the pesky attachEvent method which seems to not really abide by a standard way of doing things (namely copying the function ‘pointer’ instead of instancing it).
Kevin, I personally don’t using the term “method of” for these discussions. It is more like you are overriding a virtual function pointer on an object. The method is always there, it’s called “onclick”, you’re just changing the value it holds.
The ruling logic is not about being a method of, but rather the difference of naming a function versus calling it. That is, these things are not limited to object methods.
In essence, it is the onclick method that has a ‘this’ context. Not the actual doSomething function. But when you set the value of onclick to doSomething, you have given it the same context.
Anyways, my two cents.
February 8th, 2006 at 3:24 am
memet,
I see what you’re trying to say, and it’s basically the same thing I’m trying to say — it’s just a different way of looking at things.
You’re saying: if a function is assigned as the value of an object property (”named”), then it receives the object as the value of
this. But if it is just called outright (”called”), it has no object as its context and thereforethisgetswindowas its value.I’m saying: when a function is called as an object method (i.e. the function has been assigned as the value of any object property), it executes with that object as its context, so
thisis the object. But if a function is called as a simple function, then it doesn’t receive any special object as its context, sothisis thewindow.We’re saying the same thing, just using different terminology and a slightly different point of view.
February 8th, 2006 at 7:37 am
To get back on the original topic, I have tried to go to the horses mouth, to see what should happen in this situation.
If you look at the page that is labelled as p.71 of:
The ECMA-262 Spec
you will see three variations on ‘Function Definition’.
The second one, (the first ‘FunctionExpression’), I believe is what we are discussing here. Unfortunatly, in that document there is at least a minor typo: since step two states:
Return Result(2).Which is recursive and illogical, and appears to have knocked out the formatting for the next line.
Does anyone have a correct copy of this section? There is no explanation of why this block differs from the other two, so I don’t want to second-guess the authors intentions.
Personally, I believe that any compound statement in any language should act the same way as the individual statements evaluated individually. If they don’t then it is a bug in the the language. Kevin: I know that you were able to synthesise a similar construct with individual statements that worked the way that you wanted, but it was not the same as merely splitting the compound statement in two.
February 9th, 2006 at 9:06 am
Omnicity,
All along I’ve been saying that the value of
thisdepends on how the function is called, not how it is declared. Quoting from the spec…From p.39:
Translation:
thisdepends on how and where the currently-executing code was called, and what type of code is currently executing (global code, eval code, or function code). In our discussion, the type of code executing is function code.So how is
thisdetermined for function code?From p.40:
The “global object” in a browser environment is
window.Translation: when the caller doesn’t provide a value for
this(i.e. the function was called as a standalone function (e.g.doAlert();),thisiswindow. But when the caller does provide a value (e.g.value.doAlert();),thiswill be that value.Not quite convinced? This is stated more formally later in the spec.
From p.44:
There are a lot of formal declarations to pull apart to understand this, but basically it comes down to Result(7) being
callerincaller.someFunction()when MemberExpression is of the formcaller.someFunction, and beingnullwhen MemberExpression is of the formsomeFunction.Back to you, omnicity:
In this case, it is exactly the same. Starting with this:
…if the only change I make is to declare the function with a name rather than using an anonymous function, then this is what I get:
The only side-effect of this change is to declare
doAlertas a function in the global (windowcontext) namespace. But the fact that I assign a copy of it toonload(thereby putting that copy inimgcontext) means that there will be no change to the way the function executes in response to the event.To get your form, I need to make an additional change, which is to wrap the reference to the newly-declared function in another anonymous function reference:
With this change,
doAlertis no longer inimgcontext when it runs in response to the event. It’s the anonymous function that hasimgcontext, and it callsdoAlertin the global namespace, thus causingthisto bewindow.…of course I don’t expect you to be convinced of this, as you seem to have a different understanding of the meaning of the syntax involved than I do anyway. Allow me to take another approach to convincing you.
Consider my code again:
Also consider my alternative formulation:
If we set this script up so that
imgis a reference to an HTMLimgelement, and then assignimg.srca new value (as my original code above does), then executing either of these code formulations on a range of current browsers (Internet Explorer 6, Firefox 1, and Opera 8 among others) will reveal thatthisis a reference to theimgobject on every browser except Safari.Are you suggesting that Safari is the only browser getting it right? If so, read on…
Now consider this code:
Also consider the alternative formulation:
This time, make
mylinka reference to an HTML hyperlink (anaelement with anhrefattribute). When you click the link in the range of current browsers (including Internet Explorer 6, Firefox 1, Opera 8, and Safari!), either of these formulations will reveal thatthisis the hyperlink (the value of itshrefwill be displayed, as that is what thetoStringmethod of a hyperlink is supposed to do).If Safari were right in the previous case, why is it wrong in this case? Are you saying Safari is the only browser to get it right, and it only gets it right when it comes to the onload event handler of an
Imageobject?You can test even more generally by running this code on various browsers:
As you can see,
thisis notwindow, but the object that I stored inmyObject. This result is consistent across all current browsers, including Safari.And my alternative formulation still holds:
February 9th, 2006 at 9:49 am
That is your assertion, not mine. You are drawing a conclusion based on an initial assumption that you are correct.
It appeared to me that this was Global code, which invalidates the rest ot that portion of your argument.
That’s exactly what I was suggesting, as you are well aware.
I presume that you have seen a program similar to our
Who wants to be a Millionaire?It is well known that in the game, you must only useAsk the Audienceon the easier questions - ‘collective ignorance’ trumps ‘collect intelligence’ once things get tricky.Alternatively, do you think that either George Bush or Tony Blair are really the best people to run their respective countries?
Three against one is hardly an over-whelming majority anyway!
And I don’t expect you to convince me, I expect you to convince yourself before you raise a bug report.
I’m sure you have a valid reason for presenting your example code, but one of the reasons that I have had to think hard about this is that I have never written code in this style, and don’t expect to: __proto__ etc. is there for a reason; I hope you have made that clear in your writings.
Regards, Mike
March 14th, 2006 at 2:19 pm
“You are drawing a conclusion based on an initial assumption that you are correct.” — omnicity, this sentence should apply to yourself.
September 3rd, 2006 at 12:08 pm
Wow - that was way too much to wade through because somebody didn’t know that “global” functions are methods of the window object. Troll?
Anyway, did anyone figure out a workaround for this? I’ve noticed that Safari also returns an onload event for broken images :( and I can’t reference reference it’s properties because of this bug.
September 3rd, 2006 at 12:22 pm
Oh. WHoops. Closure, right
November 28th, 2006 at 4:05 pm
I agree that this is a safari bug, as in all other browsers behaves correctly.
June 23rd, 2007 at 2:22 am
Looks like Safari might doing this:
var theEvent = img.onload; theEvent();When it should be doing this:
var theEvent = img.onload; theEvent.call(img);