How to handle anchor hash linking in AngularJS

232,059

Solution 1

You're looking for $anchorScroll().

Here's the (crappy) documentation.

And here's the source.

Basically you just inject it and call it in your controller, and it will scroll you to any element with the id found in $location.hash()

app.controller('TestCtrl', function($scope, $location, $anchorScroll) {
   $scope.scrollTo = function(id) {
      $location.hash(id);
      $anchorScroll();
   }
});

<a ng-click="scrollTo('foo')">Foo</a>

<div id="foo">Here you are</div>

Here is a plunker to demonstrate

EDIT: to use this with routing

Set up your angular routing as usual, then just add the following code.

app.run(function($rootScope, $location, $anchorScroll, $routeParams) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    $location.hash($routeParams.scrollTo);
    $anchorScroll();  
  });
});

and your link would look like this:

<a href="#/test?scrollTo=foo">Test/Foo</a>

Here is a Plunker demonstrating scrolling with routing and $anchorScroll

And even simpler:

app.run(function($rootScope, $location, $anchorScroll) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    if($location.hash()) $anchorScroll();  
  });
});

and your link would look like this:

<a href="#/test#foo">Test/Foo</a>

Solution 2

In my case, I noticed that the routing logic was kicking in if I modified the $location.hash(). The following trick worked..

$scope.scrollTo = function(id) {
    var old = $location.hash();
    $location.hash(id);
    $anchorScroll();
    //reset to old to keep any additional routing logic from kicking in
    $location.hash(old);
};

Solution 3

There is no need to change any routing or anything else just need to use target="_self" when creating the links

Example:

<a href="#faq-1" target="_self">Question 1</a>
<a href="#faq-2" target="_self">Question 2</a>
<a href="#faq-3" target="_self">Question 3</a>

And use the id attribute in your html elements like this:

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>

There is no need to use ## as pointed/mentioned in comments ;-)

Solution 4

<a href="##faq-1">Question 1</a>
<a href="##faq-2">Question 2</a>
<a href="##faq-3">Question 3</a>

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>

Solution 5

If you always know the route, you can simply append the anchor like this:

href="#/route#anchorID

where route is the current angular route and anchorID matches an <a id="anchorID"> somewhere on the page

Share:
232,059
Rasmus
Author by

Rasmus

Working as a frontend developer in central Stockholm.

Updated on July 09, 2021

Comments

  • Rasmus
    Rasmus almost 3 years

    Do any of you know how to nicely handle anchor hash linking in AngularJS?

    I have the following markup for a simple FAQ-page

    <a href="#faq-1">Question 1</a>
    <a href="#faq-2">Question 2</a>
    <a href="#faq-3">Question 3</a>
    
    <h3 id="faq-1">Question 1</h3>
    <h3 id="faq-2">Question 2</h3>
    <h3 id="fa1-3">Question 3</h3>
    

    When clicking on any of the above links AngularJS intercepts and routes me to a completely different page (in my case, a 404-page as there are no routes matching the links.)

    My first thought was to create a route matching "/faq/:chapter" and in the corresponding controller check $routeParams.chapter after a matching element and then use jQuery to scroll down to it.

    But then AngularJS shits on me again and just scrolls to the top of the page anyway.

    So, anyone here done anything similar in the past and knows a good solution to it?

    Edit: Switching to html5Mode should solve my problems but we kinda have to support IE8+ anyway so I fear it's not an accepted solution :/