167

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
  • 4,936
  • 10
  • 57
  • 84
  • 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

13 Answers13

209

You could do it like this:

<button (click)="scroll(target)"></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?

Frank Modica
  • 9,610
  • 3
  • 21
  • 35
  • 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
  • 1
    @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 – abbastec 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
  • 3
    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"! – yashthakkar2898 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
91

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,010
  • 8
  • 10
  • 12
    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
  • 3
    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
  • 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 ace Feb 24 '22 at 13:05
78

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
  • 4,936
  • 10
  • 57
  • 84
LH7
  • 1,275
  • 2
  • 12
  • 16
57

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,708
  • 1
  • 36
  • 40
  • 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
25

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.
  • 331
  • 4
  • 8
25

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
  • 4,936
  • 10
  • 57
  • 84
Schnapz
  • 1,128
  • 12
  • 9
17

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
  • 4,936
  • 10
  • 57
  • 84
Ifesinachi Bryan
  • 2,014
  • 1
  • 17
  • 19
9

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

<div #myDiv > 

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

BuZZ-dEE
  • 4,936
  • 10
  • 57
  • 84
M.Amer
  • 578
  • 5
  • 11
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
  • 881
  • 11
  • 21
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
  • 1
  • 2
-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
  • 278
  • 6
  • 15
-9

I have done something like what you're asking just by using jQuery to find the element (such as document.getElementById(...)) and using the .focus() call.

BuZZ-dEE
  • 4,936
  • 10
  • 57
  • 84
Kenneth Salomon
  • 1,256
  • 11
  • 17
  • 4
    Is jQuery really needed when using Angular? Feels like Angular should be enough –  May 12 '17 at 20:29
  • Not 100% sure but I was in an environment that has not been validated and refactored for Angular2 so I did not search too hard for a solution in Angular and just went with something that I knew I would have access to. – Kenneth Salomon May 12 '17 at 20:35
  • 10
    You shouldn't use Jquery and Angular – Stefdelec Aug 28 '17 at 14:17
  • 2
    Yes yes, I’ve learnt a lot and everyone comments that I shouldn’t use jQuery. You’re not saying anything that others haven’t commented right above you. – Kenneth Salomon Dec 06 '18 at 04:05
  • Upvoted you for the humility. I'd like to add that jQuery answers have some value for those that are transitioning to SPA frameworks. By showing how we used to do it, and seeing the comments recommending other solutions. – B2K Jul 08 '19 at 16:15
-20

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,213
  • 4
  • 21
  • 33
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
  • this is typescript solution. – Gns Jul 16 '18 at 07:37
  • 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