List Info

Thread: Event.observe problems




Event.observe problems
user name
2006-09-07 11:20:50
Cheers for your quick reply -
that's really odd that they all use the same callback, I
thought that
as the app steps through the for loop, then as the 'i' is
incremented
then it would also act as the input variable for the event
it's being
assigned to.
If i put:
Event.observe($('star1'), 'click',
function(e){objRating.setRating(1)});
Event.observe($('star2'), 'click',
function(e){objRating.setRating(2)});
Event.observe($('star3'), 'click',
function(e){objRating.setRating(3)});
Event.observe($('star4'), 'click',
function(e){objRating.setRating(4)});
Event.observe($('star5'), 'click',
function(e){objRating.setRating(5)});

then it all works ok.
very odd - still trying to get my head around it!

Sorry to ask, but in your example, do you suggest using that
line 5
times or is that an indication to looping 5 times?

Cheers,
j


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Ruby on Rails: Spinoffs" group.
To post to this group, send email to
rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to
rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at h
ttp://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 11:40:54
Actually, no worries, I figured it out from your example -
thanks
loads, that's been causing me headaches!
j


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Ruby on Rails: Spinoffs" group.
To post to this group, send email to
rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to
rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at h
ttp://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 14:42:42
What Eric was saying was not that they are all using the same callback, but that they are all referencing the same i variable at the same address in memory, which at the end of your loop holds the value 6. You would have been fine if you had created a locally scoped variable new_i within the loop and used that in the callback (of course Eric's alternative also works fine, but I'm just trying to clear this issue up a little in your mind so you know next time)

This would have worked:

for (var i=1; i<=5;i++){
 &nbsp; var new_i = i;
   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

On 9/7/06, jumblesale <gmail.com">mcgantsgmail.com> wrote:

Actually, no worries, I figured it out from your example - thanks
loads, that's been causing me headaches!
j







--
Ryan Gahl
Application Development Consultant
Athena Group, Inc.
Inquire: 1-888-919-8700 x2903
Blog: http://www.someElement.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 15:33:11
* Ryan Gahl wrote (07/09/06 15:42):
> What Eric was saying was not that they are all using
the same callback, 
> but that they are all referencing the same i variable
at the same 
> address in memory, which at the end of your loop holds
the value 6. You 
> would have been fine if you had created a locally
scoped variable new_i 
> within the loop and used that in the callback (of
course Eric's 
> alternative also works fine, but I'm just trying to
clear this issue up 
> a little in your mind so you know next time)
> 
> This would have worked:
> 
> for (var i=1; i<=5;i++){
>    var new_i = i;
>    Event.observe($('star'+new_i), 'click',
> function(e){objRating.setRating(new_i)});
> }

I don't think even this works. new_i is in the same scope.
When I tried 
it, I got all 5s (rather than 6s), which would correspond to
all the 
closures using one value for new_i - the last one assigned.
Variable scoping in closures is hard (for me, anyway) to get
to grips 
with, so I don't think I'll try an explanation - probably
Ryan could doo 
better than me anyway.

These should both work, and are viable alternatives to
Eric's solution:


// Solution One (simplest case of returning a closure from a
function)

for (var i=1; i<=5;i++){
     Event.observe($('star'+(x)), 'click', myfunc(i));
}
// Return the closure from a new function that uses a
variable in local 
// scope
function myfunc(x) {
     return function(e){objRating.setRating(x)}
}


// Two
// Use the prototype bind function to bind the
"this" variable
for (var i=1; i<=5;i++){
     Event.observe(
	$('star'+(x)),
	'click',
	function(e){objRating.setRating(this)}.bind(i)
     );
}

Eric's solution does the same thing, but hides the detail
behind more 
prototype cleverness.

Apologies if I've transcribed these wrong. They worked in a
test page I 
made, but I just used alerts rather than objRating.setRating
etc.

This is all a bit academic considering that the OP says
he's happy now, 
but I find closure code fascinating. I also find the bind()
and 
bindAsEventListener() methods to be some of the best things
about prototype.

Chris

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Ruby on Rails: Spinoffs" group.
To post to this group, send email to
rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to
rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at h
ttp://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 16:18:57
Interesting. I assumed new_i = i would be making a value copy to the new reference, which should be scoped each time through the loop... hmm.

So I wonder if this is what I needed to do... (yes agreed it's academic, but fun to carry on)

for (var i=1; i<=5;i++){
 &nbsp; var new_i = new Number(i);
   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

...or even...

for (var i=1; i<=5;i++){
   var new_i = i.toString();
   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

Now I would really assume either of those should work to force a value copy to the new variable with each iteration. If not, I give up

On 9/7/06, Chris Lear <laculine.com">chris.learlaculine.com> wrote:

* Ryan Gahl wrote (07/09/06 15:42):
&gt; What Eric was saying was not that they are all using the same callback,
> but that they are all referencing the same i variable at the same
> address in memory, which at the end of your loop holds the value 6. You
> would have been fine if you had created a locally scoped variable new_i
> within the loop and used that in the callback (of course Eric's
>; alternative also works fine, but I'm just trying to clear this issue up
> a little in your mind so you know next time)
>
> This would have worked:
&gt;
> for (var i=1; i<=5;i++){
>&nbsp; &nbsp; var new_i = i;
>&nbsp; &nbsp; Event.observe($('star'+new_i), 'click',
&gt; function(e){ objRating.setRating(new_i)});
> }

I don't think even this works. new_i is in the same scope. When I tried
it, I got all 5s (rather than 6s), which would correspond to all the
closures using one value for new_i - the last one assigned.
Variable scoping in closures is hard (for me, anyway) to get to grips
with, so I don't think I'll try an explanation - probably Ryan could doo
better than me anyway.

These should both work, and are viable alternatives to Eric's solution:


// Solution One (simplest case of returning a closure from a function)

for (var i=1; i<=5;i++){
 &nbsp;   Event.observe($('star'+(x)), 'click', myfunc(i));
}
// Return the closure from a new function that uses a variable in local
// scope
function myfunc(x) {
 &nbsp; &nbsp; return function(e){objRating.setRating(x)}
}


// Two
// Use the prototype bind function to bind the "this" variable
for (var i=1; i<=5;i++){
   ;  Event.observe(
   ; &nbsp; &nbsp; $('star'+(x)),
 &nbsp; &nbsp; &nbsp;  'click',
 &nbsp;   ; &nbsp; function(e){objRating.setRating(this)}.bind(i)
  ; &nbsp; );
}

Eric's solution does the same thing, but hides the detail behind more
prototype cleverness.

Apologies if I've transcribed these wrong. They worked in a test page I
made, but I just used alerts rather than objRating.setRating etc.

This is all a bit academic considering that the OP says he's happy now,
but I find closure code fascinating. I also find the bind() and
bindAsEventListener() methods to be some of the best things about prototype.

Chris

Athena Group, Inc.
Inquire: 1-888-919-8700 x2903
Blog: http://www.someElement.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 16:04:57
Shouldn't something like this work and provide more
meaning:

var stars = $('star1', 'star2', 'star3', 'star4',
'star5');
stars.each(function(star, num) {
  star.observe('click',
function(e){objRating.setRating(num);});
});

That of course is with the 1.5_rc1 of prototype and it is
untested.

Brandon

On 9/7/06, Chris Lear <chris.learlaculine.com> wrote:
>
> * Ryan Gahl wrote (07/09/06 15:42):
> > What Eric was saying was not that they are all
using the same callback,
> > but that they are all referencing the same i
variable at the same
> > address in memory, which at the end of your loop
holds the value 6. You
> > would have been fine if you had created a locally
scoped variable new_i
> > within the loop and used that in the callback (of
course Eric's
> > alternative also works fine, but I'm just trying
to clear this issue up
> > a little in your mind so you know next time)
> >
> > This would have worked:
> >
> > for (var i=1; i<=5;i++){
> >    var new_i = i;
> >    Event.observe($('star'+new_i), 'click',
> > function(e){objRating.setRating(new_i)});
> > }
>
> I don't think even this works. new_i is in the same
scope. When I tried
> it, I got all 5s (rather than 6s), which would
correspond to all the
> closures using one value for new_i - the last one
assigned.
> Variable scoping in closures is hard (for me, anyway)
to get to grips
> with, so I don't think I'll try an explanation -
probably Ryan could doo
> better than me anyway.
>
> These should both work, and are viable alternatives to
Eric's solution:
>
>
> // Solution One (simplest case of returning a closure
from a function)
>
> for (var i=1; i<=5;i++){
>      Event.observe($('star'+(x)), 'click',
myfunc(i));
> }
> // Return the closure from a new function that uses a
variable in local
> // scope
> function myfunc(x) {
>      return function(e){objRating.setRating(x)}
> }
>
>
> // Two
> // Use the prototype bind function to bind the
"this" variable
> for (var i=1; i<=5;i++){
>      Event.observe(
>         $('star'+(x)),
>         'click',
>         function(e){objRating.setRating(this)}.bind(i)
>      );
> }
>
> Eric's solution does the same thing, but hides the
detail behind more
> prototype cleverness.
>
> Apologies if I've transcribed these wrong. They worked
in a test page I
> made, but I just used alerts rather than
objRating.setRating etc.
>
> This is all a bit academic considering that the OP says
he's happy now,
> but I find closure code fascinating. I also find the
bind() and
> bindAsEventListener() methods to be some of the best
things about prototype.
>
> Chris
>
> >
>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Ruby on Rails: Spinoffs" group.
To post to this group, send email to
rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to
rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at h
ttp://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 16:53:11
As far as "providing more meaning&quot;... you're simply skinng the same cat another way. Not sure how much "meaning" you can give to a loop by doing way X instead of way Y. The meaning of the loop is still the same

But yes, your way is great as well. I was really just trying to shed some light on the reasons why the OP's (and my) original ways weren't working, so was avoiding the prototype magic.

On 9/7/06, Brandon Aaron <gmail.com">brandon.aarongmail.com> wrote:

Shouldn't something like this work and provide more meaning:

var stars = $('star1', 'star2', 'star3', 'star4', 'star5');
stars.each(function(star, num) {
 &nbsp;star.observe('click', function(e){objRating.setRating (num);});
});

That of course is with the 1.5_rc1 of prototype and it is untested.

Brandon

On 9/7/06, Chris Lear <laculine.com">chris.learlaculine.com> wrote:
&gt;
> * Ryan Gahl wrote (07/09/06 15:42):
&gt; > What Eric was saying was not that they are all using the same callback,
> > but that they are all referencing the same i variable at the same
> > address in memory, which at the end of your loop holds the value 6. You
> > would have been fine if you had created a locally scoped variable new_i
> > within the loop and used that in the callback (of course Eric's
>; > alternative also works fine, but I'm just trying to clear this issue up
> > a little in your mind so you know next time)
> >
> > This would have worked:
&gt; >
> > for (var i=1; i<=5;i++){
> >   ; var new_i = i;
> >   ; Event.observe ($('star'+new_i), 'click',
&gt; > function(e){objRating.setRating(new_i)});
&gt; > }
>
>; I don't think even this works. new_i is in the same scope. When I tried
> it, I got all 5s (rather than 6s), which would correspond to all the
> closures using one value for new_i - the last one assigned.
> Variable scoping in closures is hard (for me, anyway) to get to grips
> with, so I don't think I'll try an explanation - probably Ryan could doo
> better than me anyway.
&gt;
> These should both work, and are viable alternatives to Eric's solution:
>
>
&gt; // Solution One (simplest case of returning a closure from a function)
>
> for (var i=1; i<=5;i++){
>&nbsp; &nbsp; &nbsp; Event.observe($('star'+(x)), 'click', myfunc(i));
> }
> // Return the closure from a new function that uses a variable in local
> // scope
> function myfunc(x) {
>&nbsp; &nbsp; &nbsp; return function(e){objRating.setRating(x)}
> }
>
>;
> // Two
> // Use the prototype bind function to bind the "this" variable
&gt; for (var i=1; i<=5;i++){
>&nbsp; &nbsp; &nbsp;  Event.observe(
>&nbsp; &nbsp;   ; &nbsp; $('star'+(x)),
>&nbsp; &nbsp;   ; &nbsp; 'click',
&gt; &nbsp; &nbsp;   ;  function(e){objRating.setRating(this)}.bind(i)
>&nbsp; &nbsp; &nbsp; );
> }
>
>; Eric's solution does the same thing, but hides the detail behind more
> prototype cleverness.
>
> Apologies if I've transcribed these wrong. They worked in a test page I
> made, but I just used alerts rather than objRating.setRating etc.
>
> This is all a bit academic considering that the OP says he's happy now,
> but I find closure code fascinating. I also find the bind() and
> bindAsEventListener() methods to be some of the best things about prototype.
>
> Chris
>
> >
>

Athena Group, Inc.
Inquire: 1-888-919-8700 x2903
Blog: http://www.someElement.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 16:42:14


On 9/7/06, Ryan Gahl <gmail.com">ryan.gahlgmail.com> wrote:
Interesting. I assumed new_i = i would be making a value copy to the new reference, which should be scoped each time through the loop... hmm.

So I wonder if this is what I needed to do... (yes agreed it's academic, but fun to carry on)


for (var i=1; i<=5;i++){
&nbsp;  var new_i = new Number(i);

   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating (new_i)});
}

...or even...


for (var i=1; i<=5;i++){
&nbsp;  var new_i = i.toString();

   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

Now I would really assume either of those should work to force a value copy to the new variable with each iteration. If not, I give up


The two ways above both end up referencing the same execution context. In basic terms,
new_i is the same place on the "stack" each time, and the created functions all point to it.
(Using quotes because the implementation is not important, just the idea.)
That's why the "function returning a closure&quot; bit mentioned previously works; each time
that function is invoked, an new "stack frame";/execution context is created.

Of course, in Lisp one rarely has these problems, as the loop is typically represented as
a tail recursive procedure... I think that would really throw people off in JS.

--
Matt Jones
gmail.com"> mdj.acmegmail.com
President/Technical Director, Acme Art Company (acmeartco.org)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 17:21:36
Wow, that's in sharp contrast to how I thought it worked, and in fact does work in most languages. For instance in C# or Java, the {} of the for loop would represent a new scope per iteration, so the declared variables would only apply to that iteration's scope, and get garbage collected with each iteration, meaning a new address in memory would be used for the next one. Therefore, it would be possible to force a value copy of a referenced object, possibly using a boxing method if required (like .toString()) or of course constructing a new object using the value of the referenced one.

Obviously, I've been babied now for a while with the $A().each() iterators of prototype, so haven't had to face this problem for a little while in the javascript domain.

Ok, so how about these? I'm moving the variable declaration around the lexical pads here to see where the contexts are actually being recreated, so to speak... (still academic discussion for the sole purpose of getting smarter )

for the "new_i = i" assignment, I'd try any of the three options to try to achieve the desired result, but won't write all the permutations here. (the options being "new_i = i", "new_i = new Number(i)&quot; and "new_i = i.toString()")

var new_i;
for (var i=1; i<=5;i++){
 &nbsp; new_i = i;
   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

or... (in this one, the regular i in the first argument of the Event.observe function is fine, and actually I'm assuming that part has worked fine all along) -- now, this one should really work because the var new_i is being declared in the closure, which should be a brand new scope per iteration of the loop, so one of the 3 assignment options cited above should be able to coerce the value copy we're looking for.

for (var i=1; i<=5;i++){
   Event.observe($('star'+i), 'click',
function(e){var new_i = i; objRating.setRating(new_i)});
}

On 9/7/06, Matt Jones <gmail.com">mdj.acmegmail.com> wrote:


On 9/7/06, Ryan Gahl <gmail.com" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)"> ryan.gahlgmail.com> wrote:
Interesting. I assumed new_i = i would be making a value copy to the new reference, which should be scoped each time through the loop... hmm.

So I wonder if this is what I needed to do... (yes agreed it's academic, but fun to carry on)


for (var i=1; i<=5;i++){
&nbsp;  var new_i = new Number(i);

   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating (new_i)});
}

...or even...


for (var i=1; i<=5;i++){
&nbsp;  var new_i = i.toString();

   Event.observe($('star'+new_i), 'click',
function(e){objRating.setRating(new_i)});
}

Now I would really assume either of those should work to force a value copy to the new variable with each iteration. If not, I give up


The two ways above both end up referencing the same execution context. In basic terms,
new_i is the same place on the "stack" each time, and the created functions all point to it.
(Using quotes because the implementation is not important, just the idea.)
That's why the "function returning a closure&quot; bit mentioned previously works; each time
that function is invoked, an new "stack frame";/execution context is created.

Of course, in Lisp one rarely has these problems, as the loop is typically represented as
a tail recursive procedure... I think that would really throw people off in JS.



--
Ryan Gahl
Application Development Consultant
Athena Group, Inc.
Inquire: 1-888-919-8700 x2903
Blog: http://www.someElement.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

Event.observe problems
user name
2006-09-07 18:38:56


On 9/7/06, Ryan Gahl <gmail.com">ryan.gahlgmail.com> wrote:
Wow, that's in sharp contrast to how I thought it worked, and in fact does work in most languages. For instance in C# or Java, the {} of the for loop would represent a new scope per iteration, so the declared variables would only apply to that iteration's scope, and get garbage collected with each iteration, meaning a new address in memory would be used for the next one. Therefore, it would be possible to force a value copy of a referenced object, possibly using a boxing method if required (like .toString()) or of course constructing a new object using the value of the referenced one.

Obviously, I've been babied now for a while with the $A().each() iterators of prototype, so haven't had to face this problem for a little while in the javascript domain.

Ok, so how about these? I'm moving the variable declaration around the lexical pads here to see where the contexts are actually being recreated, so to speak... (still academic discussion for the sole purpose of getting smarter )

for the "new_i = i" assignment, I'd try any of the three options to try to achieve the desired result, but won't write all the permutations here. (the options being "new_i = i", "new_i = new Number(i)&quot; and "new_i = i.toString()")


The big difference is that Javascript only creates a new execution context for each function, not each block
(different from C++). I found this reference:

http://www.jibbering.com/faq/faq_notes/closures.html

The explanation I found was that Javascript "does not have block scoping&quot;.

As a very short example, this code:

if (true) {
 
var i=1;
}
alert(i);

will show 1, not undefined. (Example from http://overstimulate.com/articles/2006/02/06/javascript-scoping.html )

Hope this helps!


--
Matt Jones
gmail.com"> mdj.acmegmail.com
President/Technical Director, Acme Art Company (acmeartco.org)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs&quot; group.
To post to this group, send email to rubyonrails-spinoffsgooglegroups.com
To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribegooglegroups.com
For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs
-~----------~----~----~----~------~----~------~--~---

[1-10] [11]

about | contact  Other archives ( Real Estate discussion Medical topics )