The function gets the value as undefined

I have a timeout that calls a function before the completion of a 100% move. Then it performs the function assigned to me. Only the value that has been assigned to it is undefined, or at least part of it.

I'm not sure at what stage the code loses the passed value, thereby returning it undefined , but I did a JS Fiddle with it in action so you can see it:

Js fiddle

My end result is to get the correct value and then remove the given element as follows:

 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); } 
+4
source share
2 answers

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

You can solve this problem by creating another variable at each iteration, which you will not 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); } 

Decision 2

Demo: http://jsfiddle.net/qJ42h/8/ <a3>

 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); } 
+1
source

The problem is that you did not isolate the loop variable i inside the closure. However, this can be solved much more widely using objects.

First, I present an object that will encapsulate what you want; it gets initialized using a panel item and a function to call when it is counted to 100. Therefore, I will call it BarCounter :

 function BarCounter(element, fn) { this.element = element; this.fn = fn; this.text = element.getElementsByTagName('div')[0]; this.counter = 0; } 

This is only a constructor; he does nothing useful; it resolves the text element, which is just the first <div> tag that it can find under this element and saves this link for later use.

Now we need a function that will do the job; 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 checks if the counter has reached another 100; until then it will update the text element with the current value, increment the counter, and then call itself again after 70 ms. You can see how the reference to this is stored in advance to preserve the context in which the run() function is called later.

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

The completion function is much simpler if you pass the element to be deleted:

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

The final step is to edit 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 is very simple; it creates a new BarCounter object and calls its run() method. Done :)

Btw, you have the option to remove an item from an object; this, of course, depends on your own needs.

Demo

+1
source

Source: https://habr.com/ru/post/1493333/


All Articles