开发者

Failure to override Element's addEventListener in Firefox

I'm trying to override the Element object's addEventListener method, in a cross-browser manner. The purpose is so that I can load some 3rd party scripts asynchronously, and those scripts call this method prematurely.

I created an HTML file that works perfectly in Chrome, but on Firefox I get this exception:

"Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)"

If you comment out the lines in the file that change the INSTANCE methods, it works. But I need to do it on the "class type" (i.e. prototype).

Any suggestions would be appreciated.

Thanks, Guypo

Here's the file I created

<html><body>
<img id="testImg" src="http://www.blaze.io/wp-content/themes/Blaze/images/header_logoB.png">
<script>
function myLog(msg) { "undefined" != typeof(console) && console.log("Log: " + msg); }
function customListener(type, event, useCapture) {
  // Register the event
  my开发者_StackOverflow社区Log('Registering event');
  this._origListener.apply(this, arguments);
}
// Also tried HTMLImageElement
Element.prototype._origListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = customListener;

var img = document.getElementById("testImg");
// Uncommenting these lines works - but in the real case I can't access these objects
//img._origListener = img.addEventListener;
//img.addEventListener = customListener;
img.addEventListener('load',function() { myLog('load callback'); }, false);

</script>
</body>
</html>


Like Marcel says, this has to do with the way host objects are exposed. The problem is that if you override a predefined property of an interface, it doesn't get changed in the interfaces that inherit from it (at least in Firefox).

Although I agree with Marcel's remarks, there is in fact a way to do this. You should override the property of the lowest possible interface of the object you want to do this for. In your case this would be the HTMLImageElement.

This will do the trick:

(function() {
    var _interfaces = [ HTMLDivElement, HTMLImageElement /* ... (add as many as needed) */ ];
    for (var i = 0; i < _interfaces.length; i++) {
        (function(original) {
            _interfaces[i].prototype.addEventListener = function(type, listener, useCapture) {

                // DO SOMETHING HERE

                return original.apply(this, arguments);
            }
        })(_interfaces[i].prototype.addEventListener);
    }
})();


  1. Please use a valid Doctype, preferably one that cast browsers into (Almost) Standards mode (<!DOCTYPE html> is fine).

  2. typeof is an operator, you don't need the parentheses (but it doesn't hurt)

  3. Most important: don't try to manipulate DOM Element's prototype: those are host objects and you can't rely on host objects exposing any of the core JavaScript functionality of an object. Worse, there may be a performance penalty and you might break other things. Stay away from that technique. For more information, read What’s wrong with extending the DOM.

What is it that you want to achieve, that you want your event listeners to be called automatically when using 3rd party scripts?

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜