9 Jul

Scroll watcher directive for Angular 1.5.7

Here is a directive I came up with to help with keeping track of page scroll position and when scrolling has started and stopped. I had a need for this in trying to hide page content while the user was scrolling up/down a page, and then re-showing the content once the scrolling had stopped. Currently this is only setup up to work at the document level, but and easy modification could be made to allow a new property to drive what scroll area is being monitored. I hope this helps others in case they need a way to tell if page scrolling has started or stopped.

How to Implement

HTML
Simply add the directive to the page you want to monitor scrolling on. Next add the scroll-callback function you want to be called from directive when scrolling starts and stops

<div page-scroll-watcher scroll-callback="cntl.scrollStop($event, isEndEvent, isScrollingEvent)">

Callback Function
Note: sample code is in ES6 format. This is an excerpt from a angular controller

 //$event is the standard scroll event from the browser. This contains the X,Y information
 //isEndEvent signals when scrolling has stopped
 //isScrollingEvent signals when scrolling has started
 scrollStop($event, isEndEvent, isScrollingEvent) {
    if (isEndEvent) {
      this.showBottomBar = true;
      return;
    }
    if(isScrollingEvent)
    {
      this.showBottomBar = false;
      return;
    }
  }

Now that we have the how to implement lets get to the good stuff. The code that makes this all work
Page Scroll Directive
Note: code is in ES6 format


//this would just need to be registered with your Angular app
import angular from "angular";
import * as _ from "lodash";

const directivesModule = angular.module("MyDirectives", [])
  .directive("pageScrollWatcher", ["$window", "$document", pageScrollWatcher]);

function pageScrollWatcher($document) {
  return {
    restrict: "A",
    scope: {
      scrollCallback: "&"
    },
    link: function (scope) {
      //here could be updated to use the element this directive is attached to if needed to watch a scrollable div container
      const el = angular.element($document); 

      //here we delay evaluating the scrolling events until they have stopped
      const dbnce = _.debounce(function (e) {
        //send event that scrolling stopped
        scope.$apply(function () {
          //execute the provided callback
          scope.scrollCallback({ $event: e, isEndEvent: true, isScrollingEvent: false });
        });

        //register first scroll interceptor. Since scrolling has stopped we now need to register a start scrolling event binding
        el.bind("scroll", firstScrollFunc);

      }, 200);

      const firstScrollFunc = function (e) {
        //so we have detected the scrolling needs to start. Since this is a one time event between starts/stops we need to
        //unregister the start scrolling event
        el.unbind("scroll", firstScrollFunc);
        scope.$apply(function () {
          //execute the provided callback
          scope.scrollCallback({ $event: e, isEndEvent: false, isScrollingEvent: true });
          //We do this incase angular removes dom parts causing the scroll bar to disappear or change.
          //we need to trigger the end event again 
          dbnce(e);
        });
      };

      //on first load of directive register the start and stop events
      el.bind("scroll", firstScrollFunc);
      el.bind("scroll", dbnce);

      scope.$on("$destroy", function handleDestroyEvent() {
        //when switching pages remove event
        el.unbind("scroll", dbnce);
        el.unbind("scroll", firstScrollFunc);
      });

    }
  };
}

Want the source? Visit my GitHub