我正在从我的服务器获取一些数据并通过 Angular 双向绑定(bind)更新 DOM。然而,它没有按预期工作,我需要将它包装在一个丑陋的 setTimeout
函数中,以便 DOM 有时间更新。
$http.post('myBackend.php', someData)
.then(function(res){
$scope.data = res.data;
doStuffWithDOMElements(); // Does not work
});
虽然这有效:
$http.post('myBackend.php', someData)
.then(function(res){
$scope.someDataToPopulateDOMwith = res.data;
setTimeout(function(){ doStuffWithDOMElements();}, 50); // Yup, works
});
在没有超时的情况下给出错误的行 “无法正确读取 null” 是这样的:
let y_0 = document.getElementById("l_0").offsetTop;
在我的 index.html 中
<div id="l_{{$index}}" ng-repeat = "x in data"></div>
这很奇怪。不应该自动更新包含在 Angular“事件”中的每个 DOM 元素吗? $scope.$apply()
不起作用,也没有必要。这里有什么问题吗?
最佳答案
需要$timeout在 angularjs 中每隔一段时间就会出现一次。很可能是为了初始化一个 jQuery 插件
。
您的错误行:
let y_0 = document.getElementById("l_0").offsetTop;
是因为您的 DOM 尚未设置,您正在尝试获取尚未设置的元素,或者更确切地说是在 DOM< 中呈现/strong>.
当你使用 $timeout
时,它应该在 DOM 被 Angular 操作之后运行,并且在浏览器渲染之后运行(这在某些情况下可能会导致闪烁)。这就是为什么它在您设置 $timeout
时的情况。
如果您想了解有关摘要循环的更多信息。你应该知道 $evalAsync以及。
- 如果代码使用来自指令的$evalAsync 进行排队,它应该在 Angular 操作 DOM 之后,但在浏览器呈现之前运行
- 如果代码使用来自 Controller 的 $evalAsync 进行排队,它应该在 Angular 操作 DOM 之前(以及浏览器呈现之前)运行——您很少希望这样
- 如果代码使用 $timeout 排队,它应该在 DOM 被 Angular 操作之后以及浏览器呈现之后运行(这在某些情况下可能会导致闪烁)。
此外,Angularjs 是一个javascript 框架。浏览器几乎必须同时做很多事情,其中之一就是执行 JavaScript。但是 JavaScript 最常用于的事情之一是要求浏览器构建一个显示元素。这通常被认为是同步完成的(特别是因为 JavaScript 不是并行执行的),但不能保证是这种情况,而且 JavaScript 没有明确定义的等待机制。
解决方案是“暂停” JavaScript 执行,让渲染线程 catch 进度。这就是超时为 0 的 setTimeout()
的效果。它就像 C 语言中的线程/进程 yield。虽然它似乎在说“立即运行”,但它实际上让浏览器有机会完成一些非 JavaScript 的事情,这些事情在处理这段新的 JavaScript 之前一直在等待完成.
(实际上,setTimeout()
将新的 JavaScript 重新排入执行队列的末尾。请参阅评论以获得更长解释的链接。)
IE6 恰好更容易出现此错误,但我已经看到它发生在旧版本的 Mozilla 和 Firefox 中。
此外,关于为什么使用 $timeout 有时会派上用场,已经有很多文章和解释。
您可以在其中找到很好解释的链接:
关于javascript - 异步调用后 DOM 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35744028/