如何获取滚动事件?

103

我需要在我的Angular 2应用程序中从一个带有overflow: scroll的div获取滚动事件。

看起来onscroll事件在Angular 2上无法正常工作。

我该如何实现这一点?


1
很多时候,您可以应用事件,例如onclick、onsubmit等。在Angular中,相当于通常只需从事件名称中删除“on”并添加()。例如(click)、(submit)等... :) - AT82
https://dev59.com/FLDla4cB1Zd3GeqP7mr5#75073987 - MAQ
8个回答

215
// @HostListener('scroll', ['$event']) // for scroll events of the current element
@HostListener('window:scroll', ['$event']) // for window scroll events
onScroll(event) {
  ...
}
或者
<div (scroll)="onScroll($event)"></div>

(scroll)="onScroll($event)" 很好地解决了我的问题。您知道是否有一个等效的绑定来监听元素的 onresize 事件吗? - Natanael
@GünterZöchbauer 你能否从事件中判断滚动的方向?也就是说,不需要检查和比较div的scrollY值与之前的值? - Katana24
https://developer.mozilla.org/zh-CN/docs/Web/Events/scroll 列出了事件提供的信息,其中并不包括有关方向的任何内容。 - Günter Zöchbauer
然而,HostListener是最好的选择吗?正如在这里提到的那样,HostListener会导致过多的变更检测。https://github.com/angular/angular/issues/16986 我确实看到使用HostListener会对性能产生影响,难道没有其他方法吗? - jcdsr
@jcdsr,不深入研究这个问题,并且我回答的很久以前了 - 通常情况下,您希望在事件之后进行更改检测,主要问题是滚动事件(或调整大小事件)的高频率。如果您使用rxjs,则可以使用速率限制和其他一些功能,而这些功能无法使用@HostListener() - Günter Zöchbauer
显示剩余6条评论

44

您可以使用@HostListener装饰器。适用于Angular 4及以上版本。

import { HostListener } from '@angular/core';

@HostListener("window:scroll", []) onWindowScroll() {
    // do some stuff here when the window is scrolled
    const verticalOffset = window.pageYOffset 
          || document.documentElement.scrollTop 
          || document.body.scrollTop || 0;
}

将常量设置为一组或语句是如何工作的? - Sir Papilonius
1
@SirPapilonius 第一个非假值(0或未定义等)将被用作该值。 - Stack Underflow
2
@StackUnderflow 哦,当然,那很有道理。我没想到。聪明! - Sir Papilonius
HostListener会导致过多的变更检测,正如在这里提到的那样。github.com/angular/angular/issues/16986 我确实发现使用hostlistener会对性能产生影响,难道没有其他方法吗? - jcdsr

36

对于 Angular 4,有效的解决方案是在组件内部执行以下操作:

@HostListener('window:scroll', ['$event']) onScrollEvent($event){
  console.log($event);
  console.log("scrolling");
} 

1
你好,我刚刚在我的组件中使用了您的代码,然后我添加了"(scroll)="onScrollEvent($event)"但它似乎不起作用。 - gtzinos
1
(scroll)="onScrollEvent($event)" 不起作用,但我的代码目前在 Angular 4.0.0 版本上运行良好,它可以触发滚动事件。 - Thomas Ducrot
这个很好用,不需要在 HTML 中添加 (scroll)="onScrollEvent($event) - blueprintchris
1
HostListener会导致过多的变更检测,正如在这里提到的那样。https://github.com/angular/angular/issues/16986 我确实看到使用hostlistener会有性能影响,难道没有其他方法吗? - jcdsr

19

监听 window:scroll 事件以便捕获窗口/文档级别的滚动,使用元素的 scroll 事件来捕获元素级别的滚动。

window:scroll

@HostListener('window:scroll', ['$event'])
onWindowScroll($event) {

}
或者
<div (window:scroll)="onWindowScroll($event)">

滚动

@HostListener('scroll', ['$event'])
onElementScroll($event) {

}
或者
<div (scroll)="onElementScroll($event)">

@HostListener('scroll', ['$event'])只有当宿主元素本身可滚动时才起作用。

示例


1
请记得添加导入: import { HostListener } from '@angular/core'; - Mike D3ViD Tyson
<div (window:scroll)="onWindowScroll($event)">,清晰易懂,对我非常有效。谢谢伙计! - Nick09
HostListener导致过多的变更检测,就像这里提到的一样。https://github.com/angular/angular/issues/16986 我确实发现使用hostlistener会影响性能,难道没有其他方法吗? - jcdsr

12

替代@HostListener和滚动输出元素,我建议使用RxJS中的fromEvent,因为您可以将其与filter()distinctUntilChanges()链接,可以轻松跳过可能重复的事件(和变化检测)。

这里是一个简单的示例:

// {static: true} can be omitted if you don't need this element/listener in ngOnInit
@ViewChild('elementId', {static: true}) el: ElementRef;

// ...

fromEvent(this.el.nativeElement, 'scroll')
  .pipe(
    // Is elementId scrolled for more than 50 from top?
    map((e: Event) => (e.srcElement as Element).scrollTop > 50),
    // Dispatch change only if result from map above is different from previous result
    distinctUntilChanged());

4
为捕获滚动事件并查看哪个滚动事件被调用,您需要使用主机监听器来观察滚动行为,然后将其检测到在主机监听器下方的函数中。
currentPosition = window.pageYOffset;
@HostListener('window:scroll', ['$event.target']) // for window scroll events
scroll(e) {
  let scroll = e.scrollingElement.scrollTop;
  console.log("this is the scroll position", scroll)
  if (scroll > this.currentPosition) {
    console.log("scrollDown");
  } else {
    console.log("scrollUp");
  }
  this.currentPosition = scroll;
}

1

请查看此网址中提到的多个示例。

我会推荐方法3,

https://itnext.io/4-ways-to-listen-to-page-scrolling-for-dynamic-ui-in-angular-ft-rxjs-5a83f91ee487

    @Component({
        selector       : 'ngx-root',
        templateUrl    : './app.component.html',
        styleUrls      : [ './app.component.scss' ],
        changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class AppComponent implements OnDestroy {
        
        destroy = new Subject();
        
            destroy$ = this.destroy.asObservable();
        
        constructor() {
            fromEvent(window, 'scroll').pipe(takeUntil(this.destroy$))
                .subscribe((e: Event) => console.log(this.getYPosition(e)));
            }
        
            getYPosition(): number {
            return (e.target as Element).scrollTop;
            }

        ngOnDestroy(): void {
            this.destroy.next();
        }
        
    }

然而,方法4并不差。

0

滚轮事件

尝试使用(在我的情况下,scroll事件不起作用)

<div (wheel)="onScroll($event)"></div>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接