Hi all,
I am having trouble with my variable assignments always becoming the
last variable inside my loop.
var populateAvailableContent = Class.create(methodCall, {
initialize: function($super, name, message) {
$super(name,'getAllContent','Loading...')
},
result: function(ret) {
var div = $('articles');
div.innerHTML = null;
var rowCount = ret.getRowCount();
for (i=0;i<rowCount;i++) {
var item = document.createElement('div');
var id = ret.id[i]
var headline = ret.headline[i];
var html = headline + "<hr />";
item.id = "item";
item.onclick = function(){fc.selectArticle(id,headline);};
item.innerHTML = html;
div.appendChild(item);
}
}
})
In this scenario, id and headline are always becomes the last value in
the loop and that is what is passed in the fc.selectArticle()
function. I've tried a few different possible solutions I've found on
the web, but none seemed to work. I decided to try and give the bind()
function a go around but I can't seem to figure out how to use it in
my situation.
Any help would be greatly appreciated.
on 15.05.2008 23:58
on 16.05.2008 00:38
bpickens@forumcomm.com wrote: > function a go around but I can't seem to figure out how to use it in > my situation. > > Any help would be greatly appreciated. > You can use each() or curry(). In this situation, you don't need a certain scope so curry() is more suitable than bind(). See example below. - Ken Snyder // using each() makes each iteration call a function in its own scope ret.each(function(data, i) { var item = document.createElement('div'); var id = data.id[i] var headline = data.headline[i]; var html = headline + "<hr />"; // don't forget to use a unique id value item.id = "item" + i; item.onclick = function(){fc.selectArticle(id, headline);}; item.innerHTML = html; div.appendChild(item); }); // using curry returns a new function with the current variable values enclosed in that scope for (i=0; i<rowCount; i++) { var item = document.createElement('div'); var id = ret.id[i] var headline = ret.headline[i]; var html = headline + "<hr />"; // don't forget to use a unique id value item.id = "item" + i; item.onclick = (function(id, headline){fc.selectArticle(id,headline);}).curry(id, headline); item.innerHTML = html; div.appendChild(item); }
on 16.05.2008 05:57
If #bind or #curry somehow don't satisfy your needs, there's always a
plain old function wrapping (which simply stores "id" in its own
closure):
...
.onclick = (function(id){
return function(){ fc.selectArticle(id, ret.headline[i]) }
})(id);
...
- kangax
On May 15, 5:57 pm, "bpick...@forumcomm.com" <slim...@gmail.com>
on 16.05.2008 11:34
Hi, Both Ken and kangax have answered the practical question you posed, but you're probably wondering why your code didn't work. :-) It's because in this line of code: > item.onclick = function(){fc.selectArticle(id,headline);}; ...you're assuming that 'id' and 'headline' become fixed at that point in time, that you're burning their *values* into the closure function (the onclick handler) at the point the function is defined. That's not how closures work. They work by giving the function enduring *access* to the things in scope where it's defined. So for instance: function chatter() { var closures; var i; var msg; var closure; closures = []; for (i = 1; i <= 5; ++i) { msg = "This is message #" + i; closures.push(function() { alert(msg); }); } for (i = 0; i < closures.length; ++i) { closure = closures[i]; closure(); } } This *doesn't* show five alerts saying "This is message #1", "This is message #2", etc. It shows five alerts saying "This is message #5", because the functions access the 'msg' variable dynamically when they're *called*, and by the time they're called, msg is "This is message #5". This is incredibly powerful, but can be a bit surprising when you first look at it, especially when the closure lives on after the function returns and you think that the local vars have disappeared (they haven't, the closure preserves them). So this works the same way and has the same result: function chatter() { var closures; closures = createChatter(); doChatter(closures); } function createChatter() { var closures; var i; var msg; closures = []; for (i = 1; i <= 5; ++i) { msg = "This is message #" + i; closures.push(function() { alert(msg); }); } return closures; } function doChatter(closures) { var closures; var i; var closure; for (i = 0; i < closures.length; ++i) { closure = closures[i]; closure(); } } Note that the 'msg' variable lives even though createChatter() has returned; each closure keeps a reference to the scope where it was defined, and so that scope is preserved in memory even when it would have been released if there were no closures involved. (The other local vars to createChatter() also live on; but nothing uses them, so that's just wasted space.) More on closures in my blog (which sadly I haven't had time to post to lately): http://blog.niftysnippets.org/2008/02/closures-are-not-complicated.html http://blog.niftysnippets.org/2008/03/closures-by-example.html This post may also be useful: http://blog.niftysnippets.org/2008/03/horror-of-implicit-globals.html ...as you have an implicit global variable 'i' in your code which you almost certainly intended to be local to your "result" function. ;-) Hope this helps, -- T.J. Crowder tj / crowder software / com On May 15, 10:57 pm, "bpick...@forumcomm.com" <slim...@gmail.com>
on 16.05.2008 18:13
Thank you very much guys. Your suggestions worked for me. @ T.J. You explanations were very insightful. I think that I understand javascript a little better now, thank you.
on 16.05.2008 19:09
heh, sorry but I've got another related problem where the onclick
assignment doesn't seem to take to the element.
I have a function, and inside this function I have another function
assigned to a var scoped variable name. This function creates and
returns a <a> element with a <img> element appended inside it. The in
the original function append the <a> element inside a <div> element.
Here is some of the code:
var deletef = function(id) {
var deleteImg = document.createElement('img');
deleteImg.id = id;
deleteImg.src = '';
deleteImg.alt = 'delete';
var deleteLink = document.createElement('a');
deleteLink.id = id;
deleteLink.href = "javascript:;";
deleteLink.onClick = (function(id){
return function(){alert(id + " deleted...");}
})(id);
deleteLink.appendChild(deleteImg);
return deleteLink;
}
I am then calling the function inside a appendChild function like so:
div.appendChild(deletef(id));
Does any one see here why the onclick assignment doesn't seem to work?
On May 16, 11:12 am, "bpick...@forumcomm.com" <slim...@gmail.com>
on 16.05.2008 19:33
ps, I also tried the curry() method mentioned above by ken to no avail... On May 16, 12:08 pm, "bpick...@forumcomm.com" <slim...@gmail.com>
on 16.05.2008 21:00
bpickens@forumcomm.com wrote: > ... > deleteLink.onClick = (function(id){ > ... > > Does any one see here why the onclick assignment doesn't seem to work? > Try using "onclick"--all lowercase. Of course deleteLink.observe('click', function(id) {... would be even better :) - Ken
on 16.05.2008 21:17
yes, a coworker helped me to discover the error... ugg. You gotta love those. :)
on 17.05.2008 10:36
It also looks like you're assigning the same ID to both the image and the link. IDs have to be unique within the document. -- T.J. Crowder tj / crowder software / com On May 16, 8:16 pm, "bpick...@forumcomm.com" <slim...@gmail.com>