-4

Code gives me: A B C

When I click on A B C it always shows me the last one "vodka". I want "martin" (for A), "lindsay"(for B), "vodka" (for C)
Please help me on my example.

myArray = [
    {
        letter: "A",
        brand: "martin"
    },
    {
        letter: "B",
        brand: "lindsay"
    },
    {
        letter: "C",
        brand: "vodka"
    }
];
    var list = '';
    for (var i = 0; i < myArray.length; i++) {
    list += "<br>" + myArray[i].letter;

    new_info = myArray[i].link;
    (function(new_info) {
         $(this).click(function(){        //this - refers to A or B or C
                 $('#box2').text(new_info);
                 });
    }).call(this, myArray[i])
}

$('#box1').append(list);

1 Answers1

2

Edit:
I said I wasn't going to write your code for you... well, I did: this fiddle does exactly what you're looking for. I solved the issue with context (this), closure issues and implied globals. It still needs a lot of work, but the fiddle shows what everybody has been saying: $(this) does not, cannot and will never point to a string constant like "A", or "B".

Sorry to say this, but your code is full of problems, but I will address the specific issue you're asking about here.
Inside the loop, you're assigning a click handler that, basically looks like this:

function()
{
    $('#box2').text(new_info);
}

Where new_info is a variable that is declared in a higher scope. So far, so good. The problem is, the function object you're creating doesn't have its own copy of whatever value that variable (new_info) happened to hold when that function was created. Instead, the function references that variable. So when any of those functions is invoked it $('#box2').text(new_info) will be resolved to $('#box2').text("whatever value new_info holds when function is called"), not $('#box2').text("whatever value new_info was holding when function was created"). You can give each callback access to a copy by simply adding a second function to your code:

$(this).click((function(currentNewInfo)
{
    return function()
    {
        $('#box2').text(currentNewInfo);
    }
}(new_info)));

What I'm doing here is creating a function, that takes an argument, and calling it immediately. I pass new_info as an argument, so the value of currentNewInfo will be what new_info holds at that time (aka a copy)
The function I called (IIFE - or Immediately Invoked Function Expression) returns the actual callback. In this callback, I don't reference new_info, but the argument of the IIFE: currentNewInfo.

Because each function has its own scope, that variable is enclosed (hence the name closure), and cannot be accessed or altered from outside. The only thing that can still access the currentNewInfo variable is the function the IIFE returned.
Perhaps you are worried about name-conflicts (each callback you create uses references currentNewInfo), but that's not the case: each callback was created by a separate function, and therefore has access to a different scope. It's not possible to have a name conflict between scopes that don't access each other... Just to make things really simple to understand:

What a closure does

Where  /\            and      /\
       ||                     ||
  is return function()      is scope of IIFE

So closures have access to a function's scope after it returns. That scope has precedence when it comes to resolving an expression to a value. To understand this better, here's a similar diagram to show you how JS resolves expressions:

JS and scope-scanning
Where each pink "outer environment record" is a scope of a function (closure scope of a function that has returned already or function currently being called). The last environment will either be the global object, or null (in strict mode). That's all there is to it.

Honestly, closures are tricky to get your head round at first, but once you grasp what I tried to explain here, they're great fun.
Check this link I can go on to explain the use cases and benefits and ways nested closures work, but I'd end up writing a book. The link I posted does a great job at explaining how closures work using rather silly drawings. It may seem childish, but they actually helped me a lot when I was trying to grasp the concept of lambda functions, closures and scopes out-living a function-call. The diagrams above are taken from the page I linked to, which explains the concepts a bit more in-depth, but I still think the simple, crude drawings are pretty self explanatory.

Other issues:
As someone pointed out: "What are you expecting this to reference". Looking at the snippet, this will just reference the global object (window), attaching the same/similar event handler to window simply doesn't make sense if you ask me.
Global variables are evil, implied globals even more so. I can't see new_info, nor myArray being declared anywhere. The way JS resolves expressions is a tad unfortunate and falls back to creating global variables, without so much as a peep:

var bar = 666;//global, always evil
function createGlobal()
{
    var local = 2;
    foo = bar * local;
}
createGlobal();

Let's look at foo:

JS is in createGlobal scope: var local is declared, and assigned 2.
   foo is used, and assigned bar*local
    ||                       ||   \\=>found in current scope, resolves to 2
    ||                       ||
    ||                       \\=>found in global scope, resolves to 666
    ||
    ||
    ||=> JS looks for foo declaration in function scope first, not found
    ||
    ||=> moves up 1 scope (either higher function, or global scope)
    ||
    \\=>Global scope, foo not found, create foo globally! - hence, implied global
             \\
              \\=>foo can now be resolved to global variable, value undefined

Excessive DOM queries: your event-handler callbacks all look like so:

$('#box2').text(new_info);

This ($('#box2')) is actually the same as writing document.getElementById('#box2'). Which is practically English. Think about it like this: each time the client clicks on $(this) - whatever that may be, you're accessing the DOM, and scanning it for an element with a given ID. Why not do this once and use a reference kept in memory to change the text. That saves countless DOM queries.You could use a variable, or (in light of what I explained about closures), a closure:

var list = (function(box2, list, i)
{//list & i are arguments, so local to scope, too
    for (i = 0; i < myArray.length; i++)
    {
        list += "<br>" + myArray[i].letter;//<-- don't know why you use this
        //new_info = myArray[i].link; no need for this var
        $(this).click((function(new_info)
        {//new_info is closure var now
            return function ()
            {//box2 references DOM element, is kept in memory to reduce DOM querying
                box2.text(link);
            };
        }(myArray[i].link));//instead of new_info, just pass value here
    }
    return list;//return string, assign to outer variable
}($('#box2'), ''));//query dom here, pass reference as argument
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 2
    excellent answer to a terrible question... +1 – tckmn May 09 '13 at 23:17
  • 1
    @Doorknob: Thanks. I might be naive, but I honestly believe that even the most obvious questions can be of service to people, if you put some effort into the answers... I just hope the OP feels the same way :P – Elias Van Ootegem May 09 '13 at 23:29
  • Could you please modified my code. That's the best way I'll get closure. I don't get it by reading other examples (I've tried). Thanks. – Ben Delton May 09 '13 at 23:32
  • @BenDelton: You may _have_ code that uses closures now, but I'm sorry to say you don't _get_ it, until you can create/use them yourself. I've edited your snippet, addressing the most obvious issues (global vars, excessive DOM querying, and callbacks not having their own distinctive behaviours) but I can't address the issue with `this`, because I don't know what you want `this` to reference – Elias Van Ootegem May 09 '13 at 23:41
  • "this" refers to "letter", A or B or B when is clicked. Hope it helps. If you understand closures so much, you can help me with my code. Thanks – Ben Delton May 09 '13 at 23:53
  • @BenDelton: I've edited my answer even further (to avoid ambiguity at places) and added [this great link](http://lostechies.com/derekgreer/2012/02/17/javascript-closures-explained/) that does a fantastic job at _showing_ you what closures are in images – Elias Van Ootegem May 09 '13 at 23:54
  • @BenDelton: What leads you to that conclusion? `this` references the current context. The call context can't change in a loop the way you're code looks now – Elias Van Ootegem May 09 '13 at 23:55
  • Let's forget about the closures and other links. Can you please see what needs to be fix in my code so that I get the results I want, as explain in description. – Ben Delton May 10 '13 at 00:01
  • @BenDelton: You can't get the results _unless_ you use closures, so you can't ignore them... you just can't. Your last comment is like saying "Let's forget about learning how to drive, how can I move my car?" – Elias Van Ootegem May 10 '13 at 00:05
  • I meant let's focus on my problem and not on closures on other pages. – Ben Delton May 10 '13 at 00:10
  • Elias, I've tried your code but still doesn't work. Thanks for your help. I appreciate that. – Ben Delton May 10 '13 at 00:12
  • @BenDelton: I didn't say that you have to say thank you - or that you don't say it enough. On this site, the way to thank somebody is to vote their answer up, not by leaving a comment. I also never said that this code is copy-paste ready, it's a suggestion that you still have to tweak for it to work. SO is not a code generator, but a place to help you get on track. I feel as though I've given you all the info you needed: explanation, code examples, links... put in a bit of effort and you'll get there. I'm not going to do it for you (unless you're willing to pay me) – Elias Van Ootegem May 10 '13 at 00:26
  • I've tried to vote-up, but I need 15 reputations. – Ben Delton May 10 '13 at 00:36
  • @BenDelton: Guess I was wrong about that, then... sorry. Back to _"Let's focus on my problem and not on closures on other pages"_ The link I provided _Explains the principle of closures_. That's _The only way_ to comprehensively solve your problem - Apart from your hiring me as a consultant. I've given you a lot of information that is, AFAIK, accurate. Nobody has down-voted my answer, so it can't be all wrong. If you just refuse to accept that examples of closures are irrelevant, then I can't help you. – Elias Van Ootegem May 10 '13 at 00:44
  • @BenDelton: You've edited your question several times, yet the code you posted initially hasn't changed one bit. You say you can't get it to work in your case, but _what have you tried?_. I've added two diagrams that, basically, tell you the same thing as I have been trying to explain. That's all the knowledge you need to have to get closures to work here... I'm starting to feel like I'm being trolled, to be perfectly honest. – Elias Van Ootegem May 10 '13 at 01:03
  • @Elias I don't blame you :/ – tckmn May 10 '13 at 01:38
  • I've modified my code. Looks ok to me, but still ain't working. – Ben Delton May 10 '13 at 09:30
  • @BenDelton: Because you still haven't solved the issue with `this`, it just points to the global object. If you click _anywhere_ on your page, you'll see what that means: [Check this fiddle, your code, but working](http://jsfiddle.net/QHdZD/) – Elias Van Ootegem May 10 '13 at 12:38
  • you're right. Just realized my mistake/problem. – Ben Delton May 10 '13 at 12:39
  • @EliasVanOotegem Thanks so much!! I'm learning so much!!! – Ben Delton May 10 '13 at 12:40
  • @BenDelton: Great to hear. I was a bit tired yesterday (have been working hard lately, so I was sort of impatient). The loop wasn't the only problem, but I hope the fiddle is pretty self-explanatory. Just to be sure: you understand all the issues I pointed out to you now? If you do, can you accept this answer, after all: the fiddle does solve the problem – Elias Van Ootegem May 10 '13 at 12:43
  • Yes, I'll accept this answer. Thanks. I'm still going through your posts and learn. Yesterday everyone criticized me here and I've even got a ban from asking new questions. – Ben Delton May 10 '13 at 12:50