jquery click function always returns highest value - javascript

I insert elements to the DOM, after that I want to bind a click function to these elements.
This works, but for some reason the links that were created all return the same value, which
is the highest value 'px_amount' has after looping. Very strange :) The first console.log();
does return the right value, and I can see it increment after each iteration. I added a simple console.log() to the click function, for sake of simplicity.
for(var i=1; i<=bullet_amount; i++)
{
$('<a id="bullet-'+i+'">'+i+' </a>').appendTo('#bullet-nav');
px_amount = (i-1)*ratio*3450;
console.log(px_amount);
$("#bullet-"+i).live('click', function() {
console.log(px_amount);
});
}

This is a very frequent problem : the variable i is the same for all callbacks, that is the one of the enclosing scope.
A common solution is this :
for(var i=1; i<=bullet_amount; i++)
{
(function(i){
$('<a id="bullet-'+i+'">'+i+' </a>').appendTo('#bullet-nav');
px_amount = (i-1)*ratio*3450;
console.log(px_amount);
$("#bullet-"+i).live('click', function() {
console.log(px_amount);
});
})(i);
}

You should use a closure to capture the value of i reather than the reference:
for(var i=1; i<=bullet_amount; i++) {
(function(iVal) {
$('<a id="bullet-'+iVal+'">'+iVal+' </a>').appendTo('#bullet-nav');
var px_amount = (iVal-1)*ratio*3450;
console.log(px_amount);
$("#bullet-"+iVal).live('click', function() {
console.log(px_amount);
});
})(i);
}

Related

onmouseover function Auto Runs with Parameter

As soon as the page loads, if you check the console, you see the function fires before I mouseover the elements. If I remove the parameters, then the page waits for me to mouseover the elements, but I lose the dynamic functionality. Am I doing something wrong with passing the elements?
var myList = ["hoverOne", "hoverTwo"];
for(var i=0; i < myList.length; i++){
document.getElementById(myList[i]).onmouseover=changeImage(myList[i]+"Image");
}
function changeImage(thisDiv){
console.log(thisDiv);
//show/hide code here
}
Here is a link to the fiddle I was playing with: http://jsfiddle.net/QtG9P/33/
if you want to maintain the value of i, try it like this:
var myList = ["hoverOne", "hoverTwo"];
for(var i=0; i < myList.length; i++){
(function(i){
document.getElementById(myList[i]).onmouseover = function(){
changeImage(myList[i]+"Image");
};
})(i);
}
see it in action here:
http://jsfiddle.net/QtG9P/36/
You are calling the function and assigning the return value to the event attribute. You need to wrap the function call in a function expression so that you get a function that you can assign to the attribute.
Also, you need to make a copy of the variable i for each iteration, otherwise the event handler will use the value that i has after the loop. You can do that by wrapping the code inside the loop in an immediately executed function expression:
for(var i=0; i < myList.length; i++){
(function(i){
document.getElementById(myList[i]).onmouseover = function(){
changeImage(myList[i]+"Image");
};
})(i);
}
Change your code like;
myList = ["hoverOne", "hoverTwo"];
for(var i=0; i < myList.length; i++){
document.getElementById(myList[i]).onmouseover=function (evnt) { changeImage(evnt.srcElement.id); };
}
function changeImage(thisDiv){
console.log(thisDiv + "Image");
//show/hide code here
}
jsfiddle link

jQuery - passing variable to event by data

I would like to create some objects dynamically and bind events to them (not important what events).
I'm passing a number to the event in order to distinguish those items. There is my code:
$('#add_btn').click(function() {
var cont = $('#buttons');
for(var i=0; i<5; i++) {
var new_btn = $('<input>').attr('type', 'button').val(i);
new_btn.click(function() {
alert(i);
});
cont.append(new_btn);
}
});
When I click on any from newly created buttons, displayed number is 5.
I think that i variable is passing by reference, but the question is: how to avoid passing variable by reference? More, even if I crate new variable before binding event (so the reference should point to another object, for example new_val = i.toString()), value is still same for all buttons (then its 4, understandable).
I know that I can attach new_btn.data() and read it in event, but I'm not sure if it won't be an overhead.
Link to jsfiddle: http://jsfiddle.net/Jner6/5/.
Since you are using a closure scoped variable in a loop, inside the loop you need to create a private closure.
$('#add_btn').click(function () {
var cont = $('#buttons');
for (var i = 0; i < 5; i++) {
(function (i) {
var new_btn = $('<input>').attr('type', 'button').val(i);
new_btn.click(function () {
alert(i);
});
cont.append(new_btn);
})(i)
}
});
Seems like you run into closures issue, try this:
(function( i ) {
new_btn.click(function() {
alert(i);
});
})( i );
This will create immediate invoked function that will closure your variable i so you can use it in future. For now you just overriding i variable in your for-loop so you will have always same value that will equal last for-loop iteration.
Don't make functions within a loop.
DEMO
var cont = $('#buttons');
$('#add_btn').click(function() {
for(var i=0; i<5; i++) {
$('<input>', {type:'button', value:i}).appendTo( cont );
}
});
cont.on('click', ':button', function() {
alert( this.value );
});

Correct usage of closure in for loop?

I'm adding an event listener to some elements I'm looping through and need a closure in order to preserve the index in the event function.
<button>solution 1</button>
<button>solution 2</button>
<script>
var buttons = document.getElementsByTagName('button');
for (var i = 0; i < 3; i++) {
var log = (function closure(number) {
return function () {
console.log(number);
};
})(i);
buttons[0].addEventListener("click", log);
}
for (var i = 0, len = 3; i < len; i++) {
(function (i) {
var log = function () {
console.log(i);
};
buttons[1].addEventListener("click", log);
})(i);
}
</script>
http://jsfiddle.net/paptd/11/
Both these solutions output 0, 1, 2 correctly (try 'wrong' to see what happens without a closure) but I'm trying to understand which one I should use and why.
Which way is the correct way of doing it?
The first one works because you are defining a closure, returning a function from it, then assigning that function to a listener.
The second one seems more proper, since the closure encompasses the entire loop content, making it more obvious that the value of i is to be "locked" there.
You shouldn't use any of these--you're creating n identical functions inside of your loop. You should refactor your code into a named function that returns the event handler:
var buttons = document.getElementsByTagName('button');
function createHandler(number) {
return function () {
console.log(number);
};
}
for (var i = 0; i < 3; i++) {
buttons[0].addEventListener("click", createHandler(i));
}
Example: http://jsfiddle.net/paptd/12/

parameter error with addEventListener

Okey so I got this following javaScript code.
function test(id)
{
alert(id);
}
var elem = document.getElementsByClassName('outsideDiv');
for(var i=0; i < elem.length; i++)
{
elem[i].addEventListener('mouseover', function(){test(i);}, false);
}
this gives all divs with the class a mouse over but the function always returns the latest i index. in this case i got 5 div elements and the alert is allways 5 no mather witch one i hover. Can anyone explain why?
Try using this instead:
function mouseOverFunc(i) {
return function () {
test(i);
};
}
function test(id) {
alert(id);
}
var elem = document.getElementsByClassName('outsideDiv');
for(var i=0; i < elem.length; i++) {
elem[i].addEventListener('mouseover', mouseOverFunc(i), false);
}
Just because you add event listeners to the elements doesn't mean the value of i is preserved for each listener. You need to create a closure that will create a new scope with i.
The reason this is happening is because the function bound to each listener is just a reference. When the event happens (mouseover), the function is finally called, but what's the value of i? The for loop finished executing a long time ago, so the value of i is the end value - 5.

Refer back to old iterator value in click() function for javascript/jQuery (closure question)

I'm trying to get the "click()" function to display the value of 'i' at the time I passed in the function. But its referring back to the value of 'i' after it finished. I'm drawing a blank on how to get the function to refer to the value of 'i' when I first passed the function in.
for( var i=0; i<10; i++){
var ts = $('#<span></span>').clone().click(function(){
alert(i);
});
}
NOTE:
The '#' shouldn't be there, neither should the '.clone()'
Something like this will work:
for(var i=0; i<10; i++){
(function(j) {
var ts = $('<span></span>').click(function(){
alert(j);
});
})(i);
}
You can give it a try here. Though, your creation is a bit off, I'm not sure why you'd want to create a new element just to clone it, and there's an extra # in there....I removed both of these above, but it doesn't affect the solution of an inner function.
You need to move the body of the loop to a separate function that takes i as a parameter.
You can use a normal function, like this:
for(var i=0; i<10; i++) {
makeCopy(i);
}
function makeCopy(i) {
var ts = $('#<span></span>').clone().click(function(){
alert(i);
});
}
You can also use an inline method, like this: (beware confusing syntax)
for(var i=0; i<10; i++) {
(function(i) { //Note i parameter
var ts = $('#<span></span>').clone().click(function(){
alert(i);
});
...
})(i); //Note i parameter
}

Categories