Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Cannot wrap 'Element''s 'addEventListener' method

69 views
Skip to first unread message

christopherbalz

unread,
Jul 28, 2008, 3:11:58 PM7/28/08
to
On Firefox ( v2 and v3, and probably the earlier versions) it does not
seem possible, at least under some circumstances, to extend the
'addEventListener' prototype of the 'Element' object in JS or to
extend the prototype of its inheriting objects. This forces
JavaScript frameworks that would like to leverage Element's prototype
for this (or its inheriting objects) to treat Firefox like IE in this
case -- they have to perform an extension operation on every single
individual dom element that they need framework-style behavior from.
That's a significant performance hit.

In a case where WebKit (Safari 3) works perfectly, I get either a
silent failure (property extensions of 'Element.prototype' are
successfully made, but 'addEventListener' is still the native one and
not the extended one intended by the code) or (for example when trying
to extend 'HTMLDivElement.prototype) an explicit exception ("Illegal
operation on WrappedNative prototype object").

Given this code at line 469 of https://us.etrade.com/javascript/et1/src/core/dom/Element.js


if( defined( self.HTMLElement )
&&
( !
HTMLElement.prototype.hasOwnProperty( "addEventListener" ) &&
!
HTMLDivElement.prototype.hasOwnProperty( "addEventListener" ) ) ) {
Element.prep( Element.prototype );
}

If I add a blanket pass to the conditional above like so:

if( true || defined . . .

Then what happens is that the 'addEventListener' method acts like (and
possibly is) the original one, resulting in a loss of the intended
object context. The JavaScript framework intends that the new
'addEventListener' method (wrapped in 'Element.js'; url above) take
an object-context argument, but that is lost, resulting in the object
context being the dom element.

That causes an error since the expected methods are not on that
object:

FF 3 (but same on FF 2):

Welcome to “Venkman”, the JavaScript debugger. Please read the FAQ
at <http://www.hacksrus.com/~ginda/venkman/faq/venkman-faq.html>.
Visit the Venkman homepage <http://www.mozilla.org/projects/venkman/>
for more information.
You are running Venkman version 0.9.87.4.
Use “/help <command-name>” for help on specific commands.
Visit <x-jsd:help> for a searchable command reference.
Commands start with a forward-slash ('/') character. Any text that
DOES NOT start with a forward-slash will be evaluated as JavaScript.
For example, to execute the“step” command, type “/step”. To evaluate
“1 + 1”, just type “1 + 1”.
Recorded local startup 119, global 5805809.
Error ``this.emitEvent is not a function'' [x-] in file ``https://
lxdev6m0.etrade.com:14755/javascript/et1/src/lib/model/messenger/
request/mechanism/ScriptInclude.js'', line 135, character 0.
Stopped for error handler.
#0: function anonymous(p_eEvent=Event:{0}) in <https://
lxdev6m0.etrade.com:14755/javascript/et1/src/lib/model/messenger/
request/mechanism/ScriptInclude.js> line 135
133: eventLoad : function( p_eEvent ) {
134: // Let the application know that a response has been received
from the server:
135: this.emitEvent( p_eEvent );
136: },
137:
Continuing from error handler.

Unfortunately, allowing custom js objects to listen to dom events with
methods (not plain functions) is very critical. The only way
currently to get this on FF (and on IE) is to wrap the dom objects on
an individual basis. Apparently, using the prototype property here is
not possible. But as mentioned above, it works fine on WebKit (Safari
3).

When I try an alternative approach, by moving down the prototype chain
to 'HTMLDivElement' (which is a much less preferable alternative than
just extending 'Element'), doing this:

Element.prep( HTMLDivElement.prototype );

I get this error (same on FF 3 and 2):

Exception ``[Exception... "Illegal operation on WrappedNative
prototype object" nsresult: "0x8057000c
(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame ::
https://lxdev6m0.etrade.com:14755/javascript/et1/src/core/dom/Element.js
:: anonymous :: line 430" data: no]'' thrown from function
anonymous(p_oElement=XPC_WN_ModsAllowed_NoCall_Proto_JSClass:{69}) in
<https://lxdev6m0.etrade.com:14755/javascript/et1/src/core/dom/
Element.js> line 430.
Stopped for thrown exception.
#0: function
anonymous(p_oElement=XPC_WN_ModsAllowed_NoCall_Proto_JSClass:{69}) in
<https://lxdev6m0.etrade.com:14755/javascript/et1/src/core/dom/
Element.js> line 430
428: // If not an Element/HTMLElement prototype, and 'nodeType' !
= 1, return here:
429: if( !( oE === self.Element.prototype ) &&
430: ( oE.nodeType != 1 ) ) { // 'self' is used as an
optimization.
431: oE.bPreppedByEt1 = true;

A complete example of this code in context may be found at:

https://us.etrade.com/javascript/et1/dev_tools/test/harness/lazy_loader_inline_script/index-src.html

In sum: Is this a bug in Firefox? It seems so. Requesting feedback
on that point: If this is a bug, I'll report it.

Last but not least: Does anyone know of any workarounds?

Thank you in advance,

- cb

christopherbalz

unread,
Jul 28, 2008, 6:15:27 PM7/28/08
to
= Edit =

a) "extend the 'addEventListener' prototype of the 'Element' object "
in the first paragraph above should read instead, "extend the
'addEventListener' method of 'Element' object's prototype"

= Clarification =

The framework operation performed on 'addEventListener' is technically
a wrap, but has the effect of extending its functionality:

oE.addEventListener = function( p_sEventName,
p_fnHandler, p_oContext, p_bCapture ) {
var fnHandler = Et1.getHandler( p_fnHandler,
p_oContext );
this.plainAddEventListener( p_sEventName,
fnHandler, p_bCapture );

return fnHandler;
};

In the code snippet above (from https://us.etrade.com/javascript/et1/src/core/dom/Element.js
), 'this.plainAddEventListener' is a cached version of the original,
native event listener.

biju

unread,
Jul 31, 2008, 9:03:35 PM7/31/08
to
Sorry if I misunderstood your question..
Try extending Node

Node.prototype.aaaa=1;
document.body.aaaa; //gives 1

BTW, I think this is off-topic to this group "js-engine",
you may want to try on Web-Dev group or a DOM group

christopherbalz

unread,
Aug 1, 2008, 12:15:26 AM8/1/08
to

Thanks for the message. I get properties added to 'Element.prototype'
fine -- except for 'addEventListener'.

The problem is that asymmetry. Since 'Element.prototype' is exposed
in the js-engine, it should work like any other object.

That's why I posted here in js-engine. However, possibly there is a
bug in the exposure of 'Element.prototype' in the DOM implementation.
Then it would be best to post in the DOM group.

If this is actually a browser bug, it would not be a WebDev issue.

Jason Orendorff

unread,
Aug 13, 2008, 1:05:47 PM8/13/08
to
On Jul 31, 11:15 pm, christopherbalz

<ChristopherMB...@stanfordalumni.org> wrote:
> On Jul 31, 6:03 pm, biju <bijumaill...@yahoo.com> wrote:
>
> > Sorry if I misunderstood your question..
> > Try extending Node
>
> >    Node.prototype.aaaa=1;
> >    document.body.aaaa; //gives 1
>
> > BTW, I think this is off-topic to this group "js-engine",
> > you may want to try on Web-Dev group or a DOM group
>
> Thanks for the message.  I get properties added to 'Element.prototype'
> fine -- except for 'addEventListener'.

Well, let's see what we can figure out by playing with JavaScript a
little. I'm using FF3. Answers may vary.

document.body.__proto__ === HTMLBodyElement.prototype
===> true
document.body.__proto__.__proto__ === HTMLElement.prototype
===> true
document.body.__proto__.__proto__.__proto__ === Element.prototype
===> true
document.body.__proto__.__proto__.__proto__.__proto__ ===
Node.prototype
===> true

So far so good. Now, where does an element inherit the
addEventListener method from?

document.body.hasOwnProperty("addEventListener")
===> false
HTMLBodyElement.prototype.hasOwnProperty("addEventListener")
===> true

Hmm. It looks like Gecko reflects interface methods on *every*
prototype that implements them. This is bad news for anyone who wants
to hook this method. Not all addEventListener methods are inherited
from one central place. Instead, there is one per DOM element class.

> The problem is that asymmetry.  Since 'Element.prototype' is exposed
> in the js-engine, it should work like any other object.

Maybe you're right; the currently existing standards don't specify
this.

-j

0 new messages