Angularjs: How to trigger event when scroll reaches to the bottom of the scroll bar in div?

All we need is an easy explanation of the problem, so here it is.

I’m trying to trigger an event when scroll bar reaches the end. I found this this example. Here is my code. The problem is that it doesn’t call loadmore() at all.
The values of the console statments are:

848
899
in scroll
881
899
in scroll
892
899
in scroll
897
899
in scroll
900
899

It seems that it never goes to that if statement! The weird part is that if I debug it in inspect element then it triggers the event.

…..

my directive:

directive('scrolly', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var raw = element[0];
            console.log('loading directive');
            element.bind('scroll', function () {
                console.log('in scroll');
                console.log(raw.scrollTop + raw.offsetHeight);
                console.log(raw.scrollHeight);
                if (raw.scrollTop + raw.offsetHeight == raw.scrollHeight) { //at the bottom
                    scope.$apply(attrs.scrolly);
                }
            })
        }
    }

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Instead of checking for equality, please check if the left side is greater than the right side in the if statement since that’s the case for the scrollbar to be at the bottom.

raw.scrollTop + raw.offsetHeight > raw.scrollHeight

Here is the working jsfiddle.

Method 2

You gave me a few good tips … heres a complete working example for anyone else who stumbles across this post:

JS:

app.controller("controller", function ($scope) {
    $scope.test = function () {
        alert("hello!");
    }
}

HTML:

<div class="br-section1" on-scroll-to-bottom="test()">
</div>

App Directive:

app.directive('onScrollToBottom', function ($document) {
    //This function will fire an event when the container/document is scrolled to the bottom of the page
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {

            var doc = angular.element($document)[0].body;

            $document.bind("scroll", function () {

                //console.log('in scroll');
                //console.log("scrollTop + offsetHeight:" + (doc.scrollTop + doc.offsetHeight));
                //console.log("scrollHeight: " + doc.scrollHeight);

                if (doc.scrollTop + doc.offsetHeight >= doc.scrollHeight) {
                    //run the event that was passed through
                    scope.$apply(attrs.onScrollToBottom);
                }
            });
        }
    };
});

Method 3

I was looking for a snippet to help me do this, but couldn’t find anything worthwhile so I came up with this fairly easy to use directive to do this

'use strict';
// Tested with Angular 1.3, 1.4.8
angular.module('scrollToEnd', [])
  /**
   * @ngdoc directive
   * @name scrollToEnd:scrollToEnd
   * @scope
   * @restrict A
   *
   * @description
   * Supply a handler to be called when this element is scrolled all the way to any extreme.
   * The callback must have the following signature:
   * void function (direction:'top'|'bottom'|'left'|'right')
   * If the `bindToWindow` attribute is truthy, the callback will be issued for the window
   * instead of the element that the directive is on.
   *
   * Example usage:
   * `<div scroll-to-end="scrollToEndWindow" bind-to-window="true">`
   * This will call the controller's `scrollToEndWindow` function whenever the window reaches
   * the edges when scrolling. If the div itself is a scrollable element for which the
   * handler should be called instead, remove the bind-to-window attribute entirely.
   *
   * @param {function}  emScrollToEnd   Callback to be invoked
   * @param {boolean}       bindToWindow        Bind to the window instead of the element
   *
   */
  .directive('scrollToEnd', function ($window) {
    // Get the specified element's computed style (height, padding, etc.) in integer form
    function getStyleInt(elem, prop) {
      try {
        return parseInt(window.getComputedStyle(elem, null).getPropertyValue(prop), 10);
      } catch (e) {
        return parseInt(elem.currentStyle[prop], 10);
      }
    }

    // Get the 'innerHeight' equivalent for a non-window element, including padding
    function getElementDimension(elem, prop) {
      switch (prop) {
        case 'width':
          return getStyleInt(elem, 'width') +
          getStyleInt(elem, 'padding-left') +
          getStyleInt(elem, 'padding-right');
        case 'height':
          return getStyleInt(elem, 'height') +
          getStyleInt(elem, 'padding-top') +
          getStyleInt(elem, 'padding-bottom');
        /*default:
          return null;*/
      }
    }
    return {
      restrict: 'A',
      scope: {
        callback: '=scrollToEnd'
      },
      link: function (scope, elem, attr) {
        var callback = scope.callback || function () {};
        var boundToWindow = attr.bindToWindow;
        var body = document.body;
        var html = document.documentElement;
        var boundElement = boundToWindow ? angular.element($window) : elem;
        var oldScrollX = 0;
        var oldScrollY = 0;
        var handleScroll = function () {
          // Dimensions of the content, including everything scrollable
          var contentWidth;
          var contentHeight;
          // The dimensions of the container with the scrolling, only the visible part
          var viewportWidth;
          var viewportHeight;
          // The offset of how much the user has scrolled
          var scrollX;
          var scrollY;

          if (boundToWindow) {
            // Window binding case - Populate Dimensions
            contentWidth = Math.max(
              body.scrollWidth,
              body.offsetWidth,
              html.clientWidth,
              html.scrollWidth,
              html.offsetWidth
            );
            contentHeight = Math.max(
              body.scrollHeight,
              body.offsetHeight,
              html.clientHeight,
              html.scrollHeight,
              html.offsetHeight
            );
            viewportWidth = window.innerWidth;
            viewportHeight = window.innerHeight;
            scrollX = (window.pageXOffset || html.scrollLeft) - (html.clientLeft || 0);
            scrollY = (window.pageYOffset || html.scrollTop) - (html.clientTop || 0);
          } else {
            // DOM element case - Populate Dimensions
            var domElement = boundElement[0];
            contentWidth = domElement.scrollWidth;
            contentHeight = domElement.scrollHeight;
            viewportWidth = getElementDimension(domElement, 'width');
            viewportHeight = getElementDimension(domElement, 'height');
            scrollX = domElement.scrollLeft;
            scrollY = domElement.scrollTop;
          }

          var scrollWasInXDirection = oldScrollX !== scrollX;
          var scrollWasInYDirection = oldScrollY !== scrollY;
          oldScrollX = scrollX;
          oldScrollY = scrollY;

          if (scrollWasInYDirection && scrollY === 0) {
            callback('top');
          } else if (scrollWasInYDirection && scrollY === contentHeight - viewportHeight) {
            callback('bottom');
          } else if (scrollWasInXDirection && scrollX === 0) {
            callback('left');
          } else if (scrollWasInXDirection && scrollX === contentWidth - viewportWidth) {
            callback('right');
          }
        };
        boundElement.bind('scroll', handleScroll);
        // Unbind the event when scope is destroyed
        scope.$on('$destroy', function () {
          boundElement.unbind('scroll', handleScroll);
        });
      }
    };
  });

https://gist.github.com/podrezo/f80f35d6d0655f4d550cac4747c110ff

Here’s a jsfiddle to try it out:

https://jsfiddle.net/va4x5r26/2/

Works with Angular 1.3 and 1.4.8, and tested on IE10 and Chrome 55.

Method 4

I took the Mark’s code and I made some changes to add support on div elements.
Note that you can define another attribute to configure the threshold (50 in my case).

angular.module('O2DigitalSite').directive('onScrollToBottom', function ($document) {
    //This function will fire an event when the container/document is scrolled to the bottom of the page
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var ele = element[0];
            element.bind("scroll", function () {
                //console.log((ele.scrollTop + ele.offsetHeight) + " / " + (ele.scrollHeight));
                if (ele.scrollTop + ele.offsetHeight >= ele.scrollHeight - 50) {
                    //run the event that was passed through
                    scope.$apply(attrs.onScrollToBottom);
                }
            });
        }
    };
});

Method 5

When browser’s scroll bar reached to down, we need to fire an event. Below code will work for Angular 6.

 import { HostListener } from '@angular/core';
     @HostListener('window:scroll', ['$event'])
      onScroll(): void {
        if ((window.innerHeight + window.scrollY) >= (document.documentElement.scrollHeight - 1)) {
          // Place your code here
        }
      }

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply