W3C home > Mailing lists > Public > www-dom@w3.org > April to June 2011

Re: Functions that Implement the EventListener Interface (DOM Level 3 Events)

From: Rob Brackett <rbrackett@apple.com>
Date: Fri, 17 Jun 2011 20:07:33 -0700
Cc: www-dom@w3.org
Message-id: <6D97E5E5-7C68-4996-A8A1-0E6100BA236B@apple.com>
To: Cameron McCormack <cam@mcc.id.au>
Now that I understand better exactly what the specified behavior is for functions that also implement the "handleEvent" property of the EventListener interface, I am curious as to *why* it is specified the way that it is. Specifically, the WebIDL spec states that if the object implementing the an interface with the [callback] attribute and only one operation is a function, that function should be considered the implementation of the operation, even if the function has that operation on it as well.

If the above is a bit confusing, this example from the spec will make it clearer:

    IDL:
    [Callback] interface Listener {
      void eventOccurred();
    };

    interface Thing {
      void addListener(in Listener listener);
    };

    Script:
    // This also works, but it is the function with /* 1 */ in it that is the
    // implementation of eventOccurred.
    var t = getThing();
    var x = function() { /* 1 */ };                    
    x.eventOccurred = function() { /* 2 */ };          
    t.addListener(x);

In the example, when the listener for t is called, it's essentially the same as calling:
    x();
instead of:
    x.eventOccurred();

It seems to me like the other way around (calling x.eventOccurred()) would make more sense.

When I first encountered this with addEventListener(), it was very surprising. At first this might seem like a very strange edge case, but consider the following code (apologies for length, but I don't think it could get much shorter and still be clear):

    var MyConstructor = function (info) {
      // ...create an object that does something with the element...
    };

    MyConstructor.handleEvent = function (event) {
      if (event.type === 'DOMContentLoaded') {
        this.setUp();
      }
      else if (event.type === 'load') {
        new MyConstructor(JSON.parse(event.target.responseText));
      }
      else if (event.type === 'error') {
        console.error('Lookup failed!');
      }
    };

    MyConstructor.setUp = function () {
      // Run through the DOM and find interesting elements
      // Do an XHR to get additional info based on what we found in the DOM
      var request = new XMLHttpRequest();
      request.addEventListener('load', this, false);
      request.addEventListener('error', this, false);
      request.open('GET', someUrlWeMadeBasedOnTheDom, true);
      request.send();
    };

    document.addEventListener('DOMContentLoaded', MyConstructor, false);

While there's a lot cut out of this example and object names have been changed to protect the innocent, this is someone else's real code that I was debugging to figure out what was going wrong. Depending on what browser it was run in, MyConstructor() might be called or MyConstructor.handleEvent() might be called as the listener for various events. Obviously, the latter was the intention of the author. I found the fact that this didn't work in some browsers to be quite surprising!

In the abstract sense, I can understand that functions seem like they should just be called, but it's not at all uncommon for constructor functions in ECMAScript to have properties and other functions attached to them. Since you rarely ever call a constructor function directly (usually you'd use the "new" operator), authors generally don't think of constructor functions as functions. Hence the above error is a very easy one to make.

Is there a reason the behavior of the [Callback] attribute is specified this way? It seems like this conversion was the basis for this decision: http://lists.w3.org/Archives/Public/public-script-coord/2010OctDec/0058.html

But it seems disappointing that such flexibility and consistency in the API would be sacrificed for an optimization in Internet Explorer. I was previously working on a patch for WebKit to match this spec until I realized it is not yet a recommendation. Implementing the patch felt more like I was writing a regression than an improvement, so I wanted to question whether there was any possibility of changing this in the spec. Is there?

Thanks,

Rob Brackett



On Jun 14, 2011, at 3:40 PM, Cameron McCormack wrote:

> Rob Brackett:
>> Ah, those links certainly make the intention much more clear. Thanks!
>> 
>> What is the process for suggesting a revision to the spec? At the very
>> least, the wording for addEventListener's "listener" argument needs to
>> be cleaned up so it doesn't read like it's not finished.
> 
> This list is the right place.  (I’m CCing the editor of that document
> for good measure.)
> 
> -- 
> Cameron McCormack ≝ http://mcc.id.au/
> 
Received on Saturday, 18 June 2011 03:08:01 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Friday, 22 June 2012 06:14:07 GMT