【问题标题】:How can I run a directive after the dom has finished rendering?dom 完成渲染后如何运行指令?
【发布时间】:2012-08-27 18:21:07
【问题描述】:

我有一个看似简单的问题,没有明显的(通过阅读 Angular JS 文档)解决方案。

我有一个 Angular JS 指令,它根据其他 DOM 元素的高度进行一些计算,以定义 DOM 中容器的高度。

指令内部正在发生类似的事情:

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

问题是当指令运行时,$('site-header') 找不到,返回一个空数组而不是我需要的 jQuery 包装的 DOM 元素。

是否有一个回调可以在我的指令中使用,它只在 DOM 加载后运行并且我可以通过正常的 jQuery 选择器样式查询访问其他 DOM 元素?

【问题讨论】:

  • 您可以使用 scope.$on() 和 scope.$emit() 来使用自定义事件。不确定这是否是正确/推荐的方法。

标签: jquery dom callback angularjs document-ready


【解决方案1】:

这取决于你的 $('site-header') 是如何构造的。

您可以尝试使用$timeout 0 延迟。比如:

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

解释其工作原理:onetwo

别忘了在你的指令中注入$timeout

.directive('sticky', function($timeout)

【讨论】:

  • 谢谢,我尝试让它工作很长时间,直到我意识到我没有将$timeout 传递到指令中。多哈。现在一切正常,干杯。
  • 是的,您需要将$timeout 传递给这样的指令:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
  • 您的链接解释解释了为什么超时技巧在 JavaScript 中有效,但在 AngularJS 的上下文中无效。来自official documentation: "[...] 4. $evalAsync 队列用于调度需要在当前堆栈帧之外发生的工作,但在浏览器的视图呈现之前。这通常完成使用 setTimeout(0),但 setTimeout(0) 方法速度慢,并且可能导致视图闪烁,因为浏览器在每个事件之后呈现视图。[...]"(强调我的)
  • 我遇到了类似的问题,我发现在执行我的指令之前我需要大约 300 毫秒来允许 DOM 加载。我真的不喜欢像那样插入看似任意的数字。我确信 DOM 加载速度会因用户而异。那么如何确保 300 毫秒适用于使用我的应用的任何人?
  • 对这个答案不太满意.. 虽然它似乎回答了 OP 的问题.. 这对他们的情况非常具体,并且与问题的更一般形式相关(即在dom 已加载)并不明显 + 它太 hacky.. 没有任何关于 Angular 的具体内容
【解决方案2】:

这是我的做法:

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});

【讨论】:

  • 甚至不应该需要 angular.element 因为元素已经在那里可用:element.ready(function(){
  • @timhc22 元素是对触发指令的 DOMElement 的引用,您的建议不会导致对页面 Document 对象的 DOMElement 引用。
  • 不能正常工作。我通过这种方法得到 offsetWidth = 0
【解决方案3】:

可能作者不再需要我的回答了。不过,为了完整起见,我觉得其他用户可能会觉得它很有用。最好和最简单的解决方案是在返回函数的主体内使用$(window).load()。 (或者你可以使用document.ready。这真的取决于你是否需要所有的图像)。

在我看来,使用$timeout 是一个非常弱的选项,在某些情况下可能会失败。

这是我要使用的完整代码:

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});

【讨论】:

  • 你能详细说明为什么它“在某些情况下可能会失败”吗?你指的是什么情况?
  • 你假设 jQuery 在这里可用。
  • @JonathanCremin 根据 OP,jQuery 选择是手头的问题
  • 这很好用,但是如果有一个帖子使用该指令构建新项目,那么窗口加载将不会在初始加载后触发,因此将无法正常运行。
  • @BrianScott - 我使用 $(window).load 的组合进行初始页面渲染(我的用例是等待嵌入的字体文件),然后 element.ready 负责切换视图。
【解决方案4】:

有一个ngcontentloaded事件,我想你可以使用它

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});

【讨论】:

  • 你能解释一下$ $window在做什么吗?
  • 看起来像一些coffeescript,可能是为了将$($window) 和$window 注入到指令中
【解决方案5】:

如果由于外部资源而无法使用 $timeout 并且由于特定的时间问题而无法使用指令,请使用广播。

在所需的外部资源或长时间运行的控制器/指令完成后添加$scope.$broadcast("variable_name_here");

然后在加载外部资源后添加以下内容。

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

例如在延迟 HTTP 请求的承诺中。

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}

【讨论】:

  • 这不会解决问题,因为加载的数据并不意味着它们已经在 DOM 中呈现,即使它们位于绑定到 DOM 元素的正确范围变量中。在它们被加载到作用域和在 dom 中呈现输出之间有一个时间跨度。
【解决方案6】:

我遇到了类似的问题,想在这里分享我的解决方案。

我有以下 HTML:

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

问题:在父 div 指令的链接功能中,我想对子 div#sub 进行 jquery。但它只是给了我一个空对象,因为当指令的链接功能运行时 ng-include 还没有完成。所以首先我用 $timeout 做了一个肮脏的解决方法,它有效,但延迟参数取决于客户端速度(没有人喜欢这样)。

工作但很脏:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

这是干净的解决方案:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

也许它对某人有帮助。

【讨论】:

    猜你喜欢
    • 2017-10-30
    • 2012-12-23
    • 2018-04-05
    • 2019-04-27
    • 2018-06-27
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    相关资源
    最近更新 更多