23

I need to set an ng-click event so that that it loads a new page, and then, once the page has loaded, scrolls to an anchor point on the page. I've tried every solution proposed on this SO post but I can't get it to work correctly.

Most of those solutions center around scrolling to an anchor on a page that's already loaded. I need the scroll to occur after a new page has loaded.

Here is my view code:

<div class="see-jobs-btn" ng-click="$event.stopPropagation();goToResultJobs(r)">See Jobs</div>

This represents a button inside a 'profile card'. When the user clicks on the card, it takes them to a profile page. However, when they click the button, it needs to take them to the #jobs portion of that profile page (hence the $stopPropogation() before the goToResultJobs(r) in the code).

Here is what my goToResultJobs method looks like.

$scope.goToResultJobs = function(result) {
    var profileUrl = result.slug;
    window.location = profileUrl;
};

I've tried using $anchorScroll and just hardcoding in the anchor into the profileUrl, but neither one works. I'm fairly new to Angular, so I don't know what I'm missing here.

UPDATE 1: Trying to use $timeout

Here is my goToResultJobs method within my ResultsCtrl that is triggered when the user clicks the button:

$scope.goToResultJobs = function(result) {
    var url = window.location + result.slug + '#jobs';
    location.replace(url);
};

That loads the /name#jobs path, which calls the ProfileCtrl below:

app.controller('ProfileCtrl', ['$scope', '$http', '$timeout', '$location', '$anchorScroll',
function($scope, $http, $timeout, $location, $anchorScroll) {
    if(window.location.hash) {
        $timeout(function() {
            console.log('TEST');
            // $location.hash('jobs');
            // $location.hash('jobs');
            $anchorScroll();
        }, 1000);
    };
}]);

This setup seems to work, as TEST only appears in the console when the jobs button is clicked, but not when the user just clicks on the profile. The problem I'm now facing is that the page starts loading, and the path in the url bar changes to /name#jobs, but before the page finishes loading, jobs is stripped from the url. Therefore, when $anchorScroll() is called, there is no anchor tag in the hash to scroll to.

Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88
  • 1
    Have you tried using `$timeout($anchorScroll())`? – Tony May 14 '15 at 19:59
  • Try [anchorscroll](https://docs.angularjs.org/api/ng/service/$anchorScroll) – PSL May 14 '15 at 20:01
  • I tried using $timeout but it doesn't seem to fire. I inserted a console.log statement, but 'TEST' never appears in the console. See my question above for updated code: – Daniel Bonnell May 14 '15 at 21:09
  • Yes, because you navigate away from the page then try and make a timeout, which is gone as the page has changed. Try letting the `$timeout` just hang out so it's executed when it's loaded. The hash should already be in the URL. – Tony May 14 '15 at 21:17
  • Where should $timeout be in my Angular code then? There is no controller for the page that is being loaded, only for the page that lists all the profile cards. – Daniel Bonnell May 14 '15 at 21:23
  • I would wager, if you replace your `$rootScope.$on` to the `$timeout` it should work, assuming your url is being set properly. I'm working on a plunker, but this kind of problem isn't suited for that environment. – Tony May 14 '15 at 21:27
  • So I made the change and $timeout is now firing, but it fires before the page finishes loading. Updated code is above. – Daniel Bonnell May 14 '15 at 21:33

6 Answers6

19

So as pointed out, the $anchorScroll has to occur after the page has been rendered, otherwise the anchor doesn't exist. This can be achieved using $timeout().

$timeout(function() {
  $anchorScroll('myAnchor');
});

You can see this plunkr. Make sure to view it in pop-out mode (the little blue button in the upper right corner of the output screen).

Tony
  • 2,473
  • 1
  • 21
  • 34
  • I moved this to just inside my controller (same as in the plunkr), but now it doesn't fire at all. – Daniel Bonnell May 14 '15 at 21:51
  • Are you getting any errors in your console? The only way the code in that block wouldn't get executed is if you didn't inject `$timeout`. – Tony May 14 '15 at 21:54
  • I'll note that I am having trouble with the `$location.hash` version. I see in your update you dropped the tag from the `$anchorScroll`, which was the only way I could get it to work in the plunkr (granted, it is plunkr). – Tony May 14 '15 at 22:06
  • 3
    A further note, maybe you actually have to wait: `$timeout(function() {...}, 500);` – Tony May 14 '15 at 22:07
  • So I decided to create a new controller for my profile view that just has the $timeout function in it. Now the function fires when the page finishes loading, but it doesn't work like I intended. I have it set up so that when you click on the profile card, it sends you to `/name#jobs`, but for some reason, before the page finishes loading, the path chances from that to `/name#` and there is no scrolling. I'm trying to figure out why `jobs` is being stripped out of the url. – Daniel Bonnell May 15 '15 at 01:41
  • From what I've found, even though my `goToResultJob` function sends the user to `/name#jobs`, when the ProfileCtrl is triggered, `$location.hash` is empty. I think what I need is a way to set a variable `var scroll = true` inside my `goToResultJobs` function and then pass that to ProfileCtrl after the page loads. ProfileCtrl will then scroll the page is `scroll = true`. – Daniel Bonnell May 15 '15 at 02:20
  • I don't like the ad-hoc time delay length. For example, I added the argument to wait 500 milliseconds and that was not long enough (at least in my development environment with debuggable javascript running). Eventually I found a value that was long enough but I don't want the scroll to happen too late. There must be a way to catch a deterministic event that fires after the browser renders the window. – nmgeek Oct 24 '16 at 00:50
  • Wrapping the `$anchorScroll('myAnchor')` call to a `$timeout` function did the trick for me ;). Thanks – kpagan Dec 05 '16 at 10:05
  • Awesome! That timeout trick is also great for firing off functions after page load too. Thank you. – Nhan Mar 09 '17 at 00:16
4

Holy moly, this can be accomplished by simply adding autoscroll="true" to your template:

<div autoscroll="true" data-ng-include='"/templates/partials/layout/text-column.html"'></div>

Documentation

duhaime
  • 25,611
  • 17
  • 169
  • 224
0

In my case using fragment in Router did not work. ViewportScroller did not work either. Even if they did - I'd prefer to keep URL clean. So, this is what I used:

(document.querySelector('.router-wrapper') as HTMLElement)?.scrollIntoView();
Alexander
  • 1,152
  • 1
  • 16
  • 18
0

How to enable Anchor Scrolling of Angular Router and their problems

https://quandh19.medium.com/how-to-enable-anchor-scrolling-of-angular-router-in-the-right-way-42e9b19657b5

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      scrollPositionRestoration: 'enabled',
      anchorScrolling: 'enabled',
      //scrollOffset: [0, 64] // [x, y] takes into account your header
    })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Working scrolling in Angular: https://stackblitz.com/edit/solution-anchor-scrolling?file=src%2Fapp%2Fproduct%2Fproduct.component.ts

Royer Adames
  • 868
  • 9
  • 13
-1

Maybe you can use native Javascript's Element.scrollIntoView() after the page has been loaded?

Brent Washburne
  • 12,904
  • 4
  • 60
  • 82
  • 2
    The key here is whatever scroll technique is used has to happen _after_ page load. Even a small `$timeout` should work. – Tony May 14 '15 at 19:58
  • It is better to not answer a question with a question. – Luke Mar 21 '18 at 20:19
-1

Try:

angular.module('anchorScrollExample', [])
.controller('ScrollController', ['$scope', '$location', '$anchorScroll',
  function ($scope, $location, $anchorScroll) {
      $scope.gotoBottom = function() {
      // set the location.hash to the id of
      // the element you wish to scroll to.
      $location.hash('bottom');

      $anchorScroll();
    };
}]);

'bottom' here is the id of your html element to which you want to scroll to.

Documentation here: https://docs.angularjs.org/api/ng/service/$anchorScroll

bamboo_inside
  • 462
  • 4
  • 15
  • 1
    This was suggested in the link I posted with my question. This doesn't work because $anchorScroll needs to fire AFTER the page loads. – Daniel Bonnell May 14 '15 at 21:12