onclick behaves differently when defined via javascript

J

Jeremy J Starcher

http://www.frostjedi.com/terra/scripts/demo/this-alert.html
http://www.frostjedi.com/terra/scripts/demo/this-alert2.html

Why, when you click in the black box, do the alert boxes say different
things? Shouldn't they say the same thing?

Your problem is here:
<div onclick="test()"> </div>

It is NOT the same as:
<div onclick=test> </div>

The first form runs the "test()" function within the global context. The
Global Object does not have a property called tagName.


The second form (as well as your second example) use a function
reference, and runs within the context of the div.

Understanding the environment the script runs in is essential to
understanding what "this" refers to.
 
H

Henry

When an intrinsic event attribute is provided for an element (assuming
it is recognised, etc.), such as:-

<div onclick="test()"> </div>

- the browser uses the string value (the "text()" in this case) as the
body text of a function that it creates and assigned to the
corresponding property of the representation of the element in the
DOM. So what the browser does here is the equivalent of:-

divRef.onclick = function(event){
test();
};

(with or without the - event - formal parameter, depending on the
browser)

The difference between the browser doing this and your doing the
equivalent of:-

divRef.onclick = test;

- is that when the browser calls the function it calls the function as
- divRef.onclick(); - (with or without an event object as the
argument, depending on the browser) so the - this - value for the
execution of the function assigned to - divRef.onclick - is a
reference to the DIV element, but the function assigned is different.
One is the function that the browser created (the function that will
then call - test -) and the other is test itself. In the event that
the function called is the one created by the browser then when it
calls - test - it does so in a way that will make the - this - value
inside that call be a reference to the global object.
 
J

Jorge

When an intrinsic event attribute is provided for an element (assuming
it is recognised, etc.), such as:-

<div onclick="test()"> </div>

- the browser uses the string value (the "text()" in this case) as the
body text of a function that it creates and assigned to the
corresponding property of the representation of the element in the
DOM. So what the browser does here is the equivalent of:-

divRef.onclick = function(event){
    test();

};

Correct me if I'm mistaken, but I think I once read somewhere that it
does an eval() of the text: like (in this case) eval('test()')... ?
 
H

Henry

Correct me if I'm mistaken, but I think I once read somewhere that
it does an eval() of the text: like (in this case)
eval('test()')... ?

I cannot tell whether you are mistaken in thinking that you once read
that somewhere, but it is not the case.
 
Y

yawnmoth

Your problem is here:
<div onclick="test()"> </div>

It is NOT the same as:
<div onclick=test> </div>

The first form runs the "test()" function within the global context.  The
Global Object does not have a property called tagName.

The second form (as well as your second example) use a function
reference, and runs within the context of the div.

Understanding the environment the script runs in is essential to
understanding what "this" refers to.

Why, then, don't any of these work?:

http://www.frostjedi.com/terra/scripts/demo/this-alert3.html
http://www.frostjedi.com/terra/scripts/demo/this-alert4.html
http://www.frostjedi.com/terra/scripts/demo/this-alert5.html

I can understand the first one. The first one doesn't, presumably,
work for the same reason that "x=y; y=2;" doesn't result in x equaling
2 - ie. test hasn't been defined and so onclick is set to undefined.

But what about the second and third ones? I tried the third one since
your "It is NOT the same as" didn't include quote marks.
 
J

Jorge

I cannot tell whether you are mistaken in thinking that you once read
that somewhere, but it is not the case.

:)

Still, there's something weird here, something that seems to be
against your theory, see:

<html>
<head>
</head>
<body onload= "alert((document.body.onload === arguments.callee)+'\r
\n'+arguments.callee)">
</body>
</html>

how do you explain this ?
 
D

dhtml

The body tag's onload attribute is really window onload. window has no
tag, so, somebody (probably in Netscape) once had the idea to make the
body tag the place to put event handlers from window. Now, event
handlers for window and body go in the body tag. It is an idea that
seems to be quite popular, even to this day.

Ian Hickson has stated that there is a benefit to having window event
handlers for body attributes and has included at least one new window
event handler as a body attribute (hashchange). Ian could not comment on
what that benefit was. I still haven't figured it out...

The following thread may help explain some more:
http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2008-September/016184.html

Your example's title says:
<title>
onclick behaves differently when defined via javascript
</title>

But the code has:
<body onload= "alert((document.body.onload ===
^^^^^^

So I think you might got onload mixed up with onclick.

if you would change:
<body onload= "alert(window.onload === arguments.callee)">

Op, FF, Saf (and IE, but I cannot test it).
'true'

Or,
<body onclick= "alert(window.onclick === arguments.callee)">

FF3
"true"

Because of the reasons mentioned in the whatwg thread. In firefox, the
body content handlers all map to window. I don't see this as being
invalid behavior, though it's filed as bug in Firefox.

Also, as mentioned in the whatwg thread, the event handler is really on
the window. You can trigger the event by clicking outside of the body
element (give body style="border: 1px solid" to see this in action).

We can also do one better and see that in firefox, the scope chain is
not augmented by the body element nor document.

<body onclick="alert(typeof getElementsByName)" style="border: 1px solid">

FF3:
undefined

Others:
function (or similar impl dependent string)

As we saw in an earlier thread, event handler attributes get an aug'd
scope chain, but this doesn't happen when window "wins" the event
handler attribute. In Firefox, window "wins" for all mouse events.

An event that applies to both body and window, the handler goes to window:
<body onfocus="alert(typeof getElementsByName)" style="border: 1px
solid" tabindex=1>

Best to avoid using event handler attributes in body.

For other elements, event handler attributes have the augmented scope
chain. To be fair and complete, I should also mention that event
handlers that reference other identifiers need those identifiers in the
document first (to avoid reference errors). This often means that
external script tags have to go in the head, which can slow down page
load. (Though loading strategy is a bit off-topic here). Regardless,
there are other ways of registering callbacks.

Garrett
 
H

Henry

:)

Still, there's something weird here, something that seems to
be against your theory, see:

<html>
<head>
</head>
<body onload= "alert((document.body.onload === arguments.callee)+'\r
\n'+arguments.callee)">
</body>
</html>

how do you explain this ?

How do I explain what exactly? If you want something explaining it is
generally a good idea to state what that something is, and given the
very inconsistent handling of the 'promotion' of BODY element event
handlers and scope chain augmentation across browsers it would be a
very good idea to state where you are observing whatever it is you
want explaining in addition to what it was you observed.
 
J

Jorge

The body tag's onload attribute is really window onload. window has no
tag, so, somebody (probably in Netscape) once had the idea to make the
body tag the place to put event handlers from window. Now, event
handlers for window and body go in the body tag. It is an idea that
seems to be quite popular, even to this day.

Ian Hickson has stated that there is a benefit to having window event
handlers for body attributes and has included at least one new window
event handler as a body attribute (hashchange). Ian could not comment on
what that benefit was. I still haven't figured it out...

The following thread may help explain some more:http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2008-September/01...

Your example's title says:
<title>
   onclick behaves differently when defined via javascript
</title>

But the code has:
<body onload= "alert((document.body.onload ===
       ^^^^^^

So I think you might got onload mixed up with onclick.

if you would change:
<body onload= "alert(window.onload === arguments.callee)">

Op, FF, Saf (and IE, but I cannot test it).
'true'

Or,
<body onclick= "alert(window.onclick === arguments.callee)">

FF3
"true"

Because of the reasons mentioned in the whatwg thread. In firefox, the
body content handlers all map to window. I don't see this as being
invalid behavior, though it's filed as bug in Firefox.

Also, as mentioned in the whatwg thread, the event handler is really on
the window. You can trigger the event by clicking outside of the body
element (give body style="border: 1px solid" to see this in action).

We can also do one better and see that in firefox, the scope chain is
not augmented by the body element nor document.

<body onclick="alert(typeof getElementsByName)" style="border: 1px solid">

FF3:
undefined

Others:
function (or similar impl dependent string)

As we saw in an earlier thread, event handler attributes get an aug'd
scope chain, but this doesn't happen when window "wins" the event
handler attribute. In Firefox, window "wins" for all mouse events.

An event that applies to both body and window, the handler goes to window:
<body onfocus="alert(typeof getElementsByName)" style="border: 1px
solid" tabindex=1>

Best to avoid using event handler attributes in body.

For other elements, event handler attributes have the augmented scope
chain. To be fair and complete, I should also mention that event
handlers that reference other identifiers need those identifiers in the
document first (to avoid reference errors). This often means that
external script tags have to go in the head, which can slow down page
load. (Though loading strategy is a bit off-topic here). Regardless,
there are other ways of registering callbacks.

Garrett

Thanks. Wonderful explanation.
I have updated the link, now it can be seen 'this', 'event.target',
and where the method has been attached : it certainly a mess, every
browser does it in a different way.

http://jorgechamorro.com/cljs/018/
 
J

Jorge

How do I explain what exactly? If you want something explaining it is
generally a good idea to state what that something is, and given the
very inconsistent handling of the 'promotion' of BODY element event
handlers and scope chain augmentation across browsers it would be a
very good idea to state where you are observing whatever it is you
want explaining in addition to what it was you observed.

Garret got it perfectly. Never mind. Thanks anyway.
 
S

slebetman

Correct me if I'm mistaken, but I think I once read somewhere that it
does an eval() of the text: like (in this case) eval('test()')... ?


Event assignment API is drastically different between javascript and
HTML. In HTML it does indeed do an eval of the string text supplied as
the event handler, because the event handler is supplied as a string:

<div onclick="alert('hello')"></div>

BUT, (assuming you care about cross browser compatibility, I'm not
sure what some weird browser does if you pass it a string in JS), in
javascript the API is different. It is specified to accept a function
reference instead of a string. In most browsers the following won't
work:

div.onclick = "alert('hello')";

Again, I'd like to stress that there MAY be a couple of browsers out
there that will accept the code above, but in general DON'T DO IT.
Instead, in javascript you must pass it a function:

function sayHello () {alert('hello')}
div.onclick = sayHello;

Which means that if you don't want to declare a function then you must
wrap your code in an anonymous function.

div.onclick = function () {
alert('hello');
}

A word of warning. "this" points to different things depending on
weather you use the HTML API or the javascript API. The HTML API
magically points "this" to the clicked object while the javascript API
points it to the global object. Why is this? I don't know. But if I
were to guess I'd say that the people who created and implemented the
two different sets of APIs were different people and they have
different opinions on what is the right thing to do. As is usual in
software engineering, the standard answer to a "why" question tend to
be "historical reasons".

So, to get the clicked element in the javascript API you usually need
to do someting along the lines of:

div.onclick = function (evt) {
// because IE is different:
evt = evt || window.event;
// evt now points to the event object

// because IE is different:
var target = evt.target || evt.srcElement;
// target now points to the clicked element
}
 
H

Henry

Event assignment API is drastically different between javascript
and HTML. In HTML it does indeed do an eval of the string text
supplied as the event handler, because the event handler is
supplied as a string:
<snip>

This - eval - nonsense needs to be knocked on the head once and for
all. Browsers do not - eval - the string values of intrinsic event
attributes, they use them as (all or part of) the bodies of function
objects that they create internally. This can illustrated by a simple
example; the attribute:-

onclick="return false;"

- is successful, and does what it can be expected to do in whichever
context it is used in. However, if you attempt:-

eval("return false;")

- you will always get an error (a syntax error). For example, IE's
error dialog reports "'return' statement outside of function".

Applying - eval - to intrinsic event attribute values would not work,
so it cannot be what the browser does.
A word of warning. "this" points to different things depending
on weather you use the HTML API or the javascript API. The
HTML API magically points "this" to the clicked object while
the javascript API points it to the global object.
<snip>

Bullshit! With the exception of intrinsic event attributes of the BODY
element (as previously mentioned here) the - this - value in code
specified as the string value of an intrinsic event attribute is the
same vale as the - this - value in functions assigned to the
corresponding intrinsic event properties of DOM elements.

In javascript the - this - value is determined (only and entirely) by
how you call a function and the browser calls the functions it created
from intrinsic event attribute string values and assigned to DOM
element intrinsic event properties in exactly the same way as it calls
the functions programmers my directly assign to DOM element intrinsic
event properties.
Why is this? I don't know.
<snip>

No you don't, but what you don't know is the 'what' not the 'why'.

So, to get the clicked element in the javascript API you usually
need to do someting along the lines of:

div.onclick = function (evt) {
// because IE is different:
evt = evt || window.event;
// evt now points to the event object

// because IE is different:
var target = evt.target || evt.srcElement;
// target now points to the clicked element
}

Whenever the browser triggers this onclick event (and assuming - div -
is a reference to a DOM element that is not the BODY element, as its
name suggests) within the above function the expression - div === this
- would be true.
 
D

dhtml

Henry said:
[snip]
div.onclick = function (evt) {
// because IE is different:
evt = evt || window.event;
// evt now points to the event object

// because IE is different:
var target = evt.target || evt.srcElement;
// target now points to the clicked element
}

Whenever the browser triggers this onclick event (and assuming - div -
is a reference to a DOM element that is not the BODY element, as its
name suggests) within the above function the expression - div === this
- would be true.

In which browser would |div| being the BODY element make any difference?
 
T

Thomas 'PointedEars' Lahn

Henry said:
<snip>

Bullshit!

Yes, indeed. There is no "HTML API" or "javascript API". The DOM
implementation is the API that can be used e.g. with ECMAScript implementations.
With the exception of intrinsic event attributes of the BODY
element (as previously mentioned here) the - this - value in code
specified as the string value of an intrinsic event attribute is the
same vale as the - this - value in functions assigned to the
corresponding intrinsic event properties of DOM elements.

Maybe "slebetman" knows only IE and attachEvent(), whereas the latter is
known to have the "`this' bug" described here in MSHTML. Also, the
`onreadystatechange' event handler attribute (invalid)/property
(proprietary) makes an exception here.


PointedEars
 
H

Henry

In which browser would |div| being the BODY element make any
difference?

My statement does not say there would be a difference if - div - was -
document.body -. It is as assertion about what pertains when it is
not.
 
D

dhtml

Henry said:
My statement does not say there would be a difference if - div - was -
document.body -. It is as assertion about what pertains when it is
not.

Just curious why you mentioned about document.body there. I can't see
how it makes any difference.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top