Javascript 异步调用后的DOM更新

Javascript 异步调用后的DOM更新,javascript,html,angularjs,dom,Javascript,Html,Angularjs,Dom,我正在从服务器获取一些数据,并通过双向绑定更新DOM。然而,它并没有像预期的那样工作,我需要将它封装在一个丑陋的setTimeout函数中,以便DOM有时间进行更新 $http.post('myBackend.php', someData) .then(function(res){ $scope.data = res.data; doStuffWithDOMElements(); // Does not work }); 虽然这样做有效: $http.post('myBack

我正在从服务器获取一些数据,并通过双向绑定更新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>


这很奇怪。难道不是应该自动更新每个包含在角度“事件”中的DOM元素吗<代码>$scope.$apply()不起作用,也不需要。这里怎么了?

在angular中进行DOM操作时,这是众所周知的

请看这个

在angularjs中每隔一段时间就会需要一次。最有可能的是初始化一个
jQuery插件

您的错误行:

 let y_0 = document.getElementById("l_0").offsetTop; 
这是因为您的DOM尚未设置,并且您正在尝试在DOM中获取尚未设置或渲染的元素

当您使用
$timeout
时,它应该在通过Angular操作DOM之后以及在浏览器渲染之后运行(在某些情况下可能会导致闪烁)。这就是为什么在您设置
$timeout
时它会在您的情况下工作

如果你想了解更多关于消化循环的知识。你也应该知道

  • 如果使用指令中的$evalAsync对代码进行排队,则它应该在Angular操作DOM之后,但在浏览器呈现之前运行
  • 如果代码是使用控制器中的$evalAsync排队的,那么它应该在Angular操作DOM之前(以及在浏览器呈现之前)运行—您很少希望这样做
  • 如果使用$timeout对代码进行排队,则应在Angular操作DOM后以及浏览器渲染后运行(在某些情况下可能会导致闪烁)
此外,Angularjs是一个javascript框架。浏览器必须同时完成很多事情,其中之一就是执行JavaScript。但是JavaScript经常被用来要求浏览器构建一个显示元素。这通常被认为是同步完成的(特别是当JavaScript不是并行执行时),但不能保证这种情况会发生,并且JavaScript没有定义良好的等待机制

解决方案是“暂停”JavaScript执行,让渲染线程跟上进度。这就是超时为0
setTimeout()
的效果。这就像C中的线程/进程产出。虽然它似乎说“立即运行”,但实际上它让浏览器有机会完成一些非JavaScript的事情,这些事情在处理新的JavaScript之前一直在等待完成

(实际上,
setTimeout()
将新JavaScript重新排列在执行队列的末尾。请参阅注释以获得指向更详细解释的链接。)

IE6恰好更容易出现这种错误,但我已经在旧版本的Mozilla和Firefox上看到过这种情况

此外,关于为什么使用$timeout会时不时地派上用场,已经有很多文章和解释

链接,您可以在其中找到好的解释:

中断操作 要做的事情之一是通过链接操作来分解操作

$http.post('myBackend.php', someData)
    .then (function onFulfilled (response) {
        $scope.someDataToPopulateDOMwith = response.data;
        return response;
    }).then (function onFulfilled2 (response) {
        doStuffWithDOMElements();
    });
这允许
$q
服务在调用第二个实现处理程序之前执行摘要循环。AngularJS框架需要执行一个摘要循环,以便
ng repeat
指令的监视处理程序有机会更新DOM。
ng repeat
指令需要在
dostuffWithDomeElements
函数能够安全地操作DOM之前完成DOM的更新


使用
$timeout
服务 避免使用原始浏览器
setTimeout
功能,而是使用
$timeout
服务。然后AngularJS
$q
服务会自动进行周期消化

由于
$timeout
服务返回承诺,因此它可以用于链接

$http.post('myBackend.php', someData)
    .then (function onFulfilled (response) {
         $scope.someDataToPopulateDOMwith = response.data;
         return response;
    }).then (function onFulfilled2 (response) {
         //return $timeout promise
         return $timeout(function(){return response}, 1000);
    }).then (function onFulfilled3 (response) {
         doStuffWithDOMElements();
    });
因为调用允诺的
then
方法会返回一个新的派生允诺,所以很容易创建一个允诺链。可以创建任意长度的链,并且由于一个承诺可以用另一个承诺解决(这将进一步推迟其解决),因此可以在链中的任何点暂停/推迟承诺的解决。这使得实现强大的API成为可能


重新考虑设计因素 AngularJS是一个MVW*框架

避免以编程方式操作HTMLDOM:操作HTMLDOM是AJAX应用程序的基石,但它既麻烦又容易出错。通过声明性地描述UI应该如何随着应用程序状态的变化而变化,您可以从低级DOM操作任务中解脱出来。大多数使用Angular编写的应用程序都不必以编程方式操纵DOM,尽管如果愿意,您可以这样做

研究如何建模
dostuffWithDoElements()
函数,并创建自定义指令来监视该模型和更新DOM。这更适合AngularJS框架,并将避免这些时间问题