doorman
doorman

Reputation: 16949

Scroll to anchor not working

Trying to scroll to an anchor link using the following syntax.

<a [routerLink]="['./']" fragment="test">Testing</a>

And the anchor node looks like this

<div id="test">

When clicked the browser address bar shows the #test fragment but the automatic scrolling does not occur. Any idea why it does not scroll?

Upvotes: 25

Views: 37139

Answers (10)

Fernando Raposo
Fernando Raposo

Reputation: 51

My scenario was that after pressing a button at the top of the screen and receiving some data from backend I may have a message to allow the scrolling to a certain point. If the buttom is pressed again I need to be able to scroll again and again...

After using successfully

this.viewportScroller.scrollToAnchor

or

scrollIntoView

locally, I found out that it was not working in production environment (really don't know why). So,

router.navigate([], { fragment: 'yourFragment' });

Did the job, and of course I had to "clean" the fragment to enable future scrollings, so inside button's logic I added:

router.navigate(['/']);

Important: remember to configure the ExtraOptions inside app-routing.module.ts

Upvotes: 0

Saurabh Gangamwar
Saurabh Gangamwar

Reputation: 802

This is the extension of https://stackoverflow.com/a/52415783/9646878 with extra config onSameUrlNavigation: 'reload'

For me setTimeout worked.

complete example :

  1. Set this in app.module.ts
       imports: [
         RouterModule.forRoot(routes, {
           scrollPositionRestoration: 'enabled', // or 'top'
           anchorScrolling: 'enabled',
           scrollOffset: [0, 64], // [x, y] - adjust scroll offset
           onSameUrlNavigation: 'reload'
         })
       ],
       exports: [RouterModule]
  1. app.component.html
<p>
 <div id = "componentId"> </div>
</p>
  1. app.component.ts
onScrollTo(location: string){
  setTimeout(() => { this.router.navigate([], { fragment: location }); }, 500);
}
use this method on Button click.

Alternate Approach

I used this approach for scrolling on samepage. 1.component.ts

  scrollTo(fragment): void {
    this.router.navigate([], { fragment: fragment }).then(res => {
      const element = document.getElementById(fragment);
      if (element != undefined) element.scrollIntoView();
    });
  }

2.component.html

<section id="some-section">
 <div></div>
 ....
</section>

3.call the method

<a (click)="scrollTo('some-section')">Navigate to Fragment</a>

Upvotes: 8

Langy
Langy

Reputation: 335

ViewportScroller doesn't work fine on IE if your content is loaded dynamicly.

Instead you need to implement AfterViewChecked

TestComponent implements AfterViewChecked{

     ngAfterViewChecked(){
        var element = document.getElementById("youranchor")
        element.scrollIntoView(false)
      }
}

for detail,see https://angular.io/api/core/AfterViewChecked

Upvotes: 1

RameshD
RameshD

Reputation: 962

I have recreated an reusalbe service that can be used in any component in order to either scroll to fragment if present else to top of the page.The following contains the complete echo system in angular anchoring use through reusable service approach.

//(In service.ts)
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ViewportScroller } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class RouterPathService {

  constructor(
    private activatedRoute: ActivatedRoute,
    private viewportScroller: ViewportScroller,


  ) { }

  scroll() {
    this.activatedRoute.fragment.subscribe((fragment: string) => {
      if (fragment) {
        this.scrollToAnchor(fragment)
      } else {
        this.scrollToTop();
      }
    });
  }

  scrollToTop() {
    this.viewportScroller.scrollToPosition([0, 0])

  }

  scrollToAnchor(elementId: string): void {
    this.viewportScroller.scrollToAnchor(elementId);
  }
}

We can call function from above reusable service from any component as :

//(In component.ts)
......
constructor(
        private routerPathService: RouterPathService,
     
    ) {
        this.routerPathService.scroll();
    }
....

HTML component would like :

<a [routerLink]="['/my-route']" fragment="test">Testing</a>

And app-routing.module.ts should enable the anchoring as:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
.........
];

@NgModule({
    imports: [RouterModule.forRoot(routes, {
        anchorScrolling: 'enabled',
    })],
    exports: [RouterModule]
})
export class AppRoutingModule { }

Upvotes: 2

frogcoder
frogcoder

Reputation: 354

From Angular 6.1 there is anchorScrolling for the router:

  1. Set this in app.module.ts

       imports: [
         RouterModule.forRoot(routes, {
           scrollPositionRestoration: 'enabled', // or 'top'
           anchorScrolling: 'enabled',
           scrollOffset: [0, 64] // [x, y] - adjust scroll offset
         })
       ],
       exports: [RouterModule]
    
  2. html

     <div id="test">
       // contents goes here...
     </div>
    
    
    
     <a [routerLink]="['./']" fragment="test">Testing</a>
    
  3. Import now the viewScroller which Angular v6 new feature (this might be not neccesary):

     import { ViewportScroller } from '@angular/common';
    
     constructor( private viewportScroller: ViewportScroller ) 
    
     ...
    
     scrollToTest() { 
       this.viewportScroller.scrollToAnchor('test');
     }
    

Upvotes: 11

pearpages
pearpages

Reputation: 21927

Based on @vsavkin workaround and taking advantage that fragments are provided as an observable (using "@angular/core": "^2.3.1"):

class MyAppComponent implements OnInit{

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.fragment.subscribe(f => {
      const element = document.querySelector("#" + f)
      if (element) element.scrollIntoView()
    })
  }
}

Upvotes: 26

Davis
Davis

Reputation: 3017

Look in Angular 6 new feature ViewportScroller

https://angular.io/api/common/ViewportScroller

Upvotes: 3

Gianluca Beritognolo
Gianluca Beritognolo

Reputation: 96

I can suggest to user ng2-page-scroll

ng2-page-scroll

install

npm install ng2-page-scroll --save

import in your app.module.ts

import {Ng2PageScrollModule} from 'ng2-page-scroll';

@NgModule({
    imports: [
        /* Other imports here */
        Ng2PageScrollModule
        ]
})
export class AppModule {
}

test it in your html component

<a pageScroll href="#test">Testing</a>
<div id="test">

Upvotes: 2

maburdi94
maburdi94

Reputation: 41

I liked using this

scroll(container, target) {
    let scrollTo = target.getBoundingClientRect().top;

    $(container).animate({
      scrollTop: `+=${scrollTo}`
    }, 900);
}

then in HTML do something like

<div #container> <!-- Scrolling container -->
    <button (click)="scroll(container, intro)">Go To Intro</button>
    <!-- other content -->
    <div #intro></div>
</div>

Upvotes: 0

Leukonoe
Leukonoe

Reputation: 649

I suppose scrolling isn't implemented with angular 2 yet. My solution to similar problem (scrolling to anchor on the same page) was to use ng2-page-scroll.

Upvotes: 3

Related Questions