Ramana
Ramana

Reputation: 1945

Angular 6: TypeError: Cannot read property 'scrollIntoView' of null

I'm trying to test scrollIntoView() in angular 6 and got this error --> TypeError: Cannot read property 'scrollIntoView' of null

spec:

beforeEach(() => {
    fixture = TestBed.createComponent(BusinessInfoComponent);
    component = fixture.componentInstance;
    component.ngOnInit();
    spyOn(document.getElementById('info-banner'), 'scrollIntoView').and.callThrough(); 
    expect(document.getElementById('info-banner').scrollIntoView).toHaveBeenCalled();
    expect(document.getElementById('info-banner')).not.toBeDefined();
    fixture.detectChanges();
  });

ts:

ngOnInit() {
  document.getElementById('info-banner').scrollIntoView();

}

Upvotes: 2

Views: 8400

Answers (4)

Felix Olszewski
Felix Olszewski

Reputation: 798

I tried all the previous answers but none worked for me. I came up with a workaround:

⚠️⚠️ WARNING ⚠️⚠️: this code is not recommended for enterprise applications. The problem is that it will not always take less than 50ms for myTarget from @ViewChild to become defined, thus the scrolling might not take place 100% of the time. However as for performance it is not a big problem I think.

That being said, this worked for me in 100% of the cases in my app so far so it is a good workaround for small hobby/school/university projects. In my app the wait always took less than 3ms. You can comment-in the commented-out line in your app to see how long it would take for your component.

async ngAfterViewInit() {
      let timesThat1MSWasWaited = 0;
      while (this.myTarget === undefined) {
        await new Promise(r => setTimeout(r, 1));
        if (timesThat1MSWasWaited > 50) {
          console.error('FatalException: Waited too long for highlighted song to appear in DOM.');
          return;
        }
        timesThat1MSWasWaited++;
      }
      // console.debug('Needed to wait at least ' + timesThat1MSWasWaited + 'ms for myTarget to appear.');

      this.myTarget.nativeElement.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    }
  }

Upvotes: 1

Hitech Hitesh
Hitech Hitesh

Reputation: 1635

Try using this.

  // Get the reference of the variable 
@ViewChild("myDiv") divView: ElementRef;

   this.divView.nativeElement.scrollIntoView();
<div id="info-banner" #myDiv >Info </div>

Look into this link

https://www.techiediaries.com/angular-elementref/

For accessing outside elements check this

Access DOM element outside of Angular root component

Upvotes: -1

Antediluvian
Antediluvian

Reputation: 723

document.getElementById('info-banner').scrollIntoView(); should go into ngAfterViewInit() lifecycle hook.

Basically any DOM referencing should go into that hook. When ngOnInit() is executed, DOM doesn't exist yet and therefore the error.

Upvotes: 2

Bruno Farias
Bruno Farias

Reputation: 913

Try:

Spec:

beforeEach(() => {
    fixture = TestBed.createComponent(BusinessInfoComponent);
    component = fixture.componentInstance;
    component.ngOnInit();
    spyOn(window.document.getElementById('info-banner'), 'scrollIntoView').and.callThrough(); 
    expect(window.document.getElementById('info-banner').scrollIntoView).toHaveBeenCalled();
    expect(window.document.getElementById('info-banner')).not.toBeDefined();
    fixture.detectChanges();
  });

Angular class

ngOnInit() {
  window.document.getElementById('info-banner').scrollIntoView();
}

Upvotes: 0

Related Questions