Re: ACTION-70: Define the scope chain of onFoo events reference issue-1

On Mar 13, 2006, at 4:45 PM, Jonas Sicking wrote:

> Maciej Stachowiak wrote:
>> Hi everyone,
>> This action item was assigned to me while I wasn't present, so I'm  
>> not sure what it means. I could imagine the following possible  
>> things intended to be covered:
>> 1) Define scope chain for event listeners attached via HTML event  
>> attributes, e.g. <img onclick="handleClick(event)">.
>> 2) Define `this' binding behavior for #1.
>> 3) Define scope chain for events attached via HTML DOM properties,  
>> e.g. myImage.onclick = handleClick
>> 4) Define `this' binding behavior for #3.
>> 5) Define scope chain for events attached via addEventListener().
>> 6) Define `this' binding for #5 (already done).
>> 7) Define scope chain for event listeners attached via SVG 1.1  
>> event attributes, e.g. <image onclick="handleClick(event)">.
>> 8) Define `this' binding behavior for #1.
>> I do not feel like I have the relevant expertise to answer 7 and  
>> 8, but I can try to do some research if it is desirable to define  
>> those.
>
> Ideally I would hope that we can define 7 and 8 to be as similar to  
> 1 and 2 as possible, to reduce author confusion and make CDF more  
> sane.

That would be nice, but I am not sure if it is feasible.

>
>> Here's brief outlines of the behavior I would propose. I have not  
>> tested thoroughly or written any of this up in formal language, so  
>> please let me know which ones need testing and formal language.
>> 1) The attribute text is coverted to a function as if it were the  
>> result of the following expression evaluated in global scope,  
>> where ELT is the DOM object representing the element:
>> (function() { with(ELT) { return function(event) {  ...contents of  
>> attribute here... } } })()
>
> There is need for additional code in here to deal with the return  
> value, i.e. that returning false should call .preventDefault. Other  
> then that I agree with this.

Ugh, you're right, I forgot about this.I think the preventDefault  
behavior is related to being an HTML event listener (set via <img  
onclick="..."> or someImg.onclick=... ), not specifically a property  
of being an attribute-created listener, which means this special  
behavior can't be part of the function itself. We need an extra level  
of indirection to define what happens when an HTML event listener is  
added either via element attribute or JS property.

A first stab at it would be that it's like calling addEventListener with

    function(event) { if (HTML_LISTENER.call(this, event) == false)  
event.preventDefault(); }

Where HTML_LISTENER is the html event listener function, as created  
from an attribute or assigned to the HTML DOM property. This is then  
used as F in the language below.

> However we should probably also define the scope chain for  
> elements. This should clearly ideally have lived in another spec,  
> but I guess we're stuck with having it in events for now?

Scope chain for elements? I don't think they have a scope chain, as  
they are not callable. Maybe I am missing someting...

>
>> The behavior is then as if resulting function F were added with  
>> addEventListener(event, F, false).
>> The upshot of this is that a function is made with ELT at the head  
>> of its [[Scope]] internal property, and "event" is bound to the  
>> event in the function body.
>> I think describing this in terms of the language beats trying to  
>> write prose about the [[Scope]] property though.
>> Also this is subtly incompatible with Win IE, since I believe it  
>> has only window.event, not an actual event parameter to attribute  
>> handlers.
>
> As far as events go I think compatibility with IE is a lost cause.  
> So i'm not too worried here.
>
>> Also, some event listener properties on the <body> element  
>> actually create event listeners on the window object, not the  
>> body, and so do not include the body in their scope chain.
>
> I just verified this in mozilla. Adding on* attributes to the  
> <body> or the <html> tag will make both |this| and the scope chain  
> start on the window object.
>
>> 2) See above - no special considerations for "this" beyond the  
>> effect of addEventListener of a function.
>> 3) Event handler DOM properties have a function value. Getting the  
>> property retrieves any event listener function previously set  
>> using the property or corresponding markup attribute. Setting the  
>> property removes any previous event listener set via that property  
>> or the corresponding attribute, and adds the newly set function as  
>> if with addEventListener. There is no change made to the scope  
>> chain of a function set this way.
>
> You mean that if you call
> element.onclick = handler;
>
> then the scope chain of |handler| is determined by what scope chain  
> the function had when it was created, right. Saying "no change"  
> could be interpreted as if its the same as when the handler is  
> defined inside the attribute.

Right. The scope chain is not changed from its original state by the  
assignment to element.onlclick.

>> The slightly odd implication of this is that you can retrieve an  
>> event listener created via attribute and attach it to another  
>> element, and its scope chain continues to include the original  
>> element, not the new element.
>> 4) See above - no special considerations for "this" beyond the  
>> effect of addEventListener of a function.
>
> I think we should explicitly say that |this| will get the same  
> binding as when a function is provided to addEventListener.

Agreed, it would be good to make it explicit.

>
>> 5) addEventListener does not alter any function parameters passed  
>> to it. In particular it does not alter the scope chain.
>> 6) I already came up with language for this. In brief my proposal  
>> was to define this as equivalent to adding an EventListener  
>> created by the expression { handleEvent: function(event)  
>> { F.applyTo(event.currentTarget, [event]); } } where F is the  
>> function.
>
> I would prefer if we used "call" rather then "apply" to make it  
> clear that only one parameter is passed. It would also make it  
> cheaper for someone implementing this literally.

How about:

   { handleEvent: function(event) { F.call(event.currentTarget,  
event); } }


>> The upshot of this is that the event's current target will be the  
>> "this" object for any function event listener. But if you add an  
>> object with a handleEvent method, then its handleEvent method is  
>> invoked as normal, with the EventListener as the "this" object.  
>> Here again I think it is better to explain the behavior in terms  
>> of ECMAScript expressions than to try to explain in terms of  
>> ECMA-262 spec-internal concepts. It is more obvious how to test  
>> this way, and is more likely to be compatible with future  
>> revisions of ECMA-262.
>
> Sounds good to me.

Cool.

I think we need to decide what aspects of this should actually be in  
the spec.

Regards,
Maciej

Received on Tuesday, 14 March 2006 07:00:31 UTC