1

Okay this one's starting to freak me out: I have several div containers within the class "tooltipBox", but with different contents. In my little script, I try to store an array of all those divs in a variable, like so:

var agents = $( ".tooltipBox" );

However, when I want to use that array now, it's undefined. When I test the selector on its own (like simply by alert()-ing it), it does work. What's the problem here?

This is where the selector is used:

    $( "#runSimBtn" ).click(function runSimulation() {
        $( "#lContent h2" ).text("Simulation in progress...");

        var agents = $( "div.tooltipBox" );
        var falloutTimes = $( ".rFO" );

        var i, j = 0;

        for(i = 0, j = 0; i < agents.length, j < falloutTimes.length; i++, j++) {
            var ttl = falloutTimes[j].value;

            if((ttl != undefined) && (ttl != 999)) {
                setTimeout(function() {
                    agents[i].animate({ backgroundColor: "#FF0000" }, 500);
                }, ttl*1000);
            } else {
                continue;
            }
        }
    });

Also tried the selector in the browser-console (Firefox) and also there I can select the div, but as soon as I want to store the selection (even of a single div) into a variable, it returns undefined.

Thanks in advance...

slagjoeyoco
  • 311
  • 1
  • 5
  • 15
  • can you show your code? – Black Sheep Mar 13 '14 at 03:32
  • Use `console.log(agents);` to see the result in browser's console and please show your code how are you using `agents` – Amit Garg Mar 13 '14 at 03:32
  • Smells like a scope-related issue. `agents` should be a jQuery object. even for an empty collection. It can't be – Ram Mar 13 '14 at 03:34
  • Could you provide example code that includes the child
    elements and the selector that works?
    – Steve Harrison Mar 13 '14 at 03:34
  • added the code and what I found using the console, sry – slagjoeyoco Mar 13 '14 at 03:39
  • it is the classic closure variable in a loop problem - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures#Creating_closures_in_loops.3A_A_common_mistake – Arun P Johny Mar 13 '14 at 03:42
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Arun P Johny Mar 13 '14 at 03:42
  • value of `i` inside the loop will be `agents.length` so `agents[agents.length]` will be undefined – Arun P Johny Mar 13 '14 at 03:43
  • @slagjoeyoco why not using delay instead of setTimeout? – jogesh_pi Mar 13 '14 at 03:54
  • @jogesh_pi Frankly, at first I simply haven't thought of using jQuery's delay() function since setTimeout() did everything I needed. Moreover, I'll a possibility to cancel the timeout, and iirc delay() can't do that. – slagjoeyoco Mar 13 '14 at 17:05

3 Answers3

2

As I pointed out in the comments, it is a problem with using closure variables in a loop.

Try

$("#runSimBtn").click(function runSimulation() {
    $("#lContent h2").text("Simulation in progress...");

    var agents = $("div.tooltipBox");
    var falloutTimes = $(".rFO");

    var i, j = 0;

    for (i = 0, j = 0; i < agents.length, j < falloutTimes.length; i++, j++) {
        var ttl = falloutTimes[j].value;

        if ((ttl != undefined) && (ttl != 999)) {
            (function (i, ttl) {
                setTimeout(function () {
                    agents[i].animate({
                        backgroundColor: "#FF0000"
                    }, 500);
                }, ttl * 1000);
            })(i, ttl);
        } else {
            continue;
        }
    }
});
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • Thanks for your answer - after also reading up [here](http://stackoverflow.com/a/3572616/1637380) I think I got it now - due to the time the setTimout takes to execute and the rest of the code not waiting for it to finish, the loop runs through before the code within the setTimout() is executed. This is why I need closures, so that I can pass i to my function before its value changes again - did I get this right now? I'm rather new to this concept as you might've already noticed... – slagjoeyoco Mar 13 '14 at 15:50
  • 1
    @slagjoeyoco yes... it creates a private copy of `i` for each iteration of the loop so every `setTimeout()` will gets its own value of `i` – Arun P Johny Mar 13 '14 at 15:55
  • Alright glad I finally got this now - thank you very much for your help! – slagjoeyoco Mar 13 '14 at 16:31
0

I think what is going on here is that when the setTimeout calls the function after 1 second, this function where agents is declared has already completed and the agents variable is no longer accessible.

 $( "#runSimBtn" ).click(function runSimulation() {
        $( "#lContent h2" ).text("Simulation in progress...");

        var agents = $( "div.tooltipBox" );
        var falloutTimes = $( ".rFO" );
        var timeoutFunction = function(agent){
            agent.animate({ backgroundColor: "#FF0000" }, 500);
        }

        var i, j = 0;

        for(i = 0, j = 0; i < agents.length, j < falloutTimes.length; i++, j++) {
            var ttl = falloutTimes[j].value;

            if((ttl != undefined) && (ttl != 999)) {
                setTimeout(timeoutFunction(agents[i]), ttl*1000);
            } else {
                continue;
            }
        }
    });
scottysmalls
  • 1,231
  • 17
  • 23
0

as far as storing div selector in a variable is a concern, it works for me:

<div class="aClass">div 1</div>
<div class="aClass">div 2</div>

and jQuery:

var agents = $("div.aClass");
$(agents).each(function(key, val) {
    alert($(val).text());
});

will alert div 1 and div 2

Ahsan Shah
  • 3,931
  • 1
  • 34
  • 48