2

This is supposed to be very simple, I have a certain element in my component template that I want to scroll down to it smoothly after the page finishes loading if it is passed in the url as a fragment like this: '../componentPath#someElement'

I tried to do it by adding a #someElement to the element in html and then used

@ViewChild('someElement') someElement: ElementRef

then called the ngAfterViewInit() like this:

ngAfterViewInit() {
  this.route.fragment.subscribe((fragment: string) => {
    if (fragment === 'someElement') {
      this.someElement.nativeElement.scrollIntoView({behavior: 'smooth', block: 'start'});
    }
  });
}

But nothing happens, it works perfectly if I call the scrollIntoView() after some short period using setTimeOut() like this:

ngAfterViewInit() {
  this.route.fragment.subscribe((fragment: string) => {
    if (fragment === 'someElement') {
      setTimeout(() => {
        this.servicesSection.nativeElement.scrollIntoView({behavior: 'smooth', block: 'start'});
      }, 1000)
    }
  });
}

Of course this is wrong, but it gave me the conclusion that my problem is with the time of firing the scrollIntoView() event.

I read many discussions on many sites and couldn't get it to work properly yet, so I thought it could be a good idea to open a question here.

2 Answers 2

1

Not sure what is your question. If you asking why you need setTimeout it's because you need to fire your scroll at the end of the dom rendering. You could also just use setTimeout(()=>{...}) without value at all, it's working also

For the smoothness of the scroll, you can't achieve it through behaviour:smooth since it's only supported by mozilla browser!

The response sadly is that you can't achieve this straighforward with angular2.

I use ionic and they have a very good .scrollTo functionalities on their content.

@ViewChild(Content) content: Content;
public scrollToResults() {
 setTimeout(() => {
   let element = document.getElementById('myElement');
   //1000 is the scroll time it self, 1s:
   this.content.scrollTo(0, element.offsetTop, 1000);
 })
}

If you don't use ionic you can reproduce how they accomplish it https://github.com/ionic-team/ionic/blob/master/src/util/scroll-view.ts

I dont't think it's worthing to import ionic if you don't use it. Maybe only importing ionic-angular would be enough, you don't need to import everything.

Sign up to request clarification or add additional context in comments.

1 Comment

Firstly, thanks for your answer. Secondly, I did want to fire my scroll at the end of the dom rendering, the thing is I didn't know how to time it, I though that setting the second parameter of setTimeOut() to 0 means right after the dom is rendered, but it didn't work, so I had to put a bigger value, like 1000 ms, but not passing the second parameter at all was the actual solution, so THANKS! As for the smooth scroll, it isn't really that much important for me, and btw.. It's working in chrome!
0

After chasing race conditions with this (I'm also using Ionic) I finally had to submit to writing some UGLY CODE . Again, apologies for the hack, but the AfterViewInit hook doesn't seem to be reliable enough to find id's dynamically assigned in the DOM.

    @ViewChild('form') form: ElementRef; // Scroll root
    intervalSubscription;
    ...
            ngAfterViewInit(): void {
                
                setTimeout(() => {
                  
                  // This is the id partially generated by the database id of the underlying resource
                  let element = document.getElementById("imageId-" + this.selectedImageId);
                  if(!element) {
                   // We didn't find it yet, so keep trying (TODO - kill bailout counter) 
                     this.intervalSubscription = interval(1000)
                    .subscribe(() => {  
                       
                      element = document.getElementById("imageId-" + this.selectedImageId);
                      //console.log("element", element);
                      if(element) {
                        // We found it, now unsubscibe to stop the interval timer 
                        this.intervalSubscription.unsubscribe();
            
                        // find how far we need to scroll 
                        let top = element.offsetTop;
                        console.log("top", top);
        
                        // and scroll to the element's y offset using the parent element (has overflow scroll)
                        this.form.nativeElement.scrollTo({
                            top: top,
                            behavior: "smooth",
                          });
                      }
                    })
                  }
                })
              }

The template looks something like the following

    ...
     <form #form>
        
        <!-- uploadedItems items-->
        <div *ngFor="let item of event.uploadedItems">
            <div [id]="'imageId-' + item.imageId">
             ...
            </div>
        </div>
     </form>
...

Unsubscribe if we close the page

ngOnDestroy(): void {
    if (this.intervalSubscription && !this.intervalSubscription.closed) {
      this.intervalSubscription.unsubscribe()
    }
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.