JavaScript的setTimeout函数没有调用

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);
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秒。


3
首先,如果您没有改变时间间隔,那么这个“现在我有一个通过setTimeout调用自身的递归函数”让我想起了setInterval
其次,这是因为元素引用在字符串拼接中丢失,因为element.toString()将是“[object]”。尝试传入一个您可以重新查找的id,存储一个上层引用,或者(正如我刚刚看到matt b指出的那样)使用扩展的方法形式。

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,这有点多余。虽然在这种情况下对性能没有任何显著影响,但是避免重复计算同一内容并将结果存储以备后用总是明智的选择。请注意保留HTML标签。

1
我有过类似的经历。当你下一次传递元素时,它会在函数调用中被转换为字符串,所以当函数运行时,它会尝试找到一个名为[object].string或其他类似的元素,而不是你想要的。
尝试将你的函数参数改为接受元素的id,并在函数内部的第一件事就调用document.getElementById()。

1

你的代码问题在于没有传递元素参数。

尝试使用匿名函数而不是:

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

可以尝试使用:

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

@matt.b:setTimeout调用中的参数(你所做的方式)在IE中不受支持。


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()


嗯,我猜我从没遇到过那种语法,因为“请注意,在第一种语法中向函数传递额外的参数在Internet Explorer中不起作用”。你的第一个链接之前显示404错误,所以要么你进行了编辑,要么是个故障。-1已移除。 - Greg
我修复了链接的问题,https格式不正确。谢谢。 - matt b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接