AngularJS:如何在ng-show显示隐藏元素后滚动页面?

8
我有一个隐藏项目列表。 我需要在单击后显示该列表并滚动到其中一个项目。 我在此处复制了代码:http://plnkr.co/edit/kp5dJZFYU3tZS6DiQUKz?p=preview 从控制台中可以看到,scrollTop()在项目可见之前被调用,因此我认为ng-show不是即时的,这种方法是错误的。 通过使用超时延迟scrollTop(),它可以正常工作,但我不想这样做。
是否有其他解决方案?

这似乎是这个相关问题的特例:https://dev59.com/uWct5IYBdhLWcg3wKqQd - lex82
2个回答

16

在使用 ng-show 时,我没有看到其他解决方案,除了推迟对 scrollTop() 的调用。您必须等待模型中的更改反映到DOM中,以使元素变为可见。它们不会立即出现的原因是作用域生命周期。 ng-show 内部使用一个监视器,只有在调用作用域的 $digest() 函数之后执行完点击处理程序中的完整代码后才会触发。

有关作用域生命周期的更详细说明,请参见http://docs.angularjs.org/api/ng.$rootScope.Scope

通常,使用无延迟执行的超时器应该不是问题,像这样:

setTimeout(function() {
    $(window).scrollTop(50);  
}, 0);

无需超时的替代解决方案:

然而,如果你想避免超时事件(其执行可能在事件队列中的其他事件之前),并确保滚动发生在单击事件处理程序内部。您可以在控制器中执行以下操作:

$scope.$watch('itemsVisible', function(newValue, oldValue) {
    if (newValue === true && oldValue === false) {
        $scope.$evalAsync(function() {
            $(window).scrollTop(50);
        });
    }
});

当使用ng-show指令注册的watch listener在同一个$digest()周期内触发时,监听器会与ng-show指令注册的watch listener一起被执行。传递给$evalAsync()的函数将在Angular处理完所有的watch listener之后立即执行,所以元素已经被ng-show指令显示出来了。


我认为有一种更优雅的方法来做这件事,但如果这不会因浏览器而出现问题,使用超时也可以。谢谢! - macene
1
也许$evalAsync的行为在新版本的Angular中已经发生了变化。 - lex82
问题在于setTimeout并不适用于所有设备,因为例如手机内存较少,因此执行DOM操作较慢。因此,在桌面上可以工作,但在移动设备上则无法正常工作。如果我增加超时时间,它可以在两种设备上工作,但在桌面上看起来很糟糕,因为它会滚动页面(因为显示和隐藏不同的元素),然后再回到正确的位置。有什么好的解决方法吗? - Yulian
您可以使用短暂的超时时间,在超时处理程序中直接访问DOM,检查元素是否可见。如果不可见,则设置一个新的超时时间,并延长延迟时间。但是,如果您开始直接访问DOM,那么您也可以立即这样做,而不必首先使用Angular来切换受影响元素的可见性。 - lex82
最好注入$timeout服务并使用this.$timeout(function() { ...以便能够正确地进行单元测试。 - Nick B

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接