0

Based on the answer I received here, it seems I need a closure to get this code working correctly. Unfortunately, despite the links provided, I am still confused as to how a closure should be implemented in my case, so when a specific image is clicked a specific URL unique to that image is opened. (Currently, I am just getting an "undefined" because, as Pointy said, 'the value of "i" will be links.length'.)

In a nutshell, how do I "carry" the number/index of "i" over to "window.location.replace(pages[]);", and if a closure is indeed correct in this case, how would I make use of one?

Hate to keep bothering people but I don't think I can figure this out on my own. Thanks.

Original code:

var links = ["#portfolio", "#animations", "#games"];
var pages = ["http://www.gog.com", "http://www.google.com", "http://www.hamumu.com"];

$(function() {
        for (var i=0; i<links.length; i++) {
            //for (var e=0; e<pages.length; e++) {
                $(links[i]).click(function() {
                    window.location.replace(pages[i]);
                });
            //}
        }
    });
Community
  • 1
  • 1
Terf
  • 51
  • 8
  • 1
    If the many, many answers already provided won't help, why will more of the same help? You need to study them, try the examples, then try to apply it to your code. Take a couple hours (or days) to work on it, then if your code isn't working, *show what you tried* in a new question instead of repeating the duplicate. –  Oct 02 '14 at 17:28
  • Quite honestly I don't know that I *ever* would have figured out, on my own in a reasonable timeframe, what T.J. Crowder suggested. If StackOverflow encourages taking days to work out a single problem, I will abide by that rule; however, I feel I learn better if someone can help me learn exactly how I can apply a given concept to my specific situation. Then I can ask follow-up questions on how each line relates to my given situation. – Terf Oct 02 '14 at 17:45
  • Of course you would have figured it out. I have no doubt about that. And "a couple days" is a general comment and not a SO policy, but effort is expected, not reposting a question that you've already been shown to be a duplicate. We're *all* short on time. The links provided on your other question, especially the one in the comment, give lots of solutions, including the very same `.bind()` that TJ gave. This issue comes up a lot, and has been well covered. TJ's solution will bite you if you use it in the wrong place. Better to gain understanding of what's going on. –  Oct 02 '14 at 17:51
  • I apologize for making it seem like I did not try any other possibilities. I tried to figure out how http://stackoverflow.com/a/341759/3862501 would fit into my situation, but did not understand how `(select, callid, anotherid)` fit in, or why a `return` was necessary. _Anyway, I'm not trying to argue nor say I was correct in how I handled this, and appreciate your advice. Next time if I have a problem and am redirected to previous examples, I will spend at least one day trying to work it out._ – Terf Oct 02 '14 at 17:59
  • 1
    Listen, all I'm saying is that this stuff takes time. To learn programming, you need to be willing to put in the effort. There are people here who ask beginner's questions even though they've been asking beginner's questions for 3 years. All they do is get answers that they apply to their code without ever understanding anything. I know it seems hard right now. It's an illusion. It's merely unfamiliar, and is quite easy once it clicks. The up front efforts are crucial. –  Oct 02 '14 at 18:03

1 Answers1

1

You can do it with Function#bind, which is an ES5 feature that can be shimmmed on older browsers, like this:

var links = ["#portfolio", "#animations", "#games"];
var pages = ["http://www.gog.com", "http://www.google.com", "http://www.hamumu.com"];

$(function() {
    for (var i=0; i<links.length; i++) {
        //for (var e=0; e<pages.length; e++) {
            $(links[i]).click(function(page) { // 1. Accept argument into function
                window.location.replace(page); // 2. Use the argument
            }.bind(null, pages[i]));           // 3. Use Function#bind to bind the argument
        //}
     }
});

Function#bind returns a function that, when called, will call the original function with a particular this value (which we don't care about here) and any arguments you bind to it (followed by any arguments given when the new function is actually called).

A simpler example may help with understanding bind more:

// A function that shows its arguments
function foo(a, b, c) {
    display("a = " + a);
    display("b = " + b);
    display("c = " + c);
}

// Get a function with an argument bound to it
var f = foo.bind(null, 1);

// Call the function -- notice how we get the bound
// argument first, then the ones we supply with the
// call
f(2, 3);

// Just a utility display function
function display(msg) {
  document.body.insertAdjacentHTML(
    "beforeend",
    "<p>" + msg + "</p>"
  );
}

If you wanted to do it without bind, I usually recommend a separate builder function you reuse (rather than creating and throwing away the builder function on each loop iteration):

var links = ["#portfolio", "#animations", "#games"];
var pages = ["http://www.gog.com", "http://www.google.com", "http://www.hamumu.com"];

$(function() {
    for (var i=0; i<links.length; i++) {
        //for (var e=0; e<pages.length; e++) {
            $(links[i]).click(makeClickHandler(pages[i]));
        //}
    }

    function makeClickHandler(page)
        return function() {
            window.location.replace(page);
        };
    }
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Thanks, it worked! I'm not a "natural" programmer so wrapping my head around "bind" won't come easily, but I appreciate the added explanation. – Terf Oct 02 '14 at 17:29
  • `links[i]` would probably be a more appropriate binding than `null` for an event handler. –  Oct 02 '14 at 17:32
  • @squint: As `this` isn't used in the handler, it doesn't matter what we use for the first argument. (Note that `links` is an array of strings, not their elements, so that wouldn't make it more jQuery-like.) – T.J. Crowder Oct 02 '14 at 17:32
  • T.J.Crowder: This is a person who is clearly starting out. You're right, it isn't used in that single line of code. But what about when they add a line, or when they apply this elsewhere? I don't believe you really think `null` will be more useful than the element that would typically be there. ...But you're right, I meant the element. I doubt that `.bind()` is really a good solution for a noob here. Not sure what you mean by *"jQuery-like"* –  Oct 02 '14 at 17:34
  • @Terf: Almost no one is a natural programmer. It takes work. Lots and lots of trial and error and error and error. –  Oct 02 '14 at 17:35
  • If there is a website that is more suitable for beginner help I am willing to ask questions there instead. (I am artistically-minded/have a degree in graphic design.) Believe me, I don't enjoy bothering people, but I don't know how I would have come to the conclusion that T.J. did, even in days, without his help. I have been reading w3schools and learn.jquery.com, but the amount of information that could be learned is incredible. @T.J.Crowder: How is the numerical info from `$(links[i])` being passed to `function(page)`? Is there some sort of "natural association" between the two? – Terf Oct 02 '14 at 17:52
  • @Terf: Don't worry, this is absolutely a place for beginners. Your question being closed as a duplicate doesn't mean you shouldn't have asked it; it means that it's been answered before, and that you can find the answer at the link (provided the answer there is clear enough that you can figure out how it applies to your situation, which is not always the case). The numerical info from `$(links[i])` isn't being passed to the handler, but the value of `pages[i]` is, by passing `pages[i]` into `bind`, which creates a new function it will call with that value. – T.J. Crowder Oct 02 '14 at 21:02
  • *Thanks for replying! 1 of my main errors was not editing my original post with stuff I had tried (even though I knew those didn't really make sense), to show I was actually trying and help "bump" my initial thread.* If u don't mind I'd like to try to understand more of what's happening: So if a `links` array member is clicked, the initial `function(page)` does not run til `bind`s info "reconstructs" (or it creates a whole new function?) the function to include `pages[i]` as the info instead of the empty argument `page`? Is `bind`s `this` in a `null` state because it's not important here? – Terf Oct 02 '14 at 21:57
  • Sorry, perhaps I should have tagged you @T.J.Crowder (though I completely understand if you're doing other stuff and will simply answer later). – Terf Oct 02 '14 at 22:16
  • 1
    @Terf: `bind` creates a new function, and remembers the argument(s) you want to pass to the original. When the new function gets called (by the event), the new function calls the original function with the argument(s) it remembered. It doesn't construct anything, it's just a function call. We're passing `null` to `bind` as the first argument because we don't care what `this` is during the call to the original function (the value `this` will have during that call depends on whether you're in JavaScript's "loose" or "strict" mode, but don't worry about that right now). – T.J. Crowder Oct 03 '14 at 07:03
  • @T.J.Crowder Interesting, thanks. Now, correct me if I'm wrong, but the reason we had to do this in the first place is b/c `i` was originally "locked" (not the technical term of course) to `links.length` via the `for` loop, right? By creating a new function, does `bind` "disassociate" `i` with `links.length` and allow for the number value of `i` to be "associated" with the `pages` array instead? (something like that?) *Also*, do you recommend any sites online (besides w3schools or learn.jquery.com; I'm already going through those) to help me learn this stuff thoroughly? – Terf Oct 03 '14 at 17:03