Search code examples
angularjsscrollfixed

$anchorScroll goes to wrong point if dynamically changing element position to fixed


I'm using a directive to fix my menu to the top of the page once my header scrolls past. I also would like my menu to use $anchorScroll to navigate to different elements of the page. The trouble I'm having is that the $anchorScroll goes past the anchor point unless you have scrolled past the header.

var myApp = angular.module('myApp', ['sticky']);

    function IndexCtrl($scope, $location, $anchorScroll) {
    $scope.gotoDiv = function (id) {
        $scope.id = id;
        $location.hash(id);
        $anchorScroll();
    }
}

angular.module('sticky', [])
.directive('sticky', [ function () {
    return {
        restrict: 'A',

        link: function ($scope, $elem, $attrs) {
            var offsetTop = 0,
                $window = angular.element(window),
                initialPositionStyle = $elem.css('position'),
                stickyLine,
                scrollTop;

            // Set the top offset
            $elem.css('top', '0');
            $window.on('scroll', checkSticky);
            setInitial();

            function setInitial() {
                stickyLine = $elem[0].offsetTop;
                checkSticky();
            }

            function checkSticky() {
                scrollTop = window.pageYOffset;
                if (scrollTop >= stickyLine) {
                    $elem.css('position', 'fixed');
                } else {
                $elem.css('position', initialPositionStyle);
                }
            }
        }
    };
}]);

I created this plunker: http://plnkr.co/edit/7HfPtu4f1VoQ5vz4yVOJ?p=preview

-Click the menu the first time, the scroll goes to far. -A second click takes you the correct position. -scroll past the header, then click scroll goes to the correct position.


Solution

  • The header needs to be set to fixed before the scroll happens. like this:

    var myApp = angular.module('myApp', ['sticky']);
    
    function IndexCtrl($scope, $location, $anchorScroll) {
        $scope.gotoDiv = function (id) {
          $scope.stickyHeader();
          $scope.id = id;
          $location.hash(id);
          $anchorScroll();
        }
    
        $scope.stickyHeader = function () {
          service001.elem.css('position', 'fixed');
        }
    }
    
    var service001 = {
    
    }
    
    angular.module('sticky', [])
        .directive('sticky', [ function () {
            return {
                restrict: 'A',
    
                link: function ($scope, $elem, $attrs) {
                    var offsetTop = 0,
                        $window = angular.element(window),
                        initialPositionStyle = $elem.css('position'),
                        stickyLine,
                        scrollTop;
    
                    // Set the top offset
                    $elem.css('top', '0');
                    $window.on('scroll', checkSticky);
                    setInitial();
    
                    service001.elem = $elem;
    
                    function setInitial() {
                        stickyLine = $elem[0].offsetTop;
                        checkSticky();
                    }
    
                    function checkSticky() {
                        scrollTop = window.pageYOffset;
                        if (scrollTop >= stickyLine) {
                          $elem.css('position', 'fixed');
                        } else {
                          $elem.css('position', initialPositionStyle);
                        }
                    }
                }
            };
        }]);
    

    There should be a service in here to share the elem.css position, but there it is.