Вопрос

I am building a console like application using AngularJS. One of my requirements is that the div containing the console is scrolled down automatically. I achieved that using a directive that is monitoring the content and adjusting the scrollTop property of the div accordingly:

app.directive('autoScroll', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs, ctrls) {
            var scrollToBottom = function () {
                element[0].scrollTop = element[0].scrollHeight;
            };
            scope.$watchCollection('outputLines', scrollToBottom);
        }
    };
});

The first approach to get lines displayed was to use ng-repeat in combination with an unordered list and the curly brackets syntax for binding:

<div auto-scroll id="withHtml" class="list">
    <ul>
        <li ng-repeat="line in outputLines">{{line.text}}</li>
        <li>>> {{currentInput}}</li>
    </ul>
</div>

This worked beautifully as you can see in the green output area in this fiddle. The input line (the one that starts with >> is always visible.

enter image description here

But one of the requirements also is to render HTML in the output lines. I thus started using the ngSanitize module and switched from curly brackets syntax to using the ng-bind-html directive:

<div auto-scroll id="withHtml" class="list">
    <ul>
        <li ng-repeat="line in outputLines" ng-bind-html="line.text"></li>
        <li>>> {{currentInput}}</li>
    </ul>
</div>

That results in the input line always moving out of the visible area. As you can see in the red output area in the fiddle mentioned above:

enter image description here

The output generated looks the same in both cases. So here's my question: Why does scrolling behave differently depending on whether I use the curly brackets syntax or the ng-bind-html directive?

Это было полезно?

Решение

It's a timing issue.

If you do the following:

var scrollToBottom = function () {
  console.log(element[0].scrollHeight);
  element[0].scrollTop = element[0].scrollHeight;
};

You will notice that after a few inputs the scrollHeight at the time of setting scrollTop will differ.

I haven't dug deeper into the source code to see what causes this, but you can use $evalAsync to make sure the code is run after the DOM has been maniuplated by Angular, but before the browser renders:

var scrollToBottom = function() {
  scope.$evalAsync(function() {
    element[0].scrollTop = element[0].scrollHeight;
  });
};

Demo: http://jsfiddle.net/cBny9/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top