我想直接调用jquery.animate
来改变div的效果,但发现它没有任何效果。
相反,我需要将其放在setTimeout(..., 0)
内以使其正常工作。
我想知道为什么我需要这样做,这是最好的方法吗?
http://jsbin.com/docahu/2/edit
或者在这里:
var FooView = Backbone.View.extend({
id: 'foo',
});
var BarView = Backbone.View.extend({
render: function() {
$("#foo").animate({width: '200px'});
// !!! HERE !!!
setTimeout(function() {
$("#foo").animate({height: '100px'});
}, 0);
return this;
}
});
var fooView = new FooView();
var barView = new BarView();
var combinedView = $(fooView.render().el).append(barView.render().el);
$(document.body).append(combinedView);

#foo {
width: 50px;
height: 50px;
background-color: blue;
color: white;
}

<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Jquery animate delay problem in backbone render" />
<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="//jashkenas.github.io/underscore/underscore-min.js"></script>
<script src="//jashkenas.github.io/backbone/backbone-min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
</body>
</html>
&#13;
您可以看到高度已更改但宽度未更改。
PS:
我还发现$(document).ready()
也在运作:
$(document).ready(function() {
$("#foo").animate({height: '100px'});
});
哪一个更好用?
答案 0 :(得分:2)
这是因为它试图在元素进入DOM之前设置宽度的动画。如果你把一个选择器放在那个位置,你可能会发现它没有得到任何东西。
执行超时0(因此javascript完成呈现事情然后尝试动画)或等待文档完成渲染修复
事情按此顺序发生:
答案 1 :(得分:1)
好像是偶然的。第一个不工作的原因可能是因为对象仍未加载到屏幕上。第二个是工作,因为在调度和结束计时器后(这实际上不需要0次),页面被计算机上的另一个线程加载。因此,创建计时器和回调过程的开销显然足以完成加载页面。
您应该使用$(document).ready
来确保在文档完全加载后始终调用它,因为就像我说的那样,它现在正在工作,而另一个浏览器\机器可能无法运行任何两个(或两者兼有)。
背景:JavaScript在页面加载时开始执行,DOM同时构建(就在下载HTML和JavaScript文本时)。因此,如果您像现在一样从JavaScript代码引用DOM对象,则会得到race condition,其中未定义结果。为避免出现$(document).ready
回调。
请参阅this问题。此外,Udacity course真的很酷,无法理解正在发生的事情。
答案 2 :(得分:0)
使用$(document).ready
相当于将JavaScript放在文档的末尾......
JSBin(OP发布了他的示例代码)将在所有HTML元素呈现后执行JavaScript,但之前 $(document).ready
事件。绑定jQuery.animate
中的$(document).ready
与在视图附加到DOM后随时触发相同。
简单而稳定的解决方案是简单地调用
$("#foo").animate({width: '200px'});
OP的代码的最后一行是。 (阅读答案的结尾以查看绑定jQuery.animate
函数的更正式方法)
回答OP的原始问题:setTimeout()
适用于您的情况,因为异步函数在JavaScript运行时中的排队方式。如果已加载<body>
,则以OP的方式使用setTimeout(0)
,与将动画绑定放在$(document).ready
中具有相同的效果。
首先要理解的是JavaScript不是一个多线程框架。虽然您当然可以调用非同步函数,但异步,异步函数实际上并不与同步操作并行运行。相反,只要运行时空闲,异步函数就会排队等待运行。
例如,请执行以下三个同步功能。
function1();
function2();
function3();
正如你所期望的,异步调用中的function1 will fire first, followed, in order, by
function1 ,
function2 then
function3 . However, if I place
function1`
setTimeout(function1,0);
function2();
function3();
然后function1
将被放置在队列中,function2
然后function3
将被触发。一旦事件循环结束,就会调用function1
。也就是说,它最后发射!您可以在this fiddle中看到这一点。
在OP的示例中,setTimeout(function() { $("#foo").animate({height: '100px'});}, 0);
在运行时执行$(document.body).append(combinedView);
后立即被触发,因此jQuery能够找到#foo
元素,所以从技术上来说这是正确的方法做OP想要的事情。这是因为JSBin的工作方式。也就是说,它在加载DOM之后(但在$(document).ready
事件之前)从JavaScript模块加载JavaScript。
$(document).ready
功能...... 我认为$(document).ready
函数如何解决OP的问题存在一些混淆。大多数混淆可能源于不同网页元素(HTML,CSS,JavaScript)如何影响DOM渲染的复杂性。
浏览器使用主要的解析和渲染线程。这是处理HTML,获取和解析CSS样式表以及获取/解析JavaScript的地方。所有这些操作都会在遇到时执行,并且会阻止(除非async
/ defer
标记中指定了<link>
或<script>
。
您调整所有这些标签的顺序至关重要。如果您的脚本标记写在文档的顶部(例如在<head>
标记内),则会在将任何HTML注入DOM之前执行。
本质上,使用$(document).ready
相当于将JavaScript放在文档的末尾 ...因为JSBin(OP发布了他的示例代码)将在执行后执行JavaScript所有HTML元素都会呈现,但之前 $(document).ready
函数,绑定jQuery.animate
$(document).ready
与{em>之后 相同>视图附加到DOM。
相反,简单而稳定的解决方案是简单地调用
$("#foo").animate({width: '200px'});
在fooView
和barView
都附加到DOM之后。或者更正式地说:
var BarView = Backbone.View.extend({
render: function() {
// process your html here
return this;
}
bindTransitions: function {
$("#foo").animate({width: '200px', height: '100px'});
}
});
var fooView = new FooView();
var barView = new BarView();
var combinedView = $(fooView.render().el).append(barView.render().el);
$(document.body).append(combinedView);
barView.bindTransitions();
答案 3 :(得分:0)
如果您正确地确定了骨干视图的范围,那么当您尝试更改宽度或高度(预渲染)时,您应该能够引用当前在内存中的元素。
您可以通过执行以下操作来执行此操作:此。$ el.find(#foo“)在将标记添加到DOM之前获取对标记的访问/操作。