views:

3132

answers:

6

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);
A: 

Here is the entire 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);
    }
}
Logan Serman
+1  A: 

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.

Geoff
A: 
matt b
Is the Mozilla DOM manual wrong...?
matt b
Hmm I guess I never came across that syntax because "Note that passing additional parameters to the function in the first syntax does not work in Internet Explorer". Your first link came up 404 before so either you edited or it was a glitch. -1 removed
Greg
I fixed a problem with the links, the https wasn't formatted right. thanks.
matt b
+7  A: 

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.

Greg
+3  A: 

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.

annakata
+1  A: 

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.

Sergey Ilinsky