Skip to content

Instantly share code, notes, and snippets.

@kmturley
Created December 14, 2015 22:49
Show Gist options
  • Select an option

  • Save kmturley/043cd096a0e8239bea18 to your computer and use it in GitHub Desktop.

Select an option

Save kmturley/043cd096a0e8239bea18 to your computer and use it in GitHub Desktop.

Revisions

  1. kmturley created this gist Dec 14, 2015.
    145 changes: 145 additions & 0 deletions ng-ScrollSpy.js support for custom element
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,145 @@
    /* ng-ScrollSpy.js v3.2.1
    * https://github.com/patrickmarabeas/ng-ScrollSpy.js
    *
    * Copyright 2014, Patrick Marabeas http://marabeas.io
    * Released under the MIT license
    * http://opensource.org/licenses/mit-license.php
    *
    * Date: 31/01/2015
    */

    ;(function(window, document, angular, undefined) {
    'use strict';
    angular.module('ngScrollSpy', [])

    .value('config', {
    'offset': 200,
    'delay': 100,
    'element': window
    })

    .run(['config', 'PositionFactory', function(config, PositionFactory) {
    angular.element(window).bind('load', function () {
    PositionFactory.refreshPositions();
    angular.element(document.getElementById('main')).bind('scroll', function() {
    PositionFactory.refreshPositions();
    });
    });
    }])

    .factory('PositionFactory', ['config', '$rootScope', function(config, $rootScope){
    return {
    'position': [],
    'refreshPositions': function() {
    this.position.documentHeight = Math.max(config.element.scrollHeight, config.element.offsetHeight, config.element.clientHeight);
    if (config.element.pageYOffset) {
    this.position.windowTop = config.element.pageYOffset;
    } else if (config.element.scrollTop) {
    this.position.windowTop = config.element.scrollTop;
    } else {
    this.position.windowTop = (document.documentElement || document.body.parentNode || document.body).scrollTop;
    }
    this.position.windowBottom = this.position.windowTop + window.innerHeight
    }
    }
    }])

    .factory('SpyFactory', ['$rootScope', function($rootScope){
    return {
    'spies': [],
    'addSpy': function(id) {
    var index = this.spies.map(function(e) { return e }).indexOf(id);
    if(index == -1) {
    this.spies.push(id);
    this.broadcast();
    }
    },
    'removeSpy': function(id) {
    var index = this.spies.map(function(e) { return e }).indexOf(id);
    if(index != -1) {
    this.spies.splice(index, 1);
    this.broadcast();
    }
    },
    'broadcast': function() {
    $rootScope.$broadcast('spied');
    }
    }
    }])

    .directive('scrollspyBroadcast', ['config', 'scrollspyConfig', 'SpyFactory', 'PositionFactory', function(config, scrollspyConfig, SpyFactory, PositionFactory) {
    return {
    restrict: 'A',
    scope: true,
    link: function(scope, element, attrs) {
    angular.extend(config, scrollspyConfig.config);
    var offset = parseInt(attrs.scrollspyOffset || config.offset);
    scope.checkActive = function() {
    scope.elementTop = element[0].offsetTop;
    scope.elementBottom = scope.elementTop + Math.max(element[0].scrollHeight, element[0].offsetHeight);

    if((scope.elementTop - offset) < (PositionFactory.position.documentHeight - window.innerHeight)) {
    if(scope.elementTop <= (PositionFactory.position.windowTop + offset)) {
    SpyFactory.addSpy(attrs.id);
    } else {
    SpyFactory.removeSpy(attrs.id);
    }

    } else {
    if(PositionFactory.position.windowBottom > (scope.elementBottom - offset)) {
    SpyFactory.addSpy(attrs.id);
    } else {
    SpyFactory.removeSpy(attrs.id);
    }
    }
    };

    config.throttle
    ? angular.element(config.element).bind('scroll', config.throttle(function() { scope.checkActive() }, config.delay))
    : angular.element(config.element).bind('scroll', function() { scope.checkActive() });

    angular.element(document).ready( function() { scope.checkActive() });
    angular.element(window).bind('resize', function () { scope.checkActive() });
    }
    }
    }])

    .directive('scrollspyListen', ['config', '$timeout', 'SpyFactory', function(config, $timeout, SpyFactory) {
    return {
    restrict: 'A',
    scope: {
    scrollspyListen: '@',
    enabled: '@'
    },
    replace: true,
    transclude: true,
    template: function(element) {
    var tag = element[0].nodeName;
    return '<' + tag + ' data-ng-transclude data-ng-class="{active: enabled}"></' + tag + '>';
    },
    link: function(scope) {
    scope.$on('spied', function() {
    $timeout(function() {
    var spies = scope.scrollspyListen.split("|");
    for(var i = 0; i < spies.length; i++)
    if(scope.enabled = spies[i] === SpyFactory.spies[SpyFactory.spies.length - 1])
    break;
    });
    });
    }
    }
    }])

    .provider('scrollspyConfig', function() {
    var self = this;
    this.config = {};
    this.$get = function() {
    var extend = {};
    extend.config = self.config;
    return extend;
    };
    return this;
    });

    })(window, document, angular);