Broken object referencing from within an array?

V

VK

.... or my script, or my mind, or both?

<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<script>
function init() {
var arr = document.getElementsByTagName('DIV');
for (i=0; i<arr.length; i++) {
arr.addEventListener(
'click',function(e){myFunction(arr,e);},true);
}
}

function myFunction(obj,evt) {
alert(obj.id);
}
</script>
</head>

<body bgcolor="#FFFFFF" onload="init()">
<div id='d1'>Some <a href="javascript:void(0)" id='s1'>link</a></div>
<div id='d2'>Some <a href="javascript:void(0)" id='s2'>link</a></div>
</body>
</html>


The above simply doesn't work - myFunction gets undefined as obj ! But
with a bogus intermediary var it works like a charm:

function init() {
var arr = document.getElementsByTagName('DIV');
var foo = null;
for (i=0; i<arr.length; i++) {
foo = arr;
arr.addEventListener(
'click',function(e){myFunction(foo,e);},true);
}
}

All the same in IE (with attachEvent). What a hey?
 
V

VK

The mistery is growing... I thought that maybe this way I was working
with a collection object and not with a "blue blood" array, and it
might cause some strange problem. So I did this profoundly idiotic
algorithm just to be 100% sure I'm working with a real global array:

<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<script>
// Declaring a global array variable:
var glbArray = new Array();

function init() {
var foo = null;
var tmp = document.getElementsByTagName('DIV');
for (i=0;i<tmp.length;i++) {
// filling up the global array from the collection:
glbArray.push(tmp);
}
for (i=0; i<glbArray.length; i++) {
//
glbArray.addEventListener('click',function(e){myFunction(glbArray,e);},true);
// ^ NOOP !!! myFunction gets undefined
foo = glbArray;
foo.addEventListener('click',function(e){myFunction(foo,e);},true);
// ^ Works like a charm !!!
}
}

function myFunction(obj,evt) {
alert(obj.id);
}
</script>
</head>

<body bgcolor="#FFFFFF" onload="init()">
<div id='d1'>Some <a href="javascript:void(0)" id='s1'>link</a></div>
<div id='d2'>Some <a href="javascript:void(0)" id='s2'>link</a></div>
</body>
</html>

All the same with IE. Any light?
 
M

Michael Winter

... or my script, or my mind, or both?

Don't assume that a subject has been, or can be, read in full.

The type attribute?
function init() {
var arr = document.getElementsByTagName('DIV');
for (i=0; i<arr.length; i++) {
arr.addEventListener(
'click',function(e){myFunction(arr,e);},true);
}
}


When a closure is created, it doesn't take a snapshot of the variables
in its scope chain - they remain live. By the time the listener is
invoked, i will 'point' beyond the defined array contents, providing
undefined to myFunction.

With addEventListener, or the on<type> properties, the better approach is:

function init() {
var arr = document.getElementsByTagName('div');

for(var i = 0, n = arr.length; i < n; ++i) {

/* Unless you have a reason to use
* event capturing, don't bother.
*
* The capturing phase should only
* be used to intercept events
* before 'normal' event listeners
* receive the event.
*/
arr.addEventListener('click', myFunction, false);
}
}
function myFunction(e) {
/* The this operator will refer
* to the correct DIV element.
*/
alert(this.id);
}

Unfortunately this won't work with Microsoft's attachEvent mechanism
(the this operator isn't set correctly), which is why it's broken and I
never use it.

[snip]
But with a bogus intermediary var it works like a charm:

No it doesn't.

[snip]
for (i=0; i<arr.length; i++) {
foo = arr;


The foo variable will always refer to the last DIV element encountered.
Click the first link in your example and this is obvious.

[snip]

Mike
 
R

Richard Cornford

VK wrote:
function init() {
var arr = document.getElementsByTagName('DIV');
for (i=0; i<arr.length; i++) {
arr.addEventListener(
'click',function(e){myFunction(arr,e);},true);
}
}


The function passed to abbEventListener is executed long after the loop
has finished executing, and when the containing function's local
variable - i - has been incremented beyond the range of the contents of
the - arr - collection.
The above simply doesn't work - myFunction gets undefined as obj !

Yes, the contents of - arr[arr.length] - are undefined, and - i ==
arr.length - at the end of the loop.
But
with a bogus intermediary var it works like a charm:

You really should test these things a bit less superficially before
making that type of statement. The local variable - foo - always
contains a reference to the same DOM element regardless of how many
event listeners were attached, and which is executed.
function init() {
var arr = document.getElementsByTagName('DIV');
var foo = null;
for (i=0; i<arr.length; i++) {
foo = arr;


This assignment happens - arr.length - times, and the last assignment
sets the value of - foo - that the event handlers will be using.
arr.addEventListener(
'click',function(e){myFunction(foo,e);},true);
}
}

All the same in IE (with attachEvent). What a hey?


Closures!

<URL: http://jibbering.com/faq/faq_notes/closures.html >

Richard.
 
V

VK

You really should test these things a bit less superficially
before making that type of statement.
- Sorry, I really was in rush

I solved the problem by moving the handler assignement out of the loop:
<http://groups-beta.google.com/group...ccb9f46722f/a5395b4a5dd6472b#a5395b4a5dd6472b>

for (...) {
assignHandler(arr);
}

and:

function assignHandler(obj) {
obj.addEventListener('click', function(e){processClick(obj, e);});
}

I guess it's one of these rather rare situations when the programming
logic goes against the human one.
However it was with closures (I guess there are reasons for them to be
such), it's still "strange" that the program resolves obj at the
function creation time, but refuses to do the same for arr (an array
element).
 
R

Richard Cornford

VK wrote:
I guess it's one of these rather rare situations when
the programming logic goes against the human one.

Speak for yourself. The scope structure in javascript is completely
logical, mechanically and in the perception of (most) humans.
However it was with closures (I guess there are reasons
for them to be such), it's still "strange" that the program
resolves obj at the function creation time, but refuses to
do the same for arr (an array element).


Just once it would be nice if you would actually go and read the
resources that people refer you to. You might then eventually get into a
position where some of what you think is not twaddle.

Richard.
 
L

Lasse Reichstein Nielsen

VK said:
However it was with closures (I guess there are reasons for them to be
such), it's still "strange" that the program resolves obj at the
function creation time, but refuses to do the same for arr (an array
element).


Not so strange. There is only one "i" variable, and all the closures
refer to that one. When it changes its value, it affects all closures.
However, since "obj" is a formal parameter of a function, there is
effectively a new variable for each call. It never varies, and each
closure refers to its own version.

/L
 
L

Lasse Reichstein Nielsen

Richard Cornford said:
Speak for yourself. The scope structure in javascript is completely
logical, mechanically and in the perception of (most) humans.

The scope structure is fine and logical, it's just its interaction
with variables that won't feel natural to me. Probably because I
learned closures in a functional language where variables didn't,
well, *vary* very much. :)

/L
 

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,770
Messages
2,569,586
Members
45,090
Latest member
ActivlifeKetoDiet

Latest Threads

Top