如何在Angular 4中处理窗口滚动事件?

Rob*_*ert 30 scroll typescript angular

我似乎无法捕获Window滚动事件.在几个网站上,我发现代码与此类似:

@HostListener("window:scroll", [])
onWindowScroll() {
  console.log("Scrolling!");
}
Run Code Online (Sandbox Code Playgroud)

这些片段通常来自版本2.这在Angular 4.2.2中似乎不起作用(不再?).如果我将"window:scroll"替换为"window:touchmove",那么touchmove事件处理得很好.

有谁知道我错过了什么?非常感谢你!

Pie*_*Duc 71

可能你document不是滚动,而是div内部滚动.滚动事件只会冒泡到window它的调用document.此外,如果你从中捕获事件document并调用类似的东西stopPropagation,你将不会收到该事件window.

如果要捕获应用程序中的所有滚动事件(也可能是来自微小的可滚动容器),则必须使用设置为的默认addEventListener方法.useCapturetrue

这将触发事件DOM,而不是泡沫阶段.不幸的是,坦率地讲,一个很大的错过,angular并没有提供传递事件监听器选项的选项,所以你必须使用addEventListener:

export class WindowScrollDirective {

    ngOnInit() {
        window.addEventListener('scroll', this.scroll, true); //third parameter
    }

    ngOnDestroy() {
        window.removeEventListener('scroll', this.scroll, true);
    }

    scroll = (event): void => {
      //handle your scroll here
      //notice the 'odd' function assignment to a class field
      //this is used to be able to remove the event listener
    };

}
Run Code Online (Sandbox Code Playgroud)

现在这并不是全部,因为所有主流浏览器(IE和Edge除外)都实现了新addEventListener规范,这使得将对象作为第三个参数传递成为可能.

使用此对象,您可以将事件侦听器标记为passive.对于会激活大量时间的事件,这是一个建议的事情,这会干扰UI性能,如滚动事件.要实现此功能,您应首先检查当前浏览器是否支持此功能.在mozilla.org上,他们发布了一种方法passiveSupported,您可以使用该方法检查浏览器支持.但是,当您确定不打算使用时,您只能使用它event.preventDefault()

在我向您展示如何执行此操作之前,您可以想到另一个性能功能.为了防止变化检测运行(DoCheck每次在区域内发生异步时都会调用.就像事件触发一样),您应该在区域外运行事件监听器,并且只在真正需要时输入它.所以,让我们结合所有这些:

export class WindowScrollDirective {

    private eventOptions: boolean|{capture?: boolean, passive?: boolean};

    constructor(private ngZone: NgZone) {}

    ngOnInit() {            
        if (passiveSupported()) { //use the implementation on mozilla
            this._eventOptions = {
                capture: true,
                passive: true
            };
        } else {
            this.eventOptions = true;
        }
        this.ngZone.runOutsideAngular(() => {
            window.addEventListener('scroll', this.scroll, <any>this.eventOptions);
        });
    }

    ngOnDestroy() {
        window.removeEventListener('scroll', this.scroll, <any>this.eventOptions);
        //unfortunately the compiler doesn't know yet about this object, so cast to any
    }

    scroll = (): void => {
        if (somethingMajorHasHappenedTimeToTellAngular) {
           this.ngZone.run(() => {
               this.tellAngular();
           });
        }
    };   
}
Run Code Online (Sandbox Code Playgroud)


ttu*_*tes 15

如果您恰巧使用Angular Material,则可以执行以下操作:

import { ScrollDispatchModule } from '@angular/cdk/scrolling';
Run Code Online (Sandbox Code Playgroud)

在Ts:

import { ScrollDispatcher } from '@angular/cdk/scrolling';

  constructor(private scrollDispatcher: ScrollDispatcher) {    
    this.scrollDispatcher.scrolled().subscribe(x => console.log('I am scrolling'));
  }
Run Code Online (Sandbox Code Playgroud)

在模板中:

<div cdkScrollable>
  <div *ngFor="let one of manyToScrollThru">
    {{one}}
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

参考:https : //material.angular.io/cdk/scrolling/overview

  • 在这种情况下如何获得滚动方向和偏移量。发出的事件似乎是空白的。 (2认同)

Jas*_*son 6

I am not allowed to comment yet. @PierreDuc your answer is spot on, except as @Robert said the document does not scroll. I modified your answer a little bit to use the event sent by the listener and then monitor the source element.

 ngOnInit() {
    window.addEventListener('scroll', this.scrollEvent, true);
  }

  ngOnDestroy() {
    window.removeEventListener('scroll', this.scrollEvent, true);
  }

  scrollEvent = (event: any): void => {
    const number = event.srcElement.scrollTop;
  }
Run Code Online (Sandbox Code Playgroud)


jdc*_*ker 6

在 Angular 8 中,实现此代码,在我的情况下,它可以正确使用滚动更改导航栏的颜色...您的模板:

<div class="level" (scroll)="scrolling($event)"  [ngClass]="{'level-trans': scroll}">
<!-- your template -->
</div>
Run Code Online (Sandbox Code Playgroud)

你的.ts

export class HomeNavbarComponent implements OnInit {

  scroll:boolean=false;
  constructor() { }

  ngOnInit() {
    window.addEventListener('scroll', this.scrolling, true)
  }
  scrolling=(s)=>{
    let sc = s.target.scrollingElement.scrollTop;
    console.log();
    if(sc >=100){this.scroll=true}
    else{this.scroll=false}
  }
Run Code Online (Sandbox Code Playgroud)

你的CSS

.level{
    width: 100%;
    height: 57px;
    box-shadow: 0 0 5px 0 rgba(0, 0,0,0.7);
    background: transparent;
    display: flex;
    position: fixed;
    top: 0;
    z-index: 5;
    transition: .8s all ease;
}
.level-trans{
    background: whitesmoke;
}
Run Code Online (Sandbox Code Playgroud)