5

setTimeout 函数似乎总是给我带来麻烦。现在我有一个递归函数(通过 setTimeout 调用自身)并更改元素高度。

该函数发送两个参数:要更改的元素和该元素的最大高度。该功能的目的是展开元素,或以恒定的速度“向下滑动”。我知道我可以用 jQuery 解决这个问题,但我正在尝试我自己的函数。

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);
    }
}

我在最初调用函数时测试了参数。max_height 设置为 20,因为它是元素所需的高度(我从 .scrollHeight 得到这个值)。

当函数完成后,我想让它自己调用,直到 max_height 是元素的高度。我通过这个 setTimeout 调用来做到这一点:

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

它不工作。这确实有效:

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

我也试过把函数调用直接放到setTimeout语句中,还是不行。

请注意,元素的高度确实改变了第一次迭代,该函数永远不会调用自己。所以元素变量设置正确,我使用 alert() 显示 max_height 也是正确的。

alert(func_call);

提醒这个:

slide_down([object HTMLParagraphElement], 20);
4

6 回答 6

19

当你这样做时:

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

您正在将元素转换为字符串,因此您的超时将如下所示

slide_down("[Object]", 100);

这显然行不通。

你应该做的是创建一个闭包——它有点复杂,但基本上你创建了一个函数,旧函数中的局部变量在新函数中仍然可用。

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);
    }
}

10 也有点短,我建议至少使用 50。

于 2008-12-04T17:36:43.913 回答
3

首先,如果你不改变时间段,这个“现在我有一个递归函数(通过 setTimeout 调用自身)”向我尖叫setInterval 。

其次,这是因为元素引用在字符串 concat 中丢失,因为 element.toString() 将是“[object]”。尝试传入一个您可以重新查找的 id,将引用存储到一个级别,或者(正如我刚刚看到的 matt b 指出的那样)扩展的方法形式。

于 2008-12-04T17:37:55.720 回答
2

编写方法的更微妙的方式(作为 Gregs 答案的扩展):

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);
}

此示例代码的真正优势在于使用arguments.callee. 如果您决定重命名它,这样您就不会破坏您的功能。我还简化了new_height赋值。旧代码中的问题是您执行了element.clientHeight + factor两次,这有点不必要。并不是说在这种情况下它对你的表现有任何明显的影响。但是避免一遍又一遍地计算相同的东西总是好的,而只是存储结果以备后用。

于 2011-11-01T09:18:18.747 回答
1

我有过类似的经历。当您下次传递元素时,它会在函数调用中转换为字符串,因此当函数运行时,它会尝试查找名为 [object].string 或其他内容的元素,而不是您想要的。

尝试更改您的函数参数以获取元素的 id 并在函数内执行 document.getElementById() 调用的第一件事。

于 2008-12-04T17:34:29.907 回答
1

您的代码的问题是您无法传递元素参数。

代替:

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

window.setTimeout(func_call, 10);

尝试使用匿名函数:

window.setTimeout(function() {
    slide_down(元素,最大高度)
}, 10);

@matt.b:IE 不支持传入 setTimeout 调用的参数(您的方式)。

于 2008-12-04T17:40:44.763 回答
0

两件事情:

1.有另一种调用方式setTimeout(),即传递函数和参数,而不是要执行的字符串。事实上,Gecko DOM 手册指出setTimeout()不推荐使用您的调用版本。

代替:

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

试试这个:

window.setTimeout(slide_down, 10, element, max_height);  

更新:正如 RoBorg 提到的,这在 IE 中可能不起作用,所以你不妨忽略它。


2.我有一种感觉,setTimeout()从一个被自己调用的函数中调用setTimeout()要么是不允许的,要么是不能正常工作的。我假设您正在链接这样的调用,因为您希望代码重复执行?如果是这样,那window.setInterval()就是为了。

于 2008-12-04T17:34:37.067 回答