<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <script src="angular-vs-repeat.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-app="fasterAngular">
    <h1>Faster Rendering With angular-vs-repeat</h1>
    <p><a href="https://github.com/kamilkp/angular-vs-repeat">angular-vs-repeat</a> Keeps only visible divs in the view, try scrolling the numbers</p>
    <p>See <a href="http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/">Faster rendering with AngularJs and ReactJs</a>
    and <a href="http://bahmutov.calepin.co/improving-angular-web-app-performance-example.html">Improving Angular web app performance example</a></p>
    <div ng-controller="mycontroller">
        <button ng-click="refresh()">Refresh Data</button>
        <div vs-repeat id="container">
          <div ng-repeat="line in data" class="line">
            {{ line[0]|number:3 }} {{ line[1]|number:3 }} {{ line[2]|number:3 }} {{ line[3]|number:3 }} {{ line[4]|number:3 }}
          </div>
        </div>
    </div>
  </body>

</html>
// Code goes here

 angular.module('fasterAngular', ['vs-repeat']).
  controller('mycontroller', ['$scope', function($scope){
      $scope.framework = 'ReactJs';
      $scope.data = [];
      // Fill the data map with random data
      $scope.refresh = function(){
          for(var i = 0; i < 1500; ++i) {
              $scope.data[i] = {};
              for(var j = 0; j < 5; ++j) {
                  $scope.data[i][j] = Math.random();
              }
          }
      }
      $scope.refresh()
  }]).directive('fastRepeat', function(){
      return{
          restrict: 'E',
          scope:{
              data: '='
          },
          link:function(scope, el, attrs){
              scope.$watchCollection('data', function(newValue, oldValue){
                  React.renderComponent(
                      MYLIST({data:newValue}),
                      el[0]
                  );
              })
          }
      }
  })
/* Styles go here */

#container {
  height: 500px;
  overflow-y: hidden;
}
Author: Thierry Nicola (@littleiffel)


Link to what happens here: 

http://www.williambrownstreet.net/blog/?p=484
//
// Copyright Kamil Pękala http://github.com/kamilkp
// Angular Virtual Scroll Repeat v1.0.0-rc5 2014/08/01
//

(function(window, angular) {
    'use strict';
    /* jshint eqnull:true */
    /* jshint -W038 */

    // DESCRIPTION:
    // vsRepeat directive stands for Virtual Scroll Repeat. It turns a standard ngRepeated set of elements in a scrollable container
    // into a component, where the user thinks he has all the elements rendered and all he needs to do is scroll (without any kind of
    // pagination - which most users loath) and at the same time the browser isn't overloaded by that many elements/angular bindings etc.
    // The directive renders only so many elements that can fit into current container's clientHeight/clientWidth.

    // LIMITATIONS:
    // - current version only supports an Array as a right-hand-side object for ngRepeat
    // - all rendered elements must have the same height/width or the sizes of the elements must be known up front

    // USAGE:
    // In order to use the vsRepeat directive you need to place a vs-repeat attribute on a direct parent of an element with ng-repeat
    // example:
    // <div vs-repeat>
    // <div ng-repeat="item in someArray">
    // <!-- content -->
    // </div>
    // </div>
    //
    // You can also measure the single element's height/width (including all paddings and margins), and then speficy it as a value
    // of the attribute 'vs-repeat'. This can be used if one wants to override the automatically computed element size.
    // example:
    // <div vs-repeat="50"> <!-- the specified element height is 50px -->
    // <div ng-repeat="item in someArray">
    // <!-- content -->
    // </div>
    // </div>
    //
    // IMPORTANT!
    //
    // - the vsRepeat directive must be applied to a direct parent of an element with ngRepeat
    // - the value of vsRepeat attribute is the single element's height/width measured in pixels. If none provided, the directive
    // will compute it automatically

    // OPTIONAL PARAMETERS (attributes):
    // vs-scroll-parent="selector" - selector to the scrollable container. The directive will look for a closest parent matching
    // he given selector (defaults to the current element)
    // vs-horizontal - stack repeated elements horizontally instead of vertically
    // vs-offset-before="value" - top/left offset in pixels (defaults to 0)
    // vs-offset-after="value" - bottom/right offset in pixels (defaults to 0)
    // vs-excess="value" - an integer number representing the number of elements to be rendered outside of the current container's viewport
    // (defaults to 2)
    // vs-size-property - a property name of the items in collection that is a number denoting the element size (in pixels)
    // vs-autoresize - use this attribute without vs-size-property and without specifying element's size. The automatically computed element style will
    // readjust upon window resize if the size is dependable on the viewport size

    // EVENTS:
    // - 'vsRepeatTrigger' - an event the directive listens for to manually trigger reinitialization
    // - 'vsRepeatReinitialized' - an event the directive emits upon reinitialization done

    var isMacOS = navigator.appVersion.indexOf('Mac') != -1,
        wheelEventName = typeof window.onwheel !== 'undefined' ? 'wheel' : typeof window.onmousewheel !== 'undefined' ? 'mousewheel' : 'DOMMouseScroll',
        dde = document.documentElement,
        matchingFunction = dde.matches ? 'matches' :
            dde.matchesSelector ? 'matchesSelector' :
            dde.webkitMatches ? 'webkitMatches' :
            dde.webkitMatchesSelector ? 'webkitMatchesSelector' :
            dde.msMatches ? 'msMatches' :
            dde.msMatchesSelector ? 'msMatchesSelector' :
            dde.mozMatches ? 'mozMatches' :
            dde.mozMatchesSelector ? 'mozMatchesSelector' : null;

    var closestElement = angular.element.prototype.closest || function(selector) {
            var el = this[0].parentNode;
            while (el !== document.documentElement && el != null && !el[matchingFunction](selector)) {
                el = el.parentNode;
            }

            if (el && el[matchingFunction](selector))
                return angular.element(el);
            else
                return angular.element();
        };

    angular.module('vs-repeat', []).directive('vsRepeat', ['$compile',
        function($compile) {
            return {
                restrict: 'A',
                scope: true,
                require: '?^vsRepeat',
                controller: ['$scope',
                    function($scope) {
                        this.$scrollParent = $scope.$scrollParent;
                        this.$fillElement = $scope.$fillElement;
                    }
                ],
                compile: function($element, $attrs) {
                    var ngRepeatChild = $element.children().eq(0),
                        ngRepeatExpression = ngRepeatChild.attr('ng-repeat'),
                        childCloneHtml = ngRepeatChild[0].outerHTML,
                        expressionMatches = /^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(ngRepeatExpression),
                        lhs = expressionMatches[1],
                        rhs = expressionMatches[2],
                        rhsSuffix = expressionMatches[3],
                        collectionName = '$vs_collection',
                        attributesDictionary = {
                            'vsRepeat': 'elementSize',
                            'vsOffsetBefore': 'offsetBefore',
                            'vsOffsetAfter': 'offsetAfter',
                            'vsExcess': 'excess'
                        };

                    $element.empty();
                    if (!window.getComputedStyle || window.getComputedStyle($element[0]).position !== 'absolute')
                        $element.css('position', 'relative');
                    return {
                        pre: function($scope, $element, $attrs, $ctrl) {
                            var childClone = angular.element(childCloneHtml),
                                originalCollection = [],
                                originalLength,
                                $$horizontal = typeof $attrs.vsHorizontal !== "undefined",
                                $wheelHelper,
                                $fillElement,
                                autoSize = !$attrs.vsRepeat,
                                sizesPropertyExists = !! $attrs.vsSizeProperty,
                                $scrollParent = $attrs.vsScrollParent ? closestElement.call($element, $attrs.vsScrollParent) : $element,
                                positioningPropertyTransform = $$horizontal ? 'translateX' : 'translateY',
                                positioningProperty = $$horizontal ? 'left' : 'top',

                                clientSize = $$horizontal ? 'clientWidth' : 'clientHeight',
                                offsetSize = $$horizontal ? 'offsetWidth' : 'offsetHeight',
                                scrollPos = $$horizontal ? 'scrollLeft' : 'scrollTop';

                            if ($scrollParent.length === 0) throw 'Specified scroll parent selector did not match any element';
                            $scope.$scrollParent = $scrollParent;

                            if (sizesPropertyExists) $scope.sizesCumulative = [];

                            //initial defaults
                            $scope.elementSize = $scrollParent[0][clientSize] || 50;
                            $scope.offsetBefore = 0;
                            $scope.offsetAfter = 0;
                            $scope.excess = 2;

                            Object.keys(attributesDictionary).forEach(function(key) {
                                if ($attrs[key]) {
                                    $attrs.$observe(key, function(value) {
                                        $scope[attributesDictionary[key]] = +value;
                                        reinitialize();
                                    });
                                }
                            });


                            $scope.$watchCollection(rhs, function(coll) {
                                originalCollection = coll || [];
                                refresh();
                            });

                            function refresh() {
                                if (!originalCollection || originalCollection.length < 1) {
                                    $scope[collectionName] = [];
                                    originalLength = 0;
                                    resizeFillElement(0);
                                    $scope.sizesCumulative = [0];
                                    return;
                                } else {
                                    originalLength = originalCollection.length;
                                    if (sizesPropertyExists) {
                                        $scope.sizes = originalCollection.map(function(item) {
                                            return item[$attrs.vsSizeProperty];
                                        });
                                        var sum = 0;
                                        $scope.sizesCumulative = $scope.sizes.map(function(size) {
                                            var res = sum;
                                            sum += size;
                                            return res;
                                        });
                                        $scope.sizesCumulative.push(sum);
                                    }
                                    setAutoSize();
                                }

                                reinitialize();
                            }

                            function setAutoSize() {
                                if (autoSize) {
                                    $scope.$$postDigest(function() {
                                        if ($element[0].offsetHeight || $element[0].offsetWidth) { // element is visible
                                            var children = $element.children(),
                                                i = 0;
                                            while (i < children.length) {
                                                if (children[i].attributes['ng-repeat'] != null) {
                                                    if (children[i][offsetSize]) {
                                                        $scope.elementSize = children[i][offsetSize];
                                                        reinitialize();
                                                        autoSize = false;
                                                        if ($scope.$root && !$scope.$root.$$phase)
                                                            $scope.$apply();
                                                    }
                                                    break;
                                                }
                                                i++;
                                            }
                                        } else {
                                            var dereg = $scope.$watch(function() {
                                                if ($element[0].offsetHeight || $element[0].offsetWidth) {
                                                    dereg();
                                                    setAutoSize();
                                                }
                                            });
                                        }
                                    });
                                }
                            }

                            childClone.attr('ng-repeat', lhs + ' in ' + collectionName + (rhsSuffix ? ' ' + rhsSuffix : ''))
                                .addClass('vs-repeat-repeated-element');

                            var offsetCalculationString = sizesPropertyExists ?
                                '(sizesCumulative[$index + startIndex] + offsetBefore)' :
                                '(($index + startIndex) * elementSize + offsetBefore)';

                            if (typeof document.documentElement.style.transform !== "undefined") { // browser supports transform css property
                                childClone.attr('ng-style', '{ "transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}');
                            } else if (typeof document.documentElement.style.webkitTransform !== "undefined") { // browser supports -webkit-transform css property
                                childClone.attr('ng-style', '{ "-webkit-transform": "' + positioningPropertyTransform + '(" + ' + offsetCalculationString + ' + "px)"}');
                            } else {
                                childClone.attr('ng-style', '{' + positioningProperty + ': ' + offsetCalculationString + ' + "px"}');
                            }

                            $compile(childClone)($scope);
                            $element.append(childClone);

                            $fillElement = angular.element('<div class="vs-repeat-fill-element"></div>')
                                .css({
                                    'position': 'relative',
                                    'min-height': '100%',
                                    'min-width': '100%'
                                });
                            $element.append($fillElement);
                            $compile($fillElement)($scope);
                            $scope.$fillElement = $fillElement;

                            var _prevMouse = {};
                            if (isMacOS) {
                                $wheelHelper = angular.element('<div class="vs-repeat-wheel-helper"></div>')
                                    .on(wheelEventName, function(e) {
                                        e.preventDefault();
                                        e.stopPropagation();
                                        if (e.originalEvent) e = e.originalEvent;
                                        $scrollParent[0].scrollLeft += (e.deltaX || -e.wheelDeltaX);
                                        $scrollParent[0].scrollTop += (e.deltaY || -e.wheelDeltaY);
                                    }).on('mousemove', function(e) {
                                        if (_prevMouse.x !== e.clientX || _prevMouse.y !== e.clientY)
                                            angular.element(this).css('display', 'none');
                                        _prevMouse = {
                                            x: e.clientX,
                                            y: e.clientY
                                        };
                                    }).css('display', 'none');
                                $fillElement.append($wheelHelper);
                            }

                            $scope.startIndex = 0;
                            $scope.endIndex = 0;

                            $scrollParent.on('scroll', function scrollHandler(e) {
                                if (updateInnerCollection())
                                    $scope.$apply();
                            });

                            if (isMacOS) {
                                $scrollParent.on(wheelEventName, wheelHandler);
                            }

                            function wheelHandler(e) {
                                var elem = e.currentTarget;
                                if (elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight)
                                    $wheelHelper.css('display', 'block');
                            }

                            function onWindowResize() {
                                if (typeof $attrs.vsAutoresize !== 'undefined') {
                                    autoSize = true;
                                    setAutoSize();
                                    if ($scope.$root && !$scope.$root.$$phase)
                                        $scope.$apply();
                                }
                                if (updateInnerCollection())
                                    $scope.$apply();
                            }

                            angular.element(window).on('resize', onWindowResize);
                            $scope.$on('$destroy', function() {
                                angular.element(window).off('resize', onWindowResize);
                            });

                            $scope.$on('vsRepeatTrigger', refresh);
                            $scope.$on('vsRepeatResize', function() {
                                autoSize = true;
                                setAutoSize();
                            });

                            var _prevStartIndex,
                                _prevEndIndex;

                            function reinitialize() {
                                _prevStartIndex = void 0;
                                _prevEndIndex = void 0;
                                updateInnerCollection();
                                resizeFillElement(sizesPropertyExists ?
                                    $scope.sizesCumulative[originalLength] :
                                    $scope.elementSize * originalLength
                                );
                                $scope.$emit('vsRepeatReinitialized');
                            }

                            function resizeFillElement(size) {
                                if ($$horizontal) {
                                    $fillElement.css({
                                        'width': $scope.offsetBefore + size + $scope.offsetAfter + 'px',
                                        'height': '100%'
                                    });
                                    if ($ctrl && $ctrl.$fillElement) {
                                        var referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]');
                                        if (referenceElement)
                                            $ctrl.$fillElement.css({
                                                'width': referenceElement.scrollWidth + 'px'
                                            });
                                    }
                                } else {
                                    $fillElement.css({
                                        'height': $scope.offsetBefore + size + $scope.offsetAfter + 'px',
                                        'width': '100%'
                                    });
                                    if ($ctrl && $ctrl.$fillElement) {
                                        referenceElement = $ctrl.$fillElement[0].parentNode.querySelector('[ng-repeat]');
                                        if (referenceElement)
                                            $ctrl.$fillElement.css({
                                                'height': referenceElement.scrollHeight + 'px'
                                            });
                                    }
                                }
                            }

                            var _prevClientSize;

                            function reinitOnClientHeightChange() {
                                var ch = $scrollParent[0][clientSize];
                                if (ch !== _prevClientSize) {
                                    reinitialize();
                                    if ($scope.$root && !$scope.$root.$$phase)
                                        $scope.$apply();
                                }
                                _prevClientSize = ch;
                            }

                            $scope.$watch(function() {
                                if (typeof window.requestAnimationFrame === "function")
                                    window.requestAnimationFrame(reinitOnClientHeightChange);
                                else
                                    reinitOnClientHeightChange();
                            });

                            function updateInnerCollection() {
                                if (sizesPropertyExists) {
                                    $scope.startIndex = 0;
                                    while ($scope.sizesCumulative[$scope.startIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore)
                                        $scope.startIndex++;
                                    if ($scope.startIndex > 0) $scope.startIndex--;

                                    $scope.endIndex = $scope.startIndex;
                                    while ($scope.sizesCumulative[$scope.endIndex] < $scrollParent[0][scrollPos] - $scope.offsetBefore + $scrollParent[0][clientSize])
                                        $scope.endIndex++;
                                } else {
                                    $scope.startIndex = Math.max(
                                        Math.floor(
                                            ($scrollParent[0][scrollPos] - $scope.offsetBefore) / $scope.elementSize + $scope.excess / 2
                                        ) - $scope.excess,
                                        0
                                    );

                                    $scope.endIndex = Math.min(
                                        $scope.startIndex + Math.ceil(
                                            $scrollParent[0][clientSize] / $scope.elementSize
                                        ) + $scope.excess,
                                        originalLength
                                    );
                                }


                                var digestRequired = $scope.startIndex !== _prevStartIndex || $scope.endIndex !== _prevEndIndex;

                                if (digestRequired)
                                    $scope[collectionName] = originalCollection.slice($scope.startIndex, $scope.endIndex);

                                _prevStartIndex = $scope.startIndex;
                                _prevEndIndex = $scope.endIndex;

                                return digestRequired;
                            }
                        }
                    };
                }
            };
        }
    ]);

    angular.element(document.head).append([
        '<style>' +
        '.vs-repeat-wheel-helper{' +
        'position: absolute;' +
        'top: 0;' +
        'bottom: 0;' +
        'left: 0;' +
        'right: 0;' +
        'z-index: 99999;' +
        'background: rgba(0, 0, 0, 0);' +
        '}' +
        '.vs-repeat-repeated-element{' +
        'position: absolute;' +
        'z-index: 1;' +
        '}' +
        '</style>'
    ].join(''));
})(window, window.angular);