188

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

BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
  • 1
    Your syntax does not have problems but you need to define what is #target. – wannadream May 12 '17 at 19:58
  • 1
    So this should work? When I use a arbitrary number and call window.scrollTo(500) in my function nothing happens. I was thinking that element would be a HTMLElement –  May 12 '17 at 20:09
  • Right, however, what is #target, Angular will not resolve it? You can test scroll() with no parameter first. – wannadream May 12 '17 at 20:19
  • Yeah I tried (click)="scroll()" in my button and window.scrollTo(0, 500) in the component but nothing happens –  May 12 '17 at 20:24
  • Check this: https://plnkr.co/edit/A0WhJVx8CP1Ai78dbvPQ?p=preview – wannadream May 12 '17 at 20:36
  • Thanks for that! I've been trying to do that but it just won't scroll. I print to console in my scroll function to see if it ever got there and it does. It seems that the window.scrollTo(0, 500) doesn't get executed –  May 12 '17 at 21:05
  • 3
    But when I do window.scrollTo(0, 500) in the constructor with a 500ms delay it works –  May 12 '17 at 21:08
  • Hmm, weird. If you can recreate it in plunker, we can help to check it. – wannadream May 12 '17 at 21:12
  • I just figured it out. I wasn't using a button as I wrote in the description, I was using a a-tag. Sorry for the confusing you and thanks for the help! It works now –  May 12 '17 at 21:29
  • Which JS framework is this? and also, can we use it with Angular v8? – Csibi Norbert Nov 26 '19 at 15:50
  • Little late to this party, but I've written a plugin for Angular 4+ that does just this. It covers other issues you may bump into, like Server-Side Rendering. You can also animate to scrolling to your likings. Full disclosure, I'm the author. [NPM: @nicky-lenaers/ngx-scroll-to](https://www.npmjs.com/package/@nicky-lenaers/ngx-scroll-to) [GitHub: @nicky-lenaers/ngx-scroll-to](https://github.com/nicky-lenaers/ngx-scroll-to) Hope this helps you out! – Nicky Oct 12 '17 at 08:04
  • Hi. Very cool! I had a look at the source, a lot of code to achieve smoth scrolling, with the animations, service, directive etc. Could you not have use the Element.scrollIntoView() with 'smooth' option polyfill? Is there more to it than that, maybe I'm not getting some of the complexity? – Drenai Jan 07 '18 at 16:33
  • The "smooth" option has very instable support, see https://caniuse.com/#feat=scrollintoview. This is why I went with some more graceful decision. I agree the code might look a lot, but it serves most of the browsers. – Nicky Jan 07 '18 at 17:41
  • Ah, unfortunately I'm not familiar with the approach you describe, but it sounds interesting. Please feel free to suggest any improvements or additions to the `ngx-scroll-to` package, your knowledge and input is always very welcome. If you wanna talk more about the subject I'd suggest you file issues on the GitHub repo. Thanks for your input thus far! – Nicky Jan 07 '18 at 23:51
  • This is super good. Ended up adding because I was loading a child component setTimeout(() => { this.scrollToService.scrollTo(config); }, 300); – Mukus May 18 '18 at 01:09
  • Cool - This actually works with Angular V8? – Csibi Norbert Nov 26 '19 at 15:52

14 Answers14

239

You could do it like this:

<button (click)="scroll(target)">Scroll To Div</button>
<div #target>Your target</div>

and then in your component:

scroll(el: HTMLElement) {
    el.scrollIntoView();
}

Edit: I see comments stating that this no longer works due to the element being undefined. I created a StackBlitz example in Angular 7 and it still works. Can someone please provide an example where it does not work?

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
Frank Modica
  • 10,238
  • 3
  • 23
  • 39
  • For some reason running that exact code does nothing for me. Even a hardcoded window.scrollTo(0, 500) does nothing –  May 12 '17 at 20:28
  • Can you confirm that your `scroll` method in your component is executing? Add a `console.log(el)` and open your browser's Developer Tools to see if you are getting the correct element. Also, check the console for errors. – Frank Modica May 12 '17 at 20:30
  • I had to use el.scrollIntoView(true) – jarodsmk Jul 10 '17 at 12:39
  • 2
    @N15M0_jk Yep, there's also an object you can pass in with other options, depending on what you need. https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView – Frank Modica Jul 10 '17 at 12:57
  • @FrankModica yea that's where I read up on the parameters :) really helped me out, never knew about scrollIntoView before seeing your post actually – jarodsmk Jul 10 '17 at 16:24
  • What does adding "#target" to the div do? – Anthony Sep 19 '17 at 15:32
  • 2
    @Anthony It allows you to reference the element that you want to pass to the `scroll` function on the component. Notice that the click event calls `scroll(target)`. If you wanted to get access to the element from inside the component without having to pass the element in, you could use `@ViewChild`. – Frank Modica Sep 19 '17 at 15:42
  • 1
    Following link will be a right solution https://stackoverflow.com/questions/47616843/how-to-call-scrollintoview-on-an-element-in-angular-2 – abbas-ak Mar 15 '18 at 11:37
  • @blueprintchris I posted a link to StackBlitz where it works in Angular 7. I know this was a while ago, but if you remember a situation where it did not work please let me know. Thanks! – Frank Modica Feb 04 '19 at 14:42
  • 4
    It works fine with me now. But before i made a silly mistake and used `id="target"` instead of `#target` and it gave me "el is undefined error"! – Yash Feb 07 '19 at 12:18
  • I have seen that some people using this in Safari and some special browser and it doesn't work. – Toan Quoc Ho Aug 27 '19 at 13:54
  • 4
    I believe the undefined error happens if the element with the `#target` is inside an `*ngIf`, but it should work if you use the `@ViewChild` method in the link from @abbastec – spectacularbob Dec 12 '19 at 20:00
  • the correct answer, also about the center option from the above answer. { behavior: 'smooth', block: 'center' } – VISHMAY Jun 07 '20 at 16:52
  • 1
    simple and neat. Works for me in Angular 10 – griffinleow Sep 28 '20 at 03:44
  • The `undefined` error is probably happening when `scroll(target)` is used and the target is not defined. If the id is called target then the target in the scroll call needs to be in quotes e.g. `scroll('target')`. – Mike Poole Mar 24 '21 at 10:47
  • It works just if the #id is hardcoded. but if you want to generate the id when the page is rendered (like ngFor) it's not working. – Avi May 09 '22 at 12:12
123

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'});
}
Robert S.
  • 1,330
  • 1
  • 9
  • 10
  • 21
    bonus for {behavior: 'smooth'} – Squapl Recipes May 19 '19 at 06:43
  • 1
    It's working for me.. special thanks for {behavior: 'smooth'} – Vibhu kumar Oct 09 '19 at 05:59
  • 4
    This will not work if ```#target``` is defined after the button, which is relevant if you have a floating header for example... – Jonathan May 16 '20 at 02:57
  • Awesome, works for me too and appreciate the bonus of smooth scrolling – Leon Matota Jun 24 '21 at 16:44
  • Confirmed working as expected in Angular 12. Well done! – MacD Jan 22 '22 at 19:57
  • 1
    This works perfectly. Bonus: el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }) --- worked better for me; To have a smooth transition & not scroll the whole page in the case of a side-navigation. Ref->https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move – James Ikubi Feb 24 '22 at 13:05
85

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();
}
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
LH7
  • 1,385
  • 2
  • 12
  • 16
58

There is actually a pure javascript way to accomplish this without using setTimeout or requestAnimationFrame or jQuery.

In short, find the element in the scrollView that you want to scroll to, and use scrollIntoView.

el.scrollIntoView({behavior:"smooth"});

Here is a plunkr.

Jon
  • 7,848
  • 1
  • 40
  • 41
  • 3
    2 ways that this answer is different: 1. This is animated smooth scrolling, not a jump. 2. This answer does not scroll a window, but moves a scrollview within a window. For example, if you have a horizontal scrollview within the window. Check the plunkr for an example. – Jon Dec 11 '17 at 19:51
  • 1
    scrollIntoView's scrollIntoViewOptions (the object as argument) is only compatible with Firefox right now. – Jesús Fuentes May 18 '18 at 09:41
  • Works fine for Chrome and Firefox, but not for Safari. – Yamashiro Rion Jan 29 '19 at 12:32
  • This worked for me in Angular 9 – Hamza Iftikhar Aug 01 '23 at 13:32
27

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();
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Schnapz
  • 1,208
  • 13
  • 10
26

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>
Stephen E.
  • 351
  • 4
  • 8
19

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' });
}
BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Ifesinachi Bryan
  • 2,240
  • 1
  • 19
  • 20
10

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

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
M.Amer
  • 658
  • 6
  • 13
8

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>
Andres Gardiol
  • 1,312
  • 15
  • 22
0

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' })
  }
Harvey
  • 46
  • 5
0

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.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
0

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' })
-2

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'});
}
SimoneMSR
  • 368
  • 1
  • 6
  • 16
-21

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.

souki
  • 1,305
  • 4
  • 23
  • 39
Gns
  • 1
  • 2
  • 2
    Except this op is asking for TypeScript solutions not work arounds with an obsolete framework (down voted) – Ash Jul 14 '18 at 13:24
  • 3
    You’ve missed my point, your answer uses jQuery (might I add in the most inefficient way) rather than simply using ES*. Your constants don’t declare their type neither. It’s just a bad example with bad logic using jQuery which is not what was asked. – Ash Jul 25 '18 at 19:51