10

I have a demo here

I'm capturing the scrollLeft of one element as it's scrolled.

Is it possible to update the second div with the scrollLeft number so both div scroll left and right together?

The elements have to be separate but I need them to scroll together.

Or is there a simpler way to do this in Angular.

I had this working outside of Angular with jQuery but I don't want to use jQuery in Angular.

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular 5';
  isNavbarCollapsed = true;

  private scrollLeft: number
  //private scrolLTwo: HTMLElement = 

  onScroll(event: Event) {
    this.scrollLeft = (event.target as HTMLElement).scrollLeft;
    //console.log(this.scrollLeft);
    this.updateScroll();
  }

  updateScroll(){
    //Update second scrolling element
  }
}
Daniel W Strimpel
  • 8,190
  • 2
  • 28
  • 38
ttmt
  • 5,822
  • 27
  • 106
  • 158
  • Possible duplicate of [synchronizing scrolling between 2 divs with different text size](https://stackoverflow.com/questions/31178866/synchronizing-scrolling-between-2-divs-with-different-text-size) – bugs Apr 30 '18 at 15:19
  • 1
    That question has a jQuery answer. I want to try and do this with Angular and not jQuery. I had a jQuery solution that didn't work. – ttmt Apr 30 '18 at 15:23

3 Answers3

25

You can just get a reference to the second <div> and set it's scroll left to the same amount:

<div class="container" (scroll)="scrollTwo.scrollLeft = scrollOne.scrollLeft" #scrollOne>
    ...
</div>
<div class="container" #scrollTwo>
    ...
</div>

If you want to have more complicated logic in your component to manage what the scroll amount should be, you can just get the reference to the two DOM nodes via @ViewChild:

<div class="container" (scroll)="updateScroll()" #scrollOne>
    ...
</div>
<div class="container" #scrollTwo>
    ...
</div>
@Component(...)
export class AppComponent  {
  @ViewChild('scrollOne') scrollOne: ElementRef;
  @ViewChild('scrollTwo') scrollTwo: ElementRef;

  updateScroll(){
    const scrollOne = this.scrollOne.nativeElement as HTMLElement;
    const scrollTwo = this.scrollTwo.nativeElement as HTMLElement;

    // do logic and set
    scrollTwo.scrollLeft = scrollOne.scrollLeft;
  }
}

You don't have to get the references via @ViewChild. You could rather just pass them into the method that is called:

<div class="container" (scroll)="updateScroll(scrollOne, scrollTwo)" #scrollOne>
    ...
</div>
<div class="container" #scrollTwo>
    ...
</div>
@Component(...)
export class AppComponent  {
  updateScroll(scrollOne: HTMLElement, scrollTwo: HTMLElement){
    // do logic and set
    scrollTwo.scrollLeft = scrollOne.scrollLeft;
  }
}
Daniel W Strimpel
  • 8,190
  • 2
  • 28
  • 38
4

Angular CDK now provides cdkScrollable and ScrollDispatcher. Just add cdkScrollable directive to all your scrollable elements which need to be kept in sync and then add this logic to your component:

this.scrollDispatcher.scrolled().subscribe((scrollable: CdkScrollable) => {
  if (typeof scrollable == 'undefined') {
    return true;
  }
  const left = scrollable.measureScrollOffset('left');

  Array.from(this.scrollDispatcher.scrollContainers.keys())
    .filter(otherScrollable => otherScrollable && otherScrollable !== scrollable)
    .forEach(otherScrollable => {
      if (otherScrollable.measureScrollOffset('left') !== left) {
        otherScrollable.scrollTo({left});
      }
    });
});

Do not forget to unsubscribe from this subscription when the component is destroyed. And also inject ScrollDispatcher service in the constructor:

public constructor(private scrollDispatcher: ScrollDispatcher) {}
shaN
  • 798
  • 12
  • 23
livthomas
  • 1,160
  • 1
  • 14
  • 25
0

Just to add: if we use any material component as the scrollable element then we have to use this code:

<mat-grid-tile
  #scrollOne
  (scroll)="updateScroll(scrollOne, scrollTwo)">
</mat-grid-list>
<mat-grid-tile
  #scrollTwo
  (scroll)="updateScroll(scrollTwo, scrollOne)">
</mat-grid-list>


updateScroll(scrollOne: any, scrollTwo: any) {
   const scrO = scrollOne._element.nativeElement as HTMLElement;
   const scrT = scrollTwo._element.nativeElement as HTMLElement;

   scrT.scrollLeft = scrO.scrollLeft;
   scrT.scrollTop = scrO.scrollTop; 
}
Garuda prasad K
  • 332
  • 3
  • 8