开发者

detachEvent not working with named inline functions

I ran into a problem in IE8 today (Note that I only need to support IE) that I can't seem to explain: detachEvent wouldn't work when using a named anonymous function handler.

document.getElementById('iframeid').attachEvent("onreadystatechange", function onIframeReadyStateChange() {
    if (event.srcElement.readyState != "complete") { return; }
    
    event.srcElement.detachEvent("onreadystatechange", onIframeReadyStateChange); 

    // code here was running every time my iframe's readyState 
    // changed to "complete" instead of only the first time
});

I eventually figured out that changing onIframeReadyStateChange to us开发者_开发百科e arguments.callee (which I normally avoid) instead solved the issue:

document.getElementById('iframeid').attachEvent("onreadystatechange", function () {
    if (event.srcElement.readyState != "complete") { return; }
    
    event.srcElement.detachEvent("onreadystatechange", arguments.callee);    
    
    // code here now runs only once no matter how many times the 
    // iframe's readyState changes to "complete"
});

What gives?! Shouldn't the first snippet work fine?


Shouldn't the first snippet work fine?

Yes, arguably it should. But it doesn't. :-) Fortunately there's an easy workaround (and a better one than arguments.callee, which has issues [see below]).

The problem

The problem is that named function expressions (NFEs, which is what you have there) do not work correctly in JScript (IE) or several other implementations out in the wild. Yuriy Zaytsev (kangax) did a thorough investigation of NFEs and wrote up this useful article about them.

A named function expression is where you give a function a name and use the function statement as a right-hand value (e.g., the right-hand part of an assignment, or passing it into a function like attachEvent), like this:

var x = function foo() { /* ... */ };

That's a function expression, and the function is named. Arguably it should work, but in many implementations in the wild, including IE's JScript, it doesn't. Named functions work, and anonymous function expressions work, but not named function expressions. (Edit I shouldn't have said don't work, because in some ways they do. I should have said don't work properly; more in Yuriy's article and my answer to your follow-up question.)

The solution

Instead you have to do this:

var x = foo;
function foo() { /* ... */ };

...which does, after all, come to the same thing.

So in your case, simply do this:

document.getElementById('iframeid').attachEvent("onreadystatechange", onIframeReadyStateChange);
function onIframeReadyStateChange() {
    if (event.srcElement.readyState != "complete") { return; }

    event.srcElement.detachEvent("onreadystatechange", onIframeReadyStateChange);

    // code here was running every time my iframe's readyState
    // changed to "complete" instead of only the first time
}

That has the same effect as what you were trying to do, but without running into implementation problems.

The problem with arguments.callee

(This is slightly off-topic, but...) You're right to avoid using arguments.callee. In most implementations, using it carries a massive performance overhead, slowing down the function call by an order of magnitude (yes, really; and no, I don't know why). It's also disallowed in the new "strict mode" of ECMAScript 5 (and "strict mode" is mostly a good thing).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜