简体   繁体   中英

Is it possible to extend the EventTarget interface?

I'm looking for an answer if this is possible, or even a good idea.

Usually, as a fix for IE8 and earlier versions, a workaround similar to this is used:

var addEvent = function(obj, e, cb) {
    if(obj.attachEvent) {
        return obj.attachEvent('on' + e, cb);
    } else {
        obj.addEventListener(e, cb, false);
        return true;
    }
}

and then the programmer would always use addEvent instead of addEventListener .

Is there any way or point to create an addEventListener function that would wrap around the attachEvent when it is undefined?

I imagine it's a bit tricky since the EventTarget can be a DOM element, the document, or the window, or somethign else.

It depends on how far back you want to go. On IE8, you can extend Element.prototype to add features to all HTML elements, which is 90% at least of the work. I'm fairly sure you can't do that in IE6 (PrototypeJS had to resort to extending individual Element instances), I don't remember about IE7. Unless you're targeting east-Asian markets, you can basically ignore IE7 and earlier, though.

Here's an example of how you do that:

(function() {
  // Functions for IE
  function stopPropagation() {
    this.cancelBubble = true;
  }
  function preventDefault() {
    this.returnValue = false;
  }
  function addEventUsingAttach(eventName, handler)
  {
    this.attachEvent("on" + eventName, function() {
      var e = window.event;
      e.stopPropagation = stopPropagation;
      e.preventDefault = preventDefault;
      handler.call(this, e);
    });
  }

  // Function to add `addEvent` to the given target object
  function extendIt(target)
  {
    if (target.addEventListener) {
      target.addEvent = Element.prototype.addEventListener;
    }
    else {
      target.addEvent = addEventUsingAttach;
    }
  }

  // Add it to `Element.prototype` if we have it
  if (typeof Element !== "undefined" &&
      typeof Element.prototype !== "undefined") {
    extendIt(Element.prototype);
  }

  // Add it to `window` and `document` (etc.)
  extendIt(window);
  extendIt(document);
})();

Live Example | Source

Then you manually extend the other EventTargets, like window and document (see the end of the code listing above).


Original answer : I originally misread your question to be about adding addEventListener , specifically, on IE8, whereas in fact your question is quite clearly not about adding that, but adding your own addEvent . I'm leaving this answer in place for other readers:

It depends on how far back you want to go. On IE8, you can extend Element.prototype to add addEventListener to it, and that will be used by all HTML elements on the page (see below). The shim you add can't support the capturing phase, though, because IE didn't support it until they supported addEventListener natively. I'm fairly sure you can't extend Element.prototype on earlier versions (IE7, IE6), PrototypeJS had to fall back to extending specific elements for older versions of IE. But it works in IE8.

Having extended Element.prototype , then you'd have to manually extend the other event targets you mentioned, but extending Element.prototype does most of the work.

But if you do this and you include third-party scripts, beware that they may assume a correct implementation of addEventListeneer complete with the capturing phase.

Here's a basic shim for adding addEventListener to IE8:

(function() {
  function stopPropagation() {
    this.cancelBubble = true;
  }
  function preventDefault() {
    this.returnValue = false;
  }
  if (typeof Element !== "undefined" &&
      typeof Element.prototype !== "undefined" &&
      !Element.prototype.addEventListener) {
    Element.prototype.addEventListener = function(eventName, handler, useCapture) {
      if (useCapture) {
        throw "Browser doesn't support capturing phase";
      }
      this.attachEvent("on" + eventName, function() {
        var e = window.event;
        e.stopPropagation = stopPropagation;
        e.preventDefault = preventDefault;
        handler.call(this, e);
      });
    };
  }
})();

Live Example | Source

Generally, functions can be added to an object by attaching the function to the object

obj.addEventListener = function(/* args*/) { /* body */ }

or by attaching the function to the prototype of the object constuctor

EventTarget.prototype.addEventListener = function(/* args*/) { /* body */ }

With the former, the added function can be called on the particular object only, while the latter lets you call the function on all objects that where or will be created from that particular object constuctor.

However,

  • EventTargets are host objects and therefore it is implementation dependant whether functions can be added to such objects.
  • EventTargets are host objects and therefore it is implementation dependant whether EventTarget.prototype actually exists and has the same meaning as with native objects.
  • If an object adheres to the EventTarget interface, it most likely already has an addEventListener function.
  • The event listener attached with attachEvent has different arguments than the one added with addEventListener .

In the end, it is not feasable to try and make a browser with crappy DOM support more standards complient using JavaScript.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM