12

I'm having some trouble understanding how to get the scroll position of window within my controller, so I can build logic around it.

From all the questions and answers I've been reading the most accepted answer seems to be to write a directive that calculates the scroll position, stick that directive on an element, and that's it.

However, when you want to do something along the lines of:

if (scrollY > 100 ){
  $scope.showMenu = true;
}

if (scrollY > 500) {
  $scope.showFooter = true;
}

This approach doesn't seem to work, because the calculated position in the directive can't be accessed from the controller. What would be the right 'Angular' way of doing this, which would still allow slightly more complicated logic to be executed from the controller?

3
  • 2
    Bind the controller's scope variable to the directive's isolated scope and implement the logic in the directive's controller. Commented Oct 14, 2014 at 16:14
  • @Angad, Linial, would it be possible to illustrate this with an example? Please? :) Commented Oct 15, 2014 at 8:09
  • This may help: stackoverflow.com/questions/20253322/angular-js-scroll-window Commented Nov 5, 2014 at 5:29

3 Answers 3

13

According to @RobKohr comment, here's a optimized approach using .on('scroll') and $scope.$apply to update a scope element on scroll.

$document.on('scroll', function() {
    // do your things like logging the Y-axis
    console.log($window.scrollY);

    // or pass this to the scope
    $scope.$apply(function() {
        $scope.pixelsScrolled = $window.scrollY;
    })
});
Sign up to request clarification or add additional context in comments.

2 Comments

You'll receive more granularity on the scroll position because it's responding to the native scroll event, but you're going to be thrashing the angular scope by $applying that every time the handler fires. This is definitely not optimized. Instead refer to my comment in response to RobKhor: requestAnimationFrame is the way to go. Avoid touching the scope at all costs!
As far as I remember, I was needed $scope.pixelsScrolled in many places of my website and this allowed me to make it maintainable and I kept good performances for what I've asked.
7

Inject the $window service into your controller, which is simply a wrapper around the browser window object, and you have the $window.scrollX and $window.scrollY properties.

If you want to respond to changes in scroll, put a watch on them:

$scope.$watch(function () {
    return $window.scrollY;
}, function (scrollY) {
    /* logic */
});

3 Comments

Worked for me. A little slow though. It doesn't seem to update frequently
@RobKohr Agreed. The question hints at setting a scope variable when the scroll position changes, so a $scope.$watch made the most sense, but I should have also mentioned that requestAnimationFrame should be used for paint operations like this. In that case, any changes to scope variables should be wrapped inside a $scope.$apply function, or they won't be picked up by Angular - a small inconvenience for the increased efficiency of requestAnimationFrame.
@SimonRobb When I compare $window.scrollY to my angular.element(...).prop('offsetTop'), they are vastly out of alignment. When I scroll on the page down to the element, $window.scrollY shows 1552 and prop('offsetTop') shows 2013 even though I've arrived at that element. How do I get these to align?
0

The accepted answer lacks teardown code:

ERRONEOUS (lacks teardown)

$document.on('scroll', function() {
    // do your things like logging the Y-axis
    console.log($window.scrollY);

   // or pass this to the scope
    $scope.$apply(function() {
        $scope.pixelsScrolled = $window.scrollY;
    })
});

To avoid memory leaks and other undesired behavior, the code needs to do proper teardown when the $scope is destroyed.

$document.on('scroll', scrollHandler);

$scope.$on("$destroy", function() {
    $document.off('scroll', scrollHandler);
});

function scrollHandler(ev) {
    // do your things like logging the Y-axis
    console.log($window.scrollY);

    // or pass this to the scope
    $scope.$apply(function() {
        $scope.pixelsScrolled = $window.scrollY;
    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.