Scroll to anchor after page load in Angular
Asked Answered
S

6

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.

Striptease answered 14/5, 2015 at 19:39 Comment(7)
Have you tried using $timeout($anchorScroll())?Validate
Try anchorscrollHazardous
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:Striptease
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.Validate
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.Striptease
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.Validate
So I made the change and $timeout is now firing, but it fires before the page finishes loading. Updated code is above.Striptease
V
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).

Validate answered 14/5, 2015 at 21:35 Comment(9)
I moved this to just inside my controller (same as in the plunkr), but now it doesn't fire at all.Striptease
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.Validate
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).Validate
A further note, maybe you actually have to wait: $timeout(function() {...}, 500);Validate
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.Striptease
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.Striptease
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.Reply
Wrapping the $anchorScroll('myAnchor') call to a $timeout function did the trick for me ;). ThanksBozo
Awesome! That timeout trick is also great for firing off functions after page load too. Thank you.Amadavat
A
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

Archicarp answered 14/9, 2016 at 17:54 Comment(1)
incredible, absolutely incredible.Nubbly
W
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();
Worsley answered 13/8, 2021 at 11:15 Comment(0)
B
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

Blindstory answered 27/10, 2022 at 15:41 Comment(0)
C
-1

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

Conglomeration answered 14/5, 2015 at 19:49 Comment(2)
The key here is whatever scroll technique is used has to happen after page load. Even a small $timeout should work.Validate
It is better to not answer a question with a question.Odessa
A
-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

Aluminum answered 14/5, 2015 at 20:52 Comment(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.Striptease

© 2022 - 2024 — McMap. All rights reserved.