1

I'm building a list of buttons and I want each one to trigger the addForm() function with the current members[member].id.

But it happens that only the last button will fire the event.

I know it has something to do with closures and as you can see I have adapted the function to use this pattern.

What am I doing wrong?

function displayConnections(connections) {
 /*(...)*/
 for (var member in members) {
 connectionsDiv.innerHTML += "<p>" + members[member].firstName + " " + members[member].lastName
 + " ID: " + members[member].id;
 btn = document.createElement("input");
 btn.setAttribute("type","button");
 btn.setAttribute("value","Send Message");
 btn.setAttribute("id",members[member].id);
 btn.onclick = function (id) {
 return function () {
 addForm(id);
 };
 }(members[member].id);
 connectionsDiv.appendChild(btn);
 } 
}

Thanks.

asked Mar 18, 2011 at 9:54

4 Answers 4

2

First, remember you are not writing C# or Java. The for (var ... in ...) structure does not iterate a collection. You should always check hasOwnProperty to see if the property name belongs to the object itself:

if (!members.hasOwnProperty(member)) continue;

Then check to make sure that the property value is an object and not a function etc.

Second, your variable btn is lacking a var declaration. You are creating a global variable called btn, not a variable local to your function.

Next, you have a typo mistake in your original code. Your original code actually is interpreted this way (thanks to JavaScript's auto-semicolon-insertion feature!):

btn.onclick = function (id) {
 return function () {
 addForm(id); <-- this id is now the click event's event object, not what you want
 };
};
(members[member].id); <-- this line will have no side effect

In order to run your program in your original style, you need to bracket the function definition:

btn.onclick = (function (id) {
 return function () {
 addForm(id);
 };
})(members[member].id);
answered Mar 18, 2011 at 10:06
Sign up to request clarification or add additional context in comments.

2 Comments

I think you're wrong about simplifying the closure - members[member].id will continue to change on each pass through the loop.
This correction still gives me the last button being the one to fire the event.
2

Well, instead of those workaround you can also use:

element.setAtrribute('onClick', 'javascript:functionName(' + parameter + ');');

which worked for me and should work for you as well.

answered Sep 4, 2011 at 13:31

Comments

0

In this block of code:

btn.onclick = function (id) {
 return function () {
 addForm(id);
 };
}(members[member].id);

You have the syntax wrong - it should be:

btn.onclick = (function(id) {
 return function () {
 addForm(id);
 }
})(members[member].id);

What this does is automatically invoke a new anonymous closure, which has a locally bound copy of members[member].id in its id parameter, which itself then returns a closure which is what's actually bound to btn.onclick.

The automatic invokation only happens if you put the outer parenthesis around the closure declaration.

answered Mar 18, 2011 at 10:14

1 Comment

With your correction only the last button will fire the event.
0
for (var i = 0; i < 10; i++) {
 // Won't work
 //$('div').eq(i)[0].onclick = function() { alert(i) }; // 10
 $('div').eq(i)[0].onclick = (function(id) {
 return function() {
 alert(id)
 };
 })(i);
}

jsFiddle.

answered Mar 18, 2011 at 9:56

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.