Sir
Sir

Reputation: 8280

function receives value as undefined

I have a timeout which calls a function until 100% progress is complete. It then executes the function I have assigned to it. Only the value that was given to it is undefined or at least part of it.

I'm not sure at which stage the code is losing the value being passed, thus making it return undefined but I have made a JS Fiddle with it in action for you to see it:

JS Fiddle

My end result is to receive the value correct then remove the given element like so:

function rmv_div(div_id) {
   //div_id is not properly defined so cannot find the div.
    document.getElementById('result').innerHTML = div_id;
    var div = document.getElementById(div_id);
    div.parentNode.removeChild(div);
}

Upvotes: 4

Views: 87

Answers (2)

Ja͢ck
Ja͢ck

Reputation: 173522

The problem here is that you didn't isolate the loop variable i inside the closure. However, this can be solved much more elegantly by using objects.

First off, I'm introducing the object that will encapsulate what you want; it gets initialized with a bar element and a function to call when it's done counting to 100. So, I'll call it BarCounter:

function BarCounter(element, fn)
{
    this.element = element;
    this.fn = fn;

    this.text = element.getElementsByTagName('div')[0];
    this.counter = 0;
}

This is just the constructor; it doesn't do anything useful; it resolves the text element, which is simply the first <div> tag it can find underneath the given element and stores that reference for later use.

Now we need a function that will do the work; let's call it run():

BarCounter.prototype.run = function()
{
    var that = this;

    if (this.counter < 100) {
        this.text.innerHTML = this.counter++;

        setTimeout(function() {
            that.run();
        }, 70);
    } else {
        this.fn(this.element);
    }
}

The function will check whether the counter has reached 100 yet; until then it will update the text element with the current value, increase the counter and then call itself again after 70 msec. You can see how the reference to this is kept beforehand to retain the context in which the run() function is called later.

When all is done, it calls the completion function, passing in the element on which the BarCounter object operates.

The completion function is much easier if you pass the element to remove:

function removeDiv(element)
{
    element.parentNode.removeChild(element);
}

The final step is to adjust the rest of your code:

var array = [1];
for (var i = 0; i < array.length; ++i) {
    var bar = new BarCounter(
        document.getElementById('bar' + array[i]), 
        removeDiv
    );
    bar.run();
}

It's very simple now; it creates a new BarCounter object and invokes its run() method. Done :)

Btw, you have the option to remove the element from within the object as well; this, of course, depends on your own needs.

Demo

Upvotes: 1

Oriol
Oriol

Reputation: 287960

The problem is that the variable i used inside func is created outside the scope of that function, and is increased at each iteration. Then, when you call func at the end, i equals array.length, so array[i] is undefined.

You can solve it creating another variable at each iteration that you won't increase:

Solution 1:

Demo: http://jsfiddle.net/qJ42h/4/ http://jsfiddle.net/qJ42h/11/

for (var i = 0; i < array.length; i++) {
    var bar = document.getElementById('bar' + array[i]),
        text = document.getElementById('text' + array[i]),
        remove = 'wrap' + array[i],
        j = i;
    do_something(bar, text, function () {
        rmv_div('id' + array[j]);
    }, 1);

}

Solution 2

Demo: http://jsfiddle.net/qJ42h/8/ http://jsfiddle.net/qJ42h/12/

for (var i = 0; i < array.length; i++) {
    var bar = document.getElementById('bar' + array[i]),
        text = document.getElementById('text' + array[i]),
        remove = 'wrap' + array[i];
    do_something(bar, text, (function(i) {
        return function(){ rmv_div('id' + array[i]); }
    })(i), 1);
}

Upvotes: 1

Related Questions