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
Related
Why does this piece of code give me a result of undefined?
I have tested in the web browser. It shows me that li[i] is undefined. And I don't know why and how.
I know about closure. And maybe it should pop out the string 'dataToggle' every time I click each li element; But someone could help me with more details about this specific question and what you think is important in understanding this question?
Thanks
(function(){
var innerDiv = document.getElementsByClassName('showDiv')[0];
//var li = document.querySelectorAll('ul.idiv li');
var li = document.getElementsByTagName('li');
for(var i=0,len=li.length; i<len; i++){
li[i].addEventListener('click',function(e){
e.preventDefault();
alert(li[i]);
var data = li[i].dataset.info;
showDiv.innerHTML = data;
},false);
}
}());
<div class="showDiv">Show data: </div>
<div class="outerDiv">
<ul class="idiv">
<li data-info="datashow">111</li>
<li data-info="dataHide">222</li>
<li data-info="dataToggle">333</li>
</ul>
</div>
This is because, i is a variable defined outside your event handler function, which is incremented by each iteration. So when you finish the iterations of the for loop, the value of i is equal tolen, which causes li[i] to be undefined. And if you ask me why it's value is not considered during the iteration, it's because your event handler function is only executed when the event occurs (not when you are setting the event handlers by for loop). So you can make a variable inside the function scope which won't be changed by iteration. Better use this from inside the event handler to get the same thing.
for(var i=0,len=li.length; i<len; i++){
li[i].addEventListener('click',function(e){
e.preventDefault();
alert(this);
var data = this.dataset.info;
showDiv.innerHTML = data;
},false);
}
Understanding the for loop
Why the value of i is equal to len after for loop is finished? Let's have a simpler example to make you understand the situation.
var len = 2;
for(var i=0; i<len; i++; {
//Do anything here
}
console.log("after for loop: i = " + i);
Lets's go through the iterations.
i = 0, matches the condition i<len, which proceeds with executing the code block and executes i++ after that, which makes i=1;
i = 1 now, matches the condition i<len, which proceeds with executing the code block and executes i++ after that, which makes i=2;
i = 2 now, fails to match the condition i<len and stops the iteration.
So, you have set the i=2 before you go to step 3. So your final console.log after for loop will say, after for loop: i = 2
This occurs because of scope. Inside the EventListener, you have an anonymous function, because of this, you are no longer in the scope of li. However, you can refer to it with the this keyword. See the alert that I've replaced in your code.
(function(){
var innerDiv = document.getElementsByClassName('showDiv')[0];
//var li = document.querySelectorAll('ul.idiv li');
var li = document.getElementsByTagName('li');
for(var i=0,len=li.length; i<len; i++){
li[i].addEventListener('click',function(e){ // you are now inside a different function block here.
e.preventDefault();
alert(this.innerHTML);
var data = li[i].dataset.info;
showDiv.innerHTML = data;
},false);
}
}());
<div class="showDiv">Show data: </div>
<div class="outerDiv">
<ul class="idiv">
<li data-info="datashow">111</li>
<li data-info="dataHide">222</li>
<li data-info="dataToggle">333</li>
</ul>
</div>
Another approach in addition to the answers of other posters is to use a local scope with function. In JavaScript function is the most reliable way of creating a new scope. Taking the above example,
for (var i = 0; i < li.length; i++) {
(function(i) {
var j = i;
li[j].addEventListener('click', function(e) {
e.preventDefault();
var data = li[j].dataset.info;
showDiv.innerHTML = data;
}, false);
})(i);
}
I have created a new scope that that will isolate the value of variable i. If you're using ES6 you can use let keyword to do just the same thing. All you have to do is replace var with let. You can also use let in a for loop that will give you a whole new scope that is local to for loop.
You need to refer to the particular li on which you want to attach the event
(function(){
var innerDiv = document.getElementsByClassName('showDiv')[0];
var li = document.getElementsByTagName('li');
for(var i=0,len=li.length;i<len;i++){
var thisLi = li[i];
thisLi.addEventListener('click',function(e){
e.preventDefault();
alert(thisLi.innerHTML);
var data = thisLi.getAttribute('data-info');
innerDiv.innerHTML = data;
},false);
}
}());
When click will be executed i will have value as 3 (in your case) so li[i]will be different. You can check the console of THIS to check value of i on click event
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 );
});
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.
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);
}
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
}