Home > Net >  IntersectionObserver loses observed element
IntersectionObserver loses observed element

Time:02-17

i am trying to use an IntersectionObserver in my Angular application but it doesn't work as expected. I don't have a reproducible example as this affects a productive implementation, maybe this simple case might be enough. So i have a view that generates certain elements via the *ngFor directive:

app.component.html

<div *ngFor="let row of dataSource$ | async" #rows>

app.component.ts

@ViewChildren('rows') rows: QueryList<ElementRef<HTMLElement>>;

private observer: IntersectionObserver;

ngAfterViewInit(): void {
    this.observer = new IntersectionObserver(entries => {
      console.log(entries);

      if (entries[0].isIntersecting) {
        console.log('BottomElement is intersecting');
      }

      if (entries[1].isIntersecting) {
        console.log('TopElement is intersecting');
      }
     }

    this.rows.changes.subscribe(elements => {
      this.observer.observe(elements.last.nativeElement);
      this.observer.observe(elements.first.nativeElement);
    });
}

So i am basically adding the first and last generated to the IntersectionObserver. If i start my application, everything works fine and i get the following console outputs:

(2) [IntersectionObserverEntry, IntersectionObserverEntry]
TopElement is intersecting

But once i start scrolling down, as soon as the TopElement vanishes, the IntersectionObservable outputs:

[IntersectionObserverEntry]
BottomElement is intersecting
ERROR TypeError: Cannot read properties of undefined (reading 'isIntersecting')

So the IntersectionObservable loses the TopElement to be observed. Strange enough, the BottomElement does not has any problems, so scrolling up and down the BottomElement keeps being observed.

Any ideas what the problem might be?

CodePudding user response:

You are always expecting to have 2 entries intersecting. What happens when top element goes out of viewport area and bottom element just started intersecting? You'd only have one element on screen I guess. So you can't access elements on entries[] which ain't there to be accessed.

The Intersection Observer API allows you to configure a callback that is called when either of these circumstances occur: A target element intersects either the device's viewport or a specified element. That specified element is called the root element or root for the purposes of the Intersection Observer API. The first time the observer is initially asked to watch a target element. https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#intersection_observer_concepts_and_usage

But what if you just read what you get? I.e top is intersecting, top is leaving, bottom is intersecting, bottom is leaving. How about an directive to help you out?

  private _callback = (entries, observer) => {
    entries.forEach(entry => {
      console.log(entry.isIntersecting ? this.elementName   'I am visible' :  this.elementName   'I am not visible');
      this.visibilityChange.emit(entry.isIntersecting ? 'VISIBLE' : 'HIDDEN')
    });
  };
<div enterTheViewportNotifier elementName="TOP ELEMENT" style="height: 100px; background-color: yellow">
  I'll notify when I'm visible TOP ELEMENT
</div>

<div enterTheViewportNotifier elementName="BOTTOM ELEMENT" style="height: 100px; background-color: blue">
  I'll notify when I'm visible BOTTOM ELEMENT
</div>

Here is a working example: https://stackblitz.com/edit/angular-ivy-3mkabk?file=src/app/enter-the-view-port.directive.ts

  • Related