这个函数的递归有什么问题? (setTimeout问题)
我正在编写一个选取框脚本,因为我不喜欢这样的事实:对于大多数选取框脚本,当选取框到达最后一个项目时(考虑使用 ul,所以最后一个项目是最后一个 li),它会等到该项目离开屏幕,并且然后重置 ul 的位置,并开始在 agian 上滚动。
我的方法是创建 ul 的克隆,将其附加在当前 ul 之后,并开始滚动,然后在原始 ul 完全隐藏后将其删除。
除了一件事之外,这几乎是完美的。当您调用该函数时,它会生成 setTimout 所需的时间。此 setTimout 用于创建新的 UL 并设置其移动。
这对于第一个循环来说效果很好,但随后就会出现故障。
这有点难以解释,但似乎第二次应该调用超时,而不是等待时间,而是立即调用自己。请参阅 http://webspirited.com/marquee.html 获取示例
工作示例。 JavaScript代码如下:
var count = 0;
$('document').ready(function () {
//generate some random rows
for (var i = 0; i <= 20; i++)
$('.ulscroll').append('<li>Content ' + i + '</li>');
//add one row so we can see its the last
$('.ulscroll').append('<li>Last Item</li>');
//set the ul's width
var width = 0;
$('.ulscroll').children('li').each(function () {
width += $(this).outerWidth();
});
log('ul width: ' + width);
$('.ulscroll').width(width);
//activate the marquee
marquee('.ulscroll', 1, false);
});
function marquee(id, speed, sub) {
//next two lines debugging purposes only
count += 1;
log('Marquee ran ' + count + ' times');
//store copy of speed sent it(to pass for recursion)
var s1 = speed;
//set speed to 10*
speed = speed * 10;
//store parent width, and own width (if sub then add on width of parent div)
var pwidth = $(id).parent('div').outerWidth();
var width = (sub ? $(id).width() + pwidth : $(id).width());
//set timeout
var t = (width - pwidth) * speed;
setTimeout(function () {
var clone = $(id).clone().css('left', pwidth);
$(id).addClass('oldul');
$(id).after(clone);
marquee(id + ':not(.oldul)', s1, true);
}, t);
$(id).animate({
left: '-=' + width
}, width * speed, 'linear', function () {
$(this).remove();
});
}
function log(text) {
$('#log').append('<div>' + text + '</div>');
}
Solution
The issue was caused by passing the selector in with :not(.ulold); Here is the revised setTimeout
setTimeout(function(){
var clone = $(id).clone().css('left', pwidth);
$(id).addClass('oldul');
var idx = id.split(':');
idx = idx[0];
log('idx: '+idx);
$(idx).after(clone);
marquee(idx+':not(.oldul)', s1, true);
},t);
I am writing a marquee script because I do not like the fact that with most marquee scripts when the marquee reaches the last item (thinking using a ul, so last item is the last li), it waits till that item is off screen, and then resets the position of the ul, and starts the scrolling all over agian.
My approach is to create a clone of the ul, append it after the current ul, and start it scrolling, then delete the original ul once it is completely hidden.
This works almost perfect except for one thing. When you call the function it generates the time required for a setTimout. This setTimout is used to create the new UL and set it moving.
This works fine for the first loop, but then glitches up.
It is a bit hard to explain, but it seems to on the second time the timeout should be called, instead of waiting for the time, it just calls itself instantly. Please see http://webspirited.com/marquee.html for an example
For a working example. The javascript code is as follows:
var count = 0;
$('document').ready(function () {
//generate some random rows
for (var i = 0; i <= 20; i++)
$('.ulscroll').append('<li>Content ' + i + '</li>');
//add one row so we can see its the last
$('.ulscroll').append('<li>Last Item</li>');
//set the ul's width
var width = 0;
$('.ulscroll').children('li').each(function () {
width += $(this).outerWidth();
});
log('ul width: ' + width);
$('.ulscroll').width(width);
//activate the marquee
marquee('.ulscroll', 1, false);
});
function marquee(id, speed, sub) {
//next two lines debugging purposes only
count += 1;
log('Marquee ran ' + count + ' times');
//store copy of speed sent it(to pass for recursion)
var s1 = speed;
//set speed to 10*
speed = speed * 10;
//store parent width, and own width (if sub then add on width of parent div)
var pwidth = $(id).parent('div').outerWidth();
var width = (sub ? $(id).width() + pwidth : $(id).width());
//set timeout
var t = (width - pwidth) * speed;
setTimeout(function () {
var clone = $(id).clone().css('left', pwidth);
$(id).addClass('oldul');
$(id).after(clone);
marquee(id + ':not(.oldul)', s1, true);
}, t);
$(id).animate({
left: '-=' + width
}, width * speed, 'linear', function () {
$(this).remove();
});
}
function log(text) {
$('#log').append('<div>' + text + '</div>');
}
Solution
The issue was caused by passing the selector in with :not(.ulold);
Here is the revised setTimeout
setTimeout(function(){
var clone = $(id).clone().css('left', pwidth);
$(id).addClass('oldul');
var idx = id.split(':');
idx = idx[0];
log('idx: '+idx);
$(idx).after(clone);
marquee(idx+':not(.oldul)', s1, true);
},t);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题很可能出在这段代码上。
您重复使用选择器的事实会把事情搞砸,因为随着时间的推移,原始选择器会匹配很多东西。
在初始调用中使用
marquee(' .ulscroll:not(.oldul)', 1, false);
The problem, most likely, lies with this code..
The fact that you are re-using the selector messes things up because the original selector matches a lot of things as time progresses..
In the initial call use
marquee('.ulscroll:not(.oldul)', 1, false);
学习使用好的调试器,例如 Firebug。您将看到最终动画结束回调(删除与选择器匹配的元素的回调)在匿名超时函数(调用
marquee
的回调)之前被调用。示例页面上的代码存在问题(但不在发布的代码中):
tout
对于每次调用marquee
都是本地的。clearTimeout(tout)
调用不执行任何操作。最后,请不要使用字幕。它存在可用性问题:移动文本更难阅读、会分散注意力,并且总是会移动对某些读者来说要么太快,要么对某些读者太慢。
Learn to use a good debugger, such as Firebug. You'll see that eventually the animation end callback (the one that removes the elements matching the selector) gets called before the anonymous timeout function (the one that calls
marquee
).An issue with the code on the sample page (but not in the posted code):
tout
is local to each invocation ofmarquee
. TheclearTimeout(tout)
call does nothing.Lastly, please don't use a marquee. It has usability problems: moving text is harder to read, causes distraction, and will always move either too quickly for some readers or too slowly for some readers.