angular is scrollable

by abenrob

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<div ng-app="MyApp">
    <div show-scrollable class="outer">
        <ul>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>lovely spam</li>
            <li>wonderful spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>lovely spam</li>
            <li>wonderful spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>lovely spam</li>
            <li>wonderful spam</li>
        </ul>
    </div>
    <div show-scrollable class="outer">
        <ul>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>spam</li>
            <li>lovely spam</li>
            <li>wonderful spam</li>
        </ul>
    </div>
</div>

CSS

.outer {
    margin: 20px;
    height: 200px;
    width: 200px;
}
.scrollableWrapper {
    position: relative;
    height: inherit;
    width: inherit;
    overflow: hidden;
    border: 1px solid black;
}
.scrollableParentDiv {
    padding: 5px 30px 5px 5px;
    height: calc(100% - 10px);
    width: calc(100% - 10px);
    overflow-y: auto;
}
.scrollableArea {
    width: 100%;
}
.scrollableMsgPadding {
    padding-bottom: 30px;
}
.scrollableScrollMe {
    position: absolute;
    bottom: -1px;
    right: 0px;
    left: 0px;
    height: 30px;
    background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 40%, rgba(255,255,255,1) 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,0)), color-stop(40%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,1))); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 40%,rgba(255,255,255,1) 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 40%,rgba(255,255,255,1) 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 40%,rgba(255,255,255,1) 100%); /* IE10+ */
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 40%,rgba(255,255,255,1) 100%); /* W3C */
}
.scrollableScrollMe .scrollText {
    color: #000;
    text-align: center;
    font-size: 14px;
    font-weight: 600;
    font-family: Sans-Serif;
    position: absolute;
    bottom: 2px;
    right: 0;
    left: 0;
}
.scrollableScrollMe .scrollText span {
    font-size: 10px;
    vertical-align: 1px;
}

JavaScript

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

// for inserting html in template
scrollDir.filter('unsafe', function($sce) {
    return function(val) {
        return $sce.trustAsHtml(val);
    };
});

scrollDir.constant('directionClasses', {
	down: 'fa fa-long-arrow-down',
    updown: 'fa fa-arrows-v',
    up: 'fa fa-long-arrow-up'
});


scrollDir.directive('showScrollable',['$compile','$timeout','directionClasses', function ($compile,$timeout,directionClasses) { 
    var template = '<div class="scrollableWrapper">'+
        '<div class="scrollableParentDiv">'+
            '<div class="scrollableArea" ng-class="{\'scrollableMsgPadding\': scrollable}"'+
    'ng-bind-html="content | unsafe"></div>'+
        	'<div ng-show="scrollable" class="scrollableScrollMe"><div class="scrollText">scroll <span class="{{scrollDir}}"></span></div></div>'+
        '</div>'+
    '</div>'
    function link(scope, element, attrs) {
        scope.scrollable = false;
        scope.scrollDir = directionClasses.down;
		scope.content = element.html();
        element.html(template);
        $compile(element.contents())(scope);
        $timeout(function(){
            
            var scrollContainer = angular.element(element[0].querySelector('.scrollableParentDiv'));
            var scrollContainerHeight = scrollContainer[0].offsetHeight;
            
            var scrollContent = angular.element(element[0].querySelector('.scrollableArea'));
            var scrollHeight = scrollContent[0].offsetHeight;

            if (scrollHeight > scrollContainerHeight){
                scope.scrollable = true;
            }
            
            scrollContainer.bind("scroll", function () {
                
                if (this.scrollTop + this.offsetHeight >= this.scrollHeight) {
                    //scroll has reached the bottom
                    scope.scrollDir = directionClasses.up;
                } else if (this.scrollTop == 0) {
                    //scroll has reached the top
  ...