0

The buttons below output their numbers in the console when clicked. Fire 1 should output 1, Fire 2 should output 2, and so on.

I am using a for loop to pass 1–4 to the functions that call fire(n), but the output is never what I expect when I click any of the buttons. I tried following an answer to this question, but it is not working for me.

If I use the code I commented out instead of the for loop, everything works fine. Why will this not work if I use the for loop?

function fire(n) {
    console.log(n);
}

for (var i = 1; i <= 4; ++i) {
    $("#fire" + i).on("click", fire(i));
}

/*
$("#fire" + 1).on("click", function(){fire(1)});
$("#fire" + 2).on("click", function(){fire(2)});
$("#fire" + 3).on("click", function(){fire(3)});
$("#fire" + 4).on("click", function(){fire(4)});
*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="fire1">Fire 1</button>
<button id="fire2">Fire 2</button>
<button id="fire3">Fire 3</button>
<button id="fire4">Fire 4</button>

3
  • 1
    on("click", fire(i)); you are calling fire not setting it as a callback Commented May 17, 2016 at 17:31
  • Possible duplicate of JavaScript closure inside loops – simple practical example Commented May 17, 2016 at 17:32
  • there is no need for using for in this example Commented May 17, 2016 at 17:47

3 Answers 3

3

You need to pass a function to the click handler. You are passing undefined. This is because fire(i) runs the function, so you are setting its return value - undefined - as the handler.

You need to generate a new function for each iteration - a closure - to "capture" each i value.

function fire(n) {
    return function(){
        console.log(n);
    }
}

for (var i = 1; i <= 4; ++i) {
    $("#fire" + i).on("click", fire(i));
}

Notice how now the fire() function is returning something. It's returning a function. A different one for each click handler, one that "captures" each iteration's i value.

Sign up to request clarification or add additional context in comments.

4 Comments

Isn't that what's known as a closure?
@user1958756: Yeah. Because it's "closing" around the n value.
So basically, I just needed to add return function(){ } around console.log() so fire() has something to return? Thanks anyway!
@JonKantner: Basically, yeah. What I am doing is making fire() return a (new) function each time it's called.
3

When you are doing $("#fire" + i).on("click", fire(i)); you are actually calling the fire function. What you would like to do is to bind the parameter of the method.

One simple way to do that is to make a function that returns a new function with the correct value, which can then be added to your listener.

function fire(n) {
    return function() {
        console.log(n);
    }
}

Another way could be to use bind, which does this for you. Something like

for (var i = 1; i <= 4; ++i) {
    $("#fire" + i).on("click", fire.bind(this, i));
}

The first parameter is what to use for this when calling the function, and then the next parameters are parameters to the original function.

Comments

0

Since each button id has the information that you want to extract and use in your loop i propose the following workaround.

function fire(n) {
    console.log(n);
}

$('button[id^=fire]').on('click',function() {
fire(this.id.split("fire")[1]);
});

/*
$("#fire" + 1).on("click", function(){fire(1)});
$("#fire" + 2).on("click", function(){fire(2)});
$("#fire" + 3).on("click", function(){fire(3)});
$("#fire" + 4).on("click", function(){fire(4)});
*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="fire1">Fire 1</button>
<button id="fire2">Fire 2</button>
<button id="fire3">Fire 3</button>
<button id="fire4">Fire 4</button>

Comments

Your Answer

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.