Búsqueda de sitios web

typescript - ¿Cómo puedo capturar cualquier evento de desplazamiento de un elemento en la página usando HostListener (Angular 7)?


Estoy creando un componente de barra de migas de pan reutilizable en Angular 7. Quiero que se oculte una vez que la página haya sido desplazada un poco y luego reaparezca cuando el usuario se desplace hacia arriba.

Hice una directiva para adjuntarla a la barra en sí que hace referencia al offsetParent para capturar el scrollTop del contenedor que realmente se desplaza. Todo ese código funciona, pero no estoy seguro de cómo activar el HostListener para que realmente ejecute el código.

Voy a comenzar esta pregunta con el hecho de que realmente no quiero tener que adjuntar una directiva a cada contenedor que se desplace, ya que eso haría que el componente fuera menos flexible. ¿Cómo puedo capturar cualquier evento de desplazamiento en una página? El fragmento de código a continuación es la plantilla HTML y la directiva. Tampoco me gustaría usar un montón de eventos de ratón. Seguramente debe haber alguna manera de capturar los eventos de desplazamiento o al menos hacer que se propaguen hasta el nivel del documento.

Gracias de antemano.

import { Directive, HostListener, Output, EventEmitter, ElementRef, Inject } from '@angular/core';


@Directive({
  selector: '[scrollListener]'
})
export class ScrollListenerDirective {
  previousScrollContainerScrollTop = 0;

  @Output() scroll = new EventEmitter<boolean>();

  constructor(private el: ElementRef) { }

  @HostListener('document:scroll', ['$event'])
  onListenerTriggered(): void {
    let currentScrollTop: number;
    let elementHeight: number;

    const scrollContainerScrollTop = this.el.nativeElement.offsetParent.scrollTop;
    elementHeight = this.el.nativeElement.offsetHeight;
    currentScrollTop = scrollContainerScrollTop;

    if (this.previousScrollContainerScrollTop < currentScrollTop && scrollContainerScrollTop > elementHeight + elementHeight) {
      this.scroll.emit(true);
    } else if (this.previousScrollContainerScrollTop > currentScrollTop && !(scrollContainerScrollTop <= elementHeight)) {
      this.scroll.emit(false);
    }

    this.previousScrollContainerScrollTop = currentScrollTop;

  }
}
<ng-container *ngIf="!hide && breadcrumbs && breadcrumbs.length > 1">
  <div scrollListener (scroll)="trackScroll($event)" class="breadcrumb-container" [class.scroll-up]="breadcrumbBarHidden" data-id="breadcrumb-navigation">
    <div data-id="back-button" class="back-button" (click)="onBackClick()">
      <div class="arrow-container">
        <mat-icon svgIcon="left"></mat-icon>
      </div>
      <span>Back</span>
    </div>
    <div class="crumb-container">
      <ng-container *ngFor="let crumb of breadcrumbs; index as i;">
        <div class="crumb">
          <div class="crumb-label" (click)="onBreadcrumClick(crumb)" [attr.data-id]="'crumb-' + i" [title]="crumb.label">{{crumb.label}}</div>
          <div class="chevron-container">
            <mat-icon svgIcon="chevron-right"></mat-icon>
          </div>
        </div>
      </ng-container>
    </div>
  </div>
</ng-container>

Respuesta #1

Lo averigüé. Terminé usando un listener de eventos de Javascript puro que activa mi función usando el emisor de eventos. Funcionó muy bien.

import { Directive, HostListener, Output, EventEmitter, ElementRef, Inject } from '@angular/core';


@Directive({
  selector: '[ScrollListener]'
})
export class ScrollListenerDirective {
  previousScrollContainerScrollTop = 0;

  @Output() scroll = new EventEmitter<boolean>();

  constructor(private el: ElementRef) {
    document.addEventListener('scroll', (event) => {
      this.onListenerTriggered(event);
    }, true);  // This is so we can use a UseCapture event listener.  Document scrolls do not bubble back up to the document level
  }

  onListenerTriggered(event): void {
    let currentScrollTop: number;
    let elementHeight: number;

    const scrollContainerScrollTop = this.el.nativeElement.offsetParent.scrollTop;
    elementHeight = this.el.nativeElement.offsetHeight;
    currentScrollTop = scrollContainerScrollTop;

    if (this.previousScrollContainerScrollTop < currentScrollTop && scrollContainerScrollTop > elementHeight + elementHeight) {
      this.scroll.emit(true);
    } else if (this.previousScrollContainerScrollTop > currentScrollTop && !(scrollContainerScrollTop <= elementHeight)) {
      this.scroll.emit(false);
    }

    this.previousScrollContainerScrollTop = currentScrollTop;

  }
}