Angularjs: How to scroll the page after ng-show shows hidden elements?

macene picture macene · Nov 10, 2013 · Viewed 10k times · Source

I have a list of hidden items. I need to show the list and then scroll to one of them with a single click. I reproduced the code here: http://plnkr.co/edit/kp5dJZFYU3tZS6DiQUKz?p=preview

As I see in the console, scrollTop() is called before the items are visible, so I think that ng-show is not instant and this approach is wrong. It works deferring scrollTop() with a timeout, but I don't want to do that.

Are there other solutions?

Answer

lex82 picture lex82 · Nov 10, 2013

I don't see any other solution than deferring the invocation of scrollTop() when using ng-show. You have to wait until the changes in your model are reflected in the DOM, so the elements become visible. The reason why they do not appear instantly is the scope life cycle. ng-show internally uses a watch listener that is only fired when the $digest() function of the scope is called after the execution of the complete code in your click handler.

See http://docs.angularjs.org/api/ng.$rootScope.Scope for a more detailed explanation of the scope life cycle.

Usually it should not be a problem to use a timeout that executes after this event with no delay like this:

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

Alternative solution without timeout:

However, if you want to avoid the timeout event (the execution of which may be preceded by other events in the event queue) and make sure that scrolling happens within the click event handler. You can do the following in your controller:

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

The watch listener fires within the same invocation of $digest() as the watch listener registered by the ng-show directive. The function passed to $evalAsync() is executed by angular right after all watch listeners are processed, so the elements have been already made visible by the ng-show directive.