7

I am developing a website with angularJS and using twitter bootstrap for UI. I have some hash Ids associated with certain section, so that when user click on navigation, page scrolls down to that section. And its working fine for me.

Here the site link: https://powerful-cliffs-1105.herokuapp.com/about

Now in about page. I have right navigation which scrolls page to specific position. But when i reload that page, page starts from top and doesn't scroll down to hash position, like https://powerful-cliffs-1105.herokuapp.com/about#management

There are two solutions for the above problem:

  1. Either remove hash tag from URL when we reload that page.
  2. Scroll page to particular(hash) section on page reload.

Since i am still learning angularJS, i am unable to find any concrete solution of above problem.

Any help is greatly appreciated.

Mohit Pandey
  • 3,679
  • 7
  • 26
  • 38
  • did you find a solution? because it appears that the sample page you posted in the question is working correctly now. – Claies Dec 30 '14 at 23:48
  • @AndrewCounts: Yes, now i changed my code (also upload it to provided link) using javascript `scroll` method. But unfortunately, `$anchorScroll` doesn't work as expected for me while reloading the page. Maybe DOM isn't ready when it called. If you have a solution with angular, then i love to implement it. Have a look at the answer for approach which i used now. – Mohit Pandey Dec 31 '14 at 05:31

3 Answers3

7

Use the $anchorScroll service.

The problem here, is that Angular loads the about content dinamically, so when you have the hash you don't have the content ready. $anchorScroll tracks the hash and scroll it for you.

// Just borrow from your code    
angular.module('aboutController', []).controller('aboutController',['$scope', '$location', '$anchorScroll', 
    function($scope, $location, $anchorScroll) {

  $scope.isActive = function(route){
     return route === $location.hash();
  }

  $anchorScroll();
}]);

Edit 31/12:

Since Mohit (the creator of the question) said that my solution didn't work for him, I decided to download the code and work locally. My solution worked locally, which made me scrach my head, but I'd like to point a few editions in the code that probably (or most probably not) can solve the issue.

Move the scripts to the bottom of the page (index.html)

<!-- ANGULAR DYNAMIC CONTENT -->
  <div class="body" ng-view></div>

  <!-- FOOTER -->
  <footer ng-include="'views/footer.html'"></footer>

  <!-- JS -->
  <script src="js/libs/angular/angular.min.js"></script>
  <script src="js/libs/angular-bootstrap/ui-bootstrap.min.js"></script>
  <script src="js/libs/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
  <script src="js/libs/angular-route/angular-route.min.js"></script>

  <!-- ANGULAR CUSTOM CONTROLLERS -->
  <script src="js/controllers/home.js"></script>
  <script src="js/controllers/about.js"></script>
  <script src="js/controllers/contact.js"></script>

  <!-- ANGULAR CUSTOM FACTORIES -->
  <script src="js/services/contact.js"></script>

  <!-- UTILITY FILE -->
  <script src="js/utils/util.js"></script>

  <!-- ANGULAR CUSTOM ROUTES AND APP -->
  <script src="js/route-provider.js"></script>
  <script src="js/app.js"></script>
</body>

Remove/comment, for now, the html5Mode (route-provider.js)

// $locationProvider.html5Mode(true);

and append /#/ before accessing the URLs, like this

Put a timeout

This is by far the one I don't suggest, but certainly you can give a shot

$timeout(function() {
    $anchorScroll(); 
}, 0)
Rigotti
  • 2,766
  • 23
  • 28
  • @Rigoti: You are right but unfortunately this doesn't work when i reload the content. When i add `$anchorScroll`, it does nothing when i reload the page. – Mohit Pandey Dec 25 '14 at 15:36
  • @MohitPandey Bit late here, I can't guess what could be wrong here, I already had this issue and this solved for me. Have you tried moving your scripts to the bottom of your ``? – Rigotti Dec 31 '14 at 18:14
  • @Rigoti: Thanks, moving script files to the bottom of `` do the trick. – Mohit Pandey Jan 01 '15 at 09:27
1

Well you can use a directive and watch the scope value into it :

var app = angular.module('plunker', ['LocalStorageModule']);

app.controller('MainCtrl', [ '$scope', 'localStorageService', function($scope, localStorageService) {
  if(localStorageService.isSupported) {
    $scope.util = localStorageService.get('anchor');
    console.log(localStorageService.get('anchor'));
  }
  else{
    $scope.util = "third";
  }

  $scope.moveTo = function(anchor){
    console.log('controller anchor value:', anchor);
    $scope.util = anchor;
    if(localStorageService.isSupported) {
      localStorageService.set('anchor', anchor);
    }
  };


}]);

app.directive('scrollToAnchor', ['$location', '$anchorScroll', function($location, $anchorScroll){
  return{
    restrict: 'A',
    scope: {
      anchor: '='
    },
    link: function(scope, element){
        scope.$watch('anchor', function(){
          $location.hash(scope.anchor);
          $anchorScroll();
          console.log('directive anchor value:', scope.anchor);
        });
    }
  };
}]);

I don't know if it's the best way but you need to have your dom rendered before scrolling to the element.

here the plnkr : http://plnkr.co/edit/niFzeX7XAJVNzDoD0CXa?p=preview

rbinsztock
  • 3,025
  • 2
  • 21
  • 34
  • Thanks. I don't know whether its a best solution or not but it definitely provide a solution using angular. Also, i think that there might be a solution available without using localStorage. – Mohit Pandey Dec 31 '14 at 05:51
0

After investing more time in angularJS, i discovered $anchorScroll will need to work as @Rigoti said. But somehow it didn't work for me.

Alternatively, i discover that we can create our own method using factories/services or we can also create a separate class/object like utility file which can then be used in angular module. So i create a util file and add a method to provide smooth scrolling using this great fiddle by brettdewoody http://jsfiddle.net/brettdewoody/y65G5/

After that i modified my code, so that i can access that method and call it.

about.js

angular.module('aboutController', []).controller('aboutController',['$scope', '$location', '$anchorScroll', function($scope, $location, $anchorScroll) {

    // create local instance of utility object
    $scope.util = util;

    // move scroll position to passed id
    $scope.moveTo = function(id){
        // set hash location to clicked id so that isActive class is added to it.
        $location.hash(id);
        // scroll to element id
        $scope.util.scrollTo(id);
    }

    // set active class to right hand navigation on route change
    $scope.isActive = function(route){
        return route === $location.hash();
    }

    // if hash is available, scroll to hash position on page reload
    //$anchorScroll();
    if(!$scope.util.isNull($location.hash()))
        $scope.util.scrollTo($location.hash());

}]);

util.js

/**
 * Created by admin on 22/12/14.
 */

var util = {
    scrollTo: function(id) {

        // This scrolling function
        // is from http://www.itnewb.com/tutorial/Creating-the-Smooth-Scroll-Effect-with-JavaScript

        var startY = currentYPosition();
        var stopY = elmYPosition(id);
        var distance = stopY > startY ? stopY - startY : startY - stopY;
        if (distance < 100) {
            scrollTo(0, stopY);
            return;
        }
        var speed = Math.round(distance / 100);
        if (speed >= 20) speed = 20;
        var step = Math.round(distance / 25);
        var leapY = stopY > startY ? startY + step : startY - step;
        var timer = 0;
        if (stopY > startY) {
            for (var i = startY; i < stopY; i += step) {
                setTimeout("window.scrollTo(0, " + leapY + ")", timer * speed);
                leapY += step;
                if (leapY > stopY) leapY = stopY;
                timer++;
            }
            return;
        }
        for (var i = startY; i > stopY; i -= step) {
            setTimeout("window.scrollTo(0, " + leapY + ")", timer * speed);
            leapY -= step;
            if (leapY < stopY) leapY = stopY;
            timer++;
        }

        function currentYPosition() {
            // Firefox, Chrome, Opera, Safari
            if (self.pageYOffset) return self.pageYOffset;
            // Internet Explorer 6 - standards mode
            if (document.documentElement && document.documentElement.scrollTop)
                return document.documentElement.scrollTop;
            // Internet Explorer 6, 7 and 8
            if (document.body.scrollTop) return document.body.scrollTop;
            return 0;
        }

        function elmYPosition(id) {
            var elm = document.getElementById(id);
            var y = elm.offsetTop;
            var node = elm;
            while (node.offsetParent && node.offsetParent != document.body) {
                node = node.offsetParent;
                y += node.offsetTop;
            }
            return y;
        }
    },
    isNull: function (o) {
        return angular.isUndefined(o) || o === null || o === '';
    }
}

about.html

<li><a ng-class="{active:isActive('about')}" ng-click="moveTo('about')">About</a></li>
<li><a ng-class="{active:isActive('keraleeyam')}" ng-click="moveTo('keraleeyam')">Keraleeyam Ayurveda</a></li>
<li><a ng-class="{active:isActive('management')}" ng-click="moveTo('management')">Management</a></li>
<li><a ng-class="{active:isActive('treatment')}" ng-click="moveTo('treatment')">Panchakarma Treatment</a></li>

And, it worked for me :). I also made changes to live URL so that you can look into it.

NOTE: I am still waiting for a solution using $anchorScroll method. So please help me if you found anything which i missed.

Mohit Pandey
  • 3,679
  • 7
  • 26
  • 38