Scroll to element on click in Angular 4

Viewed 285212

I want to be able to scroll to a target when a button is pressed. I was thinking something like this.

<button (click)="scroll(#target)">Button</button>

And in my component.ts a method like.

scroll(element) {
    window.scrollTo(element.yPosition)
}

I know that the code above is not valid but just to show what I was thinking. I've just started to learn Angular 4 with no previous experience of Angular. I've been searching around for something like this but all the examples are in AngularJs which differs alot to Angular 4

14 Answers

In Angular 13 works perfect

HTML

<button (click)="scroll(target)">Click to scroll</button>
<div #target>Your target</div>

In component

scroll(el: HTMLElement) {
    el.scrollIntoView({behavior: 'smooth'});
}

Here is how I did it using Angular 4.

Template

<div class="col-xs-12 col-md-3">
  <h2>Categories</h2>
  <div class="cat-list-body">
    <div class="cat-item" *ngFor="let cat of web.menu | async">
      <label (click)="scroll('cat-'+cat.category_id)">{{cat.category_name}}</label>
    </div>
  </div>
</div>

add this function to the Component.

scroll(id) {
  console.log(`scrolling to ${id}`);
  let el = document.getElementById(id);
  el.scrollIntoView();
}

Jon has the right answer and this works in my angular 5 and 6 projects.

If I wanted to click to smoothly scroll from navbar to footer:

<button (click)="scrollTo('.footer')">ScrolltoFooter</button>
<footer class="footer">some code</footer>

scrollTo(className: string):void {
   const elementList = document.querySelectorAll('.' + className);
   const element = elementList[0] as HTMLElement;
   element.scrollIntoView({ behavior: 'smooth' });
}

Because I wanted to scroll back to the header from the footer, I created a service that this function is located in and injected it into the navbar and footer components and passed in 'header' or 'footer' where needed. just remember to actually give the component declarations the class names used:

<app-footer class="footer"></app-footer>

Another way to do it in Angular:

Markup:

<textarea #inputMessage></textarea>

Add ViewChild() property:

@ViewChild('inputMessage')
inputMessageRef: ElementRef;

Scroll anywhere you want inside of the component using scrollIntoView() function:

this.inputMessageRef.nativeElement.scrollIntoView();

You can scroll to any element ref on your view by using the code block below. Note that the target (elementref id) could be on any valid html tag.

On the view(html file)

<div id="target"> </div>
<button (click)="scroll()">Button</button>
 

  

on the .ts file,

scroll() {
   document.querySelector('#target').scrollIntoView({ behavior: 'smooth', block: 'center' });
}

In Angular you can use ViewChild and ElementRef: give your HTML element a ref

<div #myDiv></div> 

and inside your component:

import { ViewChild, ElementRef } from '@angular/core';
@ViewChild('myDiv') myDivRef: ElementRef;

you can use this.myDivRef.nativeElement to get to your element

You can achieve that by using the reference to an angular DOM element as follows:

Here is the example in stackblitz

the component template:

<div class="other-content">
      Other content
      <button (click)="element.scrollIntoView({ behavior: 'smooth', block: 'center' })">
        Click to scroll
      </button>
    </div>
    <div id="content" #element>
      Some text to scroll
</div>

If the scroll goes too far

Get correct y coordinate and use window.scrollTo({top: y, behavior: 'smooth'})

Set different y coordinate if using multiple elements

Html file

<div class="sub-menu">
  <button (click)="scroll('#target1', -565)">Target One</button>
  <button (click)="scroll('#target2', -490)">Target Two</button>
</div>

<div>
  <div id="target1">Target One</div>
</div>

<div>
  <div id="target2">Target Two</div>
</div>

ts file

  scroll(elem: string, offset: number) {
    const yOffset = offset;
    const element = document.querySelector(elem)!;
    const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;

    window.scrollTo({ top: y, behavior: 'smooth' })
  }

First, I tried the simplest solution, which was the use of DOM commands like Element.scrollIntoView(), or

document.getElementById("<yourTarget>").scrollIntoView({
  behavior: "smooth",
  block: "start",
  inline: "nearest"
});

(Remember to add id="<yourtarget>" where you want to scroll) It worked on development, and it was quite easy, but after deploying to production environment I noticed that the scroll was not working!

So, I tried another way, using Angular's ViewportScroller. In order to do that, you have to inject ViewportScroller at Component's constructor, like constructor(private scroller: ViewportScroller) and just call this.scroller.scrollToAnchor("<yourTarget>");. Again, no big deal, and again it was NOT WORKING on production environment.

The third way to do it, is to use Router to provide navigation to the anchor I wanted. Similarly to the last option, inject Router to constructor, like

constructor(private router: Router), and use the command: this.router.navigate([], { fragment: "<yourTarget>" }); Finally it DID WORK on production environment! I don't know for sure why the previous methods failed, I read some sources that says Angular Material blocks scrolling, but I'm not sure.

In order to present the different options there's a Stackblitz as an example.

https://stackblitz.com/edit/scrolling-types

There you can click on tree buttons, each one using a different method to scroll down to some anchor.

For me the top answer moved my entire component causing areas of the page to be blocked off. I fixed it by adding some additional properties

element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })

I need to do this trick, maybe because I use a custom HTML element. If I do not do this, target in onItemAmounterClick won't have the scrollIntoView method

.html

<div *ngFor"...">
      <my-component #target (click)="clicked(target)"></my-component>
</div>

.ts

onItemAmounterClick(target){
  target.__ngContext__[0].scrollIntoView({behavior: 'smooth'});
}

You can do this by using jquery :

ts code :

    scrollTOElement = (element, offsetParam?, speedParam?) => {
    const toElement = $(element);
    const focusElement = $(element);
    const offset = offsetParam * 1 || 200;
    const speed = speedParam * 1 || 500;
    $('html, body').animate({
      scrollTop: toElement.offset().top + offset
    }, speed);
    if (focusElement) {
      $(focusElement).focus();
    }
  }

html code :

<button (click)="scrollTOElement('#elementTo',500,3000)">Scroll</button>

Apply this on elements you want to scroll :

<div id="elementTo">some content</div>

Here is a stackblitz sample.

Related