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

IT技术 javascript angular typescript
2021-03-02 01:38:07

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

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

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

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

5个回答

可能你document不是在滚动,而是在div里面。window如果从 调用滚动事件,它只会冒泡到document此外,如果您从中捕获事件document并调用类似的方法stopPropagation,您将不会在window.

如果要捕获应用程序中的所有滚动事件,这些事件也来自可滚动的小容器,则必须使用默认addEventListener方法useCaptureset to true

当它下降到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
    };

}

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

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

在我向您展示如何做到这一点之前,您可以想到另一个性能特性。为了防止运行更改检测(DoCheck每次在区域内发生异步事件时都会调用它。就像事件触发一样),您应该在区域外运行您的事件侦听器,并且仅在确实需要时才输入它。Soo,让我们把所有这些东西结合起来:

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();
           });
        }
    };   
}
这正是我在回答中所说的:)
2021-04-28 01:38:07
@mkb 它并没有过时。这是非常有用的。它只是不支持使用本机事件选项。
2021-04-30 01:38:07
非常感谢,这似乎按预期工作!我认为问题确实是文档本身在我的情况下没有滚动。
2021-05-06 01:38:07
第二个代码块中侦听器内的内容是否与拦截事件相关?此外,如何处理滚动的方向?
2021-05-10 01:38:07
@Robert我已经用其他信息更新了我的答案:)
2021-05-13 01:38:07

如果您碰巧使用 Angular Material,您可以这样做:

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

在 Ts:

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

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

在模板中:

<div cdkScrollable>
  <div *ngFor="let one of manyToScrollThru">
    {{one}}
  </div>
</div>

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

我明白你想提出什么建议。但该measureScrollOffset 方法在scrollDispatcher. 它仅在cdk-scrollable指令上受支持不过,我可能会从你的建议中得到一些东西。谢谢。
2021-04-17 01:38:07
在这种情况下,如何获得滚动方向和偏移量。发出的事件似乎是空白的。
2021-04-21 01:38:07
非常感谢,这对我来说似乎按预期工作!
2021-04-29 01:38:07
@SaurabhTiwari -见:measureScrollOffsethttps://material.angular.io/cdk/scrolling/api可以pairWise在 observable 中与 Rxjs 结合以查看您正在滚动的方向。可能有更直接的方法,但这是一个解决方案。
2021-05-01 01:38:07
@SaurabhTiwari measureScrollOffset 是 lambda 值中可用的值: subscribe(x => console.log(x.measureScrolloffset('top'))); 举个例子。
2021-05-17 01:38:07

我还不能发表评论。@PierreDuc 你的答案是正确的,除非@Robert 说文档没有滚动。我稍微修改了您的答案以使用侦听器发送的事件,然后监视源元素。

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

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

  scrollEvent = (event: any): void => {
    const n = event.srcElement.scrollingElement.scrollTop;
  }
感谢您包含event参数。没有它,接受的答案有点没用
2021-04-20 01:38:07
你知道我如何从函数内部访问组件的范围吗?
2021-05-07 01:38:07

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

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

你的.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}
  }

你的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;
}

以防万一我想在一个无法滚动的元素上捕获滚轮动作,因为它没有滚动条......

所以,我需要的是这个:

@HostListener('mousewheel', ['$event']) 
onMousewheel(event) {
     console.log(event)
}