views:

61

answers:

3

Hello everybody,

this seems to be a simple question and I hope you can help me. I'd like to add that my code works the way I'm describing it. I'm not content with the way I solved my problem concerning my problem in incrementing [i] outside the for-loop.

What I want is to get a sidebar menu shown when one hovers over an image.

My CSS looks like:


GM_addStyle
(
'.colormenu {       \
 display:none;      \
 margin-left: 10px;     \
 margin-top: -55px;     \
 }              \
 .colormenu a {         \
 display: block;                \
 width: 30px;                   \
 }                          \
 .colorlist {                   \
 list-style-type: none;         \
 }                          \
'
);

Now I'm defining two variables. One containing some colors, the other containing some fruits.

var color = ['red','yellow','green','blue'];
var fruit = ['strawberry','banana','apple','blueberry'];

I then define two arrays:

var hoverstring = new Array(color.length);
var idstring = new Array(color.length);

Now I want to see whether the value of an element of var color matches a title of the images on the webiste. For every element being smaller than the length of var color I create two links in a list being wrapped by a div and insert it after a href wrapping the img-element.

for (var i=0;i<color.length;i++) {
 $("<div id='colormenu"+i+"' class='colormenu'><ul class='colorlist'><li><a href='path"+color[i]+"'>Call</a></li><li><a href='path"+color[i]+"'>Chat</a></li></ul><div>").insertAfter("a[href$='"+fruit[i]+"']"); 


 hoverstring[i]="img[title$='"+fruit[i]+"']:first";
 idstring[i]="#colormenu"+i;

}

The last step I take and this is where I got problems with is to create a function for every element. This is just an example of what it looks like:

    $(hoverstring[0]).hover(function(){
        $(idstring[0]).toggle();
    });
$(hoverstring[1]).hover(function(){ $(idstring[1]).toggle(); });
$(hoverstring[2]).hover(function(){ $(idstring[2]).toggle(); });
$(hoverstring[3]).hover(function(){ $(idstring[3]).toggle(); });
$(hoverstring[4]).hover(function(){ $(idstring[4]).toggle(); });
$(hoverstring[5]).hover(function(){ $(idstring[5]).toggle(); });

This method works, but is not very handy I'd say. Instead of creating a high amount of functions I'd like to increment i and not do it manually.

Like:


for (var i=0;i<color.length;i++) {
 $("<div id='colormenu"+i+"' class='colormenu'><ul class='colorlist'><li><a href='path"+color[i]+"'>Call</a></li><li><a href='path"+color[i]+"'>Chat</a></li></ul><div>").insertAfter("a[href$='"+fruit[i]+"']");




 hoverstring[i]="img[title$='"+fruit[i]+"']:first";
 idstring[i]="#colormenu"+i;


$(hoverstring[i]).hover(function(){
        $(idstring[i]).toggle();
    });
}

I tried it several times, but for some reason i isn't incremented in the for-loop. Have you got an idea why?

I really hope you can help me. Thank you in advance.

+2  A: 

You're facing one of biggest mistakes a ECMA-/ Javascript programmer can make:

Forgetting about closures and hoisting of variables.

Putting this in your for-loop:

$(hoverstring[i]).hover(function(){
    $(idstring[i]).toggle();
});

would cause that i would be closured by all of your methods. You need to invoke another function to solve that issue:

for (var i=0;i<color.length;i++) {
   $("<div id='colormenu"+i+"' class='colormenu'><ul class='colorlist'><li><a        href='path"+color[i]+"'>Call</a></li><li><a        href='path"+color[i]+"'>Chat</a></li></ul><div>").insertAfter("a[href$='"+fruit[i]+"']"); 


   hoverstring[i]="img[title$='"+fruit[i]+"']:first";
   idstring[i]="#colormenu"+i;

   (function(index){
       $(hoverstring[index]).hover(function(){
          $(idstring[index]).toggle();
       });
   }(i));
}
jAndy
What can I say than the biggest 'Thank You' I've got. I'm not a programmer, just trying to understand the syntax and trying to learn every day a little bit more. Thanks for pointing out my mistake and everything.
tombolatwo
+1  A: 

It is incrementing i just fine, but the i inside your hover function is a reference to the i in the loop. After the loop is complete, the i inside the hover function, inside all hover functions, is the value of i after the loop. This is called a closure.

If you want to freeze the value of i at the specific point in time you create the function, you need to do this:

$(hoverstring[i]).hover((function (x) {
    return function () {
        $(idstring[x]).toggle();
    }
})(i));
deceze
+3  A: 

You can do something quick like this:

$.each(color, function(i) {
  $("<div id='colormenu"+i+"' class='colormenu'><ul class='colorlist'><li><a href='path"+this+"'>Call</a></li><li><a href='path"+this+"'>Chat</a></li></ul><div>").insertAfter("a[href$='"+fruit[i]+"']");
  $("img[title$='"+fruit[i]+"']:first").hover(function(){
    $("#colormenu"+i).toggle();
  });
});

The difference here is that i is scoped to this callback, not referencing the same variable that's changing with the loop each time.

Nick Craver
That's just brilliant. Would have take me days to figure it out. Thank you.
tombolatwo
@tombolatwo - welcome :)
Nick Craver
Hey, congratulations on reaching 100K!
Kobi
@Kobi - thanks! :)
Nick Craver