3

I try to set the navbar li object to active when i currently watch the corresponding section. In other words: When scrolling through my page (single page with multiple sections), the section i'm watching should be highlighted in the navbar.

I already got the scrolling position via directive. Where I fail is to trigger ng-class on the .nav ul li.

<li class="" ng-class="{'active': $parent.scrollPos > document.getElementById('something').offset()}">
   <a class="" ng-click="isCollapsed = true" href="#something" du-smooth-scroll>Something</a>
</li>

I doesn't make any difference when I use

ng-class="{'active': $parent.scrollPos > angular.element('#something').offset()}"

How do I get the distance from document-top of any element?

Thanks a lot.

PS: I use jQuery (not lite)!

EDIT How it works (Thanks to @Sharikov Vladislav): On body I use the "PageCtrl":

<body id="page-top" class="index bg-default" ng-app="previewApp" ng-controller="PageCtrl">

which looks like that:

angular.module('previewApp')
  .controller('PageCtrl', function($scope, $element) {
    $scope.scrollPos = 0;
    $scope.getOffset = function (elementId) {
      var element = $element.find(elementId);      
      return element.offset().top;
    };
  });

Right after the body I use a span

<span scroll-position="scrollPos"></span>

to initialise my "scrollPosition"-directive, which allows me to access the current scroll position over the $scope.scrollPos in PageCtrl. Directive:

angular.module('previewApp')
  .directive('scrollPosition', function ($window) {
    return {
      scope: {
        scrollPos: "=scrollPosition"
      },
      link: function (scope, element, attrs) {
        var windowElement = angular.element($window);
        var handler = function () {
          scope.scrollPos = windowElement.scrollTop();
        }
        windowElement.on('scroll', scope.$apply.bind(scope, handler));
        handler();
      }
    };
  });

In my .nav ul li looks like the following. The "- 150" is for a more acurate highlighting.

<div class="navbar-collapse" uib-collapse="isCollapsed" id="menu-center">
   <ul class="nav navbar-nav navbar-right">
       <li class="hidden">
           <a href="#page-top"></a>
       </li>
       <li ng-class="{'active': $parent.scrollPos > $parent.getOffset('#section1') - 150 && $parent.scrollPos < $parent.getOffset('#section2') - 150}">
         <a ng-click="isCollapsed = true" href="#section1" du-smooth-scroll>Section1</a>
       </li>
       <li ng-class="{'active': $parent.scrollPos > $parent.getOffset('#section2') - 150 && $parent.scrollPos < $parent.getOffset('#section3')}">
         <a ng-click="isCollapsed = true" href="#section2" du-smooth-scroll>Section2</a>
       </li>                 
    </ul>
</div>

I hope this helps somebody out there who is struggling over the same problem like me. Greets

Ore
  • 972
  • 2
  • 12
  • 24

3 Answers3

8

No no no. Wrong! You should not use jQuery, when you are using AngularJS. Inject $element service in your controller:

JavaScript:

myCtrl.$inject = ['$scope', '$element']; 
function myCtrl($scope, $element) {
 var element = $element.find('#someId');
 $scope.getOffset = function () {
  return element.offset().top;
 };
}

HTML:

<div ng-class='{ "active": getOffset() > xxx }'></div>

You have not to use $(document) or pure jQuery in AngularJS at all.

Sharikov Vladislav
  • 7,049
  • 9
  • 50
  • 87
  • Thanks for that. I'm aware that this isn't good, but I was missing something like your method. In fact your solution works well, thanks a lot. I'll post the answer as soon as I fully implemented it. – Ore Nov 24 '15 at 08:48
  • Why do you think this is not good? Using pure jQuery in angularJS is not good :) Another advantage of $element - you don't search in full document, only in element with ng-controller. – Sharikov Vladislav Nov 24 '15 at 09:16
  • I edited my question with the full solution. And yeah, this is what I meant. Using something pure js in an angular app always causes problems. This is my experience. – Ore Nov 24 '15 at 09:28
1

you could try something like:

$(document).scrollTop() > document.getElementById('something').offset().top

(caveat - I havent' time to test right now - but will later)

I would also suggest saving all your element's Y positions to refer to rather than doing this calculation each time

Rich
  • 970
  • 2
  • 16
  • 42
  • Since I use sections that could expand and double its height, saving the Y positions may not be a good idea. I'll test your suggestion as soon as I'm able to. – Ore Nov 23 '15 at 18:05
  • @Ore do let me know how you get on as I'll hopefully get time to expand on this tonight if you haven't solved it by then – Rich Nov 23 '15 at 18:54
  • Thank you for the solution. I didn't test it cause like @Sharikov Vladislav sayed, it isn't nice to use pure jQuery in Angular. – Ore Nov 24 '15 at 08:51
  • agreed his solution is a better way wihtin Angular where as mine was aimed at getting your jQuery solution working – Rich Nov 24 '15 at 11:02
0

Use .offset() to get the distance between an element and the top of the document:

angular.element(("li .active").offset().top