39

I have a simple list of items. I want to be able to scroll to the bottom of the element displaying the items whenever I add more items. I understood there is no way of hooking to the end of the $apply() function, so what might be my solution?

Here is a jsfiddle to illustrate my problem. after adding enough items, the ul element doesn't scroll to the bottom...

6 Answers 6

70

There's the very awesome angularjs-scroll-glue available, which does exactly what you want.

All you need to do is to apply the scroll-glue directive to your container element, and you get exactly what you're looking for.

There's also a demo available.

Sign up to request clarification or add additional context in comments.

3 Comments

this is a good solution, but I dont want to use many external lib. do you know how to code it?
@JohnNguyen: Well, it's open source.
worth the dependency.. this kind of process of watch for the DOM and then proceed with the scroll generates unnecessary code.. in this case, this library fits perfectly..
29

Another valid solution to this is using $timeout. Using a timeout value of 0, angular will wait until the DOM is rendered before calling the function you pass to $timeout. So, after you add an element to the list, you can use this to wait for your new element to be added to the DOM before scrolling to the bottom.

Like @Mark Coleman's solution, this won't require any extra external libraries.

var myApp = angular.module('myApp', []);

function MyCtrl($scope, $timeout) {
  $scope.list = ["item 1", "item 2", "item 3", "item 4", "item 5"];
  $scope.add = function() {
    $scope.list.push("new item");
    $timeout(function() {
      var scroller = document.getElementById("autoscroll");
      scroller.scrollTop = scroller.scrollHeight;
    }, 0, false);
  }
}
ul {
  height: 150px;
  overflow: scroll;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <button ng-click="add()">Add</button>
    <ul id="autoscroll">
      <li ng-repeat="item in list">{{item}}</li>
    </ul>
  </div>
</div>

2 Comments

Or you could just watch your collection in the link and then scroll to the element. Also you have easy access to elements in link...
This solution makes the controller aware of the DOM, and directly manipulates it. Wouldn't that be considered un-angular? Better to do that part in a directive perhaps?
9

A simple working example (no need for plugins or directives)...

.controller('Controller', function($scope, $anchorScroll, $location, $timeout) {

  $scope.func = function(data) {

    // some data appending here...

    $timeout(function() {
      $location.hash('end');
      $anchorScroll();
    })
  }

})

The trick that did it for me was wrapping the anchorScroll command with $timeout, that way the scope was resolved and it automatically shifted to an element at the end of the page.

2 Comments

Thank you for good and simple solution. It works and helps me )
Simple, but with the caveat that it modifies the url with the hash value.
6

You could create a simple directive that bind a click handler that scrolls the <ul> to the bottom each time.

myApp.directive("scrollBottom", function(){
    return {
        link: function(scope, element, attr){
            var $id= $("#" + attr.scrollBottom);
            $(element).on("click", function(){
                $id.scrollTop($id[0].scrollHeight);
            });
        }
    }
});

example on jsfiddle

2 Comments

Thanks Mark it helped me. This can be slightly better updated with $timeout service. I have updated in jsfiddle jsfiddle.net/w5Vgw/99.
While this is nice little directive, it does not solve problem. It scroll immediately without waiting for new element to be added to DOM.
2

You can use the AnchorScroll.. here the documentation: https://docs.angularjs.org/api/ng/service/$anchorScroll

Comments

-1

You can achieve this using angularjs custom directory.

example :

<ul style="overflow: auto; max-height: 160px;" id="promptAnswerBlock">

<li ng-repeat="obj in objectKist track by $index" on-finish-render="ngRepeatFinished">                                              

  app.directive('onFinishRender', function($timeout) {
    return {
        restrict : 'A',
        link : function(scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function() {
                    $('#promptAnswerBlock').scrollTop($('#promptAnswerBlock')[0].scrollHeight + 150);
                });
            }
          }
        }
    }); 

</li>

1 Comment

ng-repeat="attributevalue in attribute.attributeValueList track by $index" on-finish-render="ngRepeatFinished"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.