Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript's setTimeout function not calling

The setTimeout function always seems to give me trouble. Right now I have a function that is recursive (calls itself through setTimeout) and changes the elements height.

The function is sent two arguments: the element to be altered and that elements maximum height. The purpose of the function is to unfold the element, or "slide down" at a constant pace. I'm aware I could probably solve this problem with jQuery, but I'm trying my own function.

function slide_down(element, max_height)
{   
    if(element.clientHeight < max_height)
    {
        var factor = 10;
        var new_height = (element.clientHeight + factor >= max_height) ? max_height : (element.clientHeight + factor);
        element.style.height = new_height + 'px';

        var func_call = 'slide_down(' + element + ', ' + max_height + ');';
        window.setTimeout(func_call, 10);
    }
}

I have tested the arguments when the function is initially called. max_height is set to 20 because it is the elements desired height (I got this value from .scrollHeight).

When the function is done, I want to have it call itself until max_height is the element's height. I do that with this setTimeout call:

var func_call = 'slide_down(' + element + ', ' + max_height + ');';
window.setTimeout(func_call, 10);

And it is not working. THIS does work:

var func_call = 'alert(1);';
window.setTimeout(func_call, 10);

I have also tried putting the function call directly into the setTimeout statement, still does not work.

Note that the element's height DOES change the first iteration, the function just never calls itself. So the element variable is set correctly, and I used alert() to display the max_height which is also correct.

alert(func_call);

Alerts this:

slide_down([object HTMLParagraphElement], 20);
like image 973
Logan Serman Avatar asked Dec 04 '08 17:12

Logan Serman


People also ask

Can I call a function in setTimeout JavaScript?

The setTimeout function is a native JavaScript function. It sets a timer (a countdown set in milliseconds) for an execution of a callback function, calling the function upon completion of the timer.

Is JavaScript setTimeout blocking?

Explanation: setTimeout() is non-blocking which means it will run when the statements outside of it have executed and then after one second it will execute. All other statements that are not part of setTimeout() are blocking which means no other statement will execute before the current statement finishes.

What is the alternative for setTimeout in JavaScript?

The setInterval method has the same syntax as setTimeout : let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...) All arguments have the same meaning. But unlike setTimeout it runs the function not only once, but regularly after the given interval of time.


5 Answers

When you do this:

var func_call = 'slide_down(' + element + ', ' + max_height + ');';

you're converting element to a string, so your timeout will look like

slide_down("[Object]", 100);

which obviously won't work.

What you should be doing is creating a closure - it's a little complicated but basically you create a function, and local variable from the old function are still available within the new function.

function slide_down(element, max_height)
{   
    if(element.clientHeight < max_height)
    {
        var factor = 10;
        var new_height = (element.clientHeight + factor >= max_height) ? max_height : (element.clientHeight + factor);
        element.style.height = new_height + 'px';

        var func = function()
        {
            slide_down(element, max_height);
        }

        window.setTimeout(func, 10);
    }
}

Also 10 is a bit short for a timeout - I'd recommend using 50 as a minimum.

like image 197
Greg Avatar answered Oct 11 '22 22:10

Greg


First of all, this "Right now I have a function that is recursive (calls itself through setTimeout)" screams setInterval to me if you're not varying the period.

Secondly, this is because the element reference is lost in the string concat as element.toString() will be "[object]". Try passing in an id you can re-find, store a reference a level up, or (as I've just seen matt b point out) the expanded method form.

like image 38
annakata Avatar answered Oct 11 '22 22:10

annakata


A more subtle way of writing the method (as extension to Gregs answer):

function slide_down(element, max_height)
{   
    if(element.clientHeight < max_height)
    { return; }

    var factor = 10,
        new_height = element.clientHeight + factor,
        f = arguments.callee;

    if( new_height > max_height )
    { new_height = max_height; }

    element.style.height = new_height + 'px';

    window.setTimeout(function() { f(element, max_height); }, 10);
}

The real advantage of this sample code is the use of arguments.callee. In this way you won't be breaking your function, should you decide to rename it. I also simplyfied the new_height value assigning. The problem in the old code is that you performed element.clientHeight + factor twice, which is kinda unnessesary. Not that it has any noticible effect on your performance in this case. But it's always good to avoid calculating the same things over and over, but just store the result for later use.

like image 21
Tokimon Avatar answered Oct 11 '22 22:10

Tokimon


I had a similar experience. When you are passing element the next time through, it is getting converted to a string in the function call, so when the function runs, it tries to find an element named [object].string or something, instead of what you intended.

Try changing your function parameter to take the id of the element and do a document.getElementById() call first thing inside the function.

like image 34
Geoff Avatar answered Oct 11 '22 22:10

Geoff


The problem with your code is that you are fail to pass element parameter.

Instead of:

var func_call = 'slide_down(' + element + ', ' + max_height + ');';

window.setTimeout(func_call, 10);

try using anonymous function:


window.setTimeout(function() {
    slide_down(element, max_height)
}, 10);

@matt.b: arguments passing into setTimeout call (the way you do) is not supported in IE.

like image 33
Sergey Ilinsky Avatar answered Oct 11 '22 23:10

Sergey Ilinsky